Install
$ agentstack add skill-itsalt-nacl-nacl-init ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ● Filesystem access Used
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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:
- CLAUDE.md — project rules, skill routing, bug fix protocol
- 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:
- Project name — if not provided in the command
- Tech stack — frontend/backend/fullstack, main frameworks
- Brief description — 1-2 sentences about the project
For an existing project (--from)
- Read existing CLAUDE.md (if present) — do not overwrite, augment
- 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.
- Detect structure:
- Does docs/ exist? → SA artifacts created
- Does .tl/ exist? → TL workflow configured
- Are there BA artifacts? → BA analysis done
- 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):
# Does the excalidraw service block exist?
grep -q "^ excalidraw:" graph-infra/docker-compose.yml 2>/dev/null
If the service is present:
- Determine the container name. The container name follows the
${CONTAINER_PREFIX}-excalidrawpattern. Read it fromgraph-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 CONTAINERPREFIX is empty, fall back to reading the containername: line from inside the excalidraw:` service block.
- 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 ``
- Remove the
excalidraw:service block fromgraph-infra/docker-compose.yml. The block runs from theexcalidraw:line to the last line before the next top-level service key or end of theservices: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
grep -qE '^(EXCALIDRAW_PORT|EXCALIDRAW_ROOM_PORT)=' graph-infra/.env 2>/dev/null
If either line exists, remove them:
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:
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):
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:
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):
- 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
- 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 DEPLOYHOST, SSH references) | | CI/CD platform | Detect: .github/workflows/ exists → "github-actions"; otherwise → leave empty | | Deploy method | ciplatform 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:
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:
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
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):
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 (runsetup-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; runconnect-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)
# 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.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.