@antoneeo/mkt-agentic-sdlc-skill 0.4.7 → 0.5.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
@@ -1,5 +1,59 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0] - 2026-08-25
4
+
5
+ F-036 — the orientation hook installs itself.
6
+
7
+ ### Fixed
8
+ - **`ENFORCEMENT.md` 4 said "wire it on every project" and nothing ever did.** The
9
+ SessionStart orientation hook was real, tested and fail-open, but installing it was a
10
+ manual step, so it was skipped — and a session that never enters Phase 1 explicitly
11
+ then never meets the guide router at all. Field result: an agent worked a governed
12
+ project without invoking the process. `init` now wires the hook itself.
13
+ - **The invariant that looked like it guarded this guarded only prose** — it asserted
14
+ the documentation section exists (`assertIn("## 4. SessionStart hook", t)`) and that
15
+ `orient` runs. It gains a companion asserting the shipped installer actually CALLS the
16
+ writer. Mutation-tested: the first draft asserted the bare symbol and passed on a file
17
+ that imports the writer without invoking it, which is exactly the disabled-installer
18
+ case it exists to catch.
19
+
20
+ ### Fixed (also)
21
+ - **ENFORCEMENT 4/2's own worked hook examples named a skill directory that two of
22
+ the three lenses do not install.** kb and mkt both printed
23
+ `.claude/skills/agentic-sdlc/scripts/...`; kb installs under `kb-agentic` and mkt
24
+ under `mkt-agentic-sdlc`, so anyone who copied the snippet got a hook that runs,
25
+ prints `can't open file`, and emits nothing — the wired-and-dead defect this release
26
+ detects, sitting in the instructions that produce it. Four paths corrected, and a new
27
+ invariant derives the expected directory and validator from what the distribution
28
+ actually ships and fails if an example names anything else. Mutation-tested.
29
+
30
+ ### Added
31
+ - **A portability rule the doctrine did not have.** The hook command names a validator,
32
+ and where that validator lives decides which settings file may carry it: a repo that
33
+ vendors the validator gets a repo-relative command in the shared, committed
34
+ `.claude/settings.json`; a normal project gets an absolute path, which goes to the
35
+ git-ignored `.claude/settings.local.json` instead — committing it would hand every
36
+ teammate a hook naming a directory they do not have. `init` picks the file and adds
37
+ the local one to `.gitignore`. ENFORCEMENT 4's own worked example showed an absolute
38
+ path and did not mention the distinction; it now does.
39
+ - **A wired-but-dead hook is detected and reported instead of counted as done.** Found
40
+ in the field: a repository whose hook named a sibling lens's path, so it ran every
41
+ session, printed `can't open file`, and emitted nothing. A bare "is a hook present?"
42
+ check answers "already wired" to that and makes the silence permanent, so the existing
43
+ command's validator path is checked on disk. Never rewritten — it may be hand-tuned —
44
+ but never passed off as working either.
45
+
46
+ ### Notes
47
+ - `init` declines rather than guessing: no Python, skill not installed, a settings file
48
+ that is not valid JSON (never rewritten — a merge would discard what is in it), or a
49
+ skill path containing a double quote. Each case prints the snippet to paste.
50
+ - Only Claude Code's hook shape is wired. Codex and Gemini keep the manual snippet: this
51
+ repository has no fixture pinning their schema, and writing a hook file in a shape
52
+ nobody has verified is how the wired-but-dead defect above was born.
53
+ - A blocking `PreToolUse` gate would be real enforcement rather than a nudge, and it is
54
+ refused by the Vision's no-ceremony-ratchet Non-Goal. Recorded in the analysis so the
55
+ next person to have the idea finds the ruling instead of re-deriving it.
56
+
3
57
  ## [0.4.7] - 2026-08-06
4
58
 
5
59
  ### Fixed
@@ -1,6 +1,6 @@
1
- {
2
- "name": "mkt-agentic-sdlc",
3
- "version": "0.4.7",
4
- "description": "Evidence-First marketing planning protocol with engagement triage, SOSTAC workflow, evidence ledger and optional devPNT integration.",
5
- "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
- }
1
+ {
2
+ "name": "mkt-agentic-sdlc",
3
+ "version": "0.5.0",
4
+ "description": "Evidence-First marketing planning protocol with engagement triage, SOSTAC workflow, evidence ledger and optional devPNT integration.",
5
+ "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
+ }
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
- {
2
- "name": "@antoneeo/mkt-agentic-sdlc-skill",
3
- "version": "0.4.7",
4
- "description": "Evidence-First marketing planning protocol for Claude Code, Gemini CLI, Google Antigravity and Codex: engagement triage, SOSTAC workflow, evidence ledger, mechanical validator and optional devPNT integration.",
5
- "keywords": [
6
- "claude-code",
7
- "claude-skill",
8
- "gemini-cli",
9
- "skill",
10
- "marketing",
11
- "marketing-plan",
12
- "sostac",
13
- "ai-agent"
14
- ],
15
- "author": "Antonio Pinto (https://github.com/Antoneeo)",
16
- "license": "MIT",
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "bin": {
21
- "mkt-sdlc-init": "scripts/init.js",
22
- "mkt-sdlc-install-skill": "scripts/postinstall.js"
23
- },
24
- "scripts": {
25
- "postinstall": "node scripts/postinstall.js",
26
- "preuninstall": "node scripts/preuninstall.js"
27
- },
28
- "files": [
29
- "skills/mkt-agentic-sdlc/SKILL.md",
30
- "skills/mkt-agentic-sdlc/frameworks.md",
31
- "skills/mkt-agentic-sdlc/elicitation.md",
32
- "skills/mkt-agentic-sdlc/research.md",
33
- "skills/mkt-agentic-sdlc/templates.md",
34
- "skills/mkt-agentic-sdlc/review.md",
35
- "skills/mkt-agentic-sdlc/scripts/sdlc_core.py",
36
- "skills/mkt-agentic-sdlc/routing.md",
37
- "skills/mkt-agentic-sdlc/guides.md",
38
- "skills/mkt-agentic-sdlc/vision.md",
39
- "skills/mkt-agentic-sdlc/dispatch.md",
40
- "skills/mkt-agentic-sdlc/ENFORCEMENT.md",
41
- "skills/mkt-agentic-sdlc/scripts/mkt_check.py",
42
- "gemini-extension.json",
43
- "README.md",
44
- "CHANGELOG.md",
45
- "scripts/lib.js",
46
- "scripts/init.js",
47
- "scripts/postinstall.js",
48
- "scripts/preuninstall.js"
49
- ]
50
- }
1
+ {
2
+ "name": "@antoneeo/mkt-agentic-sdlc-skill",
3
+ "version": "0.5.0",
4
+ "description": "Evidence-First marketing planning protocol for Claude Code, Gemini CLI, Google Antigravity and Codex: engagement triage, SOSTAC workflow, evidence ledger, mechanical validator and optional devPNT integration.",
5
+ "keywords": [
6
+ "claude-code",
7
+ "claude-skill",
8
+ "gemini-cli",
9
+ "skill",
10
+ "marketing",
11
+ "marketing-plan",
12
+ "sostac",
13
+ "ai-agent"
14
+ ],
15
+ "author": "Antonio Pinto (https://github.com/Antoneeo)",
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "bin": {
21
+ "mkt-sdlc-init": "scripts/init.js",
22
+ "mkt-sdlc-install-skill": "scripts/postinstall.js"
23
+ },
24
+ "scripts": {
25
+ "postinstall": "node scripts/postinstall.js",
26
+ "preuninstall": "node scripts/preuninstall.js"
27
+ },
28
+ "files": [
29
+ "skills/mkt-agentic-sdlc/SKILL.md",
30
+ "skills/mkt-agentic-sdlc/frameworks.md",
31
+ "skills/mkt-agentic-sdlc/elicitation.md",
32
+ "skills/mkt-agentic-sdlc/research.md",
33
+ "skills/mkt-agentic-sdlc/templates.md",
34
+ "skills/mkt-agentic-sdlc/review.md",
35
+ "skills/mkt-agentic-sdlc/scripts/sdlc_core.py",
36
+ "skills/mkt-agentic-sdlc/routing.md",
37
+ "skills/mkt-agentic-sdlc/guides.md",
38
+ "skills/mkt-agentic-sdlc/vision.md",
39
+ "skills/mkt-agentic-sdlc/dispatch.md",
40
+ "skills/mkt-agentic-sdlc/ENFORCEMENT.md",
41
+ "skills/mkt-agentic-sdlc/scripts/mkt_check.py",
42
+ "gemini-extension.json",
43
+ "README.md",
44
+ "CHANGELOG.md",
45
+ "scripts/lib.js",
46
+ "scripts/init.js",
47
+ "scripts/postinstall.js",
48
+ "scripts/preuninstall.js"
49
+ ]
50
+ }
package/scripts/init.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { execSync } = require('child_process');
6
- const { SKILL_SOURCE, INSTALLED_SKILL_NAME, SELF_LENS, SIBLING_LENSES, CLIENTS, clientDetected, skillTarget, loadTemplates, templateFor } = require('./lib');
6
+ const { SKILL_SOURCE, INSTALLED_SKILL_NAME, SELF_LENS, SIBLING_LENSES, CLIENTS, clientDetected, skillTarget, wireOrientHook, loadTemplates, templateFor } = require('./lib');
7
7
 
8
8
  const cwd = process.cwd();
9
9
 
@@ -147,8 +147,22 @@ const protocolFiles = {
147
147
  // written aside and merged by hand.
148
148
  let protocolPreexisting = false;
149
149
 
150
- for (const client of CLIENTS) {
151
- if (clientDetected(client)) {
150
+ // Probed ONCE and reused by section 6c: re-filtering CLIENTS there would
151
+ // run every client's detection a second time.
152
+ const detectedClients = CLIENTS.filter(clientDetected);
153
+ const PYTHON_CANDIDATES = ['python', 'python3', 'py'];
154
+ const detectedPython = (() => {
155
+ for (const py of PYTHON_CANDIDATES) {
156
+ try {
157
+ execSync(`${py} --version`, { stdio: 'ignore' });
158
+ return py;
159
+ } catch (e) { /* try the next interpreter */ }
160
+ }
161
+ return null;
162
+ })();
163
+
164
+ for (const client of detectedClients) {
165
+ {
152
166
  console.log(`✅ ${client.label} detected.`);
153
167
  const created = writeIfNotExists(protocolFiles[client.key], protocolContent, `${client.label} protocol pointer`);
154
168
  if (!created) protocolPreexisting = true;
@@ -211,16 +225,119 @@ Delete it once the merge is done.
211
225
  }
212
226
  }
213
227
 
228
+
229
+ // 6c. SessionStart orientation hook (F-036).
230
+ // ENFORCEMENT.md §4 asks for this on every project with a docs root and Python.
231
+ // It was a manual step until now, so it was skipped -- and a session that never
232
+ // enters Phase 1 explicitly then never meets the guide router at all.
233
+
234
+ // devPNT projects want `--hybrid`: without it the hook reports audit-plan
235
+ // staleness that devPNT/KL owns, and noise at every session start is how a
236
+ // session-start message stops being read.
237
+ const hybridProject = fs.existsSync(path.join(cwd, '.devpnt'));
238
+
239
+ // Only touch .gitignore when the wiring landed in the machine-specific file:
240
+ // if that file is not ignored, the absolute path reaches teammates anyway and
241
+ // the whole reason for choosing it is undone. Append-only and marker-guarded,
242
+ // exactly like gitattributes() above. Wrapped, because the installer has already
243
+ // written the seed files and must not die here.
244
+ const ignoreLocalSettings = () => {
245
+ const marker = '.claude/settings.local.json';
246
+ if (!fs.existsSync(path.join(cwd, '.git'))) return;
247
+ const filePath = path.join(cwd, '.gitignore');
248
+ try {
249
+ const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
250
+ // Line-wise, not substring: a `!.claude/settings.local.json` un-ignore would
251
+ // satisfy `includes()` and leave the machine-specific path committed.
252
+ if (current.split(/\r?\n/).some((l) => l.trim() === marker)) return;
253
+ fs.writeFileSync(filePath, current + [
254
+ '',
255
+ '# agentic-sdlc: the orientation hook here names a path on THIS machine.',
256
+ marker,
257
+ '',
258
+ ].join(EOL), 'utf8');
259
+ console.log(`OK .gitignore: ${marker} ignored (it names a machine-specific path).`);
260
+ console.log(' If it is already tracked, run: git rm --cached ' + marker);
261
+ } catch (e) {
262
+ console.log(`⚠️ Could not update .gitignore (${e.message}). Add "${marker}" by hand.`);
263
+ }
264
+ };
265
+
266
+ const claudeClient = detectedClients.find((c) => c.key === 'claude');
267
+ const otherClients = detectedClients.filter((c) => c.key !== 'claude');
268
+ if (!claudeClient) {
269
+ console.log('ℹ️ Claude Code not detected: SessionStart orientation hook not wired.');
270
+ } else {
271
+ const r = wireOrientHook({
272
+ cwd, client: claudeClient, python: detectedPython,
273
+ hybrid: hybridProject, docsLabel: 'mkt_docs',
274
+ });
275
+ switch (r.code) {
276
+ case 'wired':
277
+ console.log(`🪝 Wired the SessionStart orientation hook in .claude/${r.file}.`);
278
+ if (r.local) {
279
+ console.log(' It names a path on THIS machine, so it went to the git-ignored');
280
+ console.log(' file: each teammate runs init once to get their own.');
281
+ ignoreLocalSettings();
282
+ }
283
+ break;
284
+ case 'already':
285
+ console.log(`⏭️ SessionStart orientation hook already wired (.claude/${r.file}).`);
286
+ if (r.file === 'settings.local.json') ignoreLocalSettings();
287
+ break;
288
+ case 'broken':
289
+ console.log(`⚠️ The SessionStart orientation hook in .claude/${r.file} is BROKEN —`);
290
+ console.log(' its validator does not resolve, so it has been emitting nothing:');
291
+ console.log(` ${r.existing}`);
292
+ console.log(' Not overwritten (it may be hand-tuned). Correct it to:');
293
+ console.log(` ${r.command}`);
294
+ break;
295
+ case 'unverifiable':
296
+ console.log(`ℹ️ A SessionStart orientation hook exists in .claude/${r.file} but names`);
297
+ console.log(' no validator this installer recognises, so it was left alone:');
298
+ console.log(` ${r.existing}`);
299
+ break;
300
+ case 'malformed':
301
+ console.log(`⚠️ .claude/${r.file} has a shape this installer will not rewrite`);
302
+ console.log(` (${r.why}): left untouched. Add the hook by hand — command:`);
303
+ console.log(` ${JSON.stringify(r.command)}`);
304
+ break;
305
+ case 'write-failed':
306
+ console.log(`⚠️ Could not write .claude/${r.file} (${r.error}). Add by hand:`);
307
+ console.log(` ${JSON.stringify(r.command)}`);
308
+ break;
309
+ case 'no-python':
310
+ console.log('ℹ️ Python not found: SessionStart orientation hook not wired.');
311
+ break;
312
+ case 'no-validator':
313
+ console.log('ℹ️ Skill not installed yet: orientation hook not wired. Install it');
314
+ console.log(' (below), then re-run init.');
315
+ break;
316
+ case 'unsafe-path':
317
+ console.log('⚠️ The validator path contains a character that cannot be placed in a');
318
+ console.log(` hook command safely: ${r.validator}`);
319
+ console.log(' Refusing to build one. Wire it by hand (ENFORCEMENT.md §4).');
320
+ break;
321
+ }
322
+ }
323
+ for (const c of otherClients) {
324
+ // Said out loud rather than skipped in silence: ENFORCEMENT.md §4's manual
325
+ // snippet is these clients' only route, and a silent skip is what let the
326
+ // "documented default nobody installs" defect live in the first place.
327
+ console.log(`ℹ️ ${c.label}: hook not wired (only Claude Code's shape is verified) —`);
328
+ console.log(' wire it by hand from ENFORCEMENT.md §4 if that client supports it.');
329
+ }
330
+
214
331
  // 7. Generate mkt_docs/INDEX.md so the very first `validate` is already clean.
215
332
  // The manifest is generated, never seeded: delegate to the validator if Python is available.
216
333
  const validator = path.join(SKILL_SOURCE, 'scripts', 'mkt_check.py');
217
334
  let indexed = false;
218
- for (const py of ['python', 'python3', 'py']) {
335
+ // detectedPython is probed once, above: one answer for the hook and for this.
336
+ if (detectedPython) {
219
337
  try {
220
- execSync(`${py} "${validator}" index --root "${cwd}"`, { stdio: 'ignore' });
338
+ execSync(`${detectedPython} "${validator}" index --root "${cwd}"`, { stdio: 'ignore' });
221
339
  console.log('📇 Generated mkt_docs/INDEX.md (document manifest).');
222
340
  indexed = true;
223
- break;
224
341
  } catch (e) { /* try the next interpreter */ }
225
342
  }
226
343
  if (!indexed) {