@nforma.ai/nforma 0.2.1 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +2 -2
  2. package/agents/{qgsd-codebase-mapper.md → nf-codebase-mapper.md} +1 -1
  3. package/agents/{qgsd-debugger.md → nf-debugger.md} +3 -3
  4. package/agents/{qgsd-executor.md → nf-executor.md} +14 -14
  5. package/agents/{qgsd-integration-checker.md → nf-integration-checker.md} +1 -1
  6. package/agents/{qgsd-phase-researcher.md → nf-phase-researcher.md} +6 -6
  7. package/agents/{qgsd-plan-checker.md → nf-plan-checker.md} +9 -9
  8. package/agents/{qgsd-planner.md → nf-planner.md} +9 -9
  9. package/agents/{qgsd-project-researcher.md → nf-project-researcher.md} +2 -2
  10. package/agents/{qgsd-quorum-orchestrator.md → nf-quorum-orchestrator.md} +33 -33
  11. package/agents/{qgsd-quorum-slot-worker.md → nf-quorum-slot-worker.md} +3 -3
  12. package/agents/{qgsd-quorum-synthesizer.md → nf-quorum-synthesizer.md} +3 -3
  13. package/agents/{qgsd-quorum-test-worker.md → nf-quorum-test-worker.md} +1 -1
  14. package/agents/{qgsd-quorum-worker.md → nf-quorum-worker.md} +6 -6
  15. package/agents/{qgsd-research-synthesizer.md → nf-research-synthesizer.md} +5 -5
  16. package/agents/{qgsd-roadmapper.md → nf-roadmapper.md} +3 -3
  17. package/agents/{qgsd-verifier.md → nf-verifier.md} +8 -8
  18. package/bin/accept-debug-invariant.cjs +2 -2
  19. package/bin/account-manager.cjs +10 -10
  20. package/bin/aggregate-requirements.cjs +1 -1
  21. package/bin/analyze-assumptions.cjs +3 -3
  22. package/bin/analyze-state-space.cjs +14 -14
  23. package/bin/assumption-register.cjs +146 -0
  24. package/bin/attribute-trace-divergence.cjs +1 -1
  25. package/bin/auth-drivers/gh-cli.cjs +1 -1
  26. package/bin/auth-drivers/pool.cjs +1 -1
  27. package/bin/autoClosePtoF.cjs +3 -3
  28. package/bin/budget-tracker.cjs +77 -0
  29. package/bin/build-layer-manifest.cjs +153 -0
  30. package/bin/call-quorum-slot.cjs +3 -3
  31. package/bin/ccr-secure-config.cjs +5 -5
  32. package/bin/check-bundled-sdks.cjs +1 -1
  33. package/bin/check-mcp-health.cjs +1 -1
  34. package/bin/check-provider-health.cjs +6 -6
  35. package/bin/check-spec-sync.cjs +26 -26
  36. package/bin/check-trace-schema-drift.cjs +5 -5
  37. package/bin/conformance-schema.cjs +2 -2
  38. package/bin/cross-layer-dashboard.cjs +297 -0
  39. package/bin/design-impact.cjs +377 -0
  40. package/bin/detect-coverage-gaps.cjs +7 -7
  41. package/bin/failure-mode-catalog.cjs +227 -0
  42. package/bin/failure-taxonomy.cjs +177 -0
  43. package/bin/formal-scope-scan.cjs +179 -0
  44. package/bin/gate-a-grounding.cjs +334 -0
  45. package/bin/gate-b-abstraction.cjs +243 -0
  46. package/bin/gate-c-validation.cjs +166 -0
  47. package/bin/generate-formal-specs.cjs +17 -17
  48. package/bin/generate-petri-net.cjs +3 -3
  49. package/bin/generate-tla-cfg.cjs +5 -5
  50. package/bin/git-heatmap.cjs +571 -0
  51. package/bin/harness-diagnostic.cjs +326 -0
  52. package/bin/hazard-model.cjs +261 -0
  53. package/bin/install-formal-tools.cjs +1 -1
  54. package/bin/install.js +184 -139
  55. package/bin/instrumentation-map.cjs +178 -0
  56. package/bin/invariant-catalog.cjs +437 -0
  57. package/bin/issue-classifier.cjs +2 -2
  58. package/bin/load-baseline-requirements.cjs +4 -4
  59. package/bin/manage-agents-core.cjs +32 -32
  60. package/bin/migrate-to-slots.cjs +39 -39
  61. package/bin/mismatch-register.cjs +217 -0
  62. package/bin/nForma.cjs +176 -81
  63. package/bin/{qgsd-solve.cjs → nf-solve.cjs} +327 -14
  64. package/bin/observe-config.cjs +8 -0
  65. package/bin/observe-debt-writer.cjs +1 -1
  66. package/bin/observe-handler-deps.cjs +356 -0
  67. package/bin/observe-handler-grafana.cjs +2 -17
  68. package/bin/observe-handler-internal.cjs +5 -5
  69. package/bin/observe-handler-logstash.cjs +2 -17
  70. package/bin/observe-handler-prometheus.cjs +2 -17
  71. package/bin/observe-handler-upstream.cjs +251 -0
  72. package/bin/observe-handlers.cjs +12 -33
  73. package/bin/observe-render.cjs +68 -22
  74. package/bin/observe-utils.cjs +37 -0
  75. package/bin/observed-fsm.cjs +324 -0
  76. package/bin/planning-paths.cjs +6 -0
  77. package/bin/polyrepo.cjs +1 -1
  78. package/bin/probe-quorum-slots.cjs +1 -1
  79. package/bin/promote-gate-maturity.cjs +274 -0
  80. package/bin/promote-model.cjs +1 -1
  81. package/bin/propose-debug-invariants.cjs +1 -1
  82. package/bin/quorum-cache.cjs +144 -0
  83. package/bin/quorum-consensus-gate.cjs +1 -1
  84. package/bin/quorum-preflight.cjs +89 -0
  85. package/bin/quorum-slot-dispatch.cjs +6 -6
  86. package/bin/requirements-core.cjs +1 -1
  87. package/bin/review-mcp-logs.cjs +1 -1
  88. package/bin/risk-heatmap.cjs +151 -0
  89. package/bin/run-account-manager-tlc.cjs +4 -4
  90. package/bin/run-account-pool-alloy.cjs +2 -2
  91. package/bin/run-alloy.cjs +2 -2
  92. package/bin/run-audit-alloy.cjs +2 -2
  93. package/bin/run-breaker-tlc.cjs +3 -3
  94. package/bin/run-formal-check.cjs +9 -9
  95. package/bin/run-formal-verify.cjs +30 -9
  96. package/bin/run-installer-alloy.cjs +2 -2
  97. package/bin/run-oscillation-tlc.cjs +4 -4
  98. package/bin/run-phase-tlc.cjs +1 -1
  99. package/bin/run-protocol-tlc.cjs +4 -4
  100. package/bin/run-quorum-composition-alloy.cjs +2 -2
  101. package/bin/run-sensitivity-sweep.cjs +2 -2
  102. package/bin/run-stop-hook-tlc.cjs +3 -3
  103. package/bin/run-tlc.cjs +21 -21
  104. package/bin/run-transcript-alloy.cjs +2 -2
  105. package/bin/secrets.cjs +5 -5
  106. package/bin/security-sweep.cjs +238 -0
  107. package/bin/sensitivity-report.cjs +3 -3
  108. package/bin/set-secret.cjs +5 -5
  109. package/bin/setup-telemetry-cron.sh +3 -3
  110. package/bin/stall-detector.cjs +126 -0
  111. package/bin/state-candidates.cjs +206 -0
  112. package/bin/sync-baseline-requirements.cjs +1 -1
  113. package/bin/telemetry-collector.cjs +1 -1
  114. package/bin/test-changed.cjs +111 -0
  115. package/bin/test-recipe-gen.cjs +250 -0
  116. package/bin/trace-corpus-stats.cjs +211 -0
  117. package/bin/unified-mcp-server.mjs +3 -3
  118. package/bin/update-scoreboard.cjs +1 -1
  119. package/bin/validate-memory.cjs +2 -2
  120. package/bin/validate-traces.cjs +10 -10
  121. package/bin/verify-quorum-health.cjs +66 -5
  122. package/bin/xstate-to-tla.cjs +4 -4
  123. package/bin/xstate-trace-walker.cjs +3 -3
  124. package/commands/{qgsd → nf}/add-phase.md +3 -3
  125. package/commands/{qgsd → nf}/add-requirement.md +3 -3
  126. package/commands/{qgsd → nf}/add-todo.md +3 -3
  127. package/commands/{qgsd → nf}/audit-milestone.md +4 -4
  128. package/commands/{qgsd → nf}/check-todos.md +3 -3
  129. package/commands/{qgsd → nf}/cleanup.md +3 -3
  130. package/commands/{qgsd → nf}/close-formal-gaps.md +2 -2
  131. package/commands/{qgsd → nf}/complete-milestone.md +9 -9
  132. package/commands/{qgsd → nf}/debug.md +9 -9
  133. package/commands/{qgsd → nf}/discuss-phase.md +3 -3
  134. package/commands/{qgsd → nf}/execute-phase.md +15 -15
  135. package/commands/{qgsd → nf}/fix-tests.md +3 -3
  136. package/commands/{qgsd → nf}/formal-test-sync.md +1 -1
  137. package/commands/{qgsd → nf}/health.md +3 -3
  138. package/commands/{qgsd → nf}/help.md +3 -3
  139. package/commands/{qgsd → nf}/insert-phase.md +3 -3
  140. package/commands/nf/join-discord.md +18 -0
  141. package/commands/{qgsd → nf}/list-phase-assumptions.md +2 -2
  142. package/commands/{qgsd → nf}/map-codebase.md +7 -7
  143. package/commands/{qgsd → nf}/map-requirements.md +3 -3
  144. package/commands/{qgsd → nf}/mcp-restart.md +3 -3
  145. package/commands/{qgsd → nf}/mcp-set-model.md +8 -8
  146. package/commands/{qgsd → nf}/mcp-setup.md +63 -63
  147. package/commands/{qgsd → nf}/mcp-status.md +3 -3
  148. package/commands/{qgsd → nf}/mcp-update.md +7 -7
  149. package/commands/{qgsd → nf}/new-milestone.md +8 -8
  150. package/commands/{qgsd → nf}/new-project.md +8 -8
  151. package/commands/{qgsd → nf}/observe.md +49 -16
  152. package/commands/{qgsd → nf}/pause-work.md +3 -3
  153. package/commands/{qgsd → nf}/plan-milestone-gaps.md +5 -5
  154. package/commands/{qgsd → nf}/plan-phase.md +6 -6
  155. package/commands/{qgsd → nf}/polyrepo.md +2 -2
  156. package/commands/{qgsd → nf}/progress.md +3 -3
  157. package/commands/{qgsd → nf}/queue.md +2 -2
  158. package/commands/{qgsd → nf}/quick.md +8 -8
  159. package/commands/{qgsd → nf}/quorum-test.md +10 -10
  160. package/commands/{qgsd → nf}/quorum.md +36 -86
  161. package/commands/{qgsd → nf}/reapply-patches.md +2 -2
  162. package/commands/{qgsd → nf}/remove-phase.md +3 -3
  163. package/commands/{qgsd → nf}/research-phase.md +12 -12
  164. package/commands/{qgsd → nf}/resume-work.md +3 -3
  165. package/commands/nf/review-requirements.md +31 -0
  166. package/commands/{qgsd → nf}/set-profile.md +3 -3
  167. package/commands/{qgsd → nf}/settings.md +6 -6
  168. package/commands/{qgsd → nf}/solve.md +35 -35
  169. package/commands/{qgsd → nf}/sync-baselines.md +4 -4
  170. package/commands/{qgsd → nf}/triage.md +10 -10
  171. package/commands/{qgsd → nf}/update.md +3 -3
  172. package/commands/{qgsd → nf}/verify-work.md +5 -5
  173. package/hooks/dist/config-loader.js +188 -32
  174. package/hooks/dist/conformance-schema.cjs +2 -2
  175. package/hooks/dist/gsd-context-monitor.js +118 -13
  176. package/hooks/dist/{qgsd-check-update.js → nf-check-update.js} +5 -5
  177. package/hooks/dist/{qgsd-circuit-breaker.js → nf-circuit-breaker.js} +35 -24
  178. package/hooks/dist/{qgsd-precompact.js → nf-precompact.js} +13 -13
  179. package/hooks/dist/{qgsd-prompt.js → nf-prompt.js} +110 -33
  180. package/hooks/dist/nf-session-start.js +185 -0
  181. package/hooks/dist/{qgsd-slot-correlator.js → nf-slot-correlator.js} +13 -5
  182. package/hooks/dist/{qgsd-spec-regen.js → nf-spec-regen.js} +17 -8
  183. package/hooks/dist/{qgsd-statusline.js → nf-statusline.js} +12 -3
  184. package/hooks/dist/{qgsd-stop.js → nf-stop.js} +152 -18
  185. package/hooks/dist/{qgsd-token-collector.js → nf-token-collector.js} +12 -4
  186. package/hooks/dist/unified-mcp-server.mjs +2 -2
  187. package/package.json +6 -4
  188. package/scripts/build-hooks.js +13 -6
  189. package/scripts/secret-audit.sh +1 -1
  190. package/scripts/verify-hooks-sync.cjs +90 -0
  191. package/templates/{qgsd.json → nf.json} +4 -4
  192. package/commands/qgsd/join-discord.md +0 -18
  193. package/hooks/dist/qgsd-session-start.js +0 -122
package/bin/install.js CHANGED
@@ -135,23 +135,23 @@ function getGlobalDir(runtime, explicitDir = null) {
135
135
  }
136
136
 
137
137
  const banner = '\n' +
138
- salmon + ' ███╗ ██╗' + cyan + '███████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗\n' +
139
- salmon + ' ████╗ ██║' + cyan + '██╔════╝██╔═══██╗██╔══██╗████╗ ████║██╔══██╗\n' +
140
- salmon + ' ██╔██╗ ██║' + cyan + '█████╗ ██║ ██║██████╔╝██╔████╔██║███████║\n' +
141
- salmon + ' ██║╚██╗██║' + cyan + '██╔══╝ ██║ ██║██╔══██╗██║╚██╔╝██║██╔══██║\n' +
142
- salmon + ' ██║ ╚████║' + cyan + '██║ ╚██████╔╝██║ ██║██║ ╚═╝ ██║██║ ██║\n' +
143
- salmon + ' ╚═╝ ╚═══╝' + cyan + '╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝' + reset + '\n' +
138
+ salmon + ' ' + cyan + '███████╗\n' +
139
+ salmon + ' ' + cyan + '██╔════╝\n' +
140
+ salmon + ' ██████╗ ' + cyan + '█████╗\n' +
141
+ salmon + ' ██╔══██╗ ' + cyan + '██╔══╝\n' +
142
+ salmon + ' ██║ ██║ ' + cyan + '██║\n' +
143
+ salmon + ' ╚═╝ ╚═╝ ' + cyan + '╚═╝' + reset + '\n' +
144
144
  '\n' +
145
- ' nForma — Quorum Gets Shit Done ' + dim + 'v' + pkg.version + reset + '\n' +
145
+ ' nForma — Consensus before code. Proof before production. ' + dim + 'v' + pkg.version + reset + '\n' +
146
146
  ' Built on GSD-CC by TÂCHES.\n' +
147
- ' Full automation through quorum of coding agents. By Jonathan Borduas.\n' +
147
+ ' A quorum of diverse coding agents + formal verification. By Jonathan Borduas.\n' +
148
148
  '\n' +
149
149
  cyan + ' The task of leadership is to create an alignment of strengths\n' +
150
150
  ' so strong that it makes the system\u2019s weaknesses irrelevant.\n' +
151
151
  dim + ' \u2014 Peter Drucker' + reset + '\n';
152
152
 
153
153
  // nForma: MCP auto-detection — keyword map for quorum model server matching
154
- const QGSD_KEYWORD_MAP = {
154
+ const NF_KEYWORD_MAP = {
155
155
  codex: { keywords: ['codex'], defaultPrefix: 'mcp__codex-cli-1__' },
156
156
  gemini: { keywords: ['gemini'], defaultPrefix: 'mcp__gemini-cli-1__' },
157
157
  opencode: { keywords: ['opencode'], defaultPrefix: 'mcp__opencode-1__' },
@@ -177,7 +177,7 @@ function buildRequiredModelsFromMcp() {
177
177
  const requiredModels = {};
178
178
  let anyDetected = false;
179
179
 
180
- for (const [modelKey, { keywords, defaultPrefix }] of Object.entries(QGSD_KEYWORD_MAP)) {
180
+ for (const [modelKey, { keywords, defaultPrefix }] of Object.entries(NF_KEYWORD_MAP)) {
181
181
  const matched = Object.keys(mcpServers).find(serverName =>
182
182
  keywords.some(kw => serverName.toLowerCase().includes(kw))
183
183
  );
@@ -192,7 +192,7 @@ function buildRequiredModelsFromMcp() {
192
192
  }
193
193
 
194
194
  if (!anyDetected) {
195
- console.warn(` ${yellow}⚠${reset} No quorum MCP servers detected — using hardcoded defaults. Edit ~/.claude/qgsd.json to configure.`);
195
+ console.warn(` ${yellow}⚠${reset} No quorum MCP servers detected — using hardcoded defaults. Edit ~/.claude/nf.json to configure.`);
196
196
  }
197
197
 
198
198
  return requiredModels;
@@ -255,7 +255,7 @@ function warnMissingMcpServers() {
255
255
  return;
256
256
  }
257
257
 
258
- for (const [modelKey, { keywords }] of Object.entries(QGSD_KEYWORD_MAP)) {
258
+ for (const [modelKey, { keywords }] of Object.entries(NF_KEYWORD_MAP)) {
259
259
  const found = Object.keys(mcpServers).some(serverName =>
260
260
  keywords.some(kw => serverName.toLowerCase().includes(kw))
261
261
  );
@@ -269,7 +269,7 @@ function warnMissingMcpServers() {
269
269
  }
270
270
 
271
271
  // INST-01: Detects whether any claude-mcp-server quorum agents are configured.
272
- // Used in finishInstall() to nudge new users to run /qgsd:mcp-setup.
272
+ // Used in finishInstall() to nudge new users to run /nf:mcp-setup.
273
273
  // Fail-open: returns false on read errors (never blocks install).
274
274
  function hasClaudeMcpAgents() {
275
275
  const claudeJsonPath = path.join(os.homedir(), '.claude.json');
@@ -624,8 +624,8 @@ function convertClaudeToOpencodeFrontmatter(content) {
624
624
  convertedContent = convertedContent.replace(/\bAskUserQuestion\b/g, 'question');
625
625
  convertedContent = convertedContent.replace(/\bSlashCommand\b/g, 'skill');
626
626
  convertedContent = convertedContent.replace(/\bTodoWrite\b/g, 'todowrite');
627
- // Replace /qgsd:command with /qgsd-command for opencode (flat command structure)
628
- convertedContent = convertedContent.replace(/\/qgsd:/g, '/qgsd-');
627
+ // Replace /nf:command with /nf-command for opencode (flat command structure)
628
+ convertedContent = convertedContent.replace(/\/nf:/g, '/nf-');
629
629
  // Replace ~/.claude with ~/.config/opencode (OpenCode's correct config location)
630
630
  convertedContent = convertedContent.replace(/~\/\.claude\b/g, '~/.config/opencode');
631
631
  // Replace general-purpose subagent type with OpenCode's equivalent "general"
@@ -767,12 +767,12 @@ function convertClaudeToGeminiToml(content) {
767
767
 
768
768
  /**
769
769
  * Copy commands to a flat structure for OpenCode
770
- * OpenCode expects: command/qgsd-help.md (invoked as /qgsd-help)
771
- * Source structure: commands/qgsd/help.md
770
+ * OpenCode expects: command/nf-help.md (invoked as /nf-help)
771
+ * Source structure: commands/nf/help.md
772
772
  *
773
- * @param {string} srcDir - Source directory (e.g., commands/qgsd/)
773
+ * @param {string} srcDir - Source directory (e.g., commands/nf/)
774
774
  * @param {string} destDir - Destination directory (e.g., command/)
775
- * @param {string} prefix - Prefix for filenames (e.g., 'qgsd')
775
+ * @param {string} prefix - Prefix for filenames (e.g., 'nf')
776
776
  * @param {string} pathPrefix - Path prefix for file references
777
777
  * @param {string} runtime - Target runtime ('claude' or 'opencode')
778
778
  */
@@ -781,7 +781,7 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
781
781
  return;
782
782
  }
783
783
 
784
- // Remove old qgsd-*.md files before copying new ones
784
+ // Remove old nf-*.md files before copying new ones
785
785
  if (fs.existsSync(destDir)) {
786
786
  for (const file of fs.readdirSync(destDir)) {
787
787
  if (file.startsWith(`${prefix}-`) && file.endsWith('.md')) {
@@ -799,10 +799,10 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
799
799
 
800
800
  if (entry.isDirectory()) {
801
801
  // Recurse into subdirectories, adding to prefix
802
- // e.g., commands/qgsd/debug/start.md -> command/qgsd-debug-start.md
802
+ // e.g., commands/nf/debug/start.md -> command/nf-debug-start.md
803
803
  copyFlattenedCommands(srcPath, destDir, `${prefix}-${entry.name}`, pathPrefix, runtime);
804
804
  } else if (entry.name.endsWith('.md')) {
805
- // Flatten: help.md -> qgsd-help.md
805
+ // Flatten: help.md -> nf-help.md
806
806
  const baseName = entry.name.replace('.md', '');
807
807
  const destName = `${prefix}-${baseName}.md`;
808
808
  const destPath = path.join(destDir, destName);
@@ -884,8 +884,14 @@ function cleanupOrphanedFiles(configDir) {
884
884
  const orphanedFiles = [
885
885
  'hooks/gsd-notify.sh', // Removed in v1.6.x
886
886
  'hooks/statusline.js', // Renamed to gsd-statusline.js in v1.9.0
887
- 'hooks/gsd-statusline.js', // Renamed to qgsd-statusline.js in v0.2
888
- 'hooks/gsd-check-update.js', // Renamed to qgsd-check-update.js in v0.2
887
+ 'hooks/gsd-statusline.js', // Renamed to nf-statusline.js in v0.2
888
+ 'hooks/gsd-check-update.js', // Renamed to nf-check-update.js in v0.2
889
+ 'hooks/qgsd-prompt.js', // Renamed to nf-prompt.js in v0.2
890
+ 'hooks/qgsd-stop.js', // Renamed to nf-stop.js in v0.2
891
+ 'hooks/qgsd-circuit-breaker.js', // Renamed to nf-circuit-breaker.js in v0.2
892
+ 'hooks/qgsd-session-start.js', // Renamed to nf-session-start.js in v0.2
893
+ 'hooks/qgsd-check-update.js', // Renamed to nf-check-update.js in v0.2
894
+ 'hooks/qgsd-statusline.js', // Renamed to nf-statusline.js in v0.2
889
895
  ];
890
896
 
891
897
  for (const relPath of orphanedFiles) {
@@ -907,8 +913,14 @@ function cleanupOrphanedHooks(settings) {
907
913
  'gsd-intel-index.js', // Removed in v1.9.2
908
914
  'gsd-intel-session.js', // Removed in v1.9.2
909
915
  'gsd-intel-prune.js', // Removed in v1.9.2
910
- 'hooks/gsd-check-update.js', // Renamed to qgsd-check-update.js in v0.2
911
- 'hooks/gsd-statusline.js', // Renamed to qgsd-statusline.js in v0.2
916
+ 'hooks/gsd-check-update.js', // Renamed to nf-check-update.js in v0.2
917
+ 'hooks/gsd-statusline.js', // Renamed to nf-statusline.js in v0.2
918
+ 'qgsd-prompt.js', // Renamed to nf-prompt.js in v0.2
919
+ 'qgsd-stop.js', // Renamed to nf-stop.js in v0.2
920
+ 'qgsd-circuit-breaker.js', // Renamed to nf-circuit-breaker.js in v0.2
921
+ 'qgsd-session-start.js', // Renamed to nf-session-start.js in v0.2
922
+ 'qgsd-check-update.js', // Renamed to nf-check-update.js in v0.2
923
+ 'qgsd-statusline.js', // Renamed to nf-statusline.js in v0.2
912
924
  ];
913
925
 
914
926
  let cleanedHooks = false;
@@ -941,15 +953,16 @@ function cleanupOrphanedHooks(settings) {
941
953
  console.log(` ${green}✓${reset} Removed orphaned hook registrations`);
942
954
  }
943
955
 
944
- // Fix #330 + qgsd migration: update statusLine if it points to old statusline path
956
+ // Fix #330 + nf migration: update statusLine if it points to old statusline path
945
957
  if (settings.statusLine && settings.statusLine.command) {
946
958
  const cmd = settings.statusLine.command;
947
- if ((cmd.includes('statusline.js') || cmd.includes('gsd-statusline.js')) &&
948
- !cmd.includes('qgsd-statusline.js')) {
959
+ if ((cmd.includes('statusline.js') || cmd.includes('gsd-statusline.js') || cmd.includes('qgsd-statusline.js')) &&
960
+ !cmd.includes('nf-statusline.js')) {
949
961
  settings.statusLine.command = cmd
950
- .replace(/\bgsd-statusline\.js\b/, 'qgsd-statusline.js')
951
- .replace(/\bstatusline\.js\b/, 'qgsd-statusline.js');
952
- console.log(` ${green}✓${reset} Updated statusline path → qgsd-statusline.js`);
962
+ .replace(/\bqgsd-statusline\.js\b/, 'nf-statusline.js')
963
+ .replace(/\bgsd-statusline\.js\b/, 'nf-statusline.js')
964
+ .replace(/\bstatusline\.js\b/, 'nf-statusline.js');
965
+ console.log(` ${green}✓${reset} Updated statusline path → nf-statusline.js`);
953
966
  }
954
967
  }
955
968
 
@@ -992,12 +1005,12 @@ function uninstall(isGlobal, runtime = 'claude') {
992
1005
 
993
1006
  // 1. Remove GSD commands directory
994
1007
  if (isOpencode) {
995
- // OpenCode: remove command/qgsd-*.md files
1008
+ // OpenCode: remove command/nf-*.md files (and legacy qgsd-*.md)
996
1009
  const commandDir = path.join(targetDir, 'command');
997
1010
  if (fs.existsSync(commandDir)) {
998
1011
  const files = fs.readdirSync(commandDir);
999
1012
  for (const file of files) {
1000
- if (file.startsWith('qgsd-') && file.endsWith('.md')) {
1013
+ if (file.startsWith('nf-') && file.endsWith('.md')) {
1001
1014
  fs.unlinkSync(path.join(commandDir, file));
1002
1015
  removedCount++;
1003
1016
  }
@@ -1005,21 +1018,21 @@ function uninstall(isGlobal, runtime = 'claude') {
1005
1018
  console.log(` ${green}✓${reset} Removed GSD commands from command/`);
1006
1019
  }
1007
1020
  } else {
1008
- // Claude Code & Gemini: remove commands/qgsd/ directory
1009
- const gsdCommandsDir = path.join(targetDir, 'commands', 'qgsd');
1021
+ // Claude Code & Gemini: remove commands/nf/ directory
1022
+ const gsdCommandsDir = path.join(targetDir, 'commands', 'nf');
1010
1023
  if (fs.existsSync(gsdCommandsDir)) {
1011
1024
  fs.rmSync(gsdCommandsDir, { recursive: true });
1012
1025
  removedCount++;
1013
- console.log(` ${green}✓${reset} Removed commands/qgsd/`);
1026
+ console.log(` ${green}✓${reset} Removed commands/nf/`);
1014
1027
  }
1015
1028
  }
1016
1029
 
1017
- // 2. Remove qgsd directory
1018
- const gsdDir = path.join(targetDir, 'qgsd');
1030
+ // 2. Remove nf directory
1031
+ const gsdDir = path.join(targetDir, 'nf');
1019
1032
  if (fs.existsSync(gsdDir)) {
1020
1033
  fs.rmSync(gsdDir, { recursive: true });
1021
1034
  removedCount++;
1022
- console.log(` ${green}✓${reset} Removed qgsd/`);
1035
+ console.log(` ${green}✓${reset} Removed nf/`);
1023
1036
  }
1024
1037
 
1025
1038
  // 2b. Migration: warn about old get-shit-done/ and commands/gsd/ paths from pre-v0.2 nForma installs
@@ -1036,13 +1049,13 @@ function uninstall(isGlobal, runtime = 'claude') {
1036
1049
  console.log();
1037
1050
  }
1038
1051
 
1039
- // 3. Remove GSD agents (qgsd-*.md files only)
1052
+ // 3. Remove GSD agents (nf-*.md files only)
1040
1053
  const agentsDir = path.join(targetDir, 'agents');
1041
1054
  if (fs.existsSync(agentsDir)) {
1042
1055
  const files = fs.readdirSync(agentsDir);
1043
1056
  let agentCount = 0;
1044
1057
  for (const file of files) {
1045
- if (file.startsWith('qgsd-') && file.endsWith('.md')) {
1058
+ if (file.startsWith('nf-') && file.endsWith('.md')) {
1046
1059
  fs.unlinkSync(path.join(agentsDir, file));
1047
1060
  agentCount++;
1048
1061
  }
@@ -1065,7 +1078,11 @@ function uninstall(isGlobal, runtime = 'claude') {
1065
1078
  // 4. Remove GSD hooks
1066
1079
  const hooksDir = path.join(targetDir, 'hooks');
1067
1080
  if (fs.existsSync(hooksDir)) {
1068
- const gsdHooks = ['qgsd-statusline.js', 'qgsd-check-update.js', 'gsd-check-update.sh'];
1081
+ const gsdHooks = [
1082
+ 'nf-statusline.js', 'nf-check-update.js', 'gsd-check-update.sh',
1083
+ 'qgsd-prompt.js', 'qgsd-stop.js', 'qgsd-circuit-breaker.js',
1084
+ 'qgsd-session-start.js', 'qgsd-check-update.js', 'qgsd-statusline.js',
1085
+ ];
1069
1086
  let hookCount = 0;
1070
1087
  for (const hook of gsdHooks) {
1071
1088
  const hookPath = path.join(hooksDir, hook);
@@ -1102,9 +1119,9 @@ function uninstall(isGlobal, runtime = 'claude') {
1102
1119
  let settings = readSettings(settingsPath);
1103
1120
  let settingsModified = false;
1104
1121
 
1105
- // Remove GSD statusline if it references our hook
1122
+ // Remove GSD statusline if it references our hook (nf-* or old qgsd-*)
1106
1123
  if (settings.statusLine && settings.statusLine.command &&
1107
- settings.statusLine.command.includes('qgsd-statusline')) {
1124
+ (settings.statusLine.command.includes('nf-statusline') || settings.statusLine.command.includes('qgsd-statusline'))) {
1108
1125
  delete settings.statusLine;
1109
1126
  settingsModified = true;
1110
1127
  console.log(` ${green}✓${reset} Removed GSD statusline from settings`);
@@ -1118,6 +1135,9 @@ function uninstall(isGlobal, runtime = 'claude') {
1118
1135
  // Filter out GSD hooks
1119
1136
  const hasGsdHook = entry.hooks.some(h =>
1120
1137
  h.command && (
1138
+ h.command.includes('nf-check-update') ||
1139
+ h.command.includes('nf-statusline') ||
1140
+ h.command.includes('nf-session-start') ||
1121
1141
  h.command.includes('qgsd-check-update') ||
1122
1142
  h.command.includes('qgsd-statusline') ||
1123
1143
  h.command.includes('qgsd-session-start')
@@ -1140,7 +1160,7 @@ function uninstall(isGlobal, runtime = 'claude') {
1140
1160
  if (settings.hooks && settings.hooks.UserPromptSubmit) {
1141
1161
  const before = settings.hooks.UserPromptSubmit.length;
1142
1162
  settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(entry =>
1143
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-prompt')))
1163
+ !(entry.hooks && entry.hooks.some(h => h.command && (h.command.includes('nf-prompt') || h.command.includes('qgsd-prompt'))))
1144
1164
  );
1145
1165
  if (settings.hooks.UserPromptSubmit.length < before) {
1146
1166
  settingsModified = true;
@@ -1151,7 +1171,7 @@ function uninstall(isGlobal, runtime = 'claude') {
1151
1171
  if (settings.hooks && settings.hooks.Stop) {
1152
1172
  const before = settings.hooks.Stop.length;
1153
1173
  settings.hooks.Stop = settings.hooks.Stop.filter(entry =>
1154
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-stop')))
1174
+ !(entry.hooks && entry.hooks.some(h => h.command && (h.command.includes('nf-stop') || h.command.includes('qgsd-stop'))))
1155
1175
  );
1156
1176
  if (settings.hooks.Stop.length < before) {
1157
1177
  settingsModified = true;
@@ -1162,7 +1182,7 @@ function uninstall(isGlobal, runtime = 'claude') {
1162
1182
  if (settings.hooks && settings.hooks.PreToolUse) {
1163
1183
  const before = settings.hooks.PreToolUse.length;
1164
1184
  settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(entry =>
1165
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-circuit-breaker')))
1185
+ !(entry.hooks && entry.hooks.some(h => h.command && (h.command.includes('nf-circuit-breaker') || h.command.includes('qgsd-circuit-breaker'))))
1166
1186
  );
1167
1187
  if (settings.hooks.PreToolUse.length < before) {
1168
1188
  settingsModified = true;
@@ -1181,11 +1201,11 @@ function uninstall(isGlobal, runtime = 'claude') {
1181
1201
  }
1182
1202
  if (settings.hooks.PostToolUse.length === 0) delete settings.hooks.PostToolUse;
1183
1203
  }
1184
- // Remove qgsd-spec-regen hook (uninstall path)
1204
+ // Remove nf-spec-regen hook (uninstall path)
1185
1205
  if (settings.hooks && settings.hooks.PostToolUse) {
1186
1206
  const before = settings.hooks.PostToolUse.length;
1187
1207
  settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(entry =>
1188
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-spec-regen')))
1208
+ !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-spec-regen')))
1189
1209
  );
1190
1210
  if (settings.hooks.PostToolUse.length < before) {
1191
1211
  settingsModified = true;
@@ -1196,7 +1216,7 @@ function uninstall(isGlobal, runtime = 'claude') {
1196
1216
  if (settings.hooks && settings.hooks.PreCompact) {
1197
1217
  const before = settings.hooks.PreCompact.length;
1198
1218
  settings.hooks.PreCompact = settings.hooks.PreCompact.filter(entry =>
1199
- !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-precompact')))
1219
+ !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-precompact')))
1200
1220
  );
1201
1221
  if (settings.hooks.PreCompact.length < before) {
1202
1222
  settingsModified = true;
@@ -1235,7 +1255,7 @@ function uninstall(isGlobal, runtime = 'claude') {
1235
1255
  if (config.permission[permType]) {
1236
1256
  const keys = Object.keys(config.permission[permType]);
1237
1257
  for (const key of keys) {
1238
- if (key.includes('qgsd')) {
1258
+ if (key.includes('nf')) {
1239
1259
  delete config.permission[permType][key];
1240
1260
  modified = true;
1241
1261
  }
@@ -1335,7 +1355,7 @@ function parseJsonc(content) {
1335
1355
 
1336
1356
  /**
1337
1357
  * Configure OpenCode permissions to allow reading GSD reference docs
1338
- * This prevents permission prompts when GSD accesses the qgsd directory
1358
+ * This prevents permission prompts when GSD accesses the nf directory
1339
1359
  * @param {boolean} isGlobal - Whether this is a global or local install
1340
1360
  */
1341
1361
  function configureOpencodePermissions(isGlobal = true) {
@@ -1373,8 +1393,8 @@ function configureOpencodePermissions(isGlobal = true) {
1373
1393
  // Use ~ shorthand if it's in the default location, otherwise use full path
1374
1394
  const defaultConfigDir = path.join(os.homedir(), '.config', 'opencode');
1375
1395
  const gsdPath = opencodeConfigDir === defaultConfigDir
1376
- ? '~/.config/opencode/qgsd/*'
1377
- : `${opencodeConfigDir.replace(/\\/g, '/')}/qgsd/*`;
1396
+ ? '~/.config/opencode/nf/*'
1397
+ : `${opencodeConfigDir.replace(/\\/g, '/')}/nf/*`;
1378
1398
 
1379
1399
  let modified = false;
1380
1400
 
@@ -1448,7 +1468,7 @@ function verifyFileInstalled(filePath, description) {
1448
1468
  // ──────────────────────────────────────────────────────
1449
1469
 
1450
1470
  const PATCHES_DIR_NAME = 'gsd-local-patches';
1451
- const MANIFEST_NAME = 'qgsd-file-manifest.json';
1471
+ const MANIFEST_NAME = 'nf-file-manifest.json';
1452
1472
 
1453
1473
  /**
1454
1474
  * Compute SHA256 hash of file contents
@@ -1482,24 +1502,24 @@ function generateManifest(dir, baseDir) {
1482
1502
  * Write file manifest after installation for future modification detection
1483
1503
  */
1484
1504
  function writeManifest(configDir) {
1485
- const gsdDir = path.join(configDir, 'qgsd');
1486
- const commandsDir = path.join(configDir, 'commands', 'qgsd');
1505
+ const gsdDir = path.join(configDir, 'nf');
1506
+ const commandsDir = path.join(configDir, 'commands', 'nf');
1487
1507
  const agentsDir = path.join(configDir, 'agents');
1488
1508
  const manifest = { version: pkg.version, timestamp: new Date().toISOString(), files: {} };
1489
1509
 
1490
1510
  const gsdHashes = generateManifest(gsdDir);
1491
1511
  for (const [rel, hash] of Object.entries(gsdHashes)) {
1492
- manifest.files['qgsd/' + rel] = hash;
1512
+ manifest.files['nf/' + rel] = hash;
1493
1513
  }
1494
1514
  if (fs.existsSync(commandsDir)) {
1495
1515
  const cmdHashes = generateManifest(commandsDir);
1496
1516
  for (const [rel, hash] of Object.entries(cmdHashes)) {
1497
- manifest.files['commands/qgsd/' + rel] = hash;
1517
+ manifest.files['commands/nf/' + rel] = hash;
1498
1518
  }
1499
1519
  }
1500
1520
  if (fs.existsSync(agentsDir)) {
1501
1521
  for (const file of fs.readdirSync(agentsDir)) {
1502
- if (file.startsWith('qgsd-') && file.endsWith('.md')) {
1522
+ if (file.startsWith('nf-') && file.endsWith('.md')) {
1503
1523
  manifest.files['agents/' + file] = fileHash(path.join(agentsDir, file));
1504
1524
  }
1505
1525
  }
@@ -1569,7 +1589,7 @@ function reportLocalPatches(configDir) {
1569
1589
  }
1570
1590
  console.log('');
1571
1591
  console.log(' Your modifications are saved in ' + cyan + PATCHES_DIR_NAME + '/' + reset);
1572
- console.log(' Run ' + cyan + '/qgsd:reapply-patches' + reset + ' to merge them into the new version.');
1592
+ console.log(' Run ' + cyan + '/nf:reapply-patches' + reset + ' to merge them into the new version.');
1573
1593
  console.log(' Or manually compare and merge the files.');
1574
1594
  console.log('');
1575
1595
  }
@@ -1620,38 +1640,38 @@ function install(isGlobal, runtime = 'claude') {
1620
1640
  const commandDir = path.join(targetDir, 'command');
1621
1641
  fs.mkdirSync(commandDir, { recursive: true });
1622
1642
 
1623
- // Copy commands/qgsd/*.md as command/qgsd-*.md (flatten structure)
1624
- const gsdSrc = path.join(src, 'commands', 'qgsd');
1625
- copyFlattenedCommands(gsdSrc, commandDir, 'qgsd', pathPrefix, runtime);
1626
- if (verifyInstalled(commandDir, 'command/qgsd-*')) {
1627
- const count = fs.readdirSync(commandDir).filter(f => f.startsWith('qgsd-')).length;
1643
+ // Copy commands/nf/*.md as command/nf-*.md (flatten structure)
1644
+ const gsdSrc = path.join(src, 'commands', 'nf');
1645
+ copyFlattenedCommands(gsdSrc, commandDir, 'nf', pathPrefix, runtime);
1646
+ if (verifyInstalled(commandDir, 'command/nf-*')) {
1647
+ const count = fs.readdirSync(commandDir).filter(f => f.startsWith('nf-')).length;
1628
1648
  console.log(` ${green}✓${reset} Installed ${count} commands to command/`);
1629
1649
  } else {
1630
- failures.push('command/qgsd-*');
1650
+ failures.push('command/nf-*');
1631
1651
  }
1632
1652
  } else {
1633
1653
  // Claude Code & Gemini: nested structure in commands/ directory
1634
1654
  const commandsDir = path.join(targetDir, 'commands');
1635
1655
  fs.mkdirSync(commandsDir, { recursive: true });
1636
1656
 
1637
- const gsdSrc = path.join(src, 'commands', 'qgsd');
1638
- const gsdDest = path.join(commandsDir, 'qgsd');
1657
+ const gsdSrc = path.join(src, 'commands', 'nf');
1658
+ const gsdDest = path.join(commandsDir, 'nf');
1639
1659
  copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime);
1640
- if (verifyInstalled(gsdDest, 'commands/qgsd')) {
1641
- console.log(` ${green}✓${reset} Installed commands/qgsd`);
1660
+ if (verifyInstalled(gsdDest, 'commands/nf')) {
1661
+ console.log(` ${green}✓${reset} Installed commands/nf`);
1642
1662
  } else {
1643
- failures.push('commands/qgsd');
1663
+ failures.push('commands/nf');
1644
1664
  }
1645
1665
  }
1646
1666
 
1647
- // Copy qgsd skill with path replacement
1648
- const skillSrc = path.join(src, 'qgsd-core');
1649
- const skillDest = path.join(targetDir, 'qgsd');
1667
+ // Copy nf skill with path replacement
1668
+ const skillSrc = path.join(src, 'core');
1669
+ const skillDest = path.join(targetDir, 'nf');
1650
1670
  copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime);
1651
- if (verifyInstalled(skillDest, 'qgsd')) {
1652
- console.log(` ${green}✓${reset} Installed qgsd`);
1671
+ if (verifyInstalled(skillDest, 'nf')) {
1672
+ console.log(` ${green}✓${reset} Installed nf`);
1653
1673
  } else {
1654
- failures.push('qgsd');
1674
+ failures.push('nf');
1655
1675
  }
1656
1676
 
1657
1677
  // Copy agents to agents directory
@@ -1660,10 +1680,10 @@ function install(isGlobal, runtime = 'claude') {
1660
1680
  const agentsDest = path.join(targetDir, 'agents');
1661
1681
  fs.mkdirSync(agentsDest, { recursive: true });
1662
1682
 
1663
- // Remove old qgsd-*.md files before copying new ones
1683
+ // Remove old nf-*.md files before copying new ones
1664
1684
  if (fs.existsSync(agentsDest)) {
1665
1685
  for (const file of fs.readdirSync(agentsDest)) {
1666
- if (file.startsWith('qgsd-') && file.endsWith('.md')) {
1686
+ if (file.startsWith('nf-') && file.endsWith('.md')) {
1667
1687
  fs.unlinkSync(path.join(agentsDest, file));
1668
1688
  }
1669
1689
  }
@@ -1696,7 +1716,7 @@ function install(isGlobal, runtime = 'claude') {
1696
1716
 
1697
1717
  // Copy CHANGELOG.md
1698
1718
  const changelogSrc = path.join(src, 'CHANGELOG.md');
1699
- const changelogDest = path.join(targetDir, 'qgsd', 'CHANGELOG.md');
1719
+ const changelogDest = path.join(targetDir, 'nf', 'CHANGELOG.md');
1700
1720
  if (fs.existsSync(changelogSrc)) {
1701
1721
  fs.copyFileSync(changelogSrc, changelogDest);
1702
1722
  if (verifyFileInstalled(changelogDest, 'CHANGELOG.md')) {
@@ -1707,7 +1727,7 @@ function install(isGlobal, runtime = 'claude') {
1707
1727
  }
1708
1728
 
1709
1729
  // Write VERSION file
1710
- const versionDest = path.join(targetDir, 'qgsd', 'VERSION');
1730
+ const versionDest = path.join(targetDir, 'nf', 'VERSION');
1711
1731
  fs.writeFileSync(versionDest, pkg.version);
1712
1732
  if (verifyFileInstalled(versionDest, 'VERSION')) {
1713
1733
  console.log(` ${green}✓${reset} Wrote VERSION (${pkg.version})`);
@@ -1751,10 +1771,10 @@ function install(isGlobal, runtime = 'claude') {
1751
1771
  }
1752
1772
  }
1753
1773
 
1754
- // Copy bin/*.cjs scripts to qgsd-bin/ (used by commands with absolute paths)
1774
+ // Copy bin/*.cjs scripts to nf-bin/ (used by commands with absolute paths)
1755
1775
  const binSrc = path.join(src, 'bin');
1756
1776
  if (fs.existsSync(binSrc)) {
1757
- const binDest = path.join(targetDir, 'qgsd-bin');
1777
+ const binDest = path.join(targetDir, 'nf-bin');
1758
1778
  fs.mkdirSync(binDest, { recursive: true });
1759
1779
  const binEntries = fs.readdirSync(binSrc);
1760
1780
  for (const entry of binEntries) {
@@ -1762,7 +1782,7 @@ function install(isGlobal, runtime = 'claude') {
1762
1782
  fs.copyFileSync(path.join(binSrc, entry), path.join(binDest, entry));
1763
1783
  }
1764
1784
  }
1765
- console.log(` ${green}✓${reset} Installed qgsd-bin scripts`);
1785
+ console.log(` ${green}✓${reset} Installed nf-bin scripts`);
1766
1786
  }
1767
1787
 
1768
1788
  if (failures.length > 0) {
@@ -1775,11 +1795,11 @@ function install(isGlobal, runtime = 'claude') {
1775
1795
  const settingsPath = path.join(targetDir, 'settings.json');
1776
1796
  const settings = cleanupOrphanedHooks(readSettings(settingsPath));
1777
1797
  const statuslineCommand = isGlobal
1778
- ? buildHookCommand(targetDir, 'qgsd-statusline.js')
1779
- : 'node ' + dirName + '/hooks/qgsd-statusline.js';
1798
+ ? buildHookCommand(targetDir, 'nf-statusline.js')
1799
+ : 'node ' + dirName + '/hooks/nf-statusline.js';
1780
1800
  const updateCheckCommand = isGlobal
1781
- ? buildHookCommand(targetDir, 'qgsd-check-update.js')
1782
- : 'node ' + dirName + '/hooks/qgsd-check-update.js';
1801
+ ? buildHookCommand(targetDir, 'nf-check-update.js')
1802
+ : 'node ' + dirName + '/hooks/nf-check-update.js';
1783
1803
 
1784
1804
  // Enable experimental agents for Gemini CLI (required for custom sub-agents)
1785
1805
  if (isGemini) {
@@ -1802,7 +1822,7 @@ function install(isGlobal, runtime = 'claude') {
1802
1822
  }
1803
1823
 
1804
1824
  const hasGsdUpdateHook = settings.hooks.SessionStart.some(entry =>
1805
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-check-update'))
1825
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-check-update'))
1806
1826
  );
1807
1827
 
1808
1828
  if (!hasGsdUpdateHook) {
@@ -1819,14 +1839,14 @@ function install(isGlobal, runtime = 'claude') {
1819
1839
 
1820
1840
  // Register nForma session-start secret sync hook
1821
1841
  const hasGsdSessionStartHook = settings.hooks.SessionStart.some(entry =>
1822
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-session-start'))
1842
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-session-start'))
1823
1843
  );
1824
1844
  if (!hasGsdSessionStartHook) {
1825
1845
  settings.hooks.SessionStart.push({
1826
1846
  hooks: [
1827
1847
  {
1828
1848
  type: 'command',
1829
- command: buildHookCommand(targetDir, 'qgsd-session-start.js')
1849
+ command: buildHookCommand(targetDir, 'nf-session-start.js')
1830
1850
  }
1831
1851
  ]
1832
1852
  });
@@ -1836,27 +1856,52 @@ function install(isGlobal, runtime = 'claude') {
1836
1856
  // INST-05: Warn (yellow) if quorum MCP servers are absent — runs every install
1837
1857
  warnMissingMcpServers();
1838
1858
 
1859
+ // ── MIGRATION: remove old qgsd-* hook entries ─────────────────────────
1860
+ // Old installs registered hooks as qgsd-prompt.js, qgsd-stop.js, qgsd-circuit-breaker.js.
1861
+ // Remove them so the nf-* replacements below can register cleanly.
1862
+ const OLD_HOOK_MAP = {
1863
+ UserPromptSubmit: 'qgsd-prompt',
1864
+ Stop: 'qgsd-stop',
1865
+ PreToolUse: 'qgsd-circuit-breaker',
1866
+ SessionStart: ['qgsd-check-update', 'qgsd-session-start'],
1867
+ };
1868
+ for (const [event, oldNames] of Object.entries(OLD_HOOK_MAP)) {
1869
+ if (settings.hooks[event]) {
1870
+ const names = Array.isArray(oldNames) ? oldNames : [oldNames];
1871
+ const before = settings.hooks[event].length;
1872
+ settings.hooks[event] = settings.hooks[event].filter(entry =>
1873
+ !(entry.hooks && entry.hooks.some(h =>
1874
+ h.command && names.some(n => h.command.includes(n))
1875
+ ))
1876
+ );
1877
+ if (settings.hooks[event].length < before) {
1878
+ console.log(` ${green}✓${reset} Migrated old ${names.join(', ')} hook(s) → nf equivalent`);
1879
+ }
1880
+ if (settings.hooks[event].length === 0) delete settings.hooks[event];
1881
+ }
1882
+ }
1883
+
1839
1884
  // Register nForma UserPromptSubmit hook (quorum injection)
1840
1885
  // MUST be in settings.json — plugin hooks.json silently discards UserPromptSubmit output (GitHub #10225)
1841
1886
  if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
1842
- const hasQgsdPromptHook = settings.hooks.UserPromptSubmit.some(entry =>
1843
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-prompt'))
1887
+ const hasNfPromptHook = settings.hooks.UserPromptSubmit.some(entry =>
1888
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-prompt'))
1844
1889
  );
1845
- if (!hasQgsdPromptHook) {
1890
+ if (!hasNfPromptHook) {
1846
1891
  settings.hooks.UserPromptSubmit.push({
1847
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-prompt.js') }]
1892
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-prompt.js') }]
1848
1893
  });
1849
1894
  console.log(` ${green}✓${reset} Configured nForma quorum injection hook (UserPromptSubmit)`);
1850
1895
  }
1851
1896
 
1852
1897
  // Register nForma Stop hook (quorum gate — verifies quorum evidence before Claude delivers planning output)
1853
1898
  if (!settings.hooks.Stop) settings.hooks.Stop = [];
1854
- const hasQgsdStopHook = settings.hooks.Stop.some(entry =>
1855
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-stop'))
1899
+ const hasNfStopHook = settings.hooks.Stop.some(entry =>
1900
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-stop'))
1856
1901
  );
1857
- if (!hasQgsdStopHook) {
1902
+ if (!hasNfStopHook) {
1858
1903
  settings.hooks.Stop.push({
1859
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-stop.js'), timeout: 30 }]
1904
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-stop.js'), timeout: 30 }]
1860
1905
  });
1861
1906
  console.log(` ${green}✓${reset} Configured nForma quorum gate hook (Stop)`);
1862
1907
  }
@@ -1864,11 +1909,11 @@ function install(isGlobal, runtime = 'claude') {
1864
1909
  // INST-08: Register nForma circuit breaker hook (PreToolUse — Claude Code only)
1865
1910
  if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
1866
1911
  const hasCircuitBreakerHook = settings.hooks.PreToolUse.some(entry =>
1867
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-circuit-breaker'))
1912
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-circuit-breaker'))
1868
1913
  );
1869
1914
  if (!hasCircuitBreakerHook) {
1870
1915
  settings.hooks.PreToolUse.push({
1871
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-circuit-breaker.js'), timeout: 10 }]
1916
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-circuit-breaker.js'), timeout: 10 }]
1872
1917
  });
1873
1918
  console.log(` ${green}✓${reset} Configured nForma circuit breaker hook (PreToolUse)`);
1874
1919
  }
@@ -1888,11 +1933,11 @@ function install(isGlobal, runtime = 'claude') {
1888
1933
  // Register nForma spec-regen hook (PostToolUse — auto-regenerate specs on machine file write)
1889
1934
  if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
1890
1935
  const hasSpecRegenHook = settings.hooks.PostToolUse.some(entry =>
1891
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-spec-regen'))
1936
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-spec-regen'))
1892
1937
  );
1893
1938
  if (!hasSpecRegenHook) {
1894
1939
  settings.hooks.PostToolUse.push({
1895
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-spec-regen.js') }]
1940
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-spec-regen.js') }]
1896
1941
  });
1897
1942
  console.log(` ${green}✓${reset} Configured nForma spec-regen hook (PostToolUse)`);
1898
1943
  }
@@ -1900,11 +1945,11 @@ function install(isGlobal, runtime = 'claude') {
1900
1945
  // Register nForma PreCompact hook (phase state injection at compaction time)
1901
1946
  if (!settings.hooks.PreCompact) settings.hooks.PreCompact = [];
1902
1947
  const hasPreCompactHook = settings.hooks.PreCompact.some(entry =>
1903
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-precompact'))
1948
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-precompact'))
1904
1949
  );
1905
1950
  if (!hasPreCompactHook) {
1906
1951
  settings.hooks.PreCompact.push({
1907
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-precompact.js') }]
1952
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-precompact.js') }]
1908
1953
  });
1909
1954
  console.log(` ${green}✓${reset} Configured nForma PreCompact hook (phase state injection)`);
1910
1955
  }
@@ -1912,12 +1957,12 @@ function install(isGlobal, runtime = 'claude') {
1912
1957
  // Register nForma token collector hook (SubagentStop — logs per-slot token usage)
1913
1958
  if (!settings.hooks.SubagentStop) settings.hooks.SubagentStop = [];
1914
1959
  const hasTokenCollectorHook = settings.hooks.SubagentStop.some(entry =>
1915
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-token-collector'))
1960
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-token-collector'))
1916
1961
  );
1917
1962
  if (!hasTokenCollectorHook) {
1918
1963
  settings.hooks.SubagentStop.push({
1919
- matcher: 'qgsd-quorum-slot-worker',
1920
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-token-collector.js'), async: true }]
1964
+ matcher: 'nf-quorum-slot-worker',
1965
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-token-collector.js'), async: true }]
1921
1966
  });
1922
1967
  console.log(` ${green}✓${reset} Configured nForma token collector hook (SubagentStop)`);
1923
1968
  }
@@ -1925,29 +1970,29 @@ function install(isGlobal, runtime = 'claude') {
1925
1970
  // Register nForma slot correlator hook (SubagentStart — writes agent_id correlation file)
1926
1971
  if (!settings.hooks.SubagentStart) settings.hooks.SubagentStart = [];
1927
1972
  const hasSlotCorrelatorHook = settings.hooks.SubagentStart.some(entry =>
1928
- entry.hooks && entry.hooks.some(h => h.command && h.command.includes('qgsd-slot-correlator'))
1973
+ entry.hooks && entry.hooks.some(h => h.command && h.command.includes('nf-slot-correlator'))
1929
1974
  );
1930
1975
  if (!hasSlotCorrelatorHook) {
1931
1976
  settings.hooks.SubagentStart.push({
1932
- matcher: 'qgsd-quorum-slot-worker',
1933
- hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'qgsd-slot-correlator.js'), async: true }]
1977
+ matcher: 'nf-quorum-slot-worker',
1978
+ hooks: [{ type: 'command', command: buildHookCommand(targetDir, 'nf-slot-correlator.js'), async: true }]
1934
1979
  });
1935
1980
  console.log(` ${green}✓${reset} Configured nForma slot correlator hook (SubagentStart)`);
1936
1981
  }
1937
1982
 
1938
1983
  // Write nForma config — skip if exists unless --redetect-mcps flag set
1939
- const qgsdConfigPath = path.join(targetDir, 'qgsd.json');
1984
+ const nfConfigPath = path.join(targetDir, 'nf.json');
1940
1985
 
1941
1986
  // --redetect-mcps: delete existing config so fresh detection runs below
1942
- if (hasRedetectMcps && fs.existsSync(qgsdConfigPath)) {
1943
- fs.unlinkSync(qgsdConfigPath);
1987
+ if (hasRedetectMcps && fs.existsSync(nfConfigPath)) {
1988
+ fs.unlinkSync(nfConfigPath);
1944
1989
  console.log(` ${cyan}◆${reset} Re-detecting MCP prefixes (--redetect-mcps)...`);
1945
1990
  }
1946
1991
 
1947
- if (!fs.existsSync(qgsdConfigPath)) {
1992
+ if (!fs.existsSync(nfConfigPath)) {
1948
1993
  // Build config with auto-detected MCP prefixes
1949
1994
  const detectedModels = buildRequiredModelsFromMcp();
1950
- const qgsdConfig = {
1995
+ const nfConfig = {
1951
1996
  quorum_commands: [
1952
1997
  'plan-phase', 'new-project', 'new-milestone',
1953
1998
  'discuss-phase', 'verify-work', 'research-phase',
@@ -1964,25 +2009,25 @@ function install(isGlobal, runtime = 'claude') {
1964
2009
  },
1965
2010
  };
1966
2011
 
1967
- fs.writeFileSync(qgsdConfigPath, JSON.stringify(qgsdConfig, null, 2) + '\n', 'utf8');
1968
- console.log(` ${green}✓${reset} Wrote nForma config with detected MCP prefixes (~/.claude/qgsd.json)`);
1969
- console.log(` ${green}✓${reset} Wrote quorum_active (${qgsdConfig.quorum_active.length} slots) to qgsd.json`);
2012
+ fs.writeFileSync(nfConfigPath, JSON.stringify(nfConfig, null, 2) + '\n', 'utf8');
2013
+ console.log(` ${green}✓${reset} Wrote nForma config with detected MCP prefixes (~/.claude/nf.json)`);
2014
+ console.log(` ${green}✓${reset} Wrote quorum_active (${nfConfig.quorum_active.length} slots) to nf.json`);
1970
2015
  } else {
1971
2016
  // INST-06: print active config summary on reinstall
1972
2017
  try {
1973
- const existingConfig = JSON.parse(fs.readFileSync(qgsdConfigPath, 'utf8'));
2018
+ const existingConfig = JSON.parse(fs.readFileSync(nfConfigPath, 'utf8'));
1974
2019
  const models = existingConfig.required_models || {};
1975
2020
  const summary = Object.entries(models)
1976
2021
  .map(([key, def]) => `${key} → ${def.tool_prefix || '(unset)'}`)
1977
2022
  .join(', ');
1978
- console.log(` ${dim}↳ ~/.claude/qgsd.json exists — active config: ${summary}${reset}`);
2023
+ console.log(` ${dim}↳ ~/.claude/nf.json exists — active config: ${summary}${reset}`);
1979
2024
  console.log(` ${dim} (run with --redetect-mcps to refresh MCP prefix detection)${reset}`);
1980
2025
 
1981
2026
  // INST-10: Add missing circuit_breaker block or missing sub-keys without touching existing user values
1982
2027
  if (!existingConfig.circuit_breaker) {
1983
2028
  existingConfig.circuit_breaker = { oscillation_depth: 3, commit_window: 6 };
1984
- fs.writeFileSync(qgsdConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
1985
- console.log(` ${green}✓${reset} Added circuit_breaker config block to qgsd.json`);
2029
+ fs.writeFileSync(nfConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
2030
+ console.log(` ${green}✓${reset} Added circuit_breaker config block to nf.json`);
1986
2031
  } else {
1987
2032
  // Backfill individual missing sub-keys without touching values the user has set
1988
2033
  let subKeyAdded = false;
@@ -1995,8 +2040,8 @@ function install(isGlobal, runtime = 'claude') {
1995
2040
  subKeyAdded = true;
1996
2041
  }
1997
2042
  if (subKeyAdded) {
1998
- fs.writeFileSync(qgsdConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
1999
- console.log(` ${green}✓${reset} Added missing circuit_breaker sub-keys to qgsd.json`);
2043
+ fs.writeFileSync(nfConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
2044
+ console.log(` ${green}✓${reset} Added missing circuit_breaker sub-keys to nf.json`);
2000
2045
  }
2001
2046
  }
2002
2047
 
@@ -2005,8 +2050,8 @@ function install(isGlobal, runtime = 'claude') {
2005
2050
  const discoveredSlots = buildActiveSlots();
2006
2051
  if (discoveredSlots.length > 0) {
2007
2052
  existingConfig.quorum_active = discoveredSlots;
2008
- fs.writeFileSync(qgsdConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
2009
- console.log(` ${green}✓${reset} Backfilled quorum_active (${discoveredSlots.length} slots) in qgsd.json`);
2053
+ fs.writeFileSync(nfConfigPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
2054
+ console.log(` ${green}✓${reset} Backfilled quorum_active (${discoveredSlots.length} slots) in nf.json`);
2010
2055
  }
2011
2056
  }
2012
2057
 
@@ -2017,7 +2062,7 @@ function install(isGlobal, runtime = 'claude') {
2017
2062
  const allCurrentSlots = buildActiveSlots();
2018
2063
  const newSlots = allCurrentSlots.filter(s => !existingConfig.quorum_active.includes(s));
2019
2064
  for (const newSlot of newSlots) {
2020
- const result = addSlotToQuorumActive(newSlot, qgsdConfigPath);
2065
+ const result = addSlotToQuorumActive(newSlot, nfConfigPath);
2021
2066
  if (result.added) {
2022
2067
  console.log(` ${green}✓${reset} Added new slot to quorum_active: ${newSlot}`);
2023
2068
  existingConfig.quorum_active.push(newSlot); // keep in-memory copy consistent
@@ -2026,7 +2071,7 @@ function install(isGlobal, runtime = 'claude') {
2026
2071
  }
2027
2072
  // If quorum_active is already set and non-empty: do NOT overwrite (user config preserved)
2028
2073
  } catch {
2029
- console.log(` ${dim}↳ ~/.claude/qgsd.json already exists — skipping (user config preserved)${reset}`);
2074
+ console.log(` ${dim}↳ ~/.claude/nf.json already exists — skipping (user config preserved)${reset}`);
2030
2075
  }
2031
2076
  }
2032
2077
  }
@@ -2067,11 +2112,11 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
2067
2112
  if (runtime === 'opencode') program = 'OpenCode';
2068
2113
  if (runtime === 'gemini') program = 'Gemini';
2069
2114
 
2070
- const command = isOpencode ? '/qgsd-help' : '/qgsd:help';
2115
+ const command = isOpencode ? '/nf-help' : '/nf:help';
2071
2116
 
2072
2117
  let nudge = '';
2073
2118
  if (runtime === 'claude' && !hasClaudeMcpAgents()) {
2074
- nudge = `\n ${yellow}⚠${reset} No quorum agents configured.\n Run ${cyan}/qgsd:mcp-setup${reset} in Claude Code to set up your agents.\n`;
2119
+ nudge = `\n ${yellow}⚠${reset} No quorum agents configured.\n Run ${cyan}/nf:mcp-setup${reset} in Claude Code to set up your agents.\n`;
2075
2120
  }
2076
2121
 
2077
2122
  console.log(`
@@ -2332,7 +2377,7 @@ if (hasDisableBreaker) {
2332
2377
  : {};
2333
2378
  fs.writeFileSync(stateFile, JSON.stringify({ ...existing, disabled: true, active: false }, null, 2), 'utf8');
2334
2379
  console.log(` ${yellow}⊘${reset} Circuit breaker ${yellow}disabled${reset}. Detection and enforcement paused.`);
2335
- console.log(` Run ${cyan}npx qgsd --enable-breaker${reset} to resume.`);
2380
+ console.log(` Run ${cyan}npx nforma --enable-breaker${reset} to resume.`);
2336
2381
  process.exit(0);
2337
2382
  }
2338
2383
 
@@ -2357,14 +2402,14 @@ if (hasEnableBreaker) {
2357
2402
  process.exit(0);
2358
2403
  }
2359
2404
 
2360
- // SLOT-02: --migrate-slots renames mcpServers keys and patches qgsd.json tool_prefix values
2405
+ // SLOT-02: --migrate-slots renames mcpServers keys and patches nf.json tool_prefix values
2361
2406
  if (hasMigrateSlots) {
2362
- const { migrateClaudeJson, migrateQgsdJson } = require('./migrate-to-slots.cjs');
2407
+ const { migrateClaudeJson, migrateNfJson } = require('./migrate-to-slots.cjs');
2363
2408
  const claudeJsonPath = path.join(os.homedir(), '.claude.json');
2364
- const qgsdJsonPath = path.join(os.homedir(), '.claude', 'qgsd.json');
2409
+ const nfJsonPath = path.join(os.homedir(), '.claude', 'nf.json');
2365
2410
  const dryRun = args.includes('--dry-run');
2366
2411
  const r1 = migrateClaudeJson(claudeJsonPath, dryRun);
2367
- const r2 = migrateQgsdJson(qgsdJsonPath, dryRun);
2412
+ const r2 = migrateNfJson(nfJsonPath, dryRun);
2368
2413
  if (r1.changed === 0 && r2.changed === 0) {
2369
2414
  console.log(` ${green}✓${reset} Already migrated — no changes needed`);
2370
2415
  } else {
@@ -2373,7 +2418,7 @@ if (hasMigrateSlots) {
2373
2418
  r1.renamed.forEach(r => console.log(` ${r.from} → ${r.to}`));
2374
2419
  }
2375
2420
  if (r2.changed > 0) {
2376
- console.log(` ${green}✓${reset} Patched ${r2.changed} qgsd.json tool_prefix values`);
2421
+ console.log(` ${green}✓${reset} Patched ${r2.changed} nf.json tool_prefix values`);
2377
2422
  }
2378
2423
  }
2379
2424
  process.exit(0);