# Nacl Init

> |

- **Type:** Skill
- **Install:** `agentstack add skill-itsalt-nacl-nacl-init`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ITSalt](https://agentstack.voostack.com/s/itsalt)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ITSalt](https://github.com/ITSalt)
- **Source:** https://github.com/ITSalt/NaCl/tree/main/nacl-init

## Install

```sh
agentstack add skill-itsalt-nacl-nacl-init
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Project Initialization Skill

## Your Role

You are a **project setup specialist**. You create the foundational CLAUDE.md file that ensures every AI session follows the correct workflow: spec-first bug fixing, documentation discipline, and proper skill routing.

You create CLAUDE.md and config.yaml. Directory structures (docs/, .tl/) are created by their respective skills (sa-full, ba-full, nacl-tl-plan) when invoked.

## Key Principle: CLAUDE.md is the Law

```
CLAUDE.md is read by Claude at the START of every session.
Rules not in CLAUDE.md are rules that don't exist.
```

---

## CLAUDE.md Language Rule

**CLAUDE.md is ALWAYS written in English** — regardless of the user's language or existing content language. Rationale: Claude scores ~8% higher on instruction-following with English instructions (96% vs 88% accuracy in multilingual benchmarks, March 2026).

- All rules, protocols, and routing tables → **English**
- User-facing console output (reports, recommendations) → **user's language**
- If existing CLAUDE.md has non-English sections → preserve them, but add new sections in English. Recommend migrating existing sections to English in the report.

---

## Invocation

```
/nacl-init "My Project Name"         # new project (interactive)
/nacl-init --from=.                  # retroactive for existing project
/nacl-init --dry-run                 # show what would be added, no changes
```

---

## Workflow

**This skill produces TWO artifacts. Both are mandatory:**
1. **CLAUDE.md** — project rules, skill routing, bug fix protocol
2. **config.yaml** — project configuration (git, VPS, modules, YouGile, Docmost, deploy)

**Even if CLAUDE.md needs no changes, config.yaml must still be checked/created.**

### Step 1: GATHER INFO

#### For a new project (interactive)

Ask the user:

1. **Project name** — if not provided in the command
2. **Tech stack** — frontend/backend/fullstack, main frameworks
3. **Brief description** — 1-2 sentences about the project

#### For an existing project (--from)

1. Read existing CLAUDE.md (if present) — **do not overwrite, augment**
2. Detect stack from manifest files (ordered probe; record only what is actually found):

   | Ecosystem | Manifest files |
   |-----------|---------------|
   | Node.js | `package.json` (+ `package-lock.json` / `pnpm-lock.yaml` / `yarn.lock` for the package manager) |
   | Python | `pyproject.toml`, `requirements.txt`, `Pipfile` |
   | Go | `go.mod` |
   | Rust | `Cargo.toml` |
   | JVM | `pom.xml`, `build.gradle(.kts)` |
   | .NET | `*.csproj`, `*.sln` |
   | PHP | `composer.json` |
   | Ruby | `Gemfile` |

   If detection is ambiguous or finds nothing → ASK the user. **Never fill `{{TECH_STACK}}` or `modules.*.stack` from a built-in default** — NaCl does not prescribe a technology stack.
3. Detect structure:
   - Does docs/ exist? → SA artifacts created
   - Does .tl/ exist? → TL workflow configured
   - Are there BA artifacts? → BA analysis done
4. Detect conventions from code: TypeScript/JavaScript, test framework, linter

---

### Step 1.5: AUTO-MIGRATE LEGACY ARTEFACTS

**This step runs every time `/nacl-init` is invoked** — including on a fresh project, where it will find nothing to do and produce no output. It must complete before Step 2 (CLAUDE.md) and before any `docker compose up`.

The goal is to silently bring the project's `graph-infra/` directory and `config.yaml` into the current NaCl standard. The analyst never sees underlying shell commands — only a single summary line if any migration was performed.

#### How to run this step

Read the current state first, then act only where the conditions below are true. All checks are idempotent: if the condition is already false (artefact already absent, field already present, directory already exists), skip that action.

---

#### Migration check A — legacy `excalidraw` container and service

Read `graph-infra/docker-compose.yml` (if it exists):

```bash
# Does the excalidraw service block exist?
grep -q "^  excalidraw:" graph-infra/docker-compose.yml 2>/dev/null
```

If the service is present:

1. Determine the container name. The container name follows the `${CONTAINER_PREFIX}-excalidraw` pattern. Read it from `graph-infra/.env`:
   ```bash
   CONTAINER_PREFIX=$(grep '^CONTAINER_PREFIX=' graph-infra/.env 2>/dev/null | cut -d= -f2)
   EXCALIDRAW_CONTAINER="${CONTAINER_PREFIX}-excalidraw"
   ```
   If `.env` is unreadable or `CONTAINER_PREFIX` is empty, fall back to reading the `container_name:` line from inside the `excalidraw:` service block.

2. Stop and remove the container only if it exists:
   ```bash
   if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "$EXCALIDRAW_CONTAINER"; then
     docker stop "$EXCALIDRAW_CONTAINER"
     docker rm "$EXCALIDRAW_CONTAINER"
   fi
   ```

3. Remove the `excalidraw:` service block from `graph-infra/docker-compose.yml`. The block runs from the `  excalidraw:` line to the last line before the next top-level service key or end of the `services:` map. Use a Python one-liner to remove it precisely without touching the rest of the file:
   ```bash
   python3 - graph-infra/docker-compose.yml /dev/null
```

Container name pattern: `${CONTAINER_PREFIX}-excalidraw-room`.

The Python removal pattern:
```bash
text = re.sub(r'\n  excalidraw-room:\n(?:    [^\n]*\n)*', '\n', text)
```

Record: `excalidraw-room container stopped/removed, service block removed`.

---

#### Migration check C — legacy env vars in `graph-infra/.env`

```bash
grep -qE '^(EXCALIDRAW_PORT|EXCALIDRAW_ROOM_PORT)=' graph-infra/.env 2>/dev/null
```

If either line exists, remove them:
```bash
python3 - graph-infra/.env /dev/null
```

If true, compute the project id: take the project name argument (or the current directory basename if no argument was given), lowercase it, replace spaces with hyphens, strip characters not matching `[a-z0-9_-]`, truncate to 64 characters.

Inject `id:` as the first key inside the `project:` block, immediately after the `project:` line, preserving all other content and comments:
```bash
python3 - config.yaml "$PROJECT_ID" " added to config.yaml`.

---

#### Migration check F — missing `project.name` in `config.yaml`

```bash
[ -f config.yaml ] && ! grep -q '^\s*name:' config.yaml 2>/dev/null
```

If true, inject `name:` under the `project:` block (after `id:` if it was just added, otherwise as the first key):
```bash
python3 - config.yaml "$PROJECT_NAME" " added to config.yaml`.

---

#### Migration check G — missing `intake:` scoring block in `config.yaml`

```bash
# Does config.yaml exist and lack the intake: section?
[ -f config.yaml ] && ! grep -q '^intake:' config.yaml 2>/dev/null
```

If true, append the intake self-diagnosis scoring block with the built-in
defaults (used by `nacl-tl-intake` Step 2a.5 PROBE; semantics:
`nacl-tl-core/references/intake-scoring.md`). Append at the end of the file,
preserving all existing content and comments:

```bash
python3 - config.yaml = this -> auto-route on the leading hypothesis
  high_confidence: 0.9        # score >= this -> HIGH confidence (no tracked alternative)
  scores:                     # rubric row values (verdict pattern -> score)
    leader_confirmed_all_refuted: 0.95
    leader_confirmed_some_inconclusive: 0.8
    leader_indirect_all_refuted: 0.75
    leader_indirect_inconclusive: 0.55
    contradictory: 0.4
    all_inconclusive: 0.2
"""
if not text.endswith("\n"):
    text += "\n"
open(path, 'w').write(text + block)
EOF
```

This check is **add-only**: it never runs when an `intake:` section already
exists, so user-tuned values are never overwritten (same rule as Step 2b
"fill empty fields only").

Record: `intake scoring defaults added to config.yaml`.

---

#### Migration summary output

After all checks have run:

- If **nothing was migrated** (all conditions were false): produce no output. Continue silently.
- If **any migration was performed**: print exactly one line:

  ```
  Migrated existing project: .
  ```

  Example:
  ```
  Migrated existing project: removed legacy excalidraw services (2 containers stopped), created graph-infra/boards/, added project.id="my-project" to config.yaml.
  ```

  Use generic placeholders in the skill text (``, ``). In actual execution, substitute real values.

This single line is the **only migration output** the analyst sees. Do not print the underlying commands, container IDs, or intermediate steps.

---

### Step 2: CREATE CLAUDE.md

Use the template from `nacl-tl-core/templates/claude-md-template.md` as the base.

Fill placeholders:
- `{{PROJECT_NAME}}` → project name
- `{{TECH_STACK}}` → detected stack
- `{{PROJECT_DESCRIPTION}}` → description
- `{{ARCHITECTURE_SECTION}}` → leave placeholder or fill from detected conventions
- `{{DEPLOYMENT_SECTION}}` → leave placeholder or fill from deploy/ / Dockerfile

#### For --from (retroactive mode):

1. If CLAUDE.md exists:
   - Read it
   - Identify which mandatory sections are MISSING
   - Check for **section overlap**: if the project already covers a topic under a different heading (e.g., "Деплой и миграции" covers Deployment), do NOT duplicate — skip that section
   - Add only genuinely MISSING sections, **in English**
   - Do NOT remove or modify existing sections

2. If CLAUDE.md does not exist:
   - Create full file from template, in English
   - Fill from detected conventions

**For --dry-run:** Show what sections would be added, do not write.

**IMPORTANT: Do NOT stop here. Proceed to Step 2b regardless of CLAUDE.md result.**

### Step 2b: CREATE or VERIFY config.yaml (MANDATORY — always runs)

**Always check:** Does `config.yaml` already exist at project root?

```
IF config.yaml EXISTS:
  → Read it
  → Check for empty/placeholder values that can be filled from detected sources
  → Report: "config.yaml exists, N fields populated, M fields still empty"
  → Do NOT overwrite — only fill empty fields if data can be detected

IF config.yaml DOES NOT EXIST:
  → Create from template nacl-tl-core/templates/config-yaml-template.yaml
  → Auto-detect and fill what's possible (see below)
  → Report: "config.yaml created with N fields auto-detected"
```

**This step runs independently from Step 2 (CLAUDE.md).** Even if CLAUDE.md needs no changes, config.yaml may still need to be created.

**Auto-detection sources (for --from mode):**

| Data | Where to look |
|------|--------------|
| Project name & stack | CLAUDE.md, package.json |
| Module paths | Subdirectories with package.json (frontend/, backend/, etc.) |
| Build/test commands | package.json → scripts.build, scripts.test in each module |
| Git strategy | Branch history: if `feature/*` branches exist → `"feature-branch"`, else → `"direct"` |
| Git main branch | `git symbolic-ref refs/remotes/origin/HEAD` or default `"main"` |
| Git branch prefix | Default `"feature/"` |
| VPS IPs & SSH | docs/DEPLOY.md, `.github/workflows/*.yml` (look for DEPLOY_HOST, SSH references) |
| CI/CD platform | Detect: `.github/workflows/` exists → "github-actions"; otherwise → leave empty |
| Deploy method | ci_platform value from above |
| Deploy URLs | docs/DEPLOY.md, `.github/workflows/` (look for domain names, URLs) |
| Health endpoint | grep for "/health" or "/api/health" in backend routes |
| Docmost space IDs | docs/.docmost-sync.json → spaceId, rootPageId |
| Docmost API URL | .mcp.json or env vars |
| YouGile config | .mcp.json → yougile server env |
| Ports | package.json scripts, .env.example, docker-compose*.yml |
| Credentials | .env.example (field names only, not values — leave empty with comment) |
| PM2 config | deploy/ecosystem.config.* files |

**Interactive mode:** Ask the user for anything that couldn't be auto-detected.

**Fill what can be detected, leave placeholders for the rest.** Every empty field should have a comment explaining what it needs.

#### YouGile Board Setup

**First check:** Is YouGile already configured?
```
IF config.yaml EXISTS AND yougile.board_id is NOT empty:
  → Report: "YouGile already configured (board: [id]). Skipping setup."
  → Skip to Step 3.

IF config.yaml EXISTS AND yougile section is empty/missing:
  → Ask: "Do you want to set up YouGile task tracking?"

IF config.yaml was just created (new):
  → Ask: "Do you want to set up YouGile task tracking?"
```

If no → leave yougile section empty in config.yaml. Skip to Step 3.

If yes → follow this sequence:

**Step A: Get API key**

Check if YouGile MCP is already configured globally:
```bash
grep -r "YOUGILE_API_KEY" ~/.claude.json 2>/dev/null
```
If found → extract the key. If not → ask the user:
```
"Provide your YouGile API key (Settings → API in YouGile):"
```

**Step B: List projects — let user pick**

Run:
```bash
curl -s -H "Authorization: Bearer $YOUGILE_API_KEY" \
  https://yougile.com/api-v2/projects | \
  python3 -c "import sys,json; [print(f'{p[\"id\"]}  {p[\"title\"]}') for p in json.loads(sys.stdin.read()).get('content',[])]"
```

Present the list to user:
```
Your YouGile projects:
  1. f775a373-...  Project Alpha
  2. a1b2c3d4-...  Project Beta

Which project? (number or ID)
If the project doesn't exist yet:
  → Go to YouGile, create a new project, then tell me.
```

The user either picks an existing project or creates one in YouGile and comes back with the ID.

**Step C: Run setup script**

```bash
YOUGILE_API_KEY="$KEY" \
node "$(cd -P "$HOME/.claude/skills/yougile-setup" 2>/dev/null && pwd)/dist/index.js" \
  --project-name "$PROJECT_NAME" \
  --project-id "$PROJECT_ID" \
  --modules "$MODULES" \
  --config-path ./config.yaml
```

**Step D: Present result**

Parse JSON output. Report:
- Board created: name + ID
- 9 columns created with IDs
- 3 stickers created (Type, Module, Source)
- config.yaml updated with all IDs

If any errors → show them, suggest manual fix.

---

### Step 2c: GRAPH INFRASTRUCTURE (optional)

**Goal:** Set up the project's Neo4j graph for BA/SA skills — either a LOCAL per-project Docker
container (default), or a connection to a SHARED graph on a VPS (multi-user). This step is an
**orchestrator**: it resolves the mode with a tool and dispatches to the matching tool, then reads
a deterministic `NACL_GRAPH_RESULT:` gate. It does not improvise graph logic in prose.

```
Ask: "Will this project use a Neo4j graph for BA/SA specifications? (nacl-ba-*, nacl-sa-* skills)"
If no → skip to Step 3.
If yes → continue with 2c.0.
```

#### 2c.0 Resolve the graph mode (local | create | connect)

Run the resolver (it reads any `--scale` flag plus a committed `graph.mode` in config.yaml):

```bash
REPO_ROOT="$(cd -P "$HOME/.claude/skills/nacl-init" 2>/dev/null && cd .. && pwd)"
node "$REPO_ROOT/nacl-tl-core/scripts/resolve-graph-mode.mjs" --project-root "$(pwd)" ${SCALE:+--scale "$SCALE"}
# → NACL_GRAPH_MODE: mode=local|create|connect reason="…"
```

Dispatch on `mode`:
- **`local`** → today's flow: 2c.1 (detect ports) → 2c.2 (write config) → 2c.3 (run `setup-graph`) → 2c.4 (gate).
- **`create`** → a fresh SHARED project: skip 2c.1; gather the remote endpoint (host, gateway port,
  sidecar port, project_scope) and run `create-remote` (2c-remote below). The VPS must already be
  provisioned (`graph-infra/vps/provision-vps.sh`).
- **`connect`** → JOIN an existing shared project: skip 2c.1–2c.3 entirely; run `connect-remote`
  (2c-remote below). **No Docker, no schema, no graph writes.** A teammate who cloned a repo whose
  committed `config.yaml` has `graph.mode: remote` lands here automatically — they must NOT re-init.

For `create`/`connect`, ensure the developer's mTLS tunnel is up first (one-time per machine):
`graph-infra/scripts/install-sidecar.sh --project-scope  --host  --gateway-port  --sidecar-port  --cert … --key … --cacert … --start`. The `--uri` passed below is the LOCAL
sidecar socket (e.g. `bolt://localhost:3700`); skills/.mcp.json stay on localhost.

#### 2c-remote: connect / create dispatch (deterministic tools)

```bash
# connect (join existing) — read-only verify gate; FAILS LOUD if the project marker is absent
sh "$REPO_ROOT

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ITSalt](https://github.com/ITSalt)
- **Source:** [ITSalt/NaCl](https://github.com/ITSalt/NaCl)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-itsalt-nacl-nacl-init
- Seller: https://agentstack.voostack.com/s/itsalt
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
