@antoneeo/mkt-agentic-sdlc-skill 0.4.7 → 0.5.1

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,76 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.1] - 2026-08-25
4
+
5
+ ### Fixed
6
+ - **The three README front pages did not mention the hook wiring that 1.27.0 added.**
7
+ `init` now changes what every project gets, and the README *is* the npm page, so two
8
+ of the three said nothing about it and the third barely did. Each now describes the
9
+ wiring, the settings.json / settings.local.json split and why it exists, and the
10
+ broken-hook report. `GUIDE_release.md` step 2 already made this a release duty; it was
11
+ skipped in 1.27.0 and this is the catch-up.
12
+ - **`publish_all.bat`'s verify block reported the previous version after a successful
13
+ publish — twice.** `--prefer-online` (added in kb 1.4.8) forces revalidation but cannot
14
+ outrun CDN propagation of the `latest` tag in the seconds after publishing, so the
15
+ 1.27.0 run printed mkt `0.4.7` when `0.5.0` had in fact landed. The flag alone was the
16
+ wrong fix and the note claiming otherwise is corrected: the block now **polls** each
17
+ package until the registry agrees with the local version, and prints both when it does
18
+ not, so "did it publish?" is answerable from the run instead of a manual `npm view`.
19
+
20
+ ## [0.5.0] - 2026-08-25
21
+
22
+ F-036 — the orientation hook installs itself.
23
+
24
+ ### Fixed
25
+ - **`ENFORCEMENT.md` 4 said "wire it on every project" and nothing ever did.** The
26
+ SessionStart orientation hook was real, tested and fail-open, but installing it was a
27
+ manual step, so it was skipped — and a session that never enters Phase 1 explicitly
28
+ then never meets the guide router at all. Field result: an agent worked a governed
29
+ project without invoking the process. `init` now wires the hook itself.
30
+ - **The invariant that looked like it guarded this guarded only prose** — it asserted
31
+ the documentation section exists (`assertIn("## 4. SessionStart hook", t)`) and that
32
+ `orient` runs. It gains a companion asserting the shipped installer actually CALLS the
33
+ writer. Mutation-tested: the first draft asserted the bare symbol and passed on a file
34
+ that imports the writer without invoking it, which is exactly the disabled-installer
35
+ case it exists to catch.
36
+
37
+ ### Fixed (also)
38
+ - **ENFORCEMENT 4/2's own worked hook examples named a skill directory that two of
39
+ the three lenses do not install.** kb and mkt both printed
40
+ `.claude/skills/agentic-sdlc/scripts/...`; kb installs under `kb-agentic` and mkt
41
+ under `mkt-agentic-sdlc`, so anyone who copied the snippet got a hook that runs,
42
+ prints `can't open file`, and emits nothing — the wired-and-dead defect this release
43
+ detects, sitting in the instructions that produce it. Four paths corrected, and a new
44
+ invariant derives the expected directory and validator from what the distribution
45
+ actually ships and fails if an example names anything else. Mutation-tested.
46
+
47
+ ### Added
48
+ - **A portability rule the doctrine did not have.** The hook command names a validator,
49
+ and where that validator lives decides which settings file may carry it: a repo that
50
+ vendors the validator gets a repo-relative command in the shared, committed
51
+ `.claude/settings.json`; a normal project gets an absolute path, which goes to the
52
+ git-ignored `.claude/settings.local.json` instead — committing it would hand every
53
+ teammate a hook naming a directory they do not have. `init` picks the file and adds
54
+ the local one to `.gitignore`. ENFORCEMENT 4's own worked example showed an absolute
55
+ path and did not mention the distinction; it now does.
56
+ - **A wired-but-dead hook is detected and reported instead of counted as done.** Found
57
+ in the field: a repository whose hook named a sibling lens's path, so it ran every
58
+ session, printed `can't open file`, and emitted nothing. A bare "is a hook present?"
59
+ check answers "already wired" to that and makes the silence permanent, so the existing
60
+ command's validator path is checked on disk. Never rewritten — it may be hand-tuned —
61
+ but never passed off as working either.
62
+
63
+ ### Notes
64
+ - `init` declines rather than guessing: no Python, skill not installed, a settings file
65
+ that is not valid JSON (never rewritten — a merge would discard what is in it), or a
66
+ skill path containing a double quote. Each case prints the snippet to paste.
67
+ - Only Claude Code's hook shape is wired. Codex and Gemini keep the manual snippet: this
68
+ repository has no fixture pinning their schema, and writing a hook file in a shape
69
+ nobody has verified is how the wired-but-dead defect above was born.
70
+ - A blocking `PreToolUse` gate would be real enforcement rather than a nudge, and it is
71
+ refused by the Vision's no-ceremony-ratchet Non-Goal. Recorded in the analysis so the
72
+ next person to have the idea finds the ruling instead of re-deriving it.
73
+
3
74
  ## [0.4.7] - 2026-08-06
4
75
 
5
76
  ### Fixed
package/README.md CHANGED
@@ -38,6 +38,19 @@ cd my-business-project
38
38
  npx mkt-sdlc-init # creates mkt_docs/ + protocol pointers
39
39
  ```
40
40
 
41
+ It also **wires the SessionStart orientation hook** into `.claude/`, so an agent opening
42
+ the project is handed the reading guide, the generated manifest, the guide router and the
43
+ last handoff before it does anything — instead of only when it remembers to look. That
44
+ wiring used to be a manual step documented in `ENFORCEMENT.md`; being manual, it was
45
+ skipped, and a project could run fully governed with an agent that never met the process.
46
+
47
+ The hook command names a validator, so where that validator lives decides which file gets
48
+ it: a repo that vendors it gets a repo-relative command in the shared `.claude/settings.json`
49
+ (portable — commit it); otherwise the path is machine-specific and goes to the git-ignored
50
+ `.claude/settings.local.json`, and each teammate runs `init` once. Re-running `init` never
51
+ duplicates the hook, and a hook whose validator no longer resolves is reported as **broken**
52
+ with the correction rather than counted as installed.
53
+
41
54
  ## Engagement levels (Rule Zero)
42
55
 
43
56
  | Level | Scope | Process |
@@ -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.1",
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.1",
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) {