godpowers 2.5.2 → 2.7.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 (75) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +49 -19
  3. package/RELEASE.md +41 -29
  4. package/SKILL.md +46 -48
  5. package/agents/god-deploy-engineer.md +2 -2
  6. package/agents/god-designer.md +3 -2
  7. package/agents/god-greenfieldifier.md +2 -4
  8. package/agents/god-launch-strategist.md +4 -5
  9. package/agents/god-observability-engineer.md +5 -5
  10. package/agents/god-reconciler.md +10 -4
  11. package/agents/god-retrospective.md +1 -1
  12. package/agents/god-updater.md +5 -5
  13. package/bin/install.js +9 -1
  14. package/fixtures/gate/build-pass/.godpowers/state.json +33 -0
  15. package/lib/README.md +2 -0
  16. package/lib/artifact-map.js +15 -3
  17. package/lib/cli-dispatch.js +51 -1
  18. package/lib/context-writer.js +4 -4
  19. package/lib/gate.js +107 -9
  20. package/lib/host-capabilities.js +53 -3
  21. package/lib/installer-args.js +25 -0
  22. package/lib/mcp-info.js +93 -0
  23. package/lib/pillars.js +2 -4
  24. package/lib/recipes.js +16 -0
  25. package/lib/router.js +1 -5
  26. package/lib/source-sync.js +1 -1
  27. package/lib/state-advance.js +244 -0
  28. package/lib/state-lock.js +8 -4
  29. package/lib/state-views.js +460 -0
  30. package/lib/state.js +52 -3
  31. package/package.json +7 -2
  32. package/routing/god-audit.yaml +1 -1
  33. package/routing/god-build.yaml +1 -1
  34. package/routing/god-context.yaml +1 -1
  35. package/routing/god-deploy.yaml +3 -1
  36. package/routing/god-design.yaml +2 -2
  37. package/routing/god-launch.yaml +4 -1
  38. package/routing/god-migrate.yaml +0 -1
  39. package/routing/god-mode.yaml +1 -1
  40. package/routing/god-observe.yaml +4 -1
  41. package/routing/god-prd.yaml +1 -1
  42. package/routing/god-reconcile.yaml +2 -5
  43. package/routing/god-sync.yaml +1 -1
  44. package/routing/recipes/returning-after-break.yaml +1 -1
  45. package/schema/state.v1.json +68 -1
  46. package/skills/god-arch.md +1 -1
  47. package/skills/god-build.md +6 -4
  48. package/skills/god-deploy.md +16 -14
  49. package/skills/god-design.md +3 -3
  50. package/skills/god-fast.md +2 -2
  51. package/skills/god-feature.md +1 -1
  52. package/skills/god-harden.md +3 -3
  53. package/skills/god-hotfix.md +1 -1
  54. package/skills/god-init.md +14 -10
  55. package/skills/god-launch.md +14 -12
  56. package/skills/god-lifecycle.md +2 -1
  57. package/skills/god-mode.md +5 -4
  58. package/skills/god-next.md +2 -1
  59. package/skills/god-observe.md +15 -13
  60. package/skills/god-pause-work.md +2 -2
  61. package/skills/god-prd.md +5 -4
  62. package/skills/god-quick.md +1 -1
  63. package/skills/god-repo.md +1 -1
  64. package/skills/god-resume-work.md +5 -4
  65. package/skills/god-roadmap-update.md +1 -1
  66. package/skills/god-roadmap.md +1 -1
  67. package/skills/god-rollback.md +1 -1
  68. package/skills/god-skip.md +2 -2
  69. package/skills/god-stack.md +1 -1
  70. package/skills/god-standards.md +1 -1
  71. package/skills/god-status.md +6 -5
  72. package/skills/god-story.md +1 -1
  73. package/skills/god-sync.md +2 -2
  74. package/workflows/bluefield-arc.yaml +2 -4
  75. package/workflows/brownfield-arc.yaml +2 -4
@@ -54,13 +54,13 @@ After feature work, every artifact that was impacted needs to reflect reality.
54
54
  - Validate have-nots S-01 through S-05
55
55
 
56
56
  ### DEPLOY update (if reconciler said "needs-extension")
57
- - Update deploy/STATE.md
57
+ - Update `.godpowers/state.json` deploy evidence
58
58
  - Document new env vars
59
59
  - Update CI/CD config notes
60
60
  - Have-nots D-01 through D-08
61
61
 
62
62
  ### OBSERVE update (if reconciler said "needs-slo" or "needs-alert")
63
- - Update observe/STATE.md
63
+ - Update `.godpowers/state.json` observe evidence
64
64
  - Define new SLO with error budget policy
65
65
  - Define new alert with runbook reference
66
66
  - Have-nots OB-01 through OB-08
@@ -71,7 +71,7 @@ After feature work, every artifact that was impacted needs to reflect reality.
71
71
  - Have-nots H-01 through H-11
72
72
 
73
73
  ### LAUNCH update (if reconciler said "copy-update" or "new-launch")
74
- - Update launch/STATE.md
74
+ - Update `.godpowers/state.json` launch evidence
75
75
  - Update landing copy if user-visible
76
76
  - Substitution-test new copy
77
77
 
@@ -260,8 +260,8 @@ Updated:
260
260
  - arch/ARCH.md: added ADR-007 (auth refactor)
261
261
  - arch/adr/007-auth-refactor.md: created
262
262
  - roadmap/ROADMAP.md: Milestone 2 marked complete
263
- - deploy/STATE.md: added STRIPE_WEBHOOK_SECRET env var
264
- - observe/STATE.md: added SLO for /api/stripe-webhook (99.5%)
263
+ - state.json tier-3.deploy: added STRIPE_WEBHOOK_SECRET env var
264
+ - state.json tier-3.observe: added SLO for /api/stripe-webhook (99.5%)
265
265
  - backlog/BACKLOG.md: resolved entry "Stripe webhook handling"
266
266
  - todos/TODOS.md: marked "wire stripe events" as done
267
267
  - threads/auth-migration.md: appended progress note
package/bin/install.js CHANGED
@@ -51,7 +51,9 @@ function showHelp() {
51
51
  log('Commands:');
52
52
  log(' status Show the Godpowers Dashboard for a project');
53
53
  log(' next Show the dashboard and recommended next command');
54
+ log(' state advance Update one tracked Godpowers state step');
54
55
  log(' gate Check a tier artifact gate');
56
+ log(' mcp-info Show read-only MCP companion setup instructions');
55
57
  log(' quick-proof Show a runnable proof from the shipped fixture');
56
58
  log(' automation-status Show host automation provider support');
57
59
  log(' automation-setup Show an opt-in automation setup plan');
@@ -64,7 +66,9 @@ function showHelp() {
64
66
  }
65
67
  log('');
66
68
  log('Options:');
67
- log(' --project=<path> Project root for status, next, proof, or automation commands');
69
+ log(' --project=<path> Project root for status, next, state, proof, or automation commands');
70
+ log(' --step=<name> Step for state advance, such as prd or tier-1.prd');
71
+ log(' --status=<status> Status for state advance');
68
72
  log(' --tier=<name> Tier for gate: prd, design, arch, roadmap, stack, repo, build, or harden');
69
73
  log(' --json Emit JSON for status, next, proof, or automation commands');
70
74
  log(' --brief Render compact output for status, next, or proof');
@@ -99,7 +103,9 @@ function showHelp() {
99
103
  log('Examples:');
100
104
  log(' npx godpowers status --project=.');
101
105
  log(' npx godpowers next --project=.');
106
+ log(' npx godpowers state advance --step=prd --status=done --project=.');
102
107
  log(' npx godpowers gate --tier=prd --project=.');
108
+ log(' npx godpowers mcp-info --project=.');
103
109
  log(' npx godpowers quick-proof --project=.');
104
110
  log(' npx godpowers automation-status --project=.');
105
111
  log(' npx godpowers automation-setup --project=.');
@@ -211,8 +217,10 @@ module.exports = {
211
217
  runDashboardCommand: cliDispatch.runDashboardCommand,
212
218
  runDogfoodCommand: cliDispatch.runDogfoodCommand,
213
219
  runQuickProofCommand: cliDispatch.runQuickProofCommand,
220
+ runMcpInfoCommand: cliDispatch.runMcpInfoCommand,
214
221
  runExtensionScaffoldCommand: cliDispatch.runExtensionScaffoldCommand,
215
222
  runGateCommand: cliDispatch.runGateCommand,
223
+ runStateCommand: cliDispatch.runStateCommand,
216
224
  applyDefaultRuntimeSelection,
217
225
  runInstall,
218
226
  runUninstall,
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://godpowers.dev/schema/state.v1.json",
3
+ "version": "1.0.0",
4
+ "project": {
5
+ "name": "build-pass",
6
+ "started": "2026-06-10T00:00:00.000Z"
7
+ },
8
+ "tiers": {
9
+ "tier-2": {
10
+ "build": {
11
+ "status": "done",
12
+ "updated": "2026-06-10T18:08:00.000Z",
13
+ "artifact": "build/STATE.md",
14
+ "verification": {
15
+ "commands": [
16
+ {
17
+ "command": "npm test",
18
+ "status": "pass",
19
+ "exitCode": 0,
20
+ "ranAt": "2026-06-10T18:07:00.000Z"
21
+ },
22
+ {
23
+ "command": "npm run lint",
24
+ "status": "pass",
25
+ "exitCode": 0,
26
+ "ranAt": "2026-06-10T18:07:30.000Z"
27
+ }
28
+ ]
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
package/lib/README.md CHANGED
@@ -9,6 +9,8 @@ package-level integrations.
9
9
  | Module | Purpose |
10
10
  |--------|---------|
11
11
  | `state.js` | Read, initialize, validate, and write `.godpowers/state.json`. |
12
+ | `state-views.js` | Generate managed markdown views such as `.godpowers/PROGRESS.md` and Godpowers-owned per-tier `STATE.md` files from `state.json`. |
13
+ | `state-advance.js` | Mutate one tracked state step through locking and generated state view refresh. |
12
14
  | `state-lock.js` | Coordinate state writes with a lock file. |
13
15
  | `intent.js` | Read and validate `intent.yaml` from project roots or `.godpowers/`. |
14
16
  | `frontmatter.js` | Parse shared markdown YAML frontmatter for skills, agents, Pillars, checkpoints, and design specs. |
@@ -9,7 +9,7 @@ const TIER_ARTIFACTS = {
9
9
  design: [
10
10
  { path: 'DESIGN.md', required: true, lint: true },
11
11
  { path: 'PRODUCT.md', required: false, lint: true },
12
- { path: '.godpowers/design/STATE.md', required: true, lint: true }
12
+ { path: '.godpowers/state.json', required: true, lint: false }
13
13
  ],
14
14
  arch: [
15
15
  { path: '.godpowers/arch/ARCH.md', required: true, lint: true }
@@ -24,13 +24,18 @@ const TIER_ARTIFACTS = {
24
24
  { path: '.godpowers/repo/AUDIT.md', required: true, lint: true }
25
25
  ],
26
26
  build: [
27
- { path: '.godpowers/build/STATE.md', required: true, lint: true }
27
+ { path: '.godpowers/state.json', required: true, lint: false }
28
28
  ],
29
29
  harden: [
30
30
  { path: '.godpowers/harden/FINDINGS.md', required: true, lint: true }
31
31
  ]
32
32
  };
33
33
 
34
+ const TIER_STATE_STEPS = {
35
+ design: { tierKey: 'tier-1', subStepKey: 'design' },
36
+ build: { tierKey: 'tier-2', subStepKey: 'build' }
37
+ };
38
+
34
39
  function normalizeTier(tier) {
35
40
  if (!tier) return null;
36
41
  return String(tier).replace(/^\/?god-/, '').toLowerCase();
@@ -51,10 +56,17 @@ function requiredArtifactsForTier(tier) {
51
56
  return artifacts ? artifacts.filter((artifact) => artifact.required) : null;
52
57
  }
53
58
 
59
+ function stateStepForTier(tier) {
60
+ const key = normalizeTier(tier);
61
+ if (!key || !TIER_STATE_STEPS[key]) return null;
62
+ return { ...TIER_STATE_STEPS[key] };
63
+ }
64
+
54
65
  module.exports = {
55
66
  TIER_ARTIFACTS,
56
67
  normalizeTier,
57
68
  tiers,
58
69
  artifactsForTier,
59
- requiredArtifactsForTier
70
+ requiredArtifactsForTier,
71
+ stateStepForTier
60
72
  };
@@ -4,6 +4,7 @@
4
4
 
5
5
  const gate = require('./gate');
6
6
  const identity = require('./package-identity');
7
+ const stateAdvance = require('./state-advance');
7
8
 
8
9
  const VERSION = identity.PACKAGE_VERSION;
9
10
 
@@ -74,6 +75,16 @@ function runQuickProofCommand(opts) {
74
75
  }
75
76
  }
76
77
 
78
+ function runMcpInfoCommand(opts) {
79
+ const mcpInfo = require('./mcp-info');
80
+ const result = mcpInfo.info(opts.project);
81
+ if (opts.json) {
82
+ console.log(JSON.stringify(result, null, 2));
83
+ } else {
84
+ console.log(mcpInfo.render(result));
85
+ }
86
+ }
87
+
77
88
  function runExtensionScaffoldCommand(opts) {
78
89
  const authoring = require('./extension-authoring');
79
90
  if (!opts.extensionName) {
@@ -130,10 +141,47 @@ function runGateCommand(opts) {
130
141
  }
131
142
  }
132
143
 
144
+ function runStateCommand(opts) {
145
+ if (opts.stateAction !== 'advance') {
146
+ const result = {
147
+ command: 'state',
148
+ verdict: 'fail',
149
+ project: opts.project,
150
+ step: opts.step || null,
151
+ status: opts.status || null,
152
+ previousStatus: null,
153
+ updated: null,
154
+ warnings: [],
155
+ checks: [{ id: 'state-action-required', status: 'fail', artifact: '.godpowers/state.json', reason: 'state requires subcommand advance' }],
156
+ findings: [{ id: 'state-action-required', severity: 'error', artifact: '.godpowers/state.json', reason: 'state requires subcommand advance' }],
157
+ summary: { updated: false, state: '.godpowers/state.json', views: ['.godpowers/PROGRESS.md'] }
158
+ };
159
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
160
+ else console.log(stateAdvance.render(result));
161
+ process.exitCode = 1;
162
+ return;
163
+ }
164
+
165
+ const result = stateAdvance.advance(opts.project, {
166
+ step: opts.step,
167
+ status: opts.status
168
+ });
169
+ if (opts.json) {
170
+ console.log(JSON.stringify(result, null, 2));
171
+ } else {
172
+ console.log(stateAdvance.render(result));
173
+ }
174
+ if (stateAdvance.exitCode(result) !== 0) {
175
+ process.exitCode = 1;
176
+ }
177
+ }
178
+
133
179
  const COMMAND_RUNNERS = {
134
180
  status: runDashboardCommand,
135
181
  next: runDashboardCommand,
182
+ state: runStateCommand,
136
183
  'quick-proof': runQuickProofCommand,
184
+ 'mcp-info': runMcpInfoCommand,
137
185
  'automation-status': runAutomationCommand,
138
186
  'automation-setup': runAutomationCommand,
139
187
  dogfood: runDogfoodCommand,
@@ -157,6 +205,8 @@ module.exports = {
157
205
  runDashboardCommand,
158
206
  runDogfoodCommand,
159
207
  runQuickProofCommand,
208
+ runMcpInfoCommand,
160
209
  runExtensionScaffoldCommand,
161
- runGateCommand
210
+ runGateCommand,
211
+ runStateCommand
162
212
  };
@@ -83,13 +83,13 @@ function buildCanonicalContent(state, opts = {}) {
83
83
  const scale = (state && (state.scale || (state.project && state.project.scale))) || opts.scale || 'unknown';
84
84
  lines.push(`- Project: ${projectName}`);
85
85
  lines.push(`- Mode: ${mode} Scale: ${scale}`);
86
- lines.push('- State: `.godpowers/state.json` (machine) and `.godpowers/PROGRESS.md` (human)');
86
+ lines.push('- State: `.godpowers/state.json` is authority; `.godpowers/PROGRESS.md` is generated for humans');
87
87
  lines.push('');
88
88
 
89
89
  lines.push('### Quarterback rule');
90
90
  lines.push('');
91
- lines.push('There is exactly one orchestrator: `god-orchestrator`. It owns all writes to');
92
- lines.push('`state.json`, `PROGRESS.md`, `intent.yaml`, and `events.jsonl`. Skills like');
91
+ lines.push('There is exactly one orchestrator: `god-orchestrator`. It owns writes to');
92
+ lines.push('`state.json`, `intent.yaml`, and `events.jsonl`; `PROGRESS.md` is regenerated from state. Skills like');
93
93
  lines.push('`/god`, `/god-next`, `/god-status` read state without writing.');
94
94
  lines.push('');
95
95
 
@@ -148,7 +148,7 @@ function buildCanonicalContent(state, opts = {}) {
148
148
  }
149
149
  }
150
150
 
151
- lines.push('See `.godpowers/PROGRESS.md` for the live tier table.');
151
+ lines.push('See `.godpowers/state.json` for authority and `.godpowers/PROGRESS.md` for the generated tier table.');
152
152
  return lines.join('\n');
153
153
  }
154
154
 
package/lib/gate.js CHANGED
@@ -11,6 +11,7 @@ const path = require('path');
11
11
  const artifactMap = require('./artifact-map');
12
12
  const linter = require('./artifact-linter');
13
13
  const router = require('./router');
14
+ const stateStore = require('./state');
14
15
 
15
16
  function relToAbs(projectRoot, relPath) {
16
17
  return path.join(projectRoot, relPath);
@@ -168,12 +169,108 @@ function extractCommandStatuses(text) {
168
169
  return entries;
169
170
  }
170
171
 
171
- function checkBuildEvidence(projectRoot, result) {
172
- const relPath = '.godpowers/build/STATE.md';
173
- const file = relToAbs(projectRoot, relPath);
174
- if (!fs.existsSync(file)) return;
175
- const text = fs.readFileSync(file, 'utf8');
176
- const failedCommands = extractFailedCommands(text);
172
+ function commandName(entry) {
173
+ if (!entry || typeof entry !== 'object') return null;
174
+ const value = entry.command || entry.cmd || entry.name;
175
+ return value ? String(value).trim() : null;
176
+ }
177
+
178
+ function normalizeVerificationStatus(entry) {
179
+ if (!entry || typeof entry !== 'object') return null;
180
+ const raw = entry.status || entry.result || entry.verdict;
181
+ if (raw) {
182
+ const text = String(raw).trim().toLowerCase();
183
+ if (/^(pass|passed|green|success|succeeded|ok)$/.test(text)) return 'pass';
184
+ if (/^(fail|failed|red|error)$/.test(text)) return 'fail';
185
+ }
186
+ if (Number.isInteger(entry.exitCode)) return entry.exitCode === 0 ? 'pass' : 'fail';
187
+ return null;
188
+ }
189
+
190
+ function stateVerificationCommands(subStep) {
191
+ if (!subStep || typeof subStep !== 'object') return [];
192
+ const verification = subStep.verification && typeof subStep.verification === 'object'
193
+ ? subStep.verification
194
+ : {};
195
+ const commands = verification.commands ||
196
+ subStep.verificationCommands ||
197
+ subStep['verification-commands'] ||
198
+ [];
199
+ return Array.isArray(commands) ? commands : [];
200
+ }
201
+
202
+ function commandsWithStatus(subStep, wantedStatus) {
203
+ const commands = [];
204
+ for (const entry of stateVerificationCommands(subStep)) {
205
+ const name = commandName(entry);
206
+ if (!name) continue;
207
+ if (normalizeVerificationStatus(entry) !== wantedStatus) continue;
208
+ if (!commands.includes(name)) commands.push(name);
209
+ }
210
+ return commands;
211
+ }
212
+
213
+ function checkStateStepEvidence(projectRoot, tier, result) {
214
+ const stepRef = artifactMap.stateStepForTier(tier);
215
+ if (!stepRef) return null;
216
+
217
+ const relPath = '.godpowers/state.json';
218
+ const currentState = stateStore.read(projectRoot);
219
+ if (!currentState) {
220
+ const finding = makeFinding(
221
+ `state:${tier}:missing`,
222
+ 'error',
223
+ relPath,
224
+ `${tier} gate requires structured state evidence in state.json.`
225
+ );
226
+ result.findings.push(finding);
227
+ addFindingSummary(result.summary, finding.severity);
228
+ result.checks.push(makeCheck(`state:${tier}:status`, 'fail', relPath, finding.reason));
229
+ return null;
230
+ }
231
+
232
+ const tierState = currentState.tiers && currentState.tiers[stepRef.tierKey];
233
+ const subStep = tierState && tierState[stepRef.subStepKey];
234
+ if (!subStep) {
235
+ const finding = makeFinding(
236
+ `state:${tier}:step-missing`,
237
+ 'error',
238
+ relPath,
239
+ `state.json does not record ${stepRef.tierKey}.${stepRef.subStepKey}.`
240
+ );
241
+ result.findings.push(finding);
242
+ addFindingSummary(result.summary, finding.severity);
243
+ result.checks.push(makeCheck(`state:${tier}:status`, 'fail', relPath, finding.reason));
244
+ return null;
245
+ }
246
+
247
+ const status = subStep.status || 'pending';
248
+ const complete = stateStore.isCompleteStatus(status);
249
+ if (!complete) {
250
+ const finding = makeFinding(
251
+ `state:${tier}:incomplete`,
252
+ 'error',
253
+ relPath,
254
+ `${stepRef.tierKey}.${stepRef.subStepKey} status is ${status}, expected a complete status.`
255
+ );
256
+ result.findings.push(finding);
257
+ addFindingSummary(result.summary, finding.severity);
258
+ }
259
+ result.checks.push(makeCheck(
260
+ `state:${tier}:status`,
261
+ complete ? 'pass' : 'fail',
262
+ relPath,
263
+ complete
264
+ ? `${stepRef.tierKey}.${stepRef.subStepKey} records complete status ${status}.`
265
+ : `${stepRef.tierKey}.${stepRef.subStepKey} must be complete before this gate passes.`
266
+ ));
267
+ return subStep;
268
+ }
269
+
270
+ function checkBuildEvidence(result, buildStep) {
271
+ const relPath = '.godpowers/state.json';
272
+ if (!buildStep) return;
273
+ const failedCommands = commandsWithStatus(buildStep, 'fail');
177
274
  if (failedCommands.length > 0) {
178
275
  const finding = makeFinding(
179
276
  'build-verification-failed-command',
@@ -192,7 +289,7 @@ function checkBuildEvidence(projectRoot, result) {
192
289
  result.summary.buildVerificationFailedCommands = failedCommands;
193
290
  return;
194
291
  }
195
- const passedCommands = extractPassedCommands(text);
292
+ const passedCommands = commandsWithStatus(buildStep, 'pass');
196
293
  if (passedCommands.length === 0) {
197
294
  const finding = makeFinding(
198
295
  'build-verification-evidence',
@@ -214,7 +311,7 @@ function checkBuildEvidence(projectRoot, result) {
214
311
  'build-verification-evidence',
215
312
  'pass',
216
313
  relPath,
217
- `Build state records ${passedCommands.length} passed verification command(s).`
314
+ `state.json records ${passedCommands.length} passed build verification command(s).`
218
315
  ));
219
316
  result.summary.buildVerificationCommands = passedCommands;
220
317
  }
@@ -280,7 +377,8 @@ function check(opts = {}) {
280
377
  }
281
378
 
282
379
  checkArtifacts(projectRoot, tier, artifacts, opts, result);
283
- if (tier === 'build') checkBuildEvidence(projectRoot, result);
380
+ const stateStep = checkStateStepEvidence(projectRoot, tier, result);
381
+ if (tier === 'build') checkBuildEvidence(result, stateStep);
284
382
  if (tier === 'harden') checkHardenCriticals(projectRoot, result);
285
383
  return finalize(result);
286
384
  }
@@ -48,6 +48,48 @@ function installedAgentSurfaces(homeDir) {
48
48
  };
49
49
  }
50
50
 
51
+ function detectMcpAvailability(projectRoot, opts = {}) {
52
+ if (opts.mcp) return opts.mcp;
53
+ if (Object.prototype.hasOwnProperty.call(opts, 'mcpAvailable')) {
54
+ return {
55
+ available: Boolean(opts.mcpAvailable),
56
+ source: opts.mcpAvailable ? (opts.mcpSource || 'override') : 'override'
57
+ };
58
+ }
59
+
60
+ const env = opts.env || process.env;
61
+ if (env.GODPOWERS_MCP === '1' || env.GODPOWERS_MCP === 'true') {
62
+ return { available: true, source: 'environment' };
63
+ }
64
+
65
+ const root = projectRoot || process.cwd();
66
+ if (exists(path.join(root, 'packages', 'mcp', 'package.json'))) {
67
+ return { available: true, source: 'workspace package' };
68
+ }
69
+
70
+ try {
71
+ require.resolve('@godpowers/mcp/package.json', { paths: [root, __dirname] });
72
+ return { available: true, source: 'installed package' };
73
+ } catch (error) {
74
+ // Continue to host registration detection.
75
+ }
76
+
77
+ const homeDir = opts.homeDir || os.homedir();
78
+ const codexConfig = path.join(homeDir, '.codex', 'config.toml');
79
+ if (exists(codexConfig)) {
80
+ try {
81
+ const text = fs.readFileSync(codexConfig, 'utf8');
82
+ if (/\[mcp_servers\.godpowers\]/.test(text)) {
83
+ return { available: true, source: 'codex registration' };
84
+ }
85
+ } catch (error) {
86
+ return { available: false, source: 'codex registration unreadable' };
87
+ }
88
+ }
89
+
90
+ return { available: false, source: 'not configured' };
91
+ }
92
+
51
93
  function detect(projectRoot, opts = {}) {
52
94
  const env = opts.env || process.env;
53
95
  const homeDir = opts.homeDir || os.homedir();
@@ -62,6 +104,7 @@ function detect(projectRoot, opts = {}) {
62
104
  const extensionAuthoring = exists(path.join(root, 'lib', 'extension-authoring.js'))
63
105
  && exists(path.join(root, 'schema', 'extension-manifest.v1.json'));
64
106
  const suiteReleaseDryRun = exists(path.join(root, 'lib', 'suite-state.js'));
107
+ const mcp = detectMcpAvailability(root, opts);
65
108
 
66
109
  const gaps = [];
67
110
  if (!shell) gaps.push('shell unavailable');
@@ -86,6 +129,7 @@ function detect(projectRoot, opts = {}) {
86
129
  npm,
87
130
  gh,
88
131
  agentSpawn,
132
+ mcp,
89
133
  codeIntelligence: codeIntel,
90
134
  extensionAuthoring,
91
135
  suiteReleaseDryRun
@@ -97,9 +141,13 @@ function detect(projectRoot, opts = {}) {
97
141
 
98
142
  function summary(report) {
99
143
  if (!report) return 'unknown';
100
- if (report.level === 'full') return `full on ${report.host}`;
144
+ const mcp = report.guarantees && report.guarantees.mcp;
145
+ const mcpText = mcp && mcp.available
146
+ ? `; MCP available via ${mcp.source}`
147
+ : '; MCP not configured';
148
+ if (report.level === 'full') return `full on ${report.host}${mcpText}`;
101
149
  const gap = report.gaps && report.gaps.length > 0 ? `, ${report.gaps[0]}` : '';
102
- return `${report.level} on ${report.host}${gap}`;
150
+ return `${report.level} on ${report.host}${gap}${mcpText}`;
103
151
  }
104
152
 
105
153
  function render(report) {
@@ -112,6 +160,7 @@ function render(report) {
112
160
  lines.push(` Git: ${report.guarantees.git || 'not detected'}`);
113
161
  lines.push(` npm: ${report.guarantees.npm || 'not detected'}`);
114
162
  lines.push(` GitHub CLI: ${report.guarantees.gh || 'not detected'}`);
163
+ lines.push(` MCP: ${report.guarantees.mcp.available ? `available via ${report.guarantees.mcp.source}` : 'not configured'}`);
115
164
  lines.push(` Code intelligence: ${codeIntelligence.summary(report.guarantees.codeIntelligence)}`);
116
165
  lines.push(` Gaps: ${report.gaps.length > 0 ? report.gaps.join('; ') : 'none'}`);
117
166
  return lines.join('\n');
@@ -124,6 +173,7 @@ module.exports = {
124
173
  _private: {
125
174
  commandVersion,
126
175
  hostName,
127
- installedAgentSurfaces
176
+ installedAgentSurfaces,
177
+ detectMcpAvailability
128
178
  }
129
179
  };
@@ -4,7 +4,9 @@ const { RUNTIMES } = require('./installer-runtimes');
4
4
  const COMMANDS = new Set([
5
5
  'status',
6
6
  'next',
7
+ 'state',
7
8
  'quick-proof',
9
+ 'mcp-info',
8
10
  'automation-status',
9
11
  'automation-setup',
10
12
  'dogfood',
@@ -19,6 +21,9 @@ function parseArgs(argv, cwd = process.cwd()) {
19
21
  project: cwd,
20
22
  json: false,
21
23
  brief: false,
24
+ stateAction: null,
25
+ step: null,
26
+ status: null,
22
27
  extensionName: null,
23
28
  extensionOutput: cwd,
24
29
  extensionSkill: null,
@@ -40,6 +45,10 @@ function parseArgs(argv, cwd = process.cwd()) {
40
45
  opts.command = arg;
41
46
  continue;
42
47
  }
48
+ if (opts.command === 'state' && !opts.stateAction && !arg.startsWith('-')) {
49
+ opts.stateAction = arg;
50
+ continue;
51
+ }
43
52
 
44
53
  switch (arg) {
45
54
  case '--json':
@@ -54,6 +63,18 @@ function parseArgs(argv, cwd = process.cwd()) {
54
63
  i++;
55
64
  }
56
65
  break;
66
+ case '--step':
67
+ if (args[i + 1]) {
68
+ opts.step = args[i + 1];
69
+ i++;
70
+ }
71
+ break;
72
+ case '--status':
73
+ if (args[i + 1]) {
74
+ opts.status = args[i + 1];
75
+ i++;
76
+ }
77
+ break;
57
78
  case '--project':
58
79
  if (args[i + 1]) {
59
80
  opts.project = path.resolve(args[i + 1]);
@@ -103,6 +124,10 @@ function parseArgs(argv, cwd = process.cwd()) {
103
124
  opts.extensionWorkflow = arg.slice('--workflow='.length);
104
125
  } else if (arg.startsWith('--tier=')) {
105
126
  opts.tier = arg.slice('--tier='.length);
127
+ } else if (arg.startsWith('--step=')) {
128
+ opts.step = arg.slice('--step='.length);
129
+ } else if (arg.startsWith('--status=')) {
130
+ opts.status = arg.slice('--status='.length);
106
131
  } else if (arg.startsWith('--profile=')) {
107
132
  opts.profile = arg.slice('--profile='.length);
108
133
  } else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
@@ -0,0 +1,93 @@
1
+ const path = require('path');
2
+
3
+ const identity = require('./package-identity');
4
+
5
+ const MCP_PACKAGE = '@godpowers/mcp';
6
+
7
+ function projectPath(projectRoot) {
8
+ return path.resolve(projectRoot || process.cwd());
9
+ }
10
+
11
+ function serverCommand(projectRoot, version = identity.PACKAGE_VERSION) {
12
+ return {
13
+ command: 'npx',
14
+ args: [
15
+ '-y',
16
+ '-p',
17
+ `${identity.PACKAGE_NAME}@${version}`,
18
+ '-p',
19
+ `${MCP_PACKAGE}@${version}`,
20
+ 'godpowers-mcp',
21
+ 'serve',
22
+ '--project',
23
+ projectPath(projectRoot)
24
+ ]
25
+ };
26
+ }
27
+
28
+ function setupCommand(projectRoot, version = identity.PACKAGE_VERSION) {
29
+ return {
30
+ command: 'npx',
31
+ args: [
32
+ '-y',
33
+ '-p',
34
+ `${identity.PACKAGE_NAME}@${version}`,
35
+ '-p',
36
+ `${MCP_PACKAGE}@${version}`,
37
+ 'godpowers-mcp',
38
+ 'setup',
39
+ '--host=codex',
40
+ '--project',
41
+ projectPath(projectRoot),
42
+ '--write'
43
+ ]
44
+ };
45
+ }
46
+
47
+ function info(projectRoot, version = identity.PACKAGE_VERSION) {
48
+ const project = projectPath(projectRoot);
49
+ return {
50
+ package: MCP_PACKAGE,
51
+ version,
52
+ project,
53
+ boundary: 'The MCP SDK dependency is isolated in @godpowers/mcp. The main godpowers package has no production dependencies.',
54
+ tools: ['status', 'next', 'gate_check', 'lint_artifact', 'trace_requirement'],
55
+ server: serverCommand(project, version),
56
+ setup: setupCommand(project, version),
57
+ automaticRegistration: false
58
+ };
59
+ }
60
+
61
+ function renderCommand(command) {
62
+ return `${command.command} ${command.args.join(' ')}`;
63
+ }
64
+
65
+ function render(result) {
66
+ return [
67
+ 'Godpowers MCP',
68
+ '',
69
+ `Package: ${result.package}@${result.version}`,
70
+ `Project: ${result.project}`,
71
+ `Boundary: ${result.boundary}`,
72
+ '',
73
+ 'Tools:',
74
+ ` ${result.tools.join(', ')}`,
75
+ '',
76
+ 'Run server:',
77
+ ` ${renderCommand(result.server)}`,
78
+ '',
79
+ 'Opt-in Codex setup:',
80
+ ` ${renderCommand(result.setup)}`,
81
+ '',
82
+ 'Automatic registration: disabled'
83
+ ].join('\n');
84
+ }
85
+
86
+ module.exports = {
87
+ MCP_PACKAGE,
88
+ serverCommand,
89
+ setupCommand,
90
+ info,
91
+ render,
92
+ renderCommand
93
+ };