@antoneeo/agentic-sdlc-skill 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Tutte le modifiche significative a questa skill saranno documentate in questo file.
4
4
 
5
+ ## [1.7.0] - 2026-07-02 (Phases 0-1 of the evolution roadmap)
6
+ ### Changed (breaking-soft)
7
+ - **English is now the canonical language of the skill**: `SKILL.md`, `templates.md`, `ENFORCEMENT.md`, validator messages, generated indexes and the project protocol are in English. New ANALYSIS documents use English frontmatter keys (`status`, `level`, `start_date`, `end_date`) and English section headings. **Existing projects keep working**: the validator silently accepts the deprecated Italian keys (`stato`, `livello`, `data_inizio`, `data_fine`) and Italian headings.
8
+ - The generated project protocol (`CLAUDE.md`/`GEMINI.md`/`AGENTS.md`/`.cursorrules`) is now a **thin pointer** to the skill (triage summary + where things live + closure gate) instead of a condensed copy of its rules, which had drifted from `SKILL.md`.
9
+ - `init.js` now seeds `ai_docs/` from `templates.md` (single template source) instead of inline boilerplates; it creates `ai_docs/reference/` and the curated `ai_docs/README.md`, no longer seeds the generated `features_history.md`, and generates `INDEX.md` via the validator when Python is available, so the very first `sdlc_check.py check` on a fresh project is CLEAN.
10
+ - Client detection unified between `init.js` and `postinstall.js` (`scripts/lib.js`): CLI on PATH **or** config home present (covers Claude Desktop with integrated Claude Code).
11
+
12
+ ### Added
13
+ - `sdlc_check.py validate --strict` / `check --strict`: warnings and a missing `ai_docs/` become failures (for CI).
14
+ - **Coexistence with devPNT (Phase 1, the Hybrid seam)**: new SKILL.md section with the ownership matrix (who is master per artifact in Standalone vs Hybrid), the triage equivalence table (one significance threshold, two vocabularies), the ANALYSIS↔plan-node state mapping and the shadow discipline (`SHADOW_[doc_key]_vX.Y.md`, exported BEFORE implementation; never saved under an `ANALYSIS_*` name).
15
+ - `sdlc_check.py --hybrid` (explicit, never auto-detected) on `check`/`stale` (audit-plan staleness delegated to devPNT/KL) and on `gate` (an approved E-TDD shadow in `solutions/` authorizes writes on protected paths).
16
+ - devPNT MCP doctrine (`mcp_system_prompt.md`, devPNT repo) slimmed accordingly: process (triage, phases, lifecycle, closure) deferred to the skill; `ai_docs/` layout aligned (adds `vision/` and `reference/`); shadow naming and shadow-before-implementation rule; checklist items for methodology, shadow export and hybrid closure gate.
17
+ - Evolution roadmap for v1.7.0 in `ai_docs/vision/roadmap_evoluzione_agenti.md` (subagent execution, operative guides + agent-level knowledge base, devPNT seam, open-core positioning).
18
+
19
+ ### Fixed
20
+ - Shadow detection is structural (filename `SHADOW_*` or `<!-- SHADOW` marker on the first line): an ANALYSIS merely *mentioning* shadows is no longer silently skipped from index and validation.
21
+ - `mark`/`index` fail fast when `ai_docs/` is missing instead of silently creating a second documentation root in the wrong directory.
22
+
23
+ ### Removed
24
+ - Divergent `agentic-sdlc-v2/` copy (integrated in 1.5.0, the leftover risked edits on the wrong files).
25
+ - `references/*_template.md` (duplicated `templates.md` and had already diverged).
26
+
5
27
  ## [1.6.0] - 2026-06-15
6
28
  ### Added
7
29
  - **Manifest generato dei documenti canonici** (`ai_docs/INDEX.md`): `sdlc_check.py index` ora produce, oltre a `features_history.md`, un indice completo di tutti i doc in `vision/`, `reference/`, `architecture/`, `functional/`, `strategic/`, con descrizione e stato letti dall'header. Si rigenera, quindi non drifta.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-sdlc-skill",
3
- "version": "1.6.0",
4
- "description": "Protocollo SDLC Documentation-First con triage, Vision governance e integrazione opzionale devPNT.",
3
+ "version": "1.7.0",
4
+ "description": "Documentation-First SDLC protocol with triage, Vision governance and optional devPNT integration.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@antoneeo/agentic-sdlc-skill",
3
- "version": "1.6.0",
4
- "description": "Protocollo SDLC Documentation-First per Claude Code, Gemini CLI e Codex con triage, Vision governance, support file installati e integrazione opzionale devPNT.",
3
+ "version": "1.7.0",
4
+ "description": "Documentation-First SDLC protocol for Claude Code, Gemini CLI and Codex with risk triage, Vision governance, installed support files and optional devPNT integration.",
5
5
  "keywords": [
6
6
  "claude-code",
7
7
  "claude-skill",
@@ -30,10 +30,8 @@
30
30
  "skills/agentic-sdlc-skill/ENFORCEMENT.md",
31
31
  "skills/agentic-sdlc-skill/scripts/sdlc_check.py",
32
32
  "gemini-extension.json",
33
- "references",
34
33
  "README.md",
35
34
  "CHANGELOG.md",
36
- "assets",
37
35
  "scripts"
38
36
  ]
39
37
  }
package/scripts/init.js CHANGED
@@ -1,204 +1,153 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { execSync } = require('child_process');
6
+ const { SKILL_SOURCE, CLIENTS, clientDetected, loadTemplates, templateFor } = require('./lib');
6
7
 
7
8
  const cwd = process.cwd();
8
9
 
9
- // Helper for command checking
10
- function checkCommand(cmd) {
11
- try {
12
- execSync(`${cmd} --version`, { stdio: 'ignore' });
13
- return true;
14
- } catch (e) {
15
- return false;
16
- }
10
+ // 1. Directory layout (canonical ai_docs structure, including reference/)
11
+ const directories = [
12
+ 'ai_docs',
13
+ 'ai_docs/vision',
14
+ 'ai_docs/vision/features',
15
+ 'ai_docs/reference',
16
+ 'ai_docs/strategic',
17
+ 'ai_docs/audit',
18
+ 'ai_docs/solutions',
19
+ ].map((d) => path.join(cwd, d));
20
+
21
+ // 2. Project protocol (thin pointer — the operating contract is the skill).
22
+ // Deliberately short: duplicating the skill's rules here made them drift.
23
+ const protocolContent = `# Agentic SDLC — Project Protocol (pointer)
24
+
25
+ This project follows the Agentic SDLC Documentation-First process. The full
26
+ operating contract is the \`agentic-sdlc\` skill (installed in your agent's
27
+ skills directory); this file is only the minimal always-on pointer.
28
+
29
+ ## Rule Zero — Triage every request
30
+ - L1 Trivial: ~10 lines, 1-2 files, no API/dependency/behavior change. Implement + run existing tests; no docs.
31
+ - L2 Small: clear root cause, at most 3 files, low risk. Mini-analysis in the reply; tests mandatory.
32
+ - L3 Significant: >3 files, APIs/contracts, new dependency, user-visible behavior, security-sensitive area, or architectural change. Full workflow via the skill: Vision Gate -> ANALYSIS -> plan -> implement -> test -> closure.
33
+ - Spike: time-boxed exploration; outcome in \`ai_docs/solutions/SPIKE_[topic].md\`; reclassify for production.
34
+ - Security-sensitive areas (external input parsing, authN/authZ, crypto, network, personal data, filesystem) are never L1.
35
+ - When in doubt, pick the higher level. Declare the chosen level when starting.
36
+
37
+ ## Where things live
38
+ - Vision (gate for L3): \`ai_docs/vision/\` — \`Status: DRAFT\` informs, \`Status: APPROVED\` binds.
39
+ - Feature analyses: \`ai_docs/solutions/ANALYSIS_[feature].md\` (frontmatter = feature state).
40
+ - Must-reads: \`ai_docs/README.md\`; full generated manifest: \`ai_docs/INDEX.md\`.
41
+ - If devPNT is available for this project, its M-VISION / plans / governed artifacts take over (Hybrid mode — see the skill).
42
+
43
+ ## Closure gate
44
+ Docs travel in the same commit/PR as the code they describe. If the project
45
+ adopts the validator, \`python <skill_dir>/scripts/sdlc_check.py check\` must be
46
+ CLEAN before declaring work done.
47
+
48
+ If the agentic-sdlc skill is not available in this client, ask the user to install it:
49
+ \`npm i -g @antoneeo/agentic-sdlc-skill && agentic-sdlc-install-skill\`
50
+ `;
51
+
52
+ console.log('🚀 Initializing Agentic SDLC workflow...');
53
+
54
+ // 3. Load templates from the single source (skill's templates.md)
55
+ let sections;
56
+ try {
57
+ sections = loadTemplates();
58
+ } catch (err) {
59
+ console.error(`❌ Cannot load templates: ${err.message}`);
60
+ process.exit(1);
61
+ }
62
+
63
+ // audit_plan: the template block carries illustrative rows; a fresh project
64
+ // starts from a single root PENDING row instead.
65
+ function initialAuditPlan() {
66
+ const tpl = templateFor(sections, 'audit_plan.md');
67
+ const lines = tpl.split('\n');
68
+ const sepIdx = lines.findIndex((l) => /^\|[-\s|:]+\|$/.test(l.trim()));
69
+ if (sepIdx === -1) return tpl; // unexpected shape: keep the template as-is
70
+ return lines.slice(0, sepIdx + 1).join('\n') + '\n| / | PENDING | - | Initial analysis |\n';
71
+ }
72
+
73
+ let seedFiles;
74
+ try {
75
+ seedFiles = [
76
+ ['ai_docs/README.md', templateFor(sections, 'ai_docs/README.md')],
77
+ ['ai_docs/vision/project_vision.md', templateFor(sections, 'project_vision.md')],
78
+ ['ai_docs/vision/roadmap.md', templateFor(sections, 'vision/roadmap.md')],
79
+ ['ai_docs/vision/principles.md', templateFor(sections, 'principles.md')],
80
+ ['ai_docs/strategic/architecture.md', templateFor(sections, 'architecture.md and existing_features.md', 0)],
81
+ ['ai_docs/strategic/existing_features.md', templateFor(sections, 'architecture.md and existing_features.md', 1)],
82
+ ['ai_docs/audit/audit_plan.md', initialAuditPlan()],
83
+ // NOTE: features_history.md and INDEX.md are NOT seeded — they are
84
+ // generated by `sdlc_check.py index` and would immediately fail validate.
85
+ ];
86
+ } catch (err) {
87
+ console.error(`❌ ${err.message}`);
88
+ process.exit(1);
17
89
  }
18
90
 
19
- // 1. Path definitions
20
- const directories = [
21
- path.join(cwd, 'ai_docs'),
22
- path.join(cwd, 'ai_docs', 'vision'),
23
- path.join(cwd, 'ai_docs', 'vision', 'features'),
24
- path.join(cwd, 'ai_docs', 'strategic'),
25
- path.join(cwd, 'ai_docs', 'audit'),
26
- path.join(cwd, 'ai_docs', 'solutions')
27
- ];
28
-
29
- const files = {
30
- projectVision: path.join(cwd, 'ai_docs', 'vision', 'project_vision.md'),
31
- roadmap: path.join(cwd, 'ai_docs', 'vision', 'roadmap.md'),
32
- principles: path.join(cwd, 'ai_docs', 'vision', 'principles.md'),
33
- architecture: path.join(cwd, 'ai_docs', 'strategic', 'architecture.md'),
34
- existingFeatures: path.join(cwd, 'ai_docs', 'strategic', 'existing_features.md'),
35
- featuresHistory: path.join(cwd, 'ai_docs', 'strategic', 'features_history.md'),
36
- auditPlan: path.join(cwd, 'ai_docs', 'audit', 'audit_plan.md'),
37
- handoff: path.join(cwd, 'ai_docs', 'audit', 'handoff.md'),
38
- claudeConfig: path.join(cwd, 'CLAUDE.md'),
39
- geminiConfig: path.join(cwd, 'GEMINI.md'),
40
- codexAgents: path.join(cwd, 'AGENTS.md'),
41
- cursorRules: path.join(cwd, '.cursorrules')
42
- };
43
-
44
- // 2. Operational Protocol (System Prompt)
45
- const protocolContent = `# "Agentic SDLC" Operational Protocol
46
-
47
- You are a senior software engineer following a Documentation-First and Vision-Guided process. The process is proportional to risk: do not apply heavyweight governance to trivial work, but never bypass Vision, security, or design gates for significant changes.
48
-
49
- ## 0. Triage First
50
- Classify every operational request:
51
- - L1 Trivial: small local fix, no API/dependency/behavior expansion. Implement with relevant tests; no new docs.
52
- - L2 Small: clear root cause, up to 3 files, low risk. Provide mini-analysis in the response; test.
53
- - L3 Significant: public contract, user-visible behavior, security-sensitive area, new dependency, architectural impact, or more than 3 files. Use the full workflow below.
54
- - Spike: time-boxed exploration; document result in \`ai_docs/solutions/SPIKE_[topic].md\`; production work must be reclassified.
55
-
56
- Security-sensitive areas are never L1.
57
-
58
- ## 1. Mode Selection
59
- - Standalone: if devPNT is unavailable, use \`ai_docs/\` as the complete source of truth.
60
- - Hybrid/devPNT: if devPNT is available and configured for this project, use it for governed state. The devPNT M-VISION is the milestone north star, Master Plan is strategic roadmap, Action Plan is tactical execution, and governed artifacts live in devPNT.
61
- - Do not create silent double truth. In Hybrid, \`ai_docs/\` is human-readable context, fallback, handoff, or shadow; devPNT governs plans and versioned artifacts.
62
-
63
- ## 2. Audit and Alignment
64
- For L3 or explicit audit requests:
65
- - Check \`ai_docs/\`, \`ai_docs/vision/\`, \`ai_docs/strategic/\`, \`ai_docs/audit/\`, and \`ai_docs/solutions/\`.
66
- - If missing, create them by analyzing the codebase in batches.
67
- - Never treat architecture or feature history as a substitute for Vision.
68
-
69
- ## 3. Vision Gate
70
- Standalone:
71
- - Read \`ai_docs/vision/project_vision.md\`, \`roadmap.md\`, and \`principles.md\`.
72
- - Vision documents start as \`Stato: DRAFT\`; DRAFT informs but does not block an explicit user request.
73
- - \`Stato: APPROVED\` is binding: surface conflicts before implementation.
74
-
75
- Hybrid/devPNT:
76
- - Read the active M-VISION before design or code.
77
- - Verify the request advances a stated benefit or success signal.
78
- - If request, local Vision, and M-VISION diverge, stop and surface the conflict.
79
-
80
- ## 4. Request Analysis
81
- For L3 in Standalone:
82
- - Create or update \`ai_docs/solutions/ANALYSIS_[feature].md\`.
83
- - Include Objective, Feature Vision, Impact, Security and Threat Model, Action Plan, Test Strategy, and Diary/Current State.
84
-
85
- For L3 in Hybrid:
86
- - Restore Master Plan, Action Plan, and related devPNT artifacts.
87
- - Use devPNT for D-UC, P-TM, E-ISP, E-TDD, E-TP, ADR, and plan updates.
88
- - Use Markdown shadows only as readable mirrors, never as the authoritative source over devPNT.
89
-
90
- ## 5. Development and Testing
91
- Only after the required gate for the triage level:
92
- 1. Implement surgically following the plan.
93
- 2. Write or update automated tests where possible, using AAA for unit tests.
94
- 3. Run tests/lint/smoke checks. If the environment cannot run them, document the alternative verification.
95
- 4. After 3 consecutive test runs without progress, stop and ask for guidance.
96
-
97
- ## 6. Closing
98
- - Verify the result against local Vision or devPNT M-VISION.
99
- - Update only documents actually impacted.
100
- - In Hybrid, propose ADR/KL updates when architectural facts changed.
101
- - Keep docs and code in the same commit/PR.
102
- `;
103
-
104
- const projectVisionBoilerplate = `# Project Vision
105
- Stato: DRAFT
106
-
107
- ## North Star
108
- - TBD
109
-
110
- ## Target Users
111
- - TBD
112
-
113
- ## Goals
114
- - TBD
115
-
116
- ## Non-Goals
117
- - TBD
118
-
119
- ## Success Signals
120
- - TBD
121
- `;
122
-
123
- const roadmapBoilerplate = `# Vision Roadmap
124
- Stato: DRAFT
125
-
126
- | Milestone | Expected Benefit | Priority | Success Signal | Status |
127
- |:---|:---|:---|:---|:---|
128
- | M1 | - | - | - | [PLANNED] |
129
- `;
130
-
131
- const principlesBoilerplate = `# Vision Principles
132
- Stato: DRAFT
133
-
134
- ## Principles
135
- - TBD
136
-
137
- ## Strategic Anti-Patterns
138
- - TBD
139
- `;
140
-
141
- const historyBoilerplate = `<!-- GENERATED by agentic-sdlc - update manually only if the project does not use sdlc_check.py index. -->
142
- # Feature History
143
-
144
- | ID | Feature Name | Level | Status | Start Date | End Date | Analysis Doc |
145
- |:---|:---|:---|:---|:---|:---|:---|
146
- `;
147
-
148
- console.log('🚀 Initializing Agentic SDLC workflow (Discovery Mode)...');
149
-
150
- // 3. Directory creation
151
- directories.forEach(dir => {
91
+ // 4. Create directories
92
+ directories.forEach((dir) => {
152
93
  if (!fs.existsSync(dir)) {
153
94
  fs.mkdirSync(dir, { recursive: true });
154
95
  console.log(`📁 Created directory: ${path.relative(cwd, dir)}`);
155
96
  }
156
97
  });
157
98
 
158
- // 4. Basic file writing
159
- const writeIfNotExists = (filePath, content, description) => {
99
+ // 5. Write seed files (never overwrite)
100
+ const writeIfNotExists = (relPath, content, description) => {
101
+ const filePath = path.join(cwd, relPath);
160
102
  if (!fs.existsSync(filePath)) {
161
103
  fs.writeFileSync(filePath, content, 'utf8');
162
- console.log(`📄 Created file: ${path.relative(cwd, filePath)} (${description})`);
104
+ console.log(`📄 Created file: ${relPath}${description ? ` (${description})` : ''}`);
163
105
  return true;
164
- } else {
165
- console.log(`⏭️ Skipped: ${path.relative(cwd, filePath)} already exists.`);
166
- return false;
167
106
  }
168
- };
169
-
170
- writeIfNotExists(files.projectVision, projectVisionBoilerplate, 'Project Vision Boilerplate');
171
- writeIfNotExists(files.roadmap, roadmapBoilerplate, 'Vision Roadmap Boilerplate');
172
- writeIfNotExists(files.principles, principlesBoilerplate, 'Vision Principles Boilerplate');
173
- writeIfNotExists(files.architecture, '# Project Architecture\n\n- Stack:\n- Patterns:\n', 'Architecture Boilerplate');
174
- writeIfNotExists(files.existingFeatures, '# Existing Features\n\n- \n', 'Features Boilerplate');
175
- writeIfNotExists(files.featuresHistory, historyBoilerplate, 'History Table');
176
- writeIfNotExists(files.auditPlan, '# Audit Plan\n\nStates: PENDING | ANALYZED | SKIPPED.\n\n| Percorso | Stato | Riferimento | Note |\n|---|---|---|---|\n| / | PENDING | - | Initial analysis |\n', 'Audit Plan');
177
-
178
- // 5. Client Discovery and Configuration
107
+ console.log(`⏭️ Skipped: ${relPath} already exists.`);
108
+ return false;
109
+ };
110
+
111
+ seedFiles.forEach(([relPath, content]) => writeIfNotExists(relPath, content));
112
+
113
+ // 6. Client discovery and protocol pointers
179
114
  console.log('\n--- Environment Analysis ---');
180
115
 
181
- if (checkCommand('claude')) {
182
- console.log('✅ Claude Code detected.');
183
- writeIfNotExists(files.claudeConfig, protocolContent, 'Claude Configuration');
184
- }
116
+ const protocolFiles = {
117
+ claude: 'CLAUDE.md',
118
+ gemini: 'GEMINI.md',
119
+ codex: 'AGENTS.md',
120
+ };
185
121
 
186
- if (checkCommand('gemini')) {
187
- console.log('✅ Gemini CLI detected.');
188
- writeIfNotExists(files.geminiConfig, protocolContent, 'Gemini Configuration');
122
+ for (const client of CLIENTS) {
123
+ if (clientDetected(client)) {
124
+ console.log(`✅ ${client.label} detected.`);
125
+ writeIfNotExists(protocolFiles[client.key], protocolContent, `${client.label} protocol pointer`);
126
+ }
189
127
  }
190
128
 
191
- if (checkCommand('codex')) {
192
- console.log('✅ Codex AI detected.');
193
- writeIfNotExists(files.codexAgents, protocolContent, 'Codex AGENTS.md');
194
- }
195
-
196
129
  // Cursor/Windsurf (always recommended)
197
- writeIfNotExists(files.cursorRules, protocolContent, 'Cursor/Windsurf Rules');
130
+ writeIfNotExists('.cursorrules', protocolContent, 'Cursor/Windsurf rules');
131
+
132
+ // 7. Generate ai_docs/INDEX.md so the very first `validate` is already clean.
133
+ // The manifest is generated, never seeded: delegate to the validator if Python is available.
134
+ const validator = path.join(SKILL_SOURCE, 'scripts', 'sdlc_check.py');
135
+ let indexed = false;
136
+ for (const py of ['python', 'python3', 'py']) {
137
+ try {
138
+ execSync(`${py} "${validator}" index --root "${cwd}"`, { stdio: 'ignore' });
139
+ console.log('📇 Generated ai_docs/INDEX.md (document manifest).');
140
+ indexed = true;
141
+ break;
142
+ } catch (e) { /* try the next interpreter */ }
143
+ }
144
+ if (!indexed) {
145
+ console.log('ℹ️ Python not found: generate the manifest later with '
146
+ + '"python <skill_dir>/scripts/sdlc_check.py index" (validate reports it until then).');
147
+ }
198
148
 
199
149
  console.log('\n✅ Setup completed successfully!');
200
150
  console.log('💡 Next steps:');
201
- console.log(' 1. If using Claude Code, start it: it will read CLAUDE.md.');
202
- console.log(' 2. If using Gemini CLI, commands will use GEMINI.md.');
203
- console.log(' 3. If using Codex, restart/open the project so it reads AGENTS.md.');
204
- console.log(' 4. Start analyzing the codebase following ai_docs/audit/audit_plan.md.');
151
+ console.log(' 1. Make sure the agentic-sdlc skill is installed (agentic-sdlc-install-skill).');
152
+ console.log(' 2. Restart/open the project in your AI client so it reads the protocol pointer.');
153
+ console.log(' 3. Start with an audit following ai_docs/audit/audit_plan.md.');
package/scripts/lib.js ADDED
@@ -0,0 +1,138 @@
1
+ // Shared helpers for the Agentic SDLC npm scripts (init / postinstall / preuninstall).
2
+ // Single source for client detection and skill-target paths: init and postinstall
3
+ // must never disagree on what "Claude Code is installed" means.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const { execSync } = require('child_process');
9
+
10
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
11
+ const SKILL_SOURCE = path.join(PACKAGE_ROOT, 'skills', 'agentic-sdlc-skill');
12
+ const TEMPLATES_PATH = path.join(SKILL_SOURCE, 'templates.md');
13
+
14
+ // One entry per supported AI client. `home` may be overridden by an env var
15
+ // (Claude Desktop / portable installs); presence of the home dir counts as
16
+ // detection even when the CLI is not on PATH.
17
+ const CLIENTS = [
18
+ {
19
+ key: 'claude',
20
+ label: 'Claude Code',
21
+ cmd: 'claude',
22
+ home: process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'),
23
+ envVar: 'CLAUDE_CONFIG_DIR',
24
+ reload: 'Restart Claude Code to load it. Invoke via Skill tool as "agentic-sdlc".',
25
+ },
26
+ {
27
+ key: 'gemini',
28
+ label: 'Gemini CLI',
29
+ cmd: 'gemini',
30
+ home: process.env.GEMINI_HOME || path.join(os.homedir(), '.gemini'),
31
+ envVar: 'GEMINI_HOME',
32
+ reload: 'Run "gemini skills reload" or restart Gemini CLI to load it.',
33
+ },
34
+ {
35
+ key: 'codex',
36
+ label: 'Codex AI',
37
+ cmd: 'codex',
38
+ home: process.env.CODEX_HOME || path.join(os.homedir(), '.codex'),
39
+ envVar: 'CODEX_HOME',
40
+ reload: 'Restart Codex to load it. Invoke it as "$agentic-sdlc" or by asking for Agentic SDLC.',
41
+ },
42
+ ];
43
+
44
+ function commandExists(cmd) {
45
+ try {
46
+ execSync(`${cmd} --version`, { stdio: 'ignore' });
47
+ return true;
48
+ } catch (e) {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ function clientDetected(client) {
54
+ return commandExists(client.cmd)
55
+ || Boolean(process.env[client.envVar])
56
+ || fs.existsSync(client.home);
57
+ }
58
+
59
+ function skillTarget(client) {
60
+ return path.join(client.home, 'skills', 'agentic-sdlc');
61
+ }
62
+
63
+ function copyRecursive(src, dest) {
64
+ if (typeof fs.cpSync === 'function') {
65
+ fs.cpSync(src, dest, { recursive: true, force: true });
66
+ return;
67
+ }
68
+ // Fallback for Node < 16.7
69
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
70
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
71
+ const s = path.join(src, entry.name);
72
+ const d = path.join(dest, entry.name);
73
+ if (entry.isDirectory()) copyRecursive(s, d);
74
+ else fs.copyFileSync(s, d);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Parse templates.md into { headingText: [fencedBlock, ...] }.
80
+ * Templates are single-sourced there: the init script must extract them
81
+ * instead of carrying its own inline copies (which historically drifted).
82
+ */
83
+ function loadTemplates() {
84
+ const text = fs.readFileSync(TEMPLATES_PATH, 'utf8');
85
+ const lines = text.split(/\r?\n/);
86
+ const sections = {};
87
+ let heading = null;
88
+ let block = null;
89
+ for (const line of lines) {
90
+ const h = line.match(/^##\s+(.*)$/);
91
+ if (h && block === null) {
92
+ heading = h[1].trim();
93
+ sections[heading] = sections[heading] || [];
94
+ continue;
95
+ }
96
+ if (/^```/.test(line)) {
97
+ if (block === null) {
98
+ block = [];
99
+ } else {
100
+ if (heading) sections[heading].push(block.join('\n') + '\n');
101
+ block = null;
102
+ }
103
+ continue;
104
+ }
105
+ if (block !== null) block.push(line);
106
+ }
107
+ return sections;
108
+ }
109
+
110
+ /**
111
+ * Return the Nth fenced block of the section whose heading contains `needle`.
112
+ * Throws with a clear message when missing: writing a wrong or empty
113
+ * boilerplate silently would be worse than failing the init.
114
+ */
115
+ function templateFor(sections, needle, index = 0) {
116
+ const heading = Object.keys(sections).find((h) => h.includes(needle));
117
+ const blocks = heading ? sections[heading] : undefined;
118
+ if (!blocks || !blocks[index]) {
119
+ throw new Error(
120
+ `Template section containing "${needle}" (block ${index}) not found in ${TEMPLATES_PATH}. ` +
121
+ 'The package is corrupted or templates.md was restructured: fix templates.md, do not improvise content.'
122
+ );
123
+ }
124
+ return blocks[index];
125
+ }
126
+
127
+ module.exports = {
128
+ PACKAGE_ROOT,
129
+ SKILL_SOURCE,
130
+ TEMPLATES_PATH,
131
+ CLIENTS,
132
+ commandExists,
133
+ clientDetected,
134
+ skillTarget,
135
+ copyRecursive,
136
+ loadTemplates,
137
+ templateFor,
138
+ };