@antoneeo/kb-agentic-skill 1.0.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 +332 -0
- package/README.md +85 -0
- package/gemini-extension.json +6 -0
- package/package.json +50 -0
- package/scripts/init.js +216 -0
- package/scripts/lib.js +152 -0
- package/scripts/postinstall.js +42 -0
- package/scripts/preuninstall.js +17 -0
- package/skills/kb-agentic-skill/ENFORCEMENT.md +123 -0
- package/skills/kb-agentic-skill/SKILL.md +134 -0
- package/skills/kb-agentic-skill/dispatch.md +87 -0
- package/skills/kb-agentic-skill/distillation.md +79 -0
- package/skills/kb-agentic-skill/elicitation.md +131 -0
- package/skills/kb-agentic-skill/guides.md +287 -0
- package/skills/kb-agentic-skill/reconciliation.md +79 -0
- package/skills/kb-agentic-skill/review.md +168 -0
- package/skills/kb-agentic-skill/routing.md +100 -0
- package/skills/kb-agentic-skill/scripts/sdlc_check.py +846 -0
- package/skills/kb-agentic-skill/scripts/sdlc_core.py +1996 -0
- package/skills/kb-agentic-skill/taxonomy.md +80 -0
- package/skills/kb-agentic-skill/templates.md +579 -0
- package/skills/kb-agentic-skill/vision.md +245 -0
package/scripts/init.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const { SKILL_SOURCE, CLIENTS, clientDetected, skillTarget, loadTemplates, templateFor } = require('./lib');
|
|
7
|
+
|
|
8
|
+
// Sibling lenses of the same family: one shared core, one `ai_docs/` tree, a different
|
|
9
|
+
// fidelity discipline each. Keyed by the installed skill directory name.
|
|
10
|
+
const SIBLING_LENSES = {
|
|
11
|
+
'agentic-sdlc': 'code',
|
|
12
|
+
'mkt-agentic-sdlc': 'marketing',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
|
|
17
|
+
// 1. Directory layout (canonical ai_docs structure, including reference/)
|
|
18
|
+
const directories = [
|
|
19
|
+
'ai_docs',
|
|
20
|
+
'ai_docs/vision',
|
|
21
|
+
'ai_docs/vision/features',
|
|
22
|
+
'ai_docs/reference',
|
|
23
|
+
'ai_docs/strategic',
|
|
24
|
+
'ai_docs/audit',
|
|
25
|
+
'ai_docs/solutions',
|
|
26
|
+
].map((d) => path.join(cwd, d));
|
|
27
|
+
|
|
28
|
+
// 2. Project protocol (thin pointer — the operating contract is the skill).
|
|
29
|
+
const protocolContent = `# KB Agentic — Project Protocol (pointer)
|
|
30
|
+
|
|
31
|
+
This project follows the KB Agentic Knowledge-Base & Document-First process. The full
|
|
32
|
+
operating contract is the \`kb-agentic\` skill (installed in your agent's
|
|
33
|
+
skills directory); this file is only the minimal always-on pointer.
|
|
34
|
+
|
|
35
|
+
## Rule Zero — Triage every request
|
|
36
|
+
- L1 Quick Fact / Snippet: small edit in existing note/doc; no new docs.
|
|
37
|
+
- L2 Local Note / SOP Update: specific SOP update, local research note (1-2 files). Mini-analysis in reply.
|
|
38
|
+
- L3 Major Knowledge Unit / Corpus: >3 files, multi-part guide creation, corpus ingestion, or KB restructuring. Full workflow via skill: Vision Gate -> Spec Elicitation -> Taxonomy Pass -> Knowledge Analysis -> Distillation -> Review -> Indexing.
|
|
39
|
+
- Spike: time-boxed exploration; outcome in \`ai_docs/solutions/SPIKE_[topic].md\`.
|
|
40
|
+
- High-risk areas (personal data, credentials, authN/authZ, security specs) are never L1.
|
|
41
|
+
- When in doubt, pick the higher level. Declare the chosen level when starting.
|
|
42
|
+
|
|
43
|
+
## Where things live
|
|
44
|
+
- Vision (gate for L3): \`ai_docs/vision/\` — \`Status: DRAFT\` informs, \`Status: APPROVED\` binds.
|
|
45
|
+
- Knowledge analyses: \`ai_docs/solutions/ANALYSIS_[topic].md\` (frontmatter = topic state).
|
|
46
|
+
- Must-reads: \`ai_docs/README.md\`; full generated manifest: \`ai_docs/INDEX.md\`.
|
|
47
|
+
- If devPNT is available for this project, its M-VISION / plans / governed artifacts take over (Hybrid mode — see the skill).
|
|
48
|
+
|
|
49
|
+
## Closure gate
|
|
50
|
+
Docs travel in the same commit/PR as the text they describe. If the project
|
|
51
|
+
adopts the validator, \`python <skill_dir>/scripts/sdlc_check.py check\` must be
|
|
52
|
+
CLEAN before declaring work done.
|
|
53
|
+
|
|
54
|
+
If the kb-agentic skill is not available in this client, ask the user to install it:
|
|
55
|
+
\`npm i -g @antoneeo/kb-agentic-skill && kb-agentic-install-skill\`
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
console.log('🚀 Initializing KB Agentic workflow...');
|
|
59
|
+
|
|
60
|
+
// 3. Load templates from the single source (skill's templates.md)
|
|
61
|
+
let sections;
|
|
62
|
+
try {
|
|
63
|
+
sections = loadTemplates();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`❌ Cannot load templates: ${err.message}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function initialAuditPlan() {
|
|
70
|
+
const tpl = templateFor(sections, 'audit_plan.md');
|
|
71
|
+
const lines = tpl.split('\n');
|
|
72
|
+
const sepIdx = lines.findIndex((l) => /^\|[-\s|:]+\|$/.test(l.trim()));
|
|
73
|
+
if (sepIdx === -1) return tpl;
|
|
74
|
+
return lines.slice(0, sepIdx + 1).join('\n') + '\n| . | PENDING | - | Initial analysis |\n';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let seedFiles;
|
|
78
|
+
try {
|
|
79
|
+
seedFiles = [
|
|
80
|
+
['ai_docs/README.md', templateFor(sections, 'ai_docs/README.md')],
|
|
81
|
+
['ai_docs/vision/project_vision.md', templateFor(sections, 'project_vision.md')],
|
|
82
|
+
['ai_docs/vision/roadmap.md', templateFor(sections, 'vision/roadmap.md')],
|
|
83
|
+
['ai_docs/vision/principles.md', templateFor(sections, 'principles.md')],
|
|
84
|
+
['ai_docs/strategic/architecture.md', templateFor(sections, 'architecture.md and existing_features.md', 0)],
|
|
85
|
+
['ai_docs/strategic/existing_features.md', templateFor(sections, 'architecture.md and existing_features.md', 1)],
|
|
86
|
+
['ai_docs/audit/audit_plan.md', initialAuditPlan()],
|
|
87
|
+
];
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error(`❌ ${err.message}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 4. Create directories
|
|
94
|
+
directories.forEach((dir) => {
|
|
95
|
+
if (!fs.existsSync(dir)) {
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
console.log(`📁 Created directory: ${path.relative(cwd, dir)}`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// 5. Write seed files (never overwrite)
|
|
102
|
+
const writeIfNotExists = (relPath, content, description) => {
|
|
103
|
+
const filePath = path.join(cwd, relPath);
|
|
104
|
+
if (!fs.existsSync(filePath)) {
|
|
105
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
106
|
+
console.log(`📄 Created file: ${relPath}${description ? ` (${description})` : ''}`);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
console.log(`⏭️ Skipped: ${relPath} already exists.`);
|
|
110
|
+
return false;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
seedFiles.forEach(([relPath, content]) => writeIfNotExists(relPath, content));
|
|
114
|
+
|
|
115
|
+
// 6. Client discovery and protocol pointers
|
|
116
|
+
console.log('\n--- Environment Analysis ---');
|
|
117
|
+
|
|
118
|
+
const protocolFiles = {
|
|
119
|
+
claude: 'CLAUDE.md',
|
|
120
|
+
gemini: 'GEMINI.md',
|
|
121
|
+
codex: 'AGENTS.md',
|
|
122
|
+
antigravity: 'AGENTS.md',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// A protocol pointer that ALREADY existed was written by someone else — typically a
|
|
126
|
+
// sibling lens's init, which seeded the project with its own triage ladder. This init
|
|
127
|
+
// must not overwrite it (create-only is the T1 guarantee), so the second ladder is
|
|
128
|
+
// written aside and merged by hand.
|
|
129
|
+
let protocolPreexisting = false;
|
|
130
|
+
|
|
131
|
+
for (const client of CLIENTS) {
|
|
132
|
+
if (clientDetected(client)) {
|
|
133
|
+
console.log(`✅ ${client.label} detected.`);
|
|
134
|
+
const created = writeIfNotExists(protocolFiles[client.key], protocolContent, `${client.label} protocol pointer`);
|
|
135
|
+
if (!created) protocolPreexisting = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Cursor/Windsurf (always recommended)
|
|
140
|
+
writeIfNotExists('.cursorrules', protocolContent, 'Cursor/Windsurf rules');
|
|
141
|
+
|
|
142
|
+
// 6b. Sibling lenses: additive only. Never edits a user-authored root file.
|
|
143
|
+
function installedSiblingLenses() {
|
|
144
|
+
const found = new Map();
|
|
145
|
+
for (const client of CLIENTS) {
|
|
146
|
+
if (!clientDetected(client)) continue;
|
|
147
|
+
const skillsDir = path.dirname(skillTarget(client));
|
|
148
|
+
for (const [dirName, lens] of Object.entries(SIBLING_LENSES)) {
|
|
149
|
+
if (fs.existsSync(path.join(skillsDir, dirName))) found.set(dirName, lens);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return found;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const siblings = installedSiblingLenses();
|
|
156
|
+
if (siblings.size > 0) {
|
|
157
|
+
const list = [...siblings].map(([dir, lens]) => `- \`${dir}\` — the **${lens}** lens`).join('\n');
|
|
158
|
+
console.log(`\n🔀 Sibling lens detected: ${[...siblings.keys()].join(', ')}.`);
|
|
159
|
+
const wrote = writeIfNotExists('AGENTIC_MULTI_LENS.md', `# Multi-lens project — routing note (additive)
|
|
160
|
+
|
|
161
|
+
This project has more than one lens of the Agentic SDLC family installed:
|
|
162
|
+
|
|
163
|
+
${list}
|
|
164
|
+
- \`agentic-sdlc\` — the **code** lens
|
|
165
|
+
|
|
166
|
+
One \`ai_docs/\` tree, one project default (\`default_domain:\` in \`ai_docs/README.md\`),
|
|
167
|
+
one lens per unit of work. Before acting on any L2, L3 or Spike, run the domain router
|
|
168
|
+
in the skill's \`routing.md\`: it decides which lens's method and validation rules govern
|
|
169
|
+
that unit. L1 never reaches it. Never refer to a document whose meaning differs by lens
|
|
170
|
+
("threat model", "vision", \`principles.md\`, \`handoff.md\`) by its bare name.
|
|
171
|
+
|
|
172
|
+
${protocolPreexisting ? `**Merge step owed.** The always-on protocol pointer of this project (\`CLAUDE.md\` /
|
|
173
|
+
\`GEMINI.md\` / \`AGENTS.md\` / \`.cursorrules\`) was written by another lens's init and
|
|
174
|
+
carries ITS triage ladder. This init did not overwrite it. Add the knowledge lens's ladder
|
|
175
|
+
to that file by hand — the pointer below — so both are always loaded.
|
|
176
|
+
|
|
177
|
+
## Rule Zero — Triage every request (knowledge lens)
|
|
178
|
+
- L1 Quick fact: a small update to an existing note, 1-2 files.
|
|
179
|
+
- L2 Local note / SOP update: at most 1-2 files, low risk, sources named.
|
|
180
|
+
- L3 Major knowledge unit: large document sets, multi-topic research, corpus
|
|
181
|
+
restructuring. Full workflow via the skill.
|
|
182
|
+
- Spike: time-boxed exploration; outcome in \`ai_docs/solutions/SPIKE_[topic].md\`.
|
|
183
|
+
` : `The always-on protocol pointer for the knowledge lens was created by this init. When you
|
|
184
|
+
install another lens over this project, its init will leave its own ladder here for you
|
|
185
|
+
to merge.
|
|
186
|
+
`}
|
|
187
|
+
This file is NOT auto-loaded by any client: it is a note for you, deliberately additive.
|
|
188
|
+
Delete it once the merge is done.
|
|
189
|
+
`, 'multi-lens routing note');
|
|
190
|
+
if (wrote && protocolPreexisting) {
|
|
191
|
+
console.log(' ⚠️ A protocol pointer already existed and was NOT overwritten.');
|
|
192
|
+
console.log(' Merge the code-lens ladder from AGENTIC_MULTI_LENS.md into it by hand.');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 7. Generate ai_docs/INDEX.md
|
|
197
|
+
const validator = path.join(SKILL_SOURCE, 'scripts', 'sdlc_check.py');
|
|
198
|
+
let indexed = false;
|
|
199
|
+
for (const py of ['python', 'python3', 'py']) {
|
|
200
|
+
try {
|
|
201
|
+
execSync(`${py} "${validator}" index --root "${cwd}"`, { stdio: 'ignore' });
|
|
202
|
+
console.log('📇 Generated ai_docs/INDEX.md (document manifest).');
|
|
203
|
+
indexed = true;
|
|
204
|
+
break;
|
|
205
|
+
} catch (e) { /* try next */ }
|
|
206
|
+
}
|
|
207
|
+
if (!indexed) {
|
|
208
|
+
console.log('ℹ️ Python not found: generate the manifest later with '
|
|
209
|
+
+ '"python <skill_dir>/scripts/sdlc_check.py index" (validate reports it until then).');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
console.log('\n✅ Setup completed successfully!');
|
|
213
|
+
console.log('💡 Next steps:');
|
|
214
|
+
console.log(' 1. Make sure the kb-agentic skill is installed (kb-agentic-install-skill).');
|
|
215
|
+
console.log(' 2. Restart/open the project in your AI client so it reads the protocol pointer.');
|
|
216
|
+
console.log(' 3. Start with an audit following ai_docs/audit/audit_plan.md.');
|
package/scripts/lib.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Shared helpers for the KB Agentic npm scripts (init / postinstall / preuninstall).
|
|
2
|
+
// Single source for client detection and skill-target paths.
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
10
|
+
const SKILL_SOURCE = path.join(PACKAGE_ROOT, 'skills', 'kb-agentic-skill');
|
|
11
|
+
const TEMPLATES_PATH = path.join(SKILL_SOURCE, 'templates.md');
|
|
12
|
+
// The directory name each client loads the skill from. Derived from the manifest,
|
|
13
|
+
// never hard-coded by a consumer: three distributions share these scripts, and a
|
|
14
|
+
// literal here is how a copy-fork starts installing under its sibling's name.
|
|
15
|
+
const INSTALLED_SKILL_NAME = (() => {
|
|
16
|
+
const m = fs.readFileSync(path.join(SKILL_SOURCE, 'SKILL.md'), 'utf8').match(/^name:\s*(\S+)/m);
|
|
17
|
+
if (!m) throw new Error(`SKILL.md carries no 'name:' field: ${SKILL_SOURCE}`);
|
|
18
|
+
return m[1];
|
|
19
|
+
})();
|
|
20
|
+
|
|
21
|
+
// One entry per supported AI client. `home` may be overridden by an env var
|
|
22
|
+
// (Claude Desktop / portable installs); presence of the home dir counts as
|
|
23
|
+
// detection even when the CLI is not on PATH.
|
|
24
|
+
|
|
25
|
+
const CLIENTS = [
|
|
26
|
+
{
|
|
27
|
+
key: 'claude',
|
|
28
|
+
label: 'Claude Code',
|
|
29
|
+
cmd: 'claude',
|
|
30
|
+
home: process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'),
|
|
31
|
+
envVar: 'CLAUDE_CONFIG_DIR',
|
|
32
|
+
reload: 'Restart Claude Code to load it. Invoke via Skill tool as "kb-agentic".',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'gemini',
|
|
36
|
+
label: 'Gemini CLI',
|
|
37
|
+
cmd: 'gemini',
|
|
38
|
+
home: process.env.GEMINI_HOME || path.join(os.homedir(), '.gemini'),
|
|
39
|
+
envVar: 'GEMINI_HOME',
|
|
40
|
+
reload: 'Run "gemini skills reload" or restart Gemini CLI to load it.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'codex',
|
|
44
|
+
label: 'Codex AI',
|
|
45
|
+
cmd: 'codex',
|
|
46
|
+
home: process.env.CODEX_HOME || path.join(os.homedir(), '.codex'),
|
|
47
|
+
envVar: 'CODEX_HOME',
|
|
48
|
+
reload: 'Restart Codex to load it. Invoke it as "$kb-agentic" or by asking for KB Agentic.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'antigravity',
|
|
52
|
+
label: 'Google Antigravity',
|
|
53
|
+
cmd: 'agy',
|
|
54
|
+
home: process.env.ANTIGRAVITY_HOME || path.join(os.homedir(), '.gemini'),
|
|
55
|
+
envVar: 'ANTIGRAVITY_HOME',
|
|
56
|
+
skillsSubdir: 'config/skills',
|
|
57
|
+
homeMarker: path.join(
|
|
58
|
+
process.env.ANTIGRAVITY_HOME || path.join(os.homedir(), '.gemini'),
|
|
59
|
+
'config',
|
|
60
|
+
'skills',
|
|
61
|
+
),
|
|
62
|
+
reload: 'Restart Antigravity, or run "agy skills reload", to load it. Invoke by asking for KB Agentic.',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
function commandExists(cmd) {
|
|
67
|
+
try {
|
|
68
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
69
|
+
return true;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function clientDetected(client) {
|
|
76
|
+
const homePathToCheck = client.homeMarker || client.home;
|
|
77
|
+
return commandExists(client.cmd)
|
|
78
|
+
|| Boolean(process.env[client.envVar])
|
|
79
|
+
|| fs.existsSync(homePathToCheck);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function skillTarget(client) {
|
|
83
|
+
const subdir = client.skillsSubdir ? client.skillsSubdir.split('/') : ['skills'];
|
|
84
|
+
return path.join(client.home, ...subdir, INSTALLED_SKILL_NAME);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function copyRecursive(src, dest) {
|
|
88
|
+
if (typeof fs.cpSync === 'function') {
|
|
89
|
+
fs.cpSync(src, dest, { recursive: true, force: true });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
93
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
94
|
+
const s = path.join(src, entry.name);
|
|
95
|
+
const d = path.join(dest, entry.name);
|
|
96
|
+
if (entry.isDirectory()) copyRecursive(s, d);
|
|
97
|
+
else fs.copyFileSync(s, d);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function loadTemplates() {
|
|
102
|
+
const text = fs.readFileSync(TEMPLATES_PATH, 'utf8');
|
|
103
|
+
const lines = text.split(/\r?\n/);
|
|
104
|
+
const sections = {};
|
|
105
|
+
let heading = null;
|
|
106
|
+
let block = null;
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const h = line.match(/^##\s+(.*)$/);
|
|
109
|
+
if (h && block === null) {
|
|
110
|
+
heading = h[1].trim();
|
|
111
|
+
sections[heading] = sections[heading] || [];
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (/^```/.test(line)) {
|
|
115
|
+
if (block === null) {
|
|
116
|
+
block = [];
|
|
117
|
+
} else {
|
|
118
|
+
if (heading) sections[heading].push(block.join('\n') + '\n');
|
|
119
|
+
block = null;
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (block !== null) block.push(line);
|
|
124
|
+
}
|
|
125
|
+
return sections;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function templateFor(sections, needle, index = 0) {
|
|
129
|
+
const heading = Object.keys(sections).find((h) => h.includes(needle));
|
|
130
|
+
const blocks = heading ? sections[heading] : undefined;
|
|
131
|
+
if (!blocks || !blocks[index]) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Template section containing "${needle}" (block ${index}) not found in ${TEMPLATES_PATH}. ` +
|
|
134
|
+
'The package is corrupted or templates.md was restructured: fix templates.md, do not improvise content.'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return blocks[index];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
141
|
+
PACKAGE_ROOT,
|
|
142
|
+
SKILL_SOURCE,
|
|
143
|
+
INSTALLED_SKILL_NAME,
|
|
144
|
+
TEMPLATES_PATH,
|
|
145
|
+
CLIENTS,
|
|
146
|
+
commandExists,
|
|
147
|
+
clientDetected,
|
|
148
|
+
skillTarget,
|
|
149
|
+
copyRecursive,
|
|
150
|
+
loadTemplates,
|
|
151
|
+
templateFor,
|
|
152
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { SKILL_SOURCE, CLIENTS, clientDetected, skillTarget, copyRecursive } = require('./lib');
|
|
5
|
+
|
|
6
|
+
function installSkill(client) {
|
|
7
|
+
if (!fs.existsSync(SKILL_SOURCE)) {
|
|
8
|
+
console.log(`⚠️ Skill source not found at ${SKILL_SOURCE}; skipping ${client.label} install.`);
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const target = skillTarget(client);
|
|
12
|
+
try {
|
|
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}`);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.log(`⚠️ Failed to install ${client.label} skill: ${err.message}`);
|
|
20
|
+
console.log(` Manual install: copy "${SKILL_SOURCE}" to "${target}".`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log('\n--- KB Agentic Skill Discovery ---');
|
|
26
|
+
|
|
27
|
+
let detected = false;
|
|
28
|
+
for (const client of CLIENTS) {
|
|
29
|
+
if (clientDetected(client)) {
|
|
30
|
+
console.log(`✅ Detected: ${client.label}`);
|
|
31
|
+
detected = true;
|
|
32
|
+
installSkill(client);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!detected) {
|
|
37
|
+
console.log('ℹ️ No specific AI CLI detected globally, but you can still use the skill.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('\nTo initialize a project with the KB Agentic protocol, run:');
|
|
41
|
+
console.log('👉 npx kb-agentic-init');
|
|
42
|
+
console.log('------------------------------------\n');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { CLIENTS, skillTarget } = require('./lib');
|
|
5
|
+
|
|
6
|
+
function removeSkill(client) {
|
|
7
|
+
const target = skillTarget(client);
|
|
8
|
+
if (!fs.existsSync(target)) return;
|
|
9
|
+
try {
|
|
10
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
11
|
+
console.log(`🧹 Removed ${client.label} skill at: ${target}`);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.log(`⚠️ Could not remove ${target}: ${err.message}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
CLIENTS.forEach(removeSkill);
|
|
@@ -0,0 +1,123 @@
|
|
|
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 **both** validator files into the repository — `scripts/sdlc_check.py` (the entry point) **and** `scripts/sdlc_core.py` (the shared core it imports) — keeping them side by side, e.g. `tools/sdlc_check.py` + `tools/sdlc_core.py`. Then add to the pipeline:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
python tools/sdlc_check.py validate --strict
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The validator ships as two files: the core carries the family's shared behaviour and is identical in every distribution; the entry point IS the knowledge overlay — since F-024/F-025 it carries the claim ledger and the topic-graph checks. Copying only `sdlc_check.py` fails immediately with a message saying so — loudly, never as a silently green pipeline. Copying `sdlc_core.py` alone still runs, but **no longer behaves identically for kb**: it validates the family surface and runs NONE of the claim or graph checks (`graph`, `corpus`, the claim-table integrity inside `check`), so a kb project whose CI copies one file is green while its knowledge surface is unchecked. Copy both. For a kb project, add the graph step to CI:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
python tools/sdlc_check.py graph --root .
|
|
27
|
+
python tools/sdlc_check.py corpus --root .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
(Both are no-ops printing `nothing to check` on a tree without `topics/`/`corpus/`, so the step is safe to add unconditionally; `check` already includes them.)
|
|
31
|
+
|
|
32
|
+
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).
|
|
33
|
+
|
|
34
|
+
Note: the copy in the repo is the authoritative one for CI; update it when you update the skill — both files, together.
|
|
35
|
+
|
|
36
|
+
**Projects whose docs root is not `ai_docs/`.** Pass `--docs-dir <name>` (e.g. `--docs-dir mkt_docs`) on any subcommand, or set `AGENTIC_SDLC_DOCS_DIR`. Without either, the validator walks up from the working directory and takes the nearest root it recognizes. If it finds two side by side — the shape of a half-finished migration — it refuses and names both rather than validating half a project and printing a verdict. `ai_docs/` remains the default and the recommended root: the parameter exists so a legacy tree can be read and migrated, not so a second one can be kept.
|
|
37
|
+
|
|
38
|
+
## 3. PreToolUse hook (gate on writes)
|
|
39
|
+
|
|
40
|
+
Blocks Edit/Write on protected paths when no `ANALYSIS_*.md` is `IN_PROGRESS`. In the project's `.claude/settings.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"hooks": {
|
|
45
|
+
"PreToolUse": [
|
|
46
|
+
{
|
|
47
|
+
"matcher": "Write|Edit",
|
|
48
|
+
"hooks": [
|
|
49
|
+
{
|
|
50
|
+
"type": "command",
|
|
51
|
+
"command": "python \"C:\\Users\\<user>\\.claude\\skills\\agentic-sdlc\\scripts\\sdlc_check.py\" gate --hook --protected \"src/auth;src/crypto\""
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
**Usage warnings:**
|
|
63
|
+
- 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.
|
|
64
|
+
- The paths in `--protected` are prefixes relative to the project root, separated by `;`.
|
|
65
|
+
- `ai_docs/`, `tests/` and `test/` are always excluded from blocking.
|
|
66
|
+
- The hook assumes the working directory is the project root (standard behavior of Claude Code hooks).
|
|
67
|
+
- **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.
|
|
68
|
+
|
|
69
|
+
## 4. SessionStart hook (orientation, recommended default)
|
|
70
|
+
|
|
71
|
+
Emits the `ai_docs/` orientation — reading guide (`README.md`), manifest (`INDEX.md`), guide router (`reference/INDEX.md`) and last `handoff.md` — plus the Rule-Zero triage reminder to stdout at session start, so the agent begins already oriented instead of reading them only if it remembers to. It is **fail-open**: a missing, unreadable or oversized doc is skipped, the output is size-capped, and it always exits 0 — a broken or empty `ai_docs/` never blocks the session. It is **zero-execution** (it reads and prints, never runs anything).
|
|
72
|
+
|
|
73
|
+
**Wire it on every project that has `ai_docs/` and a Python interpreter.** It was opt-in until v1.16.0 and the field result was the defect this level exists to prevent: the guide router stayed unread unless the user asked for it by hand, so guides were written and never consulted. Prompt-level placement (Rule Zero declares the router verdict; Phase 1 reads the router) carries the process on its own — this hook is the backstop that survives long contexts, compaction and a session that never enters Phase 1 explicitly. Skip it only where Python is unavailable, and know what you are trading.
|
|
74
|
+
|
|
75
|
+
Wire it via each client's SessionStart mechanism — the same command everywhere (add `--hybrid` on devPNT/Hybrid projects):
|
|
76
|
+
|
|
77
|
+
Claude Code — in the project's `.claude/settings.json`:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"hooks": {
|
|
82
|
+
"SessionStart": [
|
|
83
|
+
{
|
|
84
|
+
"hooks": [
|
|
85
|
+
{
|
|
86
|
+
"type": "command",
|
|
87
|
+
"command": "python \"C:\\Users\\<user>\\.claude\\skills\\agentic-sdlc\\scripts\\sdlc_check.py\" orient"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Codex — in `.codex/hooks.json`, the same `SessionStart` → `{"type":"command","command":"… orient"}` shape (this replaces the legacy static-echo protocol some fixtures still carry).
|
|
97
|
+
|
|
98
|
+
Gemini CLI — wire the same command into its startup-hook mechanism if present; otherwise the step simply degrades to the manual Phase-1 reads (no capability lost).
|
|
99
|
+
|
|
100
|
+
**Usage notes:**
|
|
101
|
+
- The hook assumes the working directory is the project root (standard Claude Code hook behavior); it also accepts `--root <path>`.
|
|
102
|
+
- **Hybrid/devPNT projects**: add `--hybrid` — the hook then appends a one-line pointer to run `devpnt_mcp_get_bootstrap` for the Master Plan / Knowledge Layer and does not replicate them; the filesystem orientation (router + handoff + README) still emits.
|
|
103
|
+
- Like the CI gate (§2), if you copied `sdlc_check.py` into the repo, the hook references that copy — keep it current when you update the skill.
|
|
104
|
+
|
|
105
|
+
## 5. Skill eval battery (release gate)
|
|
106
|
+
|
|
107
|
+
**Skill development only.** `test_*.py` and `evals/` are deliberately absent from the npm `files` allowlist — they never reach an installed consumer, so this section applies to whoever builds the skill, not to a project that uses it. (Consumers get `sdlc_check.py`; §1–§4 are theirs.)
|
|
108
|
+
|
|
109
|
+
The skill self-tests its own doctrine invariants. Two layers over one scenario corpus:
|
|
110
|
+
|
|
111
|
+
**Static battery — the deterministic release gate.** Run before any publish:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
python -m unittest discover -s skills/kb-agentic-skill/scripts -p "test_*.py"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
It aggregates the three test files (`test_plan.py` + `test_session_start.py` + `test_skill_invariants.py`) and asserts the skill's invariants: the M4 triggers/hook/worktree doctrine is present and wired, support-file pointers resolve, and the generated indexes are idempotent. A non-zero exit **blocks the release** — a failing eval is always a real regression, never flakiness: the battery is stdlib-only, makes no model/network/subprocess call (deterministic by construction). If `test_indexes_idempotent` fails, run `sdlc_check.py index` and re-run.
|
|
118
|
+
|
|
119
|
+
**Behavioral corpus — opt-in, non-CI.** `evals/scenarios/*.md` (declarative, model-neutral) + `evals/run_behavioral.py` seed a fixture and print a prompt + pass criteria for a human/agent to run and self-assess. Because live adherence is nondeterministic, this layer **never gates** — it is the reproducible way to check that, e.g., the consult trigger actually fires on a seeded repo (the "demonstrably" artifact).
|
|
120
|
+
|
|
121
|
+
**Optional CI** (same shape as §2, not mandatory): add a `run:` step invoking the `unittest discover` command above.
|
|
122
|
+
|
|
123
|
+
**T10 note:** if you copied `sdlc_check.py` and the `test_*.py` battery into the repo for CI, that copy is authoritative — keep it current when you update the skill.
|