contextdevkit 1.8.0 → 1.9.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.
@@ -18,6 +18,7 @@
18
18
  * Every function takes the reporter `rep` ({ ok, bad }) plus only what it
19
19
  * needs. Entry point: `runSourceChecks(rep, ctx)` where `ctx = { KIT }`.
20
20
  */
21
+ import { existsSync } from 'node:fs';
21
22
  import { readFile, readdir } from 'node:fs/promises';
22
23
  import { resolve } from 'node:path';
23
24
 
@@ -268,6 +269,38 @@ async function checkSourceInvariants(rep, KIT) {
268
269
  ['/ship scopes via the ADR catalog (ADR-0027)', 'templates/claude/commands/pipeline/ship.md', /adr-digest\.mjs/],
269
270
  ['/new-adr checks for an existing decision first (ADR-0027)', 'templates/claude/commands/new-adr.md', /adr-digest\.mjs/],
270
271
  ['/deep-analysis scans existing ADRs before drafting (ADR-0027)', 'templates/claude/commands/audit/deep-analysis.md', /adr-digest\.mjs/],
272
+ // ADR-0030 — per-task complexity rubric (EVO-METHOD/BMAD-derived, MIT).
273
+ ['complexity-rubric loader exports classify (ADR-0030)', 'templates/contextkit/tools/scripts/complexity-rubric.mjs', /export function classify/],
274
+ ['complexity-rubric loader exports loadRubric', 'templates/contextkit/tools/scripts/complexity-rubric.mjs', /export function loadRubric/],
275
+ ['complexity-rubric falls back to an embedded default (never throws)', 'templates/contextkit/tools/scripts/complexity-rubric.mjs', /DEFAULT_RUBRIC/],
276
+ ['complexity-rubric single-sources the path via pathsFor (rule 4)', 'templates/contextkit/tools/scripts/complexity-rubric.mjs', /pathsFor\(root\)\.complexityRubric/],
277
+ ['rubric seed declares the lgpd domain → privacy-lgpd (ADR-0030)', 'templates/contextkit/policy/complexity-rubric.json', /"lgpd":[\s\S]*"privacy-lgpd"/],
278
+ ['rubric seed declares the three ceremony tiers', 'templates/contextkit/policy/complexity-rubric.json', /"trivial":[\s\S]*"feature":[\s\S]*"architectural":/],
279
+ ['paths.mjs exposes complexityRubric (ADR-0030)', 'templates/contextkit/runtime/config/paths.mjs', /complexityRubric:/],
280
+ ['/dev-start right-sizes via the complexity rubric (ADR-0030)', 'templates/claude/commands/pipeline/dev-start.md', /complexity-rubric\.mjs classify/],
281
+ ['/dev-start has a correct-course checkpoint (ADR-0030)', 'templates/claude/commands/pipeline/dev-start.md', /Correct-course checkpoint/],
282
+ ['/ship right-sizes via the complexity rubric (ADR-0030)', 'templates/claude/commands/pipeline/ship.md', /complexity-rubric\.mjs classify/],
283
+ ['/pipeline right-sizes a new task (ADR-0030)', 'templates/claude/commands/pipeline/pipeline.md', /complexity-rubric\.mjs classify/],
284
+ ['installer seeds the complexity rubric (ADR-0030)', 'install.mjs', /policy\/complexity-rubric\.json/],
285
+ ['installer seeds review-protocol.md — closes ADR-0029 gap (ADR-0030)', 'install.mjs', /'review-protocol\.md'/],
286
+ // ADR-0030 — document-quality validation (EVO steps-v adaptation, MIT).
287
+ ['validate-doc validates ADR sections (ADR-0030)', 'templates/contextkit/tools/scripts/validate-doc.mjs', /function validateAdr/],
288
+ ['validate-doc flags template placeholders', 'templates/contextkit/tools/scripts/validate-doc.mjs', /PLACEHOLDERS/],
289
+ ['validate-doc checks consequences own a trade-off', 'templates/contextkit/tools/scripts/validate-doc.mjs', /TRADEOFF_HINTS/],
290
+ ['validate-doc is advisory — never blocks (rule 8)', 'templates/contextkit/tools/scripts/validate-doc.mjs', /never blocks a push/],
291
+ ['/validate-doc command briefing ships (ADR-0030)', 'templates/claude/commands/audit/validate-doc.md', /document-quality rubric/],
292
+ // ADR-0030 — OSS repo-ops (gh-triage / draft-changelog / changelog-social + RCA).
293
+ ['draft-changelog groups Conventional Commits (ADR-0030)', 'templates/contextkit/tools/scripts/draft-changelog.mjs', /const SECTION = \{/],
294
+ ['draft-changelog times out git calls (rule 2)', 'templates/contextkit/tools/scripts/draft-changelog.mjs', /timeout:\s*\d/],
295
+ ['draft-changelog never writes the file (drafts only)', 'templates/contextkit/tools/scripts/draft-changelog.mjs', /never writes/],
296
+ ['/draft-changelog command briefing ships', 'templates/claude/commands/vcs/draft-changelog.md', /Draft a \[Unreleased\]/i],
297
+ ['/gh-triage classifies via the complexity rubric (ADR-0030)', 'templates/claude/commands/vcs/gh-triage.md', /complexity-rubric\.mjs classify/],
298
+ ['/gh-triage degrades cleanly without gh (rule 8)', 'templates/claude/commands/vcs/gh-triage.md', /skip, never fake/],
299
+ ['/changelog-social drafts only — never posts', 'templates/claude/commands/vcs/changelog-social.md', /never posts/i],
300
+ ['bug-hunt emits a structured RCA writeup (ADR-0030)', 'templates/claude/commands/bug-hunt.md', /root-cause analysis/i],
301
+ // ADR-0030 — mid-flight elicitation (advanced-elicitation + correct-course).
302
+ ['/roadmap new does advanced elicitation (ADR-0030)', 'templates/claude/commands/roadmap.md', /Advanced elicitation/],
303
+ ['/forge-new does advanced elicitation (ADR-0030)', 'templates/claude/commands/forge/forge-new.md', /Advanced elicitation/],
271
304
  ];
272
305
  for (const [label, rel, re] of cases) {
273
306
  re.test(await srcText(rel)) ? ok(label) : bad(`${label} — pattern ${re} missing in ${rel}`);
@@ -318,9 +351,48 @@ async function checkWorkflowsPinned(rep, KIT) {
318
351
  ? ok('ci.yml declares least-privilege permissions (contents: read)') : bad('ci.yml missing contents:read permissions');
319
352
  }
320
353
 
354
+ /**
355
+ * ADR-0030 — cross-doc link integrity. Scans the seeded top-level engine docs for
356
+ * relative markdown links to other `.md` files and asserts each target exists, so a
357
+ * deleted/renamed doc (the kind of rot the `review-protocol.md` seed gap caused)
358
+ * fails the build instead of shipping a dangling link. Scoped to the controlled set
359
+ * `templates/contextkit/*.md`; widening to `docs/` is an ADR-0030 follow-up.
360
+ */
361
+ async function checkDocLinks(rep, KIT) {
362
+ const { ok, bad } = rep;
363
+ console.log('Checking cross-doc markdown links resolve (ADR-0030)...');
364
+ const dir = resolve(KIT, 'templates/contextkit');
365
+ const linkRe = /\[[^\]]*\]\(([^)]+)\)/g;
366
+ const offenders = [];
367
+ let checked = 0;
368
+ let entries = [];
369
+ try {
370
+ entries = await readdir(dir, { withFileTypes: true });
371
+ } catch {
372
+ /* no dir — nothing to check */
373
+ }
374
+ for (const e of entries) {
375
+ if (!e.isFile() || !e.name.endsWith('.md')) continue;
376
+ const text = await readFile(resolve(dir, e.name), 'utf-8').catch(() => '');
377
+ let m;
378
+ while ((m = linkRe.exec(text))) {
379
+ let target = m[1].trim().split(/\s+/)[0]; // drop an optional "title"
380
+ if (!target || target.startsWith('http') || target.startsWith('#') || target.startsWith('<')) continue;
381
+ target = target.split('#')[0]; // strip an anchor
382
+ if (!target.endsWith('.md')) continue;
383
+ checked += 1;
384
+ if (!existsSync(resolve(dir, target))) offenders.push(`${e.name} → ${target}`);
385
+ }
386
+ }
387
+ offenders.length === 0
388
+ ? ok(`cross-doc markdown links resolve (${checked} checked)`)
389
+ : offenders.forEach((o) => bad(`dangling doc link: ${o}`));
390
+ }
391
+
321
392
  /** Runs every source/structural check in order. `ctx` = { KIT }. */
322
393
  export async function runSourceChecks(rep, { KIT }) {
323
394
  await checkSourceInvariants(rep, KIT);
324
395
  await checkNoHardcodedPaths(rep, KIT);
325
396
  await checkWorkflowsPinned(rep, KIT);
397
+ await checkDocLinks(rep, KIT);
326
398
  }