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.
- package/CHANGELOG.md +40 -0
- package/README.md +49 -19
- package/RELEASE.md +41 -29
- package/SKILL.md +46 -48
- package/agents/god-deploy-engineer.md +2 -2
- package/agents/god-designer.md +3 -2
- package/agents/god-greenfieldifier.md +2 -4
- package/agents/god-launch-strategist.md +4 -5
- package/agents/god-observability-engineer.md +5 -5
- package/agents/god-reconciler.md +10 -4
- package/agents/god-retrospective.md +1 -1
- package/agents/god-updater.md +5 -5
- package/bin/install.js +9 -1
- package/fixtures/gate/build-pass/.godpowers/state.json +33 -0
- package/lib/README.md +2 -0
- package/lib/artifact-map.js +15 -3
- package/lib/cli-dispatch.js +51 -1
- package/lib/context-writer.js +4 -4
- package/lib/gate.js +107 -9
- package/lib/host-capabilities.js +53 -3
- package/lib/installer-args.js +25 -0
- package/lib/mcp-info.js +93 -0
- package/lib/pillars.js +2 -4
- package/lib/recipes.js +16 -0
- package/lib/router.js +1 -5
- package/lib/source-sync.js +1 -1
- package/lib/state-advance.js +244 -0
- package/lib/state-lock.js +8 -4
- package/lib/state-views.js +460 -0
- package/lib/state.js +52 -3
- package/package.json +7 -2
- package/routing/god-audit.yaml +1 -1
- package/routing/god-build.yaml +1 -1
- package/routing/god-context.yaml +1 -1
- package/routing/god-deploy.yaml +3 -1
- package/routing/god-design.yaml +2 -2
- package/routing/god-launch.yaml +4 -1
- package/routing/god-migrate.yaml +0 -1
- package/routing/god-mode.yaml +1 -1
- package/routing/god-observe.yaml +4 -1
- package/routing/god-prd.yaml +1 -1
- package/routing/god-reconcile.yaml +2 -5
- package/routing/god-sync.yaml +1 -1
- package/routing/recipes/returning-after-break.yaml +1 -1
- package/schema/state.v1.json +68 -1
- package/skills/god-arch.md +1 -1
- package/skills/god-build.md +6 -4
- package/skills/god-deploy.md +16 -14
- package/skills/god-design.md +3 -3
- package/skills/god-fast.md +2 -2
- package/skills/god-feature.md +1 -1
- package/skills/god-harden.md +3 -3
- package/skills/god-hotfix.md +1 -1
- package/skills/god-init.md +14 -10
- package/skills/god-launch.md +14 -12
- package/skills/god-lifecycle.md +2 -1
- package/skills/god-mode.md +5 -4
- package/skills/god-next.md +2 -1
- package/skills/god-observe.md +15 -13
- package/skills/god-pause-work.md +2 -2
- package/skills/god-prd.md +5 -4
- package/skills/god-quick.md +1 -1
- package/skills/god-repo.md +1 -1
- package/skills/god-resume-work.md +5 -4
- package/skills/god-roadmap-update.md +1 -1
- package/skills/god-roadmap.md +1 -1
- package/skills/god-rollback.md +1 -1
- package/skills/god-skip.md +2 -2
- package/skills/god-stack.md +1 -1
- package/skills/god-standards.md +1 -1
- package/skills/god-status.md +6 -5
- package/skills/god-story.md +1 -1
- package/skills/god-sync.md +2 -2
- package/workflows/bluefield-arc.yaml +2 -4
- package/workflows/brownfield-arc.yaml +2 -4
package/agents/god-updater.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
264
|
-
- observe
|
|
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. |
|
package/lib/artifact-map.js
CHANGED
|
@@ -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/
|
|
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/
|
|
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
|
};
|
package/lib/cli-dispatch.js
CHANGED
|
@@ -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
|
};
|
package/lib/context-writer.js
CHANGED
|
@@ -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`
|
|
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
|
|
92
|
-
lines.push('`state.json`, `
|
|
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
|
|
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
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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 =
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
}
|
package/lib/host-capabilities.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
package/lib/installer-args.js
CHANGED
|
@@ -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)]) {
|
package/lib/mcp-info.js
ADDED
|
@@ -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
|
+
};
|