@nforma.ai/nforma 0.2.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.
Files changed (215) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1024 -0
  3. package/agents/qgsd-codebase-mapper.md +764 -0
  4. package/agents/qgsd-debugger.md +1201 -0
  5. package/agents/qgsd-executor.md +472 -0
  6. package/agents/qgsd-integration-checker.md +443 -0
  7. package/agents/qgsd-phase-researcher.md +502 -0
  8. package/agents/qgsd-plan-checker.md +643 -0
  9. package/agents/qgsd-planner.md +1182 -0
  10. package/agents/qgsd-project-researcher.md +621 -0
  11. package/agents/qgsd-quorum-orchestrator.md +628 -0
  12. package/agents/qgsd-quorum-slot-worker.md +41 -0
  13. package/agents/qgsd-quorum-synthesizer.md +133 -0
  14. package/agents/qgsd-quorum-test-worker.md +37 -0
  15. package/agents/qgsd-quorum-worker.md +161 -0
  16. package/agents/qgsd-research-synthesizer.md +239 -0
  17. package/agents/qgsd-roadmapper.md +660 -0
  18. package/agents/qgsd-verifier.md +628 -0
  19. package/bin/accept-debug-invariant.cjs +165 -0
  20. package/bin/account-manager.cjs +719 -0
  21. package/bin/aggregate-requirements.cjs +466 -0
  22. package/bin/analyze-assumptions.cjs +757 -0
  23. package/bin/analyze-state-space.cjs +921 -0
  24. package/bin/attribute-trace-divergence.cjs +150 -0
  25. package/bin/auth-drivers/gh-cli.cjs +93 -0
  26. package/bin/auth-drivers/index.cjs +46 -0
  27. package/bin/auth-drivers/pool.cjs +67 -0
  28. package/bin/auth-drivers/simple.cjs +95 -0
  29. package/bin/autoClosePtoF.cjs +110 -0
  30. package/bin/blessed-terminal.cjs +350 -0
  31. package/bin/build-phase-index.cjs +472 -0
  32. package/bin/call-quorum-slot.cjs +541 -0
  33. package/bin/ccr-secure-config.cjs +99 -0
  34. package/bin/ccr-secure-start.cjs +83 -0
  35. package/bin/check-bundled-sdks.cjs +177 -0
  36. package/bin/check-coverage-guard.cjs +112 -0
  37. package/bin/check-liveness-fairness.cjs +95 -0
  38. package/bin/check-mcp-health.cjs +123 -0
  39. package/bin/check-provider-health.cjs +395 -0
  40. package/bin/check-results-exit.cjs +24 -0
  41. package/bin/check-spec-sync.cjs +360 -0
  42. package/bin/check-trace-redaction.cjs +271 -0
  43. package/bin/check-trace-schema-drift.cjs +99 -0
  44. package/bin/compareDrift.cjs +21 -0
  45. package/bin/conformance-schema.cjs +12 -0
  46. package/bin/count-scenarios.cjs +420 -0
  47. package/bin/debt-dedup.cjs +144 -0
  48. package/bin/debt-ledger.cjs +61 -0
  49. package/bin/debt-retention.cjs +76 -0
  50. package/bin/debt-state-machine.cjs +80 -0
  51. package/bin/detect-coverage-gaps.cjs +204 -0
  52. package/bin/detect-project-intent.cjs +362 -0
  53. package/bin/export-prism-constants.cjs +164 -0
  54. package/bin/extract-annotations.cjs +633 -0
  55. package/bin/extractFormalExpected.cjs +104 -0
  56. package/bin/fingerprint-drift.cjs +24 -0
  57. package/bin/fingerprint-issue.cjs +46 -0
  58. package/bin/formal-core.cjs +519 -0
  59. package/bin/formal-ref-linker.cjs +141 -0
  60. package/bin/formal-test-sync.cjs +788 -0
  61. package/bin/generate-formal-specs.cjs +588 -0
  62. package/bin/generate-petri-net.cjs +397 -0
  63. package/bin/generate-phase-spec.cjs +249 -0
  64. package/bin/generate-proposed-changes.cjs +194 -0
  65. package/bin/generate-tla-cfg.cjs +122 -0
  66. package/bin/generate-traceability-matrix.cjs +701 -0
  67. package/bin/generate-triage-bundle.cjs +300 -0
  68. package/bin/gh-account-rotate.cjs +34 -0
  69. package/bin/initialize-model-registry.cjs +105 -0
  70. package/bin/install-formal-tools.cjs +382 -0
  71. package/bin/install.js +2424 -0
  72. package/bin/isNumericThreshold.cjs +34 -0
  73. package/bin/issue-classifier.cjs +151 -0
  74. package/bin/levenshtein.cjs +74 -0
  75. package/bin/lint-formal-models.cjs +580 -0
  76. package/bin/load-baseline-requirements.cjs +275 -0
  77. package/bin/manage-agents-core.cjs +815 -0
  78. package/bin/migrate-formal-dir.cjs +172 -0
  79. package/bin/migrate-planning.cjs +206 -0
  80. package/bin/migrate-to-slots.cjs +255 -0
  81. package/bin/nForma.cjs +2726 -0
  82. package/bin/observe-config.cjs +353 -0
  83. package/bin/observe-debt-writer.cjs +140 -0
  84. package/bin/observe-handler-grafana.cjs +128 -0
  85. package/bin/observe-handler-internal.cjs +301 -0
  86. package/bin/observe-handler-logstash.cjs +153 -0
  87. package/bin/observe-handler-prometheus.cjs +185 -0
  88. package/bin/observe-handlers.cjs +436 -0
  89. package/bin/observe-registry.cjs +131 -0
  90. package/bin/observe-render.cjs +168 -0
  91. package/bin/planning-paths.cjs +167 -0
  92. package/bin/polyrepo.cjs +560 -0
  93. package/bin/prism-priority.cjs +153 -0
  94. package/bin/probe-quorum-slots.cjs +167 -0
  95. package/bin/promote-model.cjs +225 -0
  96. package/bin/propose-debug-invariants.cjs +165 -0
  97. package/bin/providers.json +392 -0
  98. package/bin/pty-proxy.py +129 -0
  99. package/bin/qgsd-solve.cjs +2477 -0
  100. package/bin/quorum-consensus-gate.cjs +238 -0
  101. package/bin/quorum-formal-context.cjs +183 -0
  102. package/bin/quorum-slot-dispatch.cjs +934 -0
  103. package/bin/read-policy.cjs +60 -0
  104. package/bin/requirement-map.cjs +63 -0
  105. package/bin/requirements-core.cjs +247 -0
  106. package/bin/resolve-cli.cjs +101 -0
  107. package/bin/review-mcp-logs.cjs +294 -0
  108. package/bin/run-account-manager-tlc.cjs +188 -0
  109. package/bin/run-account-pool-alloy.cjs +158 -0
  110. package/bin/run-alloy.cjs +153 -0
  111. package/bin/run-audit-alloy.cjs +187 -0
  112. package/bin/run-breaker-tlc.cjs +181 -0
  113. package/bin/run-formal-check.cjs +395 -0
  114. package/bin/run-formal-verify.cjs +701 -0
  115. package/bin/run-installer-alloy.cjs +188 -0
  116. package/bin/run-oauth-rotation-prism.cjs +132 -0
  117. package/bin/run-oscillation-tlc.cjs +202 -0
  118. package/bin/run-phase-tlc.cjs +228 -0
  119. package/bin/run-prism.cjs +446 -0
  120. package/bin/run-protocol-tlc.cjs +201 -0
  121. package/bin/run-quorum-composition-alloy.cjs +155 -0
  122. package/bin/run-sensitivity-sweep.cjs +231 -0
  123. package/bin/run-stop-hook-tlc.cjs +188 -0
  124. package/bin/run-tlc.cjs +467 -0
  125. package/bin/run-transcript-alloy.cjs +173 -0
  126. package/bin/run-uppaal.cjs +264 -0
  127. package/bin/secrets.cjs +134 -0
  128. package/bin/sensitivity-report.cjs +219 -0
  129. package/bin/sensitivity-sweep-feedback.cjs +194 -0
  130. package/bin/set-secret.cjs +29 -0
  131. package/bin/setup-telemetry-cron.sh +36 -0
  132. package/bin/sweepPtoF.cjs +63 -0
  133. package/bin/sync-baseline-requirements.cjs +290 -0
  134. package/bin/task-envelope.cjs +360 -0
  135. package/bin/telemetry-collector.cjs +229 -0
  136. package/bin/unified-mcp-server.mjs +735 -0
  137. package/bin/update-agents.cjs +369 -0
  138. package/bin/update-scoreboard.cjs +1134 -0
  139. package/bin/validate-debt-entry.cjs +207 -0
  140. package/bin/validate-invariant.cjs +419 -0
  141. package/bin/validate-memory.cjs +389 -0
  142. package/bin/validate-requirements-haiku.cjs +435 -0
  143. package/bin/validate-traces.cjs +438 -0
  144. package/bin/verify-formal-results.cjs +124 -0
  145. package/bin/verify-quorum-health.cjs +273 -0
  146. package/bin/write-check-result.cjs +106 -0
  147. package/bin/xstate-to-tla.cjs +483 -0
  148. package/bin/xstate-trace-walker.cjs +205 -0
  149. package/commands/qgsd/add-phase.md +43 -0
  150. package/commands/qgsd/add-requirement.md +24 -0
  151. package/commands/qgsd/add-todo.md +47 -0
  152. package/commands/qgsd/audit-milestone.md +37 -0
  153. package/commands/qgsd/check-todos.md +45 -0
  154. package/commands/qgsd/cleanup.md +18 -0
  155. package/commands/qgsd/close-formal-gaps.md +33 -0
  156. package/commands/qgsd/complete-milestone.md +136 -0
  157. package/commands/qgsd/debug.md +166 -0
  158. package/commands/qgsd/discuss-phase.md +83 -0
  159. package/commands/qgsd/execute-phase.md +117 -0
  160. package/commands/qgsd/fix-tests.md +27 -0
  161. package/commands/qgsd/formal-test-sync.md +32 -0
  162. package/commands/qgsd/health.md +22 -0
  163. package/commands/qgsd/help.md +22 -0
  164. package/commands/qgsd/insert-phase.md +32 -0
  165. package/commands/qgsd/join-discord.md +18 -0
  166. package/commands/qgsd/list-phase-assumptions.md +46 -0
  167. package/commands/qgsd/map-codebase.md +71 -0
  168. package/commands/qgsd/map-requirements.md +20 -0
  169. package/commands/qgsd/mcp-restart.md +176 -0
  170. package/commands/qgsd/mcp-set-model.md +134 -0
  171. package/commands/qgsd/mcp-setup.md +1371 -0
  172. package/commands/qgsd/mcp-status.md +274 -0
  173. package/commands/qgsd/mcp-update.md +238 -0
  174. package/commands/qgsd/new-milestone.md +44 -0
  175. package/commands/qgsd/new-project.md +42 -0
  176. package/commands/qgsd/observe.md +260 -0
  177. package/commands/qgsd/pause-work.md +38 -0
  178. package/commands/qgsd/plan-milestone-gaps.md +34 -0
  179. package/commands/qgsd/plan-phase.md +44 -0
  180. package/commands/qgsd/polyrepo.md +50 -0
  181. package/commands/qgsd/progress.md +24 -0
  182. package/commands/qgsd/queue.md +54 -0
  183. package/commands/qgsd/quick.md +133 -0
  184. package/commands/qgsd/quorum-test.md +275 -0
  185. package/commands/qgsd/quorum.md +707 -0
  186. package/commands/qgsd/reapply-patches.md +110 -0
  187. package/commands/qgsd/remove-phase.md +31 -0
  188. package/commands/qgsd/research-phase.md +189 -0
  189. package/commands/qgsd/resume-work.md +40 -0
  190. package/commands/qgsd/set-profile.md +34 -0
  191. package/commands/qgsd/settings.md +39 -0
  192. package/commands/qgsd/solve.md +565 -0
  193. package/commands/qgsd/sync-baselines.md +119 -0
  194. package/commands/qgsd/triage.md +233 -0
  195. package/commands/qgsd/update.md +37 -0
  196. package/commands/qgsd/verify-work.md +38 -0
  197. package/hooks/dist/config-loader.js +297 -0
  198. package/hooks/dist/conformance-schema.cjs +12 -0
  199. package/hooks/dist/gsd-context-monitor.js +64 -0
  200. package/hooks/dist/qgsd-check-update.js +62 -0
  201. package/hooks/dist/qgsd-circuit-breaker.js +682 -0
  202. package/hooks/dist/qgsd-precompact.js +156 -0
  203. package/hooks/dist/qgsd-prompt.js +653 -0
  204. package/hooks/dist/qgsd-session-start.js +122 -0
  205. package/hooks/dist/qgsd-slot-correlator.js +58 -0
  206. package/hooks/dist/qgsd-spec-regen.js +86 -0
  207. package/hooks/dist/qgsd-statusline.js +91 -0
  208. package/hooks/dist/qgsd-stop.js +553 -0
  209. package/hooks/dist/qgsd-token-collector.js +133 -0
  210. package/hooks/dist/unified-mcp-server.mjs +669 -0
  211. package/package.json +95 -0
  212. package/scripts/build-hooks.js +46 -0
  213. package/scripts/postinstall.js +48 -0
  214. package/scripts/secret-audit.sh +45 -0
  215. package/templates/qgsd.json +49 -0
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ // hooks/qgsd-session-start.js
3
+ // SessionStart hook — syncs QGSD keychain secrets into ~/.claude.json
4
+ // on every session start so mcpServers env blocks always reflect current keychain state.
5
+ //
6
+ // Runs synchronously (hook expects process to exit) — uses async IIFE with catch.
7
+
8
+ 'use strict';
9
+
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const fs = require('fs');
13
+
14
+ // ─── Stdin accumulation (for hook input JSON containing cwd) ─────────────────
15
+ let _stdinRaw = '';
16
+ process.stdin.setEncoding('utf8');
17
+ process.stdin.on('data', c => _stdinRaw += c);
18
+
19
+ let _stdinReady;
20
+ const _stdinPromise = new Promise(resolve => { _stdinReady = resolve; });
21
+ process.stdin.on('end', () => _stdinReady());
22
+
23
+ // Locate secrets.cjs — try installed global path first, then local dev path.
24
+ //
25
+ // IMPORTANT: install.js copies bin/*.cjs to ~/.claude/qgsd-bin/ (not ~/.claude/qgsd/bin/).
26
+ // See bin/install.js line ~1679: binDest = path.join(targetDir, 'qgsd-bin')
27
+ // where targetDir = os.homedir() + '/.claude'.
28
+ function findSecrets() {
29
+ const candidates = [
30
+ path.join(os.homedir(), '.claude', 'qgsd-bin', 'secrets.cjs'), // installed path
31
+ path.join(__dirname, '..', 'bin', 'secrets.cjs'), // local dev path
32
+ ];
33
+ for (const p of candidates) {
34
+ try {
35
+ return require(p);
36
+ } catch (_) {}
37
+ }
38
+ return null;
39
+ }
40
+
41
+ (async () => {
42
+ // Resolve project cwd from hook input JSON
43
+ await _stdinPromise;
44
+ let _hookCwd = process.cwd();
45
+ try { _hookCwd = JSON.parse(_stdinRaw).cwd || process.cwd(); } catch (_) {}
46
+
47
+ const secrets = findSecrets();
48
+ if (!secrets) {
49
+ // silently skip — QGSD may not be installed yet or keytar absent
50
+ process.exit(0);
51
+ }
52
+ try {
53
+ await secrets.syncToClaudeJson(secrets.SERVICE);
54
+ } catch (e) {
55
+ // Non-fatal — write to stderr for debug logs, but never block session start
56
+ process.stderr.write('[qgsd-session-start] sync error: ' + e.message + '\n');
57
+ }
58
+
59
+ // Populate CCR config from keytar (fail-silent — CCR may not be installed)
60
+ try {
61
+ const { execFileSync } = require('child_process');
62
+ const nodeFsRef = require('fs');
63
+ const ccrCandidates = [
64
+ path.join(os.homedir(), '.claude', 'qgsd-bin', 'ccr-secure-config.cjs'),
65
+ path.join(__dirname, '..', 'bin', 'ccr-secure-config.cjs'),
66
+ ];
67
+ let ccrConfigPath = null;
68
+ for (const p of ccrCandidates) {
69
+ if (nodeFsRef.existsSync(p)) { ccrConfigPath = p; break; }
70
+ }
71
+ if (ccrConfigPath) {
72
+ execFileSync(process.execPath, [ccrConfigPath], { stdio: 'pipe', timeout: 10000 });
73
+ }
74
+ } catch (e) {
75
+ process.stderr.write('[qgsd-session-start] CCR config error: ' + e.message + '\n');
76
+ }
77
+
78
+ // Telemetry surfacing — inject top unsurfaced issue as additionalContext
79
+ // Guard: only active when running inside the QGSD dev repo itself
80
+ try {
81
+ const pkgPath = path.join(_hookCwd, 'package.json');
82
+ const isQgsdRepo = fs.existsSync(pkgPath) &&
83
+ JSON.parse(fs.readFileSync(pkgPath, 'utf8')).name === 'qgsd';
84
+ const fixesPath = path.join(_hookCwd, '.planning', 'telemetry', 'pending-fixes.json');
85
+ if (isQgsdRepo && fs.existsSync(fixesPath)) {
86
+ const fixes = JSON.parse(fs.readFileSync(fixesPath, 'utf8'));
87
+ const issue = (fixes.issues || []).find(i => !i.surfaced && i.priority >= 50);
88
+ if (issue) {
89
+ issue.surfaced = true;
90
+ issue.surfacedAt = new Date().toISOString();
91
+ fs.writeFileSync(fixesPath, JSON.stringify(fixes, null, 2), 'utf8');
92
+ const ctx = 'Telemetry alert [priority=' + issue.priority + ']: ' + issue.description + '\nSuggested fix: ' + issue.action;
93
+ process.stdout.write(JSON.stringify({
94
+ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: ctx }
95
+ }));
96
+ }
97
+ }
98
+ } catch (_) {}
99
+
100
+ // Memory staleness check — warn about outdated MEMORY.md entries
101
+ try {
102
+ const validateMemoryCandidates = [
103
+ path.join(os.homedir(), '.claude', 'qgsd-bin', 'validate-memory.cjs'),
104
+ path.join(__dirname, '..', 'bin', 'validate-memory.cjs'),
105
+ ];
106
+ let validateMemoryMod = null;
107
+ for (const p of validateMemoryCandidates) {
108
+ try { validateMemoryMod = require(p); break; } catch (_) {}
109
+ }
110
+ if (validateMemoryMod) {
111
+ const { findings } = validateMemoryMod.validateMemory({ cwd: _hookCwd, quiet: true });
112
+ if (findings.length > 0) {
113
+ const summary = findings
114
+ .map(f => '[memory-check] ' + f.message)
115
+ .join('\n');
116
+ process.stderr.write(summary + '\n');
117
+ }
118
+ }
119
+ } catch (_) {}
120
+
121
+ process.exit(0);
122
+ })();
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ // hooks/qgsd-slot-correlator.js
3
+ // SubagentStart hook — writes a correlation placeholder file for qgsd-quorum-slot-worker subagents.
4
+ //
5
+ // At SubagentStart time, the prompt/slot name is not available in the hook payload.
6
+ // This hook writes a stub correlation file { agent_id, ts, slot: null } so the
7
+ // token collector (SubagentStop) can locate and clean up the file per agent_id.
8
+ // The slot is resolved from last_assistant_message preamble by the token collector.
9
+ //
10
+ // Guards:
11
+ // - Only processes agent_type === 'qgsd-quorum-slot-worker' (exits 0 otherwise)
12
+ // - If agent_id is absent: exits 0 gracefully
13
+ // - Fail-open: any unhandled error exits 0
14
+
15
+ 'use strict';
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+
20
+ function main() {
21
+ let raw = '';
22
+ process.stdin.setEncoding('utf8');
23
+ process.stdin.on('data', chunk => { raw += chunk; });
24
+ process.stdin.on('end', () => {
25
+ try {
26
+ const input = JSON.parse(raw);
27
+
28
+ // Guard: only process qgsd-quorum-slot-worker subagents
29
+ if (input.agent_type !== 'qgsd-quorum-slot-worker') {
30
+ process.exit(0);
31
+ }
32
+
33
+ // Guard: agent_id required to write a meaningful correlation file
34
+ if (!input.agent_id) {
35
+ process.exit(0);
36
+ }
37
+
38
+ const pp = require(path.join(__dirname, '..', 'bin', 'planning-paths.cjs'));
39
+ const corrPath = pp.resolve(process.cwd(), 'quorum-correlation', { agentId: input.agent_id });
40
+
41
+ try {
42
+ fs.writeFileSync(corrPath, JSON.stringify({
43
+ agent_id: input.agent_id,
44
+ ts: new Date().toISOString(),
45
+ slot: null, // SubagentStart does not include prompt — slot resolved at SubagentStop
46
+ }), 'utf8');
47
+ } catch (_) {} // observational — never fails
48
+
49
+ process.exit(0);
50
+
51
+ } catch (_) {
52
+ // Fail-open
53
+ process.exit(0);
54
+ }
55
+ });
56
+ }
57
+
58
+ main();
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+ // hooks/qgsd-spec-regen.js
3
+ // PostToolUse hook: when Claude writes to qgsd-workflow.machine.ts,
4
+ // automatically trigger generate-formal-specs.cjs to regenerate TLA+/Alloy specs.
5
+ //
6
+ // LOOP-02 (v0.21-03): Self-Calibrating Feedback Loops
7
+ //
8
+ // Input (stdin): Claude Code PostToolUse JSON payload
9
+ // { tool_name, tool_input: { file_path }, tool_response, cwd, context_window }
10
+ //
11
+ // Output (stdout): JSON { hookSpecificOutput: { hookEventName, additionalContext } }
12
+ // OR: no output (exit 0) when the hook is a no-op.
13
+ //
14
+ // Fail-open: exits 0 in ALL cases — never blocks the Write tool.
15
+
16
+ const { spawnSync } = require('child_process');
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+
20
+ let raw = '';
21
+ process.stdin.setEncoding('utf8');
22
+ process.stdin.on('data', (chunk) => { raw += chunk; });
23
+ process.stdin.on('end', () => {
24
+ try {
25
+ const input = JSON.parse(raw);
26
+ const toolName = input.tool_name || '';
27
+ const filePath = (input.tool_input && input.tool_input.file_path) || '';
28
+
29
+ // Only act on Write calls to qgsd-workflow.machine.ts
30
+ if (toolName !== 'Write' || !filePath.includes('qgsd-workflow.machine.ts')) {
31
+ process.exit(0); // No-op — not a machine file write
32
+ }
33
+
34
+ // Resolve generate-formal-specs.cjs relative to cwd (the project root)
35
+ const cwd = input.cwd || process.cwd();
36
+ const genScript = path.join(cwd, 'bin', 'generate-formal-specs.cjs');
37
+
38
+ const result = spawnSync(process.execPath, [genScript], {
39
+ encoding: 'utf8',
40
+ cwd: cwd,
41
+ timeout: 60000, // 60s: spec generation can take a moment
42
+ });
43
+
44
+ let msg;
45
+ if (result.status === 0 && !result.error) {
46
+ msg = '[spec-regen] Formal specs regenerated (generate-formal-specs.cjs + xstate-to-tla.cjs) from XState machine.';
47
+ } else {
48
+ msg = '[spec-regen] WARNING: generate-formal-specs.cjs failed after machine file write. Run manually to regenerate specs.\n' +
49
+ (result.stderr ? result.stderr.slice(0, 500) : '') +
50
+ (result.error ? String(result.error) : '');
51
+ }
52
+
53
+ // Also regenerate QGSDQuorum_xstate.tla (xstate-to-tla.cjs)
54
+ const xstateScript = path.join(cwd, 'bin', 'xstate-to-tla.cjs');
55
+ const machineFile = path.join(cwd, 'src', 'machines', 'qgsd-workflow.machine.ts');
56
+ const guardsConfig = path.join(cwd, '.planning', 'formal', 'tla', 'guards', 'qgsd-workflow.json');
57
+
58
+ if (fs.existsSync(xstateScript) && fs.existsSync(guardsConfig)) {
59
+ const xstateResult = spawnSync(process.execPath, [
60
+ xstateScript, machineFile,
61
+ '--config=' + guardsConfig,
62
+ '--module=QGSDQuorum'
63
+ ], {
64
+ encoding: 'utf8',
65
+ cwd: cwd,
66
+ timeout: 60000,
67
+ });
68
+
69
+ if (xstateResult.status !== 0 || xstateResult.error) {
70
+ msg += '\n[spec-regen] WARNING: xstate-to-tla.cjs failed. Run manually.';
71
+ if (xstateResult.stderr) msg += '\n' + xstateResult.stderr.slice(0, 300);
72
+ }
73
+ }
74
+
75
+ process.stdout.write(JSON.stringify({
76
+ hookSpecificOutput: {
77
+ hookEventName: 'PostToolUse',
78
+ additionalContext: msg,
79
+ },
80
+ }));
81
+ process.exit(0); // Always exit 0 — fail-open
82
+ } catch (e) {
83
+ // Malformed JSON or unexpected error — fail-open, no output
84
+ process.exit(0);
85
+ }
86
+ });
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // Claude Code Statusline - GSD Edition
3
+ // Shows: model | current task | directory | context usage
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ // Read JSON from stdin
10
+ let input = '';
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('data', chunk => input += chunk);
13
+ process.stdin.on('end', () => {
14
+ try {
15
+ const data = JSON.parse(input);
16
+ const model = data.model?.display_name || 'Claude';
17
+ const dir = data.workspace?.current_dir || process.cwd();
18
+ const session = data.session_id || '';
19
+ const remaining = data.context_window?.remaining_percentage;
20
+
21
+ // Context window display (shows USED percentage scaled to 80% limit)
22
+ // Claude Code enforces an 80% context limit, so we scale to show 100% at that point
23
+ let ctx = '';
24
+ if (remaining != null) {
25
+ const rem = Math.round(remaining);
26
+ const rawUsed = Math.max(0, Math.min(100, 100 - rem));
27
+ // Scale: 80% real usage = 100% displayed
28
+ const used = Math.min(100, Math.round((rawUsed / 80) * 100));
29
+
30
+ // Build progress bar (10 segments)
31
+ const filled = Math.floor(used / 10);
32
+ const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
33
+
34
+ // Color based on scaled usage (thresholds adjusted for new scale)
35
+ if (used < 63) { // ~50% real
36
+ ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
37
+ } else if (used < 81) { // ~65% real
38
+ ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
39
+ } else if (used < 95) { // ~76% real
40
+ ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
41
+ } else {
42
+ ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
43
+ }
44
+ }
45
+
46
+ // Current task from todos
47
+ let task = '';
48
+ const homeDir = os.homedir();
49
+ const todosDir = path.join(homeDir, '.claude', 'todos');
50
+ if (session && fs.existsSync(todosDir)) {
51
+ try {
52
+ const files = fs.readdirSync(todosDir)
53
+ .filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
54
+ .map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
55
+ .sort((a, b) => b.mtime - a.mtime);
56
+
57
+ if (files.length > 0) {
58
+ try {
59
+ const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
60
+ const inProgress = todos.find(t => t.status === 'in_progress');
61
+ if (inProgress) task = inProgress.activeForm || '';
62
+ } catch (e) {}
63
+ }
64
+ } catch (e) {
65
+ // Silently fail on file system errors - don't break statusline
66
+ }
67
+ }
68
+
69
+ // QGSD update available?
70
+ let gsdUpdate = '';
71
+ const cacheFile = path.join(homeDir, '.claude', 'cache', 'qgsd-update-check.json');
72
+ if (fs.existsSync(cacheFile)) {
73
+ try {
74
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
75
+ if (cache.update_available) {
76
+ gsdUpdate = '\x1b[33m⬆ /qgsd:update\x1b[0m │ ';
77
+ }
78
+ } catch (e) {}
79
+ }
80
+
81
+ // Output
82
+ const dirname = path.basename(dir);
83
+ if (task) {
84
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[1m${task}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
85
+ } else {
86
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
87
+ }
88
+ } catch (e) {
89
+ // Silent fail - don't break statusline on parse errors
90
+ }
91
+ });