@antoneeo/agentic-sdlc-skill 1.5.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.
@@ -1,132 +1,36 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
- const { execSync } = require('child_process');
4
3
  const fs = require('fs');
5
- const path = require('path');
6
- const os = require('os');
4
+ const { SKILL_SOURCE, CLIENTS, clientDetected, skillTarget, copyRecursive } = require('./lib');
7
5
 
8
- const PACKAGE_ROOT = path.resolve(__dirname, '..');
9
- const SKILL_SOURCE = path.join(PACKAGE_ROOT, 'skills', 'agentic-sdlc-skill');
10
- const CLAUDE_HOME = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
11
- const CLAUDE_SKILLS_DIR = path.join(CLAUDE_HOME, 'skills');
12
- const CLAUDE_SKILL_TARGET = path.join(CLAUDE_SKILLS_DIR, 'agentic-sdlc');
13
- const CODEX_HOME = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
14
- const CODEX_SKILLS_DIR = path.join(CODEX_HOME, 'skills');
15
- const CODEX_SKILL_TARGET = path.join(CODEX_SKILLS_DIR, 'agentic-sdlc');
16
- const GEMINI_HOME = process.env.GEMINI_HOME || path.join(os.homedir(), '.gemini');
17
- const GEMINI_SKILLS_DIR = path.join(GEMINI_HOME, 'skills');
18
- const GEMINI_SKILL_TARGET = path.join(GEMINI_SKILLS_DIR, 'agentic-sdlc');
19
-
20
- function checkCommand(cmd) {
21
- try {
22
- execSync(`${cmd} --version`, { stdio: 'ignore' });
23
- return true;
24
- } catch (e) {
25
- return false;
26
- }
27
- }
28
-
29
- function copyRecursive(src, dest) {
30
- if (typeof fs.cpSync === 'function') {
31
- fs.cpSync(src, dest, { recursive: true, force: true });
32
- return;
33
- }
34
- // Fallback for Node < 16.7
35
- if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
36
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
37
- const s = path.join(src, entry.name);
38
- const d = path.join(dest, entry.name);
39
- if (entry.isDirectory()) copyRecursive(s, d);
40
- else fs.copyFileSync(s, d);
41
- }
42
- }
43
-
44
- function installClaudeSkill() {
45
- if (!fs.existsSync(SKILL_SOURCE)) {
46
- console.log(`⚠️ Skill source not found at ${SKILL_SOURCE}; skipping Claude Code install.`);
47
- return false;
48
- }
49
- try {
50
- fs.mkdirSync(CLAUDE_SKILLS_DIR, { recursive: true });
51
- copyRecursive(SKILL_SOURCE, CLAUDE_SKILL_TARGET);
52
- console.log(`📦 Installed Claude Code skill at: ${CLAUDE_SKILL_TARGET}`);
53
- console.log(' Restart Claude Code to load it. Invoke via Skill tool as "agentic-sdlc".');
54
- return true;
55
- } catch (err) {
56
- console.log(`⚠️ Failed to install Claude Code skill: ${err.message}`);
57
- console.log(` Manual install: copy "${SKILL_SOURCE}" to "${CLAUDE_SKILL_TARGET}".`);
58
- return false;
59
- }
60
- }
61
-
62
- function installCodexSkill() {
6
+ function installSkill(client) {
63
7
  if (!fs.existsSync(SKILL_SOURCE)) {
64
- console.log(`⚠️ Skill source not found at ${SKILL_SOURCE}; skipping Codex install.`);
8
+ console.log(`⚠️ Skill source not found at ${SKILL_SOURCE}; skipping ${client.label} install.`);
65
9
  return false;
66
10
  }
11
+ const target = skillTarget(client);
67
12
  try {
68
- fs.mkdirSync(CODEX_SKILLS_DIR, { recursive: true });
69
- copyRecursive(SKILL_SOURCE, CODEX_SKILL_TARGET);
70
- console.log(`📦 Installed Codex skill at: ${CODEX_SKILL_TARGET}`);
71
- console.log(' Restart Codex to load it. Invoke it as "$agentic-sdlc" or by asking for Agentic SDLC.');
13
+ fs.mkdirSync(target, { recursive: true });
14
+ copyRecursive(SKILL_SOURCE, target);
15
+ console.log(`📦 Installed ${client.label} skill at: ${target}`);
16
+ console.log(` ${client.reload}`);
72
17
  return true;
73
18
  } catch (err) {
74
- console.log(`⚠️ Failed to install Codex skill: ${err.message}`);
75
- console.log(` Manual install: copy "${SKILL_SOURCE}" to "${CODEX_SKILL_TARGET}".`);
19
+ console.log(`⚠️ Failed to install ${client.label} skill: ${err.message}`);
20
+ console.log(` Manual install: copy "${SKILL_SOURCE}" to "${target}".`);
76
21
  return false;
77
22
  }
78
- }
79
-
80
- function installGeminiSkill() {
81
- if (!fs.existsSync(SKILL_SOURCE)) {
82
- console.log(`⚠️ Skill source not found at ${SKILL_SOURCE}; skipping Gemini install.`);
83
- return false;
84
- }
85
- try {
86
- fs.mkdirSync(GEMINI_SKILLS_DIR, { recursive: true });
87
- copyRecursive(SKILL_SOURCE, GEMINI_SKILL_TARGET);
88
- console.log(`📦 Installed Gemini skill at: ${GEMINI_SKILL_TARGET}`);
89
- console.log(' Run "gemini skills reload" or restart Gemini CLI to load it.');
90
- return true;
91
- } catch (err) {
92
- console.log(`⚠️ Failed to install Gemini skill: ${err.message}`);
93
- console.log(` Manual install: copy "${SKILL_SOURCE}" to "${GEMINI_SKILL_TARGET}".`);
94
- return false;
95
- }
96
- }
97
-
98
- function hasCodexHome() {
99
- return Boolean(process.env.CODEX_HOME) || fs.existsSync(CODEX_HOME);
100
23
  }
101
24
 
102
- function hasClaudeHome() {
103
- return Boolean(process.env.CLAUDE_CONFIG_DIR) || fs.existsSync(CLAUDE_HOME);
104
- }
105
-
106
- function hasGeminiHome() {
107
- return Boolean(process.env.GEMINI_HOME) || fs.existsSync(GEMINI_HOME);
108
- }
109
-
110
25
  console.log('\n--- Agentic SDLC Skill Discovery ---');
111
26
 
112
27
  let detected = false;
113
-
114
- if (checkCommand('claude') || hasClaudeHome()) {
115
- console.log(`✅ Detected: Claude Code`);
116
- detected = true;
117
- installClaudeSkill();
118
- }
119
-
120
- if (checkCommand('gemini') || hasGeminiHome()) {
121
- console.log(`✅ Detected: Gemini CLI`);
122
- detected = true;
123
- installGeminiSkill();
124
- }
125
-
126
- if (checkCommand('codex') || hasCodexHome()) {
127
- console.log(`✅ Detected: Codex AI`);
128
- detected = true;
129
- installCodexSkill();
28
+ for (const client of CLIENTS) {
29
+ if (clientDetected(client)) {
30
+ console.log(`✅ Detected: ${client.label}`);
31
+ detected = true;
32
+ installSkill(client);
33
+ }
130
34
  }
131
35
 
132
36
  if (!detected) {
@@ -1,26 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
4
+ const { CLIENTS, skillTarget } = require('./lib');
6
5
 
7
- const CLAUDE_HOME = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
8
- const CLAUDE_SKILL_TARGET = path.join(CLAUDE_HOME, 'skills', 'agentic-sdlc');
9
- const CODEX_HOME = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
10
- const CODEX_SKILL_TARGET = path.join(CODEX_HOME, 'skills', 'agentic-sdlc');
11
- const GEMINI_HOME = process.env.GEMINI_HOME || path.join(os.homedir(), '.gemini');
12
- const GEMINI_SKILL_TARGET = path.join(GEMINI_HOME, 'skills', 'agentic-sdlc');
13
-
14
- function removeSkill(target, label) {
6
+ function removeSkill(client) {
7
+ const target = skillTarget(client);
15
8
  if (!fs.existsSync(target)) return;
16
9
  try {
17
10
  fs.rmSync(target, { recursive: true, force: true });
18
- console.log(`🧹 Removed ${label} skill at: ${target}`);
11
+ console.log(`🧹 Removed ${client.label} skill at: ${target}`);
19
12
  } catch (err) {
20
13
  console.log(`⚠️ Could not remove ${target}: ${err.message}`);
21
14
  }
22
15
  }
23
16
 
24
- removeSkill(CLAUDE_SKILL_TARGET, 'Claude Code');
25
- removeSkill(CODEX_SKILL_TARGET, 'Codex');
26
- removeSkill(GEMINI_SKILL_TARGET, 'Gemini');
17
+ CLIENTS.forEach(removeSkill);
@@ -1,55 +1,56 @@
1
- # Enforcement meccanico (opzionale, consigliato per i team)
2
-
3
- Le regole a livello di prompt dipendono dalla disciplina del modello e degradano con contesti lunghi, compaction e istruzioni concorrenti. Tre livelli di garanzia crescente:
4
-
5
- ## 1. Validazione interattiva (default, nessun setup)
6
-
7
- L'agente esegue alla chiusura (Fase 5) un solo gate:
8
-
9
- ```
10
- python "<dir_skill>/scripts/sdlc_check.py" check
11
- ```
12
-
13
- (`check` = validate + stale in un comando.) Exit code ≠ 0 ⇒ la feature non si dichiara chiusa. È il livello minimo previsto dalla skill.
14
-
15
- ## 2. Check in CI (consigliato per i team)
16
-
17
- Copia `scripts/sdlc_check.py` nel repository (es. `tools/sdlc_check.py`) e aggiungi alla pipeline:
18
-
19
- ```
20
- python tools/sdlc_check.py validate
21
- ```
22
-
23
- Effetto: indice non rigenerato, frontmatter invalidi, sezione sicurezza mancante o stati incoerenti **bloccano la pipeline** invece di affidarsi alla memoria dell'agente. Funziona perché i documenti viaggiano nello stesso PR del codice (regola di Fase 5).
24
-
25
- Nota: la copia nel repo è quella autoritativa per la CI; aggiornala quando aggiorni la skill.
26
-
27
- ## 3. Hook PreToolUse (gate sulle scritture)
28
-
29
- Blocca Edit/Write su percorsi protetti quando nessuna `ANALYSIS_*.md` è `IN_PROGRESS`. In `.claude/settings.json` del progetto:
30
-
31
- ```json
32
- {
33
- "hooks": {
34
- "PreToolUse": [
35
- {
36
- "matcher": "Write|Edit",
37
- "hooks": [
38
- {
39
- "type": "command",
40
- "command": "python \"C:\\Users\\<utente>\\.claude\\skills\\agentic-sdlc\\scripts\\sdlc_check.py\" gate --hook --protected \"src/auth;src/crypto\""
41
- }
42
- ]
43
- }
44
- ]
45
- }
46
- }
47
- ```
48
-
49
- Semantica: exit code 2 + messaggio su stderr ⇒ la scrittura viene bloccata e il messaggio è mostrato all'agente, che deve creare l'ANALYSIS (Fase 3) prima di riprovare.
50
-
51
- **Avvertenze d'uso:**
52
- - Il gate è volutamente grossolano: applicato a tutto `src/` bloccherebbe anche i task L1/L2 legittimi previsti dal Triage. Usalo **solo su directory security-critical** (`--protected "src/auth;src/crypto"`), dove "mai senza analisi" è la policy desiderata.
53
- - I percorsi in `--protected` sono prefissi relativi alla radice del progetto, separati da `;`.
54
- - `ai_docs/`, `tests/` e `test/` sono sempre esclusi dal blocco.
55
- - L'hook assume che la working directory sia la radice del progetto (comportamento standard degli hook di Claude Code).
1
+ # Mechanical enforcement (optional, recommended for teams)
2
+
3
+ Prompt-level rules depend on the model's discipline and degrade with long contexts, compaction and competing instructions. Three levels of increasing guarantee:
4
+
5
+ ## 1. Interactive validation (default, no setup)
6
+
7
+ The agent runs a single gate at closure (Phase 5):
8
+
9
+ ```
10
+ python "<skill_dir>/scripts/sdlc_check.py" check
11
+ ```
12
+
13
+ (`check` = validate + stale in one command.) Exit code ≠ 0 ⇒ the feature is not declared closed. This is the minimum level the skill expects.
14
+
15
+ ## 2. Check in CI (recommended for teams)
16
+
17
+ Copy `scripts/sdlc_check.py` into the repository (e.g. `tools/sdlc_check.py`) and add to the pipeline:
18
+
19
+ ```
20
+ python tools/sdlc_check.py validate --strict
21
+ ```
22
+
23
+ Effect: an unregenerated index, invalid frontmatter, a missing security section or incoherent states **block the pipeline** instead of relying on the agent's memory. `--strict` also fails on warnings and on a missing `ai_docs/`, so a wrong working directory cannot produce a green pipeline. This works because documents travel in the same PR as the code (Phase 5 rule).
24
+
25
+ Note: the copy in the repo is the authoritative one for CI; update it when you update the skill.
26
+
27
+ ## 3. PreToolUse hook (gate on writes)
28
+
29
+ Blocks Edit/Write on protected paths when no `ANALYSIS_*.md` is `IN_PROGRESS`. In the project's `.claude/settings.json`:
30
+
31
+ ```json
32
+ {
33
+ "hooks": {
34
+ "PreToolUse": [
35
+ {
36
+ "matcher": "Write|Edit",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "python \"C:\\Users\\<user>\\.claude\\skills\\agentic-sdlc\\scripts\\sdlc_check.py\" gate --hook --protected \"src/auth;src/crypto\""
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ }
47
+ ```
48
+
49
+ Semantics: exit code 2 + message on stderr ⇒ the write is blocked and the message is shown to the agent, which must create the ANALYSIS (Phase 3) before retrying.
50
+
51
+ **Usage warnings:**
52
+ - The gate is deliberately coarse: applied to all of `src/` it would also block the legitimate L1/L2 tasks foreseen by the Triage. Use it **only on security-critical directories** (`--protected "src/auth;src/crypto"`), where "never without analysis" is the desired policy.
53
+ - The paths in `--protected` are prefixes relative to the project root, separated by `;`.
54
+ - `ai_docs/`, `tests/` and `test/` are always excluded from blocking.
55
+ - The hook assumes the working directory is the project root (standard behavior of Claude Code hooks).
56
+ - **Hybrid/devPNT projects**: add `--hybrid` to the gate command. Governed designs live in the devPNT DB, so the gate also unlocks when an approved E-TDD shadow (`ai_docs/solutions/SHADOW_*tdd*.md`, exported before implementation — see the SKILL.md shadow discipline) is present. Without the flag the gate would block legitimate governed work. The flag is deliberately explicit: never auto-detected.