@sienklogic/plan-build-run 2.19.1 → 2.19.2

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.
Files changed (116) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CLAUDE.md +29 -16
  3. package/README.md +3 -3
  4. package/dashboard/server/index.js +10 -1
  5. package/dashboard/server/routes/agents.js +23 -2
  6. package/dashboard/server/routes/health.js +7 -4
  7. package/dashboard/server/routes/telemetry.js +20 -1
  8. package/dashboard/server/services/planning-reader.js +3 -17
  9. package/package.json +1 -1
  10. package/plan-build-run/bin/config-schema.json +23 -145
  11. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  12. package/plugins/pbr/agents/advisor-researcher.md +1 -0
  13. package/plugins/pbr/agents/debugger.md +0 -4
  14. package/plugins/pbr/agents/researcher.md +0 -4
  15. package/plugins/pbr/agents/synthesizer.md +0 -4
  16. package/plugins/pbr/dist/check-config-change.js +0 -7
  17. package/plugins/pbr/dist/check-cross-plugin-sync.js +1 -1
  18. package/plugins/pbr/dist/check-plan-format.js +0 -32
  19. package/plugins/pbr/dist/check-roadmap-sync.js +15 -11
  20. package/plugins/pbr/dist/check-subagent-output.js +4 -60
  21. package/plugins/pbr/dist/check-summary-gate.js +3 -14
  22. package/plugins/pbr/dist/feedback-loop.js +12 -29
  23. package/plugins/pbr/dist/hook-server.js +58 -6
  24. package/plugins/pbr/dist/milestone-learnings.js +6 -56
  25. package/plugins/pbr/dist/pbr-tools.js +8 -91
  26. package/plugins/pbr/dist/post-bash-triage.js +5 -63
  27. package/plugins/pbr/dist/post-hoc.js +3 -52
  28. package/plugins/pbr/dist/post-write-dispatch.js +0 -36
  29. package/plugins/pbr/dist/pre-bash-dispatch.js +1 -7
  30. package/plugins/pbr/dist/pre-task-dispatch.js +0 -28
  31. package/plugins/pbr/dist/progress-tracker.js +2 -27
  32. package/plugins/pbr/dist/session-cleanup.js +1 -31
  33. package/plugins/pbr/dist/status-line.js +13 -11
  34. package/plugins/pbr/dist/suggest-compact.js +2 -10
  35. package/plugins/pbr/dist/validate-commit.js +8 -64
  36. package/plugins/pbr/dist/validate-task.js +0 -30
  37. package/plugins/pbr/references/config-reference.md +0 -96
  38. package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +2 -72
  39. package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +5 -41
  40. package/plugins/pbr/scripts/check-config-change.js +0 -7
  41. package/plugins/pbr/scripts/check-cross-plugin-sync.js +1 -1
  42. package/plugins/pbr/scripts/check-plan-format.js +0 -32
  43. package/plugins/pbr/scripts/check-roadmap-sync.js +15 -11
  44. package/plugins/pbr/scripts/check-subagent-output.js +4 -60
  45. package/plugins/pbr/scripts/check-summary-gate.js +3 -14
  46. package/plugins/pbr/scripts/config-schema.json +16 -129
  47. package/plugins/pbr/scripts/feedback-loop.js +12 -29
  48. package/plugins/pbr/scripts/hook-server.js +58 -6
  49. package/plugins/pbr/scripts/lib/config.js +4 -11
  50. package/plugins/pbr/scripts/lib/contextual-help.js +5 -29
  51. package/plugins/pbr/scripts/lib/format-validators.js +1 -26
  52. package/plugins/pbr/scripts/lib/frontmatter.js +4 -4
  53. package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +13 -19
  54. package/plugins/pbr/scripts/lib/health.js +4 -5
  55. package/plugins/pbr/scripts/lib/help.js +3 -54
  56. package/plugins/pbr/scripts/lib/phase.js +2 -4
  57. package/plugins/pbr/scripts/lib/pre-commit-checks.js +1 -1
  58. package/plugins/pbr/scripts/lib/pre-research.js +10 -17
  59. package/plugins/pbr/scripts/lib/roadmap.js +11 -35
  60. package/plugins/pbr/scripts/lib/smart-next-task.js +11 -20
  61. package/plugins/pbr/scripts/lib/spot-check.js +3 -106
  62. package/plugins/pbr/scripts/lib/state.js +25 -130
  63. package/plugins/pbr/scripts/lib/verify.js +56 -46
  64. package/plugins/pbr/scripts/milestone-learnings.js +6 -56
  65. package/plugins/pbr/scripts/pbr-tools.js +8 -91
  66. package/plugins/pbr/scripts/post-bash-triage.js +5 -63
  67. package/plugins/pbr/scripts/post-hoc.js +3 -52
  68. package/plugins/pbr/scripts/post-write-dispatch.js +0 -36
  69. package/plugins/pbr/scripts/pre-bash-dispatch.js +1 -7
  70. package/plugins/pbr/scripts/pre-task-dispatch.js +0 -28
  71. package/plugins/pbr/scripts/progress-tracker.js +2 -27
  72. package/plugins/pbr/scripts/session-cleanup.js +1 -31
  73. package/plugins/pbr/scripts/status-line.js +13 -11
  74. package/plugins/pbr/scripts/suggest-compact.js +2 -10
  75. package/plugins/pbr/scripts/test/state.test.js +5 -13
  76. package/plugins/pbr/scripts/validate-commit.js +8 -64
  77. package/plugins/pbr/scripts/validate-task.js +0 -30
  78. package/plugins/pbr/skills/begin/SKILL.md +1 -0
  79. package/plugins/pbr/skills/begin/templates/config.json.tmpl +0 -4
  80. package/plugins/pbr/skills/build/SKILL.md +6 -6
  81. package/plugins/pbr/skills/config/SKILL.md +1 -0
  82. package/plugins/pbr/skills/help/SKILL.md +1 -0
  83. package/plugins/pbr/skills/pause/SKILL.md +1 -0
  84. package/plugins/pbr/skills/profile-user/SKILL.md +1 -0
  85. package/plugins/pbr/skills/quick/SKILL.md +2 -1
  86. package/plugins/pbr/skills/resume/SKILL.md +1 -0
  87. package/plugins/pbr/skills/scan/SKILL.md +1 -0
  88. package/plugins/pbr/skills/setup/SKILL.md +1 -0
  89. package/plugins/pbr/skills/shared/state-update.md +2 -2
  90. package/plugins/pbr/skills/status/SKILL.md +1 -0
  91. package/plugins/pbr/references/behavioral-contexts.md +0 -53
  92. package/plugins/pbr/scripts/lib/autonomy.js +0 -91
  93. package/plugins/pbr/scripts/lib/circuit-state.js +0 -133
  94. package/plugins/pbr/scripts/lib/completion.js +0 -377
  95. package/plugins/pbr/scripts/lib/hypothesis-runner.js +0 -127
  96. package/plugins/pbr/scripts/lib/local-llm/client.js +0 -237
  97. package/plugins/pbr/scripts/lib/local-llm/health.js +0 -12
  98. package/plugins/pbr/scripts/lib/local-llm/index.js +0 -89
  99. package/plugins/pbr/scripts/lib/local-llm/metrics.js +0 -20
  100. package/plugins/pbr/scripts/lib/local-llm/operations/classify-artifact.js +0 -4
  101. package/plugins/pbr/scripts/lib/local-llm/operations/classify-commit.js +0 -4
  102. package/plugins/pbr/scripts/lib/local-llm/operations/classify-error.js +0 -4
  103. package/plugins/pbr/scripts/lib/local-llm/operations/classify-file-intent.js +0 -4
  104. package/plugins/pbr/scripts/lib/local-llm/operations/score-source.js +0 -72
  105. package/plugins/pbr/scripts/lib/local-llm/operations/summarize-context.js +0 -62
  106. package/plugins/pbr/scripts/lib/local-llm/operations/triage-test-output.js +0 -12
  107. package/plugins/pbr/scripts/lib/local-llm/operations/validate-task.js +0 -4
  108. package/plugins/pbr/scripts/lib/local-llm/router.js +0 -101
  109. package/plugins/pbr/scripts/lib/local-llm/shadow.js +0 -60
  110. package/plugins/pbr/scripts/lib/local-llm/threshold-tuner.js +0 -118
  111. package/plugins/pbr/scripts/lib/team-composer.js +0 -87
  112. package/plugins/pbr/scripts/lib/team-coordinator.js +0 -153
  113. package/plugins/pbr/scripts/lib/template.js +0 -222
  114. package/plugins/pbr/scripts/lib/test-cache.js +0 -54
  115. package/plugins/pbr/scripts/lib/trust-gate.js +0 -84
  116. package/plugins/pbr/scripts/lib/wiring-check.js +0 -196
@@ -43,8 +43,6 @@
43
43
  * auto-cleanup --phase N | --milestone vN — Auto-close todos and archive notes matching phase/milestone deliverables
44
44
  * state reconcile — Detect and repair STATE.md/ROADMAP.md desync
45
45
  * state backup — Create timestamped backup of STATE.md and ROADMAP.md
46
- * llm <subcommand> — DEPRECATED: local_llm removed (no-op)
47
- * validate-project — Comprehensive .planning/ integrity check
48
46
  * phase add <slug> [--after N] [--goal "..."] [--depends-on N] — Add phase with ROADMAP.md integration
49
47
  * phase remove <N> — Remove an empty phase directory (with renumbering)
50
48
  * phase list [--status S] [--before N] — List phase directories with optional status/before filters
@@ -62,14 +60,9 @@
62
60
  * learnings query-global [--tags X] [--project P] — Query global knowledge files
63
61
  * data status — Freshness report for research/, intel/, codebase/ directories
64
62
  * data prune --before <ISO-date> [--dry-run] — Archive stale research/codebase files
65
- * incidents list [--limit N] — List recent incidents as JSON array
66
- * incidents summary — Aggregate incident stats by type/severity/source
67
- * incidents query [--type T] [--severity S] [--source S] [--last Nd|Nh] — Filter incidents
68
63
  * nk record --title "..." --category "..." --files "f1,f2" --tried "..." --failed "..." — Record negative knowledge entry
69
64
  * nk list [--category X] [--phase X] [--status X] — List negative knowledge entries
70
65
  * nk resolve <slug> — Mark a negative knowledge entry as resolved
71
- * insights import <html-path> [--project <name>] — Parse insights HTML report into learnings
72
- * audit plan-checks [--last N] — List all plan-check results from hooks.jsonl (default: last 30 days)
73
66
  * hooks perf [--last N] [--json] — Show P50/P95/P99 hook performance report from hooks-*.jsonl
74
67
  * spot-check <phaseSlug> <planId> — Verify SUMMARY, key_files, and commits exist for a plan
75
68
  * staleness-check <phase-slug> — Check if phase plans are stale vs dependencies
@@ -143,7 +136,6 @@ const {
143
136
 
144
137
  const {
145
138
  parseStateMd,
146
- updateLegacyStateField,
147
139
  updateFrontmatterField,
148
140
  stateLoad: _stateLoad,
149
141
  stateCheckProgress: _stateCheckProgress,
@@ -231,7 +223,6 @@ const {
231
223
  } = require('./lib/migrate');
232
224
 
233
225
  const {
234
- spotCheck: _spotCheck,
235
226
  verifySpotCheck: _verifySpotCheck
236
227
  } = require('./lib/spot-check');
237
228
 
@@ -253,9 +244,7 @@ const {
253
244
  queryGlobal: _queryGlobal
254
245
  } = require('./lib/learnings');
255
246
 
256
- const {
257
- insightsImport: _insightsImport
258
- } = require('./lib/insights-parser');
247
+ // insights-parser.js CLI subcommand removed as dead (plan 140-01)
259
248
 
260
249
  const {
261
250
  referenceGet: _referenceGet
@@ -318,18 +307,13 @@ const {
318
307
  quickStatus: _quickStatus
319
308
  } = require('./quick-status');
320
309
 
321
- // --- Local LLM imports removed (feature archived in phase 53) ---
322
-
323
310
  const {
324
311
  dataStatus: _dataStatus,
325
312
  dataPrune: _dataPrune
326
313
  } = require('./lib/data-hygiene');
327
314
 
328
- const {
329
- list: _incidentsList,
330
- query: _incidentsQuery,
331
- summary: _incidentsSummary
332
- } = require('./lib/incidents');
315
+ // incidents.js lib retained (used by error-analysis.js, record-incident.js, dashboard)
316
+ // but CLI subcommands removed as dead (plan 140-01)
333
317
 
334
318
  // --- Module-level state (for backwards compatibility) ---
335
319
 
@@ -360,10 +344,6 @@ function configValidate(preloadedConfig) {
360
344
  return _configValidate(preloadedConfig, planningDir);
361
345
  }
362
346
 
363
- function insightsImport(htmlPath, projectName) {
364
- return _insightsImport(htmlPath, projectName, planningDir);
365
- }
366
-
367
347
  function stateLoad() {
368
348
  return _stateLoad(planningDir);
369
349
  }
@@ -556,16 +536,6 @@ function autoArchiveNotes(context) {
556
536
  return _autoArchiveNotes(planningDir, context);
557
537
  }
558
538
 
559
- function incidentsList(opts) {
560
- return _incidentsList({ ...opts, planningDir });
561
- }
562
- function incidentsQuery(filter, opts) {
563
- return _incidentsQuery(filter, { ...opts, planningDir });
564
- }
565
- function incidentsSummary(opts) {
566
- return _incidentsSummary({ ...opts, planningDir });
567
- }
568
-
569
539
  function intelQuery(term) {
570
540
  const { intelQuery: _intelQuery } = require('./lib/intel');
571
541
  return _intelQuery(term, planningDir);
@@ -602,9 +572,6 @@ function migrate(options) {
602
572
  return _applyMigrations(planningDir, options);
603
573
  }
604
574
 
605
- function spotCheck(phaseDir, planId) {
606
- return _spotCheck(planningDir, phaseDir, planId);
607
- }
608
575
 
609
576
  function verifySpotCheck(type, dirPath) {
610
577
  return _verifySpotCheck(type, dirPath);
@@ -1285,9 +1252,6 @@ async function main() {
1285
1252
  const { logEvent } = require('./event-logger');
1286
1253
  logEvent(category, event, details);
1287
1254
  output({ logged: true, category, event });
1288
- } else if (command === 'llm') {
1289
- // local_llm feature has been removed (phase 53). All subcommands are no-ops.
1290
- output({ deprecated: true, message: 'local_llm feature has been removed. This subcommand is a no-op.' });
1291
1255
  // --- Compound init commands ---
1292
1256
  } else if (command === "init" && subcommand === "execute-phase") {
1293
1257
  const phase = args[2];
@@ -1533,21 +1497,6 @@ async function main() {
1533
1497
  error('Usage: learnings <ingest|query|check-thresholds|copy-global|query-global>');
1534
1498
  process.exit(1);
1535
1499
  }
1536
- } else if (command === 'insights') {
1537
- const subCmd = args[1];
1538
- if (subCmd === 'import') {
1539
- const htmlPath = args[2];
1540
- if (!htmlPath) { error('Usage: insights import <html-file-path> [--project <name>]'); process.exit(1); }
1541
- let projectName;
1542
- for (let i = 3; i < args.length; i++) {
1543
- if (args[i] === '--project' && args[i + 1]) { projectName = args[++i]; }
1544
- }
1545
- const result = insightsImport(htmlPath, projectName);
1546
- output(result);
1547
- } else {
1548
- error('Usage: insights <import>');
1549
- process.exit(1);
1550
- }
1551
1500
  } else if (command === 'verify' && subcommand === 'spot-check') {
1552
1501
  const scType = args[2];
1553
1502
  const scPath = args[3];
@@ -1598,10 +1547,9 @@ async function main() {
1598
1547
  // Returns JSON: { ok, summary_exists, key_files_checked, commits_present, detail }
1599
1548
  const phaseSlug = args[1];
1600
1549
  const planId = args[2];
1601
- if (!phaseSlug || !planId) {
1602
- error('Usage: spot-check <phaseSlug> <planId>');
1603
- }
1604
- output(spotCheck(phaseSlug, planId));
1550
+ if (!phaseSlug || !planId) { error('Usage: spot-check <phase-slug> <plan-id>'); process.exit(1); }
1551
+ const { verifySpotCheck } = require('./lib/spot-check');
1552
+ output(verifySpotCheck(phaseSlug, planId));
1605
1553
  } else if (command === 'staleness-check') {
1606
1554
  const slug = args[1];
1607
1555
  if (!slug) { error('Usage: staleness-check <phase-slug>'); process.exit(1); }
@@ -1715,8 +1663,6 @@ async function main() {
1715
1663
  const version = args[1];
1716
1664
  if (!version) error('Usage: pbr-tools.js milestone-stats <version>');
1717
1665
  output(milestoneStats(version));
1718
- } else if (command === 'validate-project') {
1719
- output(validateProject());
1720
1666
  } else if (command === 'skill-section') {
1721
1667
  // skill-section --list <skill> — list all headings
1722
1668
  if (args[1] === '--list') {
@@ -1894,28 +1840,6 @@ async function main() {
1894
1840
  const maxIterations = maxIterIdx !== -1 ? parseInt(args[maxIterIdx + 1], 10) : 3;
1895
1841
  output(ciFix({ dryRun, maxIterations }));
1896
1842
 
1897
- // ─── Incidents ───────────────────────────────────────────────────────────
1898
- } else if (command === 'incidents') {
1899
- const sub = args[1];
1900
- if (sub === 'list') {
1901
- const limitIdx = args.indexOf('--limit');
1902
- const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 50;
1903
- output(incidentsList({ limit }));
1904
- } else if (sub === 'summary') {
1905
- output(incidentsSummary());
1906
- } else if (sub === 'query') {
1907
- const filter = {};
1908
- for (let i = 2; i < args.length; i++) {
1909
- if (args[i] === '--type' && args[i + 1]) { filter.type = args[++i]; }
1910
- else if (args[i] === '--severity' && args[i + 1]) { filter.severity = args[++i]; }
1911
- else if (args[i] === '--source' && args[i + 1]) { filter.source = args[++i]; }
1912
- else if (args[i] === '--last' && args[i + 1]) { filter.last = args[++i]; }
1913
- }
1914
- output(incidentsQuery(filter));
1915
- } else {
1916
- error('Usage: incidents <list|summary|query> [--type T] [--severity S] [--source S] [--last Nd|Nh] [--limit N]');
1917
- }
1918
-
1919
1843
  } else if (command === 'nk') {
1920
1844
  // Lazy require to avoid loading for unrelated commands
1921
1845
  const nkLib = require(path.join(__dirname, 'lib', 'negative-knowledge'));
@@ -1992,13 +1916,6 @@ async function main() {
1992
1916
  const graphCli = require('./lib/graph-cli');
1993
1917
  graphCli.handleGraphCommand(subcommand, args, planningDir, cwd, output, error);
1994
1918
 
1995
- // --- Audit Operations ---
1996
- } else if (command === 'audit' && subcommand === 'plan-checks') {
1997
- const { auditPlanChecks } = require('./lib/audit');
1998
- const lastIdx = args.indexOf('--last');
1999
- const last = lastIdx !== -1 ? parseInt(args[lastIdx + 1], 10) : 30;
2000
- output(auditPlanChecks({ last }));
2001
-
2002
1919
  // --- Hooks Perf Report ---
2003
1920
  } else if (command === 'hooks' && subcommand === 'perf') {
2004
1921
  const { summarizeHookPerf, formatPerfTable, loadPerfEntries } = require('./lib/perf');
@@ -2031,7 +1948,7 @@ async function main() {
2031
1948
  if (result.error) process.exit(1);
2032
1949
 
2033
1950
  } else {
2034
- error(`Unknown command: ${args.join(' ')}\nCommands: state load|check-progress|update|patch|advance-plan|record-metric, config validate|load-defaults|save-defaults|resolve-depth, validate health, validate-project, verify summary|plan-structure|phase-completeness|artifacts|key-links|commits|references|spot-check, migrate [--dry-run] [--force], init execute-phase|plan-phase|quick|verify-work|resume|progress|map-codebase, state-bundle <phase>, plan-index, frontmatter, must-haves, phase-info, phase add|remove|list|complete, roadmap update-status|update-plans, history append|load, todo list|get|add|done, auto-cleanup --phase N|--milestone vN, event, llm health|status|classify|score-source|classify-error|summarize|metrics [--session <ISO>]|adjust-thresholds, learnings ingest|query|check-thresholds, incidents list|summary|query, nk record|list|resolve, data status|prune, graph build|query|impact|stats, hooks perf [--last N] [--json], spec parse|diff|reverse|impact, milestone-stats <version>, context-triage [--agents-done N] [--plans-total N] [--step NAME], ci-poll <run-id> [--timeout <seconds>], ci-fix [--dry-run] [--max-iterations N], rollback <manifest-path>, session get|set|clear|dump, claim acquire|release|list, skill-section <skill> <section>|--list <skill>, step-verify <skill> <step> <checklist-json>, suggest-alternatives phase-not-found|missing-prereq|config-invalid [args], tmux detect, quick init, generate-slug|slug-generate, parse-args plan|quick, status fingerprint, quick-status, help, skill-metadata <name>`);
1951
+ error(`Unknown command: ${args.join(' ')}\nCommands: state load|check-progress|update|patch|advance-plan|record-metric, config validate|load-defaults|save-defaults|resolve-depth, validate health, verify summary|plan-structure|phase-completeness|artifacts|key-links|commits|references|spot-check, migrate [--dry-run] [--force], init execute-phase|plan-phase|quick|verify-work|resume|progress|map-codebase, state-bundle <phase>, plan-index, frontmatter, must-haves, phase-info, phase add|remove|list|complete, roadmap update-status|update-plans, history append|load, todo list|get|add|done, auto-cleanup --phase N|--milestone vN, event, llm health|status|classify|score-source|classify-error|summarize|metrics [--session <ISO>]|adjust-thresholds, learnings ingest|query|check-thresholds, nk record|list|resolve, data status|prune, graph build|query|impact|stats, hooks perf [--last N] [--json], spec parse|diff|reverse|impact, milestone-stats <version>, context-triage [--agents-done N] [--plans-total N] [--step NAME], ci-poll <run-id> [--timeout <seconds>], ci-fix [--dry-run] [--max-iterations N], rollback <manifest-path>, session get|set|clear|dump, claim acquire|release|list, skill-section <skill> <section>|--list <skill>, step-verify <skill> <step> <checklist-json>, suggest-alternatives phase-not-found|missing-prereq|config-invalid [args], tmux detect, quick init, generate-slug|slug-generate, parse-args plan|quick, status fingerprint, quick-status, help, skill-metadata <name>`);
2035
1952
  }
2036
1953
  } catch (e) {
2037
1954
  error(e.message);
@@ -2039,6 +1956,6 @@ async function main() {
2039
1956
  }
2040
1957
 
2041
1958
  if (require.main === module || process.argv[1] === __filename) { main().catch(err => { process.stderr.write(err.message + '\n'); process.exit(1); }); }
2042
- module.exports = { KNOWN_AGENTS, initExecutePhase, initPlanPhase, initQuick, initVerifyWork, initResume, initProgress, initStateBundle: stateBundle, stateBundle, statePatch, stateAdvancePlan, stateRecordMetric, stateRecordActivity, stateUpdateProgress, parseStateMd, parseRoadmapMd, parseYamlFrontmatter, parseMustHaves, countMustHaves, stateLoad, stateCheckProgress, configLoad, configClearCache, configValidate, lockedFileUpdate, planIndex, determinePhaseStatus, findFiles, atomicWrite, tailLines, frontmatter, mustHavesCollect, phaseInfo, stateUpdate, roadmapUpdateStatus, roadmapUpdatePlans, roadmapAnalyze, updateLegacyStateField, updateFrontmatterField, updateTableRow, findRoadmapRow, resolveDepthProfile, DEPTH_PROFILE_DEFAULTS, historyAppend, historyLoad, VALID_STATUS_TRANSITIONS, validateStatusTransition, writeActiveSkill, validateProject, phaseAdd, phaseRemove, phaseList, loadUserDefaults, saveUserDefaults, mergeUserDefaults, USER_DEFAULTS_PATH, todoList, todoGet, todoAdd, todoDone, migrate, spotCheck, referenceGet, milestoneStats, contextTriage, stalenessCheck, summaryGate, checkpointInit, checkpointUpdate, seedsMatch, ciPoll, ciFix, parseJestOutput: _parseJestOutput, parseLintOutput: _parseLintOutput, autoFixLint: _autoFixLint, runCiFixLoop: _runCiFixLoop, rollbackPlan, sessionLoad, sessionSave, SESSION_ALLOWED_KEYS, claimAcquire, claimRelease, claimList, skillSectionGet, listSkillHeadings, stepVerify: _stepVerify, phaseAlternatives: _phaseAlternatives, prerequisiteAlternatives: _prereqAlternatives, configAlternatives: _configAlternatives, phaseComplete, phaseInsert, quickStatus, autoCloseTodos, autoArchiveNotes, buildCleanupContext, incidentsList, incidentsQuery, incidentsSummary, helpListCmd, skillMetadataCmd, initMapCodebase };
1959
+ module.exports = { KNOWN_AGENTS, initExecutePhase, initPlanPhase, initQuick, initVerifyWork, initResume, initProgress, initStateBundle: stateBundle, stateBundle, statePatch, stateAdvancePlan, stateRecordMetric, stateRecordActivity, stateUpdateProgress, parseStateMd, parseRoadmapMd, parseYamlFrontmatter, parseMustHaves, countMustHaves, stateLoad, stateCheckProgress, configLoad, configClearCache, configValidate, lockedFileUpdate, planIndex, determinePhaseStatus, findFiles, atomicWrite, tailLines, frontmatter, mustHavesCollect, phaseInfo, stateUpdate, roadmapUpdateStatus, roadmapUpdatePlans, roadmapAnalyze, updateFrontmatterField, updateTableRow, findRoadmapRow, resolveDepthProfile, DEPTH_PROFILE_DEFAULTS, historyAppend, historyLoad, VALID_STATUS_TRANSITIONS, validateStatusTransition, writeActiveSkill, validateProject, phaseAdd, phaseRemove, phaseList, loadUserDefaults, saveUserDefaults, mergeUserDefaults, USER_DEFAULTS_PATH, todoList, todoGet, todoAdd, todoDone, migrate, verifySpotCheck, referenceGet, milestoneStats, contextTriage, stalenessCheck, summaryGate, checkpointInit, checkpointUpdate, seedsMatch, ciPoll, ciFix, parseJestOutput: _parseJestOutput, parseLintOutput: _parseLintOutput, autoFixLint: _autoFixLint, runCiFixLoop: _runCiFixLoop, rollbackPlan, sessionLoad, sessionSave, SESSION_ALLOWED_KEYS, claimAcquire, claimRelease, claimList, skillSectionGet, listSkillHeadings, stepVerify: _stepVerify, phaseAlternatives: _phaseAlternatives, prerequisiteAlternatives: _prereqAlternatives, configAlternatives: _configAlternatives, phaseComplete, phaseInsert, quickStatus, autoCloseTodos, autoArchiveNotes, buildCleanupContext, helpListCmd, skillMetadataCmd, initMapCodebase };
2043
1960
  // NOTE: validateProject, phaseAdd, phaseRemove, phaseList were previously CLI-only (not exported).
2044
1961
  // They are now exported for testability. This is additive and backwards-compatible.
@@ -1,12 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * PostToolUse hook for Bash: Triages test failure output using the local LLM.
5
- *
6
- * When a Bash command that looks like a test invocation exits with a non-zero
7
- * exit code, this hook sends the output to the local LLM for classification.
8
- * The triage result is returned as advisory context to help the frontier model
9
- * focus on the right failure category without re-parsing raw test output.
4
+ * PostToolUse hook for Bash: Previously triaged test failures via local LLM.
5
+ * Now a no-op stub — local LLM infrastructure has been removed.
10
6
  *
11
7
  * Exit codes:
12
8
  * 0 = always (PostToolUse hook, never blocks)
@@ -14,11 +10,7 @@
14
10
 
15
11
  'use strict';
16
12
 
17
- const fs = require('fs');
18
- const path = require('path');
19
13
  const { logHook } = require('./hook-logger');
20
- const { resolveConfig } = require('./lib/local-llm/health');
21
- const { triageTestOutput } = require('./lib/local-llm/operations/triage-test-output');
22
14
 
23
15
  const TEST_COMMAND_PATTERNS = [
24
16
  /\bnpm\s+test\b/,
@@ -48,63 +40,13 @@ function detectTestRunner(command) {
48
40
  return null;
49
41
  }
50
42
 
51
- /**
52
- * Load and resolve the local_llm config block from .planning/config.json.
53
- */
54
- function loadLocalLlmConfig(cwd) {
55
- try {
56
- const configPath = path.join(cwd || process.cwd(), '.planning', 'config.json');
57
- const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
58
- return resolveConfig(parsed.local_llm);
59
- } catch (_e) {
60
- return resolveConfig(undefined);
61
- }
62
- }
63
-
64
43
  /**
65
44
  * Check if Bash output contains test failure and triage it.
66
- * @param {object} data - parsed hook data
45
+ * Previously used local LLM for classification — now a no-op stub.
46
+ * @param {object} _data - parsed hook data
67
47
  * @returns {Promise<{output: object}|null>}
68
48
  */
69
- async function checkTestTriage(data) {
70
- const command = data.tool_input?.command || '';
71
- const toolOutput = data.tool_output || '';
72
- const exitCode = data.tool_exit_code;
73
-
74
- // Only triage test commands that failed
75
- if (exitCode === 0 || exitCode === undefined) return null;
76
- if (!TEST_COMMAND_PATTERNS.some(p => p.test(command))) return null;
77
- if (!toolOutput || toolOutput.length < 20) return null;
78
-
79
- const cwd = process.cwd();
80
- const llmConfig = loadLocalLlmConfig(cwd);
81
- const planningDir = path.join(cwd, '.planning');
82
- const testRunner = detectTestRunner(command);
83
-
84
- // Truncate to last 2000 chars — test failures are usually at the end
85
- const tail = toolOutput.length > 2000 ? toolOutput.slice(-2000) : toolOutput;
86
-
87
- try {
88
- const llmResult = await triageTestOutput(llmConfig, planningDir, tail, testRunner, data.session_id);
89
- if (llmResult && llmResult.category && llmResult.category !== 'unknown') {
90
- logHook('post-bash-triage', 'PostToolUse', 'triage', {
91
- category: llmResult.category,
92
- file_hint: llmResult.file_hint,
93
- runner: testRunner
94
- });
95
-
96
- let msg = `[pbr] Test failure triage: ${llmResult.category}`;
97
- if (llmResult.file_hint) {
98
- msg += ` (likely: ${llmResult.file_hint})`;
99
- }
100
- msg += ` (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)`;
101
-
102
- return { output: { additionalContext: msg } };
103
- }
104
- } catch (_llmErr) {
105
- // Never propagate LLM errors
106
- }
107
-
49
+ async function checkTestTriage(_data) {
108
50
  return null;
109
51
  }
110
52
 
@@ -12,56 +12,7 @@
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
14
  const { execSync } = require('child_process');
15
-
16
- /**
17
- * Minimal YAML frontmatter parser. Returns key-value pairs from --- blocks.
18
- * Handles arrays (lines starting with " - ") and nested objects (indented keys).
19
- */
20
- function parseFrontmatter(content) {
21
- if (!content) return {};
22
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
23
- if (!match) return {};
24
-
25
- const lines = match[1].split(/\r?\n/);
26
- const result = {};
27
- let currentKey = null;
28
- let currentNested = null;
29
-
30
- for (const line of lines) {
31
- // Nested array item: " - value"
32
- const arrayMatch = line.match(/^(\s{2,})- "?([^"]*)"?$/);
33
- if (arrayMatch && currentKey) {
34
- if (currentNested) {
35
- if (!result[currentKey]) result[currentKey] = {};
36
- if (!result[currentKey][currentNested]) result[currentKey][currentNested] = [];
37
- result[currentKey][currentNested].push(arrayMatch[2]);
38
- } else {
39
- if (!Array.isArray(result[currentKey])) result[currentKey] = [];
40
- result[currentKey].push(arrayMatch[2]);
41
- }
42
- continue;
43
- }
44
-
45
- // Nested key: " key:"
46
- const nestedKeyMatch = line.match(/^\s{2,}(\w+):\s*$/);
47
- if (nestedKeyMatch && currentKey) {
48
- currentNested = nestedKeyMatch[1];
49
- if (!result[currentKey]) result[currentKey] = {};
50
- continue;
51
- }
52
-
53
- // Top-level key-value: "key: value" or "key:"
54
- const kvMatch = line.match(/^(\w[\w_]*):\s*(.*)$/);
55
- if (kvMatch) {
56
- currentKey = kvMatch[1];
57
- currentNested = null;
58
- const val = kvMatch[2].replace(/^"(.*)"$/, '$1').trim();
59
- result[currentKey] = val || null;
60
- }
61
- }
62
-
63
- return result;
64
- }
15
+ const { extractFrontmatter } = require('./lib/frontmatter');
65
16
 
66
17
  /**
67
18
  * Read all PLAN-*.md files from phaseDir and parse their frontmatter.
@@ -72,7 +23,7 @@ function readPlanFiles(phaseDir) {
72
23
  const files = fs.readdirSync(phaseDir).filter(f => /^PLAN.*\.md$/i.test(f));
73
24
  for (const file of files) {
74
25
  const content = fs.readFileSync(path.join(phaseDir, file), 'utf-8');
75
- const fm = parseFrontmatter(content);
26
+ const fm = extractFrontmatter(content);
76
27
  plans.push(fm);
77
28
  }
78
29
  } catch (_e) {
@@ -207,7 +158,7 @@ function generateLearnings(phaseDir, _planningDir) {
207
158
 
208
159
  try {
209
160
  const verContent = fs.readFileSync(verPath, 'utf-8');
210
- const fm = parseFrontmatter(verContent);
161
+ const fm = extractFrontmatter(verContent);
211
162
  verStatus = fm.status || '';
212
163
  if (Array.isArray(fm.gaps)) {
213
164
  gaps = fm.gaps;
@@ -185,42 +185,6 @@ async function processEvent(data, planningDir) {
185
185
  logHook('post-write-dispatch', 'PostToolUse', 'error', { check: 'checkReadFirst', error: e.message });
186
186
  }
187
187
 
188
- // LLM file intent classification — advisory enrichment for non-planning files
189
- const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
190
- const normalizedPath = filePath.replace(/\\/g, '/');
191
- if (filePath && !normalizedPath.includes('.planning/') && !normalizedPath.includes('.planning\\')) {
192
- try {
193
- const { resolveConfig } = require('./lib/local-llm/health');
194
- const { classifyFileIntent } = require('./lib/local-llm/operations/classify-file-intent');
195
- const llmConfig = (() => {
196
- try {
197
- const configPath = path.join(planningDir, 'config.json');
198
- const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
199
- return resolveConfig(parsed.local_llm);
200
- } catch (_e) {
201
- return resolveConfig(undefined);
202
- }
203
- })();
204
-
205
- let contentSnippet = '';
206
- try {
207
- const content = data.tool_input?.content || data.tool_input?.new_string || '';
208
- contentSnippet = content.slice(0, 400);
209
- } catch (_e) {
210
- // No content available
211
- }
212
-
213
- if (contentSnippet) {
214
- const llmResult = await classifyFileIntent(llmConfig, planningDir, filePath, contentSnippet, data.session_id);
215
- if (llmResult && llmResult.file_type) {
216
- results.push(`[pbr] File classified: ${llmResult.file_type}/${llmResult.intent} (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)`);
217
- }
218
- }
219
- } catch (_llmErr) {
220
- // Never propagate LLM errors
221
- }
222
- }
223
-
224
188
  // Merge all results into a single response
225
189
  if (results.length > 0) {
226
190
  return { additionalContext: results.join('\n') };
@@ -48,7 +48,7 @@
48
48
 
49
49
  const { logHook } = require('./hook-logger');
50
50
  const { checkDangerous } = require('./check-dangerous-commands');
51
- const { checkCommit, enrichCommitLlm } = require('./validate-commit');
51
+ const { checkCommit } = require('./validate-commit');
52
52
  const { checkUnmanagedCommit } = require('./enforce-pbr-workflow');
53
53
  const { checkRequirePaths, checkMirrorSync, checkLintErrors } = require('./lib/pre-commit-checks');
54
54
  // Cross-plugin sync disabled -- derivative plugins updated separately
@@ -138,12 +138,6 @@ async function processEvent(data) {
138
138
  // warnings.push(syncResult.additionalContext);
139
139
  // }
140
140
 
141
- // LLM commit semantic classification -- advisory only
142
- const llmAdvisory = await enrichCommitLlm(data);
143
- if (llmAdvisory) {
144
- warnings.push(llmAdvisory);
145
- }
146
-
147
141
  if (warnings.length > 0) {
148
142
  logHook('pre-bash-dispatch', 'PreToolUse', 'warn', { warnings: warnings.length, cmd: command.substring(0, 80) });
149
143
  return {
@@ -31,8 +31,6 @@ const fs = require('fs');
31
31
  const path = require('path');
32
32
  const { logHook } = require('./hook-logger');
33
33
  const { checkBudget } = require('./enforce-context-budget');
34
- const { resolveConfig } = require('./lib/local-llm/health');
35
- const { validateTask: llmValidateTask } = require('./lib/local-llm/operations/validate-task');
36
34
  const { checkNonPbrAgent } = require('./enforce-pbr-workflow');
37
35
  const { KNOWN_AGENTS } = require('./lib/core');
38
36
 
@@ -52,19 +50,6 @@ const { checkUserConfirmationGate } = require('./lib/gates/user-confirmation');
52
50
 
53
51
  const MAX_DESCRIPTION_LENGTH = 100;
54
52
 
55
- /**
56
- * Load and resolve the local_llm config block from .planning/config.json.
57
- */
58
- function loadLocalLlmConfig(cwd) {
59
- try {
60
- const configPath = path.join(cwd, '.planning', 'config.json');
61
- const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
62
- return resolveConfig(parsed.local_llm);
63
- } catch (_e) {
64
- return resolveConfig(undefined);
65
- }
66
- }
67
-
68
53
  /**
69
54
  * Check a parsed hook data object for Task() validation issues.
70
55
  * Returns an array of warning strings (empty if all good).
@@ -223,19 +208,6 @@ async function processEvent(data) {
223
208
  if (nonPbrAgentResult) warnings.push(nonPbrAgentResult.output.additionalContext);
224
209
  if (planValGate && planValGate.warning) warnings.push(planValGate.warning);
225
210
 
226
- // LLM task coherence check — advisory only
227
- try {
228
- const llmCwd = data.cwd || process.env.PBR_PROJECT_ROOT || process.cwd();
229
- const llmConfig = loadLocalLlmConfig(llmCwd);
230
- const llmPlanningDir = path.join(llmCwd, '.planning');
231
- const llmResult = await llmValidateTask(llmConfig, llmPlanningDir, data.tool_input || {}, data.session_id);
232
- if (llmResult && !llmResult.coherent) {
233
- warnings.push('LLM task coherence advisory: ' + (llmResult.issue || 'Task description may not match intended operation.') + ' (confidence: ' + (llmResult.confidence * 100).toFixed(0) + '%)');
234
- }
235
- } catch (_llmErr) {
236
- // Never propagate LLM errors
237
- }
238
-
239
211
  if (warnings.length > 0) {
240
212
  for (const warning of warnings) {
241
213
  logHook('pre-task-dispatch', 'PreToolUse', 'warn', { warning });
@@ -18,7 +18,6 @@ const { logHook } = require('./hook-logger');
18
18
  const { logEvent } = require('./event-logger');
19
19
  const { configLoad, sessionSave } = require('./pbr-tools');
20
20
  const { ensureSessionDir, cleanStaleSessions } = require('./lib/core');
21
- const { resolveConfig, checkHealth, warmUp } = require('./lib/local-llm/health');
22
21
 
23
22
  // Re-export from extracted modules for backward compatibility
24
23
  const {
@@ -134,34 +133,10 @@ async function main() {
134
133
  tryLaunchHookServer(config, planningDir);
135
134
  }
136
135
 
137
- // Write session-start timestamp for local-llm metrics correlation
138
- // Primary: write to .session.json (unified session state)
139
- // Legacy: also write .session-start file for session-cleanup.js backward compat
136
+ // Write session-start timestamp to .session.json (unified session state)
140
137
  const sessionStart = new Date().toISOString();
141
138
  try { sessionSave(planningDir, { sessionStart }, sessionId); } catch (_e) { /* non-fatal */ }
142
- const sessionStartFile = path.join(planningDir, '.session-start');
143
- try {
144
- fs.writeFileSync(sessionStartFile, sessionStart, 'utf8');
145
- } catch (_e) { /* non-fatal */ }
146
139
 
147
- // Local LLM health check (advisory only -- never blocks SessionStart)
148
- let llmContext = '';
149
- try {
150
- const rawLlmConfig = config && config.local_llm;
151
- const llmConfig = resolveConfig(rawLlmConfig);
152
- if (llmConfig.enabled) {
153
- const health = await checkHealth(llmConfig);
154
- if (health.available) {
155
- llmContext = `\nLocal LLM: ${llmConfig.model} (${health.warm ? 'warm' : 'cold start'})`;
156
- if (!health.warm) {
157
- // Fire warm-up without awaiting -- 23s cold start must not block hook
158
- warmUp(llmConfig);
159
- }
160
- } else if (health.reason !== 'disabled') {
161
- llmContext = `\nLocal LLM: unavailable -- ${health.detail || health.reason}`;
162
- }
163
- }
164
- } catch (_e) { /* graceful degradation -- never surface to user */ }
165
140
 
166
141
  // Enrich context with recent session activity from hook server (advisory, fail-open)
167
142
  let enrichedContext = '';
@@ -179,7 +154,7 @@ async function main() {
179
154
 
180
155
  if (context) {
181
156
  const output = {
182
- additionalContext: context + sessionWarning + llmContext + enrichedContext
157
+ additionalContext: context + sessionWarning + enrichedContext
183
158
  };
184
159
  process.stdout.write(JSON.stringify(output));
185
160
  logHook('progress-tracker', 'SessionStart', 'injected', { hasState: true });
@@ -24,7 +24,6 @@ const { logHook, getLogFilename: getHooksFilename, cleanOldHookLogs } = require(
24
24
  const { getLogFilename: getEventsFilename, cleanOldEventLogs } = require('./event-logger');
25
25
  const { tailLines, configLoad } = require('./pbr-tools');
26
26
  const { removeSessionDir, releaseSessionClaims } = require('./lib/core');
27
- const { readSessionMetrics, summarizeMetrics, formatSessionSummary } = require('./lib/local-llm/metrics');
28
27
  const { writeSnapshot } = require('./lib/snapshot-manager');
29
28
 
30
29
  function readStdin() {
@@ -492,35 +491,6 @@ function main() {
492
491
  }
493
492
  } catch (_e) { /* metrics display is best-effort */ }
494
493
 
495
- // Local LLM metrics summary (SessionEnd — sync reads only, never throws)
496
- let llmAdditionalContext = null;
497
- try {
498
- const sessionStartFile = path.join(planningDir, '.session-start');
499
- if (fs.existsSync(sessionStartFile)) {
500
- const sessionStartTime = fs.readFileSync(sessionStartFile, 'utf8').trim();
501
- const entries = readSessionMetrics(planningDir, sessionStartTime);
502
- if (entries.length > 0) {
503
- const summary = summarizeMetrics(entries);
504
- logHook('session-cleanup', 'SessionEnd', 'llm-metrics', {
505
- total_calls: summary.total_calls,
506
- fallback_count: summary.fallback_count,
507
- avg_latency_ms: summary.avg_latency_ms,
508
- tokens_saved: summary.tokens_saved,
509
- cost_saved_usd: summary.cost_saved_usd
510
- });
511
- if (summary.total_calls > 0) {
512
- let modelName = null;
513
- try {
514
- const rawConfig = configLoad(planningDir) || {};
515
- modelName = (rawConfig.local_llm && rawConfig.local_llm.model) || null;
516
- } catch (_e) { /* config read failure is non-fatal */ }
517
- llmAdditionalContext = formatSessionSummary(summary, modelName);
518
- }
519
- }
520
- // Clean up session-start file
521
- try { fs.unlinkSync(sessionStartFile); } catch (_e) { /* non-fatal */ }
522
- }
523
- } catch (_e) { /* metrics never crash the hook */ }
524
494
 
525
495
  // Surface compliance violations from this session
526
496
  let complianceContext = null;
@@ -568,7 +538,7 @@ function main() {
568
538
  // Snapshot failure must never crash SessionEnd
569
539
  }
570
540
 
571
- const combinedContext = [metricsContext, llmAdditionalContext, complianceContext].filter(Boolean).join('\n');
541
+ const combinedContext = [metricsContext, complianceContext].filter(Boolean).join('\n');
572
542
  if (combinedContext) {
573
543
  process.stdout.write(JSON.stringify({ additionalContext: combinedContext }) + '\n');
574
544
  }
@@ -16,6 +16,7 @@ const cp = require('child_process');
16
16
  const { logHook } = require('./hook-logger');
17
17
  const { configLoad } = require('./pbr-tools');
18
18
  const { resolveSessionPath } = require('./lib/core');
19
+ const { extractFrontmatter } = require('./lib/frontmatter');
19
20
  // ANSI color codes
20
21
  const c = {
21
22
  reset: '\x1b[0m',
@@ -542,26 +543,27 @@ function main() {
542
543
 
543
544
  /**
544
545
  * Parse YAML frontmatter from STATE.md content.
545
- * Returns an object with frontmatter fields, or null if no frontmatter.
546
+ * Thin adapter around canonical extractFrontmatter preserving null-on-missing
547
+ * and filtering out null/undefined string values for status-line display.
546
548
  */
547
- function parseFrontmatter(content) {
549
+ const parseFrontmatter = (content) => {
548
550
  if (!content.startsWith('---')) return null;
549
551
  const endIdx = content.indexOf('---', 3);
550
552
  if (endIdx === -1) return null;
551
- const fm = content.substring(3, endIdx);
553
+ const raw = extractFrontmatter(content);
552
554
  const result = {};
553
- for (const line of fm.split(/\r?\n/)) {
554
- const m = line.match(/^(\w[\w_]*):\s*"?([^"]*)"?\s*$/);
555
- if (m) {
556
- const val = m[2].trim();
557
- // Skip YAML nulls and empty values — they'd render as literal "null"
558
- if (val && val !== 'null' && val !== 'undefined') {
559
- result[m[1]] = val;
555
+ for (const [key, val] of Object.entries(raw)) {
556
+ if (typeof val === 'object') {
557
+ // Keep objects/arrays (non-empty)
558
+ if (Array.isArray(val) ? val.length > 0 : Object.keys(val).length > 0) {
559
+ result[key] = val;
560
560
  }
561
+ } else if (typeof val === 'string' && val && val !== 'null' && val !== 'undefined') {
562
+ result[key] = val;
561
563
  }
562
564
  }
563
565
  return result;
564
- }
566
+ };
565
567
 
566
568
  function buildStatusLine(content, ctxPercent, cfg, stdinData, planningDir) {
567
569
  const config = cfg || DEFAULTS;