@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.
- package/LICENSE +22 -0
- package/README.md +1024 -0
- package/agents/qgsd-codebase-mapper.md +764 -0
- package/agents/qgsd-debugger.md +1201 -0
- package/agents/qgsd-executor.md +472 -0
- package/agents/qgsd-integration-checker.md +443 -0
- package/agents/qgsd-phase-researcher.md +502 -0
- package/agents/qgsd-plan-checker.md +643 -0
- package/agents/qgsd-planner.md +1182 -0
- package/agents/qgsd-project-researcher.md +621 -0
- package/agents/qgsd-quorum-orchestrator.md +628 -0
- package/agents/qgsd-quorum-slot-worker.md +41 -0
- package/agents/qgsd-quorum-synthesizer.md +133 -0
- package/agents/qgsd-quorum-test-worker.md +37 -0
- package/agents/qgsd-quorum-worker.md +161 -0
- package/agents/qgsd-research-synthesizer.md +239 -0
- package/agents/qgsd-roadmapper.md +660 -0
- package/agents/qgsd-verifier.md +628 -0
- package/bin/accept-debug-invariant.cjs +165 -0
- package/bin/account-manager.cjs +719 -0
- package/bin/aggregate-requirements.cjs +466 -0
- package/bin/analyze-assumptions.cjs +757 -0
- package/bin/analyze-state-space.cjs +921 -0
- package/bin/attribute-trace-divergence.cjs +150 -0
- package/bin/auth-drivers/gh-cli.cjs +93 -0
- package/bin/auth-drivers/index.cjs +46 -0
- package/bin/auth-drivers/pool.cjs +67 -0
- package/bin/auth-drivers/simple.cjs +95 -0
- package/bin/autoClosePtoF.cjs +110 -0
- package/bin/blessed-terminal.cjs +350 -0
- package/bin/build-phase-index.cjs +472 -0
- package/bin/call-quorum-slot.cjs +541 -0
- package/bin/ccr-secure-config.cjs +99 -0
- package/bin/ccr-secure-start.cjs +83 -0
- package/bin/check-bundled-sdks.cjs +177 -0
- package/bin/check-coverage-guard.cjs +112 -0
- package/bin/check-liveness-fairness.cjs +95 -0
- package/bin/check-mcp-health.cjs +123 -0
- package/bin/check-provider-health.cjs +395 -0
- package/bin/check-results-exit.cjs +24 -0
- package/bin/check-spec-sync.cjs +360 -0
- package/bin/check-trace-redaction.cjs +271 -0
- package/bin/check-trace-schema-drift.cjs +99 -0
- package/bin/compareDrift.cjs +21 -0
- package/bin/conformance-schema.cjs +12 -0
- package/bin/count-scenarios.cjs +420 -0
- package/bin/debt-dedup.cjs +144 -0
- package/bin/debt-ledger.cjs +61 -0
- package/bin/debt-retention.cjs +76 -0
- package/bin/debt-state-machine.cjs +80 -0
- package/bin/detect-coverage-gaps.cjs +204 -0
- package/bin/detect-project-intent.cjs +362 -0
- package/bin/export-prism-constants.cjs +164 -0
- package/bin/extract-annotations.cjs +633 -0
- package/bin/extractFormalExpected.cjs +104 -0
- package/bin/fingerprint-drift.cjs +24 -0
- package/bin/fingerprint-issue.cjs +46 -0
- package/bin/formal-core.cjs +519 -0
- package/bin/formal-ref-linker.cjs +141 -0
- package/bin/formal-test-sync.cjs +788 -0
- package/bin/generate-formal-specs.cjs +588 -0
- package/bin/generate-petri-net.cjs +397 -0
- package/bin/generate-phase-spec.cjs +249 -0
- package/bin/generate-proposed-changes.cjs +194 -0
- package/bin/generate-tla-cfg.cjs +122 -0
- package/bin/generate-traceability-matrix.cjs +701 -0
- package/bin/generate-triage-bundle.cjs +300 -0
- package/bin/gh-account-rotate.cjs +34 -0
- package/bin/initialize-model-registry.cjs +105 -0
- package/bin/install-formal-tools.cjs +382 -0
- package/bin/install.js +2424 -0
- package/bin/isNumericThreshold.cjs +34 -0
- package/bin/issue-classifier.cjs +151 -0
- package/bin/levenshtein.cjs +74 -0
- package/bin/lint-formal-models.cjs +580 -0
- package/bin/load-baseline-requirements.cjs +275 -0
- package/bin/manage-agents-core.cjs +815 -0
- package/bin/migrate-formal-dir.cjs +172 -0
- package/bin/migrate-planning.cjs +206 -0
- package/bin/migrate-to-slots.cjs +255 -0
- package/bin/nForma.cjs +2726 -0
- package/bin/observe-config.cjs +353 -0
- package/bin/observe-debt-writer.cjs +140 -0
- package/bin/observe-handler-grafana.cjs +128 -0
- package/bin/observe-handler-internal.cjs +301 -0
- package/bin/observe-handler-logstash.cjs +153 -0
- package/bin/observe-handler-prometheus.cjs +185 -0
- package/bin/observe-handlers.cjs +436 -0
- package/bin/observe-registry.cjs +131 -0
- package/bin/observe-render.cjs +168 -0
- package/bin/planning-paths.cjs +167 -0
- package/bin/polyrepo.cjs +560 -0
- package/bin/prism-priority.cjs +153 -0
- package/bin/probe-quorum-slots.cjs +167 -0
- package/bin/promote-model.cjs +225 -0
- package/bin/propose-debug-invariants.cjs +165 -0
- package/bin/providers.json +392 -0
- package/bin/pty-proxy.py +129 -0
- package/bin/qgsd-solve.cjs +2477 -0
- package/bin/quorum-consensus-gate.cjs +238 -0
- package/bin/quorum-formal-context.cjs +183 -0
- package/bin/quorum-slot-dispatch.cjs +934 -0
- package/bin/read-policy.cjs +60 -0
- package/bin/requirement-map.cjs +63 -0
- package/bin/requirements-core.cjs +247 -0
- package/bin/resolve-cli.cjs +101 -0
- package/bin/review-mcp-logs.cjs +294 -0
- package/bin/run-account-manager-tlc.cjs +188 -0
- package/bin/run-account-pool-alloy.cjs +158 -0
- package/bin/run-alloy.cjs +153 -0
- package/bin/run-audit-alloy.cjs +187 -0
- package/bin/run-breaker-tlc.cjs +181 -0
- package/bin/run-formal-check.cjs +395 -0
- package/bin/run-formal-verify.cjs +701 -0
- package/bin/run-installer-alloy.cjs +188 -0
- package/bin/run-oauth-rotation-prism.cjs +132 -0
- package/bin/run-oscillation-tlc.cjs +202 -0
- package/bin/run-phase-tlc.cjs +228 -0
- package/bin/run-prism.cjs +446 -0
- package/bin/run-protocol-tlc.cjs +201 -0
- package/bin/run-quorum-composition-alloy.cjs +155 -0
- package/bin/run-sensitivity-sweep.cjs +231 -0
- package/bin/run-stop-hook-tlc.cjs +188 -0
- package/bin/run-tlc.cjs +467 -0
- package/bin/run-transcript-alloy.cjs +173 -0
- package/bin/run-uppaal.cjs +264 -0
- package/bin/secrets.cjs +134 -0
- package/bin/sensitivity-report.cjs +219 -0
- package/bin/sensitivity-sweep-feedback.cjs +194 -0
- package/bin/set-secret.cjs +29 -0
- package/bin/setup-telemetry-cron.sh +36 -0
- package/bin/sweepPtoF.cjs +63 -0
- package/bin/sync-baseline-requirements.cjs +290 -0
- package/bin/task-envelope.cjs +360 -0
- package/bin/telemetry-collector.cjs +229 -0
- package/bin/unified-mcp-server.mjs +735 -0
- package/bin/update-agents.cjs +369 -0
- package/bin/update-scoreboard.cjs +1134 -0
- package/bin/validate-debt-entry.cjs +207 -0
- package/bin/validate-invariant.cjs +419 -0
- package/bin/validate-memory.cjs +389 -0
- package/bin/validate-requirements-haiku.cjs +435 -0
- package/bin/validate-traces.cjs +438 -0
- package/bin/verify-formal-results.cjs +124 -0
- package/bin/verify-quorum-health.cjs +273 -0
- package/bin/write-check-result.cjs +106 -0
- package/bin/xstate-to-tla.cjs +483 -0
- package/bin/xstate-trace-walker.cjs +205 -0
- package/commands/qgsd/add-phase.md +43 -0
- package/commands/qgsd/add-requirement.md +24 -0
- package/commands/qgsd/add-todo.md +47 -0
- package/commands/qgsd/audit-milestone.md +37 -0
- package/commands/qgsd/check-todos.md +45 -0
- package/commands/qgsd/cleanup.md +18 -0
- package/commands/qgsd/close-formal-gaps.md +33 -0
- package/commands/qgsd/complete-milestone.md +136 -0
- package/commands/qgsd/debug.md +166 -0
- package/commands/qgsd/discuss-phase.md +83 -0
- package/commands/qgsd/execute-phase.md +117 -0
- package/commands/qgsd/fix-tests.md +27 -0
- package/commands/qgsd/formal-test-sync.md +32 -0
- package/commands/qgsd/health.md +22 -0
- package/commands/qgsd/help.md +22 -0
- package/commands/qgsd/insert-phase.md +32 -0
- package/commands/qgsd/join-discord.md +18 -0
- package/commands/qgsd/list-phase-assumptions.md +46 -0
- package/commands/qgsd/map-codebase.md +71 -0
- package/commands/qgsd/map-requirements.md +20 -0
- package/commands/qgsd/mcp-restart.md +176 -0
- package/commands/qgsd/mcp-set-model.md +134 -0
- package/commands/qgsd/mcp-setup.md +1371 -0
- package/commands/qgsd/mcp-status.md +274 -0
- package/commands/qgsd/mcp-update.md +238 -0
- package/commands/qgsd/new-milestone.md +44 -0
- package/commands/qgsd/new-project.md +42 -0
- package/commands/qgsd/observe.md +260 -0
- package/commands/qgsd/pause-work.md +38 -0
- package/commands/qgsd/plan-milestone-gaps.md +34 -0
- package/commands/qgsd/plan-phase.md +44 -0
- package/commands/qgsd/polyrepo.md +50 -0
- package/commands/qgsd/progress.md +24 -0
- package/commands/qgsd/queue.md +54 -0
- package/commands/qgsd/quick.md +133 -0
- package/commands/qgsd/quorum-test.md +275 -0
- package/commands/qgsd/quorum.md +707 -0
- package/commands/qgsd/reapply-patches.md +110 -0
- package/commands/qgsd/remove-phase.md +31 -0
- package/commands/qgsd/research-phase.md +189 -0
- package/commands/qgsd/resume-work.md +40 -0
- package/commands/qgsd/set-profile.md +34 -0
- package/commands/qgsd/settings.md +39 -0
- package/commands/qgsd/solve.md +565 -0
- package/commands/qgsd/sync-baselines.md +119 -0
- package/commands/qgsd/triage.md +233 -0
- package/commands/qgsd/update.md +37 -0
- package/commands/qgsd/verify-work.md +38 -0
- package/hooks/dist/config-loader.js +297 -0
- package/hooks/dist/conformance-schema.cjs +12 -0
- package/hooks/dist/gsd-context-monitor.js +64 -0
- package/hooks/dist/qgsd-check-update.js +62 -0
- package/hooks/dist/qgsd-circuit-breaker.js +682 -0
- package/hooks/dist/qgsd-precompact.js +156 -0
- package/hooks/dist/qgsd-prompt.js +653 -0
- package/hooks/dist/qgsd-session-start.js +122 -0
- package/hooks/dist/qgsd-slot-correlator.js +58 -0
- package/hooks/dist/qgsd-spec-regen.js +86 -0
- package/hooks/dist/qgsd-statusline.js +91 -0
- package/hooks/dist/qgsd-stop.js +553 -0
- package/hooks/dist/qgsd-token-collector.js +133 -0
- package/hooks/dist/unified-mcp-server.mjs +669 -0
- package/package.json +95 -0
- package/scripts/build-hooks.js +46 -0
- package/scripts/postinstall.js +48 -0
- package/scripts/secret-audit.sh +45 -0
- 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
|
+
});
|