godpowers 2.4.3 → 2.5.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 +20 -0
- package/README.md +17 -4
- package/RELEASE.md +27 -33
- package/SKILL.md +5 -0
- package/bin/install.js +13 -107
- package/fixtures/gate/build-pass/.godpowers/build/STATE.md +10 -0
- package/fixtures/gate/harden-pass/.godpowers/harden/FINDINGS.md +14 -0
- package/fixtures/gate/repo-pass/.godpowers/repo/AUDIT.md +9 -0
- package/lib/artifact-map.js +60 -0
- package/lib/cli-dispatch.js +162 -0
- package/lib/dashboard.js +3 -2
- package/lib/gate.js +271 -0
- package/lib/installer-args.js +11 -1
- package/lib/route-quality-sync.js +38 -0
- package/package.json +1 -1
- package/references/orchestration/GOD-MODE-RUNBOOK.md +19 -0
- package/routing/god-arch.yaml +1 -0
- package/routing/god-build.yaml +1 -0
- package/routing/god-design.yaml +1 -0
- package/routing/god-harden.yaml +1 -0
- package/routing/god-prd.yaml +1 -0
- package/routing/god-repo.yaml +1 -0
- package/routing/god-roadmap.yaml +1 -0
- package/routing/god-stack.yaml +1 -0
- package/skills/god-arch.md +2 -1
- package/skills/god-build.md +4 -2
- package/skills/god-design.md +8 -0
- package/skills/god-harden.md +2 -1
- package/skills/god-mode.md +4 -0
- package/skills/god-prd.md +2 -1
- package/skills/god-repo.md +2 -1
- package/skills/god-roadmap.md +2 -1
- package/skills/god-stack.md +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.5.0] - 2026-06-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Added `npx godpowers gate --tier=<tier> --project=.` for PRD, design,
|
|
14
|
+
architecture, roadmap, stack, repo, build, and harden tier gates.
|
|
15
|
+
- Added `lib/artifact-map.js`, `lib/gate.js`, and `lib/cli-dispatch.js` so
|
|
16
|
+
artifact paths, executable gates, and CLI command dispatch are tested outside
|
|
17
|
+
the installer binary.
|
|
18
|
+
- Added gate fixtures and tests for green tier artifacts, missing artifacts,
|
|
19
|
+
lint failures, harden Critical findings, build verification evidence, JSON
|
|
20
|
+
shape stability, async API coverage, and CLI exit codes.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Updated eight tier skills and their routing metadata to require the
|
|
24
|
+
executable gate before downstream work proceeds.
|
|
25
|
+
- Updated `/god-mode` and its runbook to run executable gates between tier
|
|
26
|
+
transitions.
|
|
27
|
+
- Updated static checks and route-quality sync so missing gate instructions and
|
|
28
|
+
missing `standards.gate-command` metadata block release readiness.
|
|
29
|
+
|
|
10
30
|
## [2.4.3] - 2026-06-09
|
|
11
31
|
|
|
12
32
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/aihxp/godpowers/actions/workflows/ci.yml)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
[](CHANGELOG.md)
|
|
6
6
|
[](https://www.npmjs.com/package/godpowers)
|
|
7
7
|
|
|
8
8
|
**Ship fast. Ship right. Ship everything. Ship accountably.**
|
|
@@ -26,9 +26,10 @@ Godpowers makes AI coding accountable: every serious run should leave disk
|
|
|
26
26
|
state, artifacts, validation gates, host guarantees, and a next action. Code is
|
|
27
27
|
only one output. The project memory and proof trail matter too.
|
|
28
28
|
|
|
29
|
-
Version 2.
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
Version 2.5.0 adds executable tier gates through `npx godpowers gate` while
|
|
30
|
+
keeping the 2.4 command-family UX, external CLI canary evidence, prompt-size
|
|
31
|
+
guardrails, legacy command quarantine, lib coverage gating, and package
|
|
32
|
+
verification before publish.
|
|
32
33
|
|
|
33
34
|
Maintainer hardening continues on the 2.x line with small, audited public
|
|
34
35
|
surface updates when they close real workflow gaps. The 2.1.0 patch closes a command-injection vector in the
|
|
@@ -251,6 +252,7 @@ npx godpowers next --project=.
|
|
|
251
252
|
npx godpowers status --project=. --brief
|
|
252
253
|
npx godpowers status --project=. --json
|
|
253
254
|
npx godpowers quick-proof --project=.
|
|
255
|
+
npx godpowers gate --tier=prd --project=.
|
|
254
256
|
npx godpowers dogfood
|
|
255
257
|
npx godpowers extension-scaffold --name=@godpowers/my-pack --output=.
|
|
256
258
|
```
|
|
@@ -271,6 +273,17 @@ test order is maintained as a readable list instead of a long package script.
|
|
|
271
273
|
decomposition, async runtime APIs, agent reference validation coverage, and God
|
|
272
274
|
Mode runbook delegation.
|
|
273
275
|
|
|
276
|
+
Tier skills now run the executable gate before downstream work proceeds:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npx godpowers gate --tier=prd --project=.
|
|
280
|
+
npx godpowers gate --tier=build --project=.
|
|
281
|
+
npx godpowers gate --tier=harden --project=.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The gate reads disk artifacts, runs the shared artifact linter, checks build
|
|
285
|
+
verification evidence, and blocks unresolved Critical harden findings.
|
|
286
|
+
|
|
274
287
|
The runtime remains dependency-free. YAML parsing is intentionally limited to
|
|
275
288
|
the documented Godpowers subset used by intent, routing, workflow, and
|
|
276
289
|
extension files, with parser coverage in `scripts/test-yaml-parser.js`.
|
package/RELEASE.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# Godpowers 2.
|
|
1
|
+
# Godpowers 2.5.0 Release
|
|
2
2
|
|
|
3
3
|
> Status: Ready for package verification
|
|
4
|
-
> Date: 2026-06-
|
|
4
|
+
> Date: 2026-06-10
|
|
5
5
|
|
|
6
|
-
Godpowers 2.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
Godpowers 2.5.0 ships executable tier gates for the code-first kernel
|
|
7
|
+
migration. It keeps the slash-command workflow intact while giving command
|
|
8
|
+
capable hosts a stable JSON gate contract and non-zero exit codes for blocked
|
|
9
|
+
tier transitions.
|
|
10
10
|
|
|
11
11
|
## What's in this release
|
|
12
12
|
|
|
@@ -14,45 +14,39 @@ verification before publish.
|
|
|
14
14
|
- 40 specialist agents
|
|
15
15
|
- 13 executable workflows
|
|
16
16
|
- 42 intent recipes
|
|
17
|
+
- 8 installer CLI helpers
|
|
17
18
|
|
|
18
19
|
## Highlights
|
|
19
20
|
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
`
|
|
32
|
-
- `npm run release:check` runs the full suite under the lib coverage floor
|
|
33
|
-
before audit and package verification.
|
|
34
|
-
- Package verification now creates the npm tarball in a temporary directory and
|
|
35
|
-
removes it automatically.
|
|
21
|
+
- `npx godpowers gate --tier=<tier> --project=.` checks PRD, design,
|
|
22
|
+
architecture, roadmap, stack, repo, build, and harden artifacts.
|
|
23
|
+
- Gate JSON returns `{tier, verdict, artifacts, checks, findings, summary}` for
|
|
24
|
+
host-visible evidence.
|
|
25
|
+
- Build gates require `.godpowers/build/STATE.md` to record exact verification
|
|
26
|
+
commands that passed.
|
|
27
|
+
- Harden gates fail unresolved Critical findings and blocked launch gates.
|
|
28
|
+
- `/god-mode` now runs executable gates between tier transitions.
|
|
29
|
+
- Tier routes declare `standards.gate-command`, and static checks enforce the
|
|
30
|
+
skill and route metadata.
|
|
31
|
+
- CLI command dispatch now lives in `lib/cli-dispatch.js`, keeping
|
|
32
|
+
`bin/install.js` thin.
|
|
36
33
|
|
|
37
34
|
## Validation
|
|
38
35
|
|
|
39
|
-
- `node scripts/
|
|
36
|
+
- `node scripts/test-gate.js` green
|
|
40
37
|
- `node scripts/test-cli-dispatch.js` green
|
|
41
|
-
- `node scripts/
|
|
42
|
-
- `node scripts/run-adoption-canary.js https://github.com/sindresorhus/is.git --output=docs/case-studies/sindresorhus-is-adoption-canary.md` green
|
|
43
|
-
- `node scripts/run-adoption-canary.js https://github.com/expressjs/cors.git --output=docs/case-studies/expressjs-cors-adoption-canary.md` green
|
|
44
|
-
- `node scripts/run-adoption-canary.js https://github.com/tinyhttp/tinyhttp.git --output=docs/case-studies/tinyhttp-adoption-canary.md` green
|
|
38
|
+
- `node scripts/static-check.js` green
|
|
45
39
|
- `npm run release:check` required before publish
|
|
46
40
|
|
|
47
41
|
## Upgrade
|
|
48
42
|
|
|
49
|
-
- `npm install -g godpowers@2.
|
|
50
|
-
- Re-run `/god-context` in each project to refresh installed runtime metadata
|
|
51
|
-
-
|
|
43
|
+
- `npm install -g godpowers@2.5.0` or `npx godpowers@2.5.0`
|
|
44
|
+
- Re-run `/god-context` in each project to refresh installed runtime metadata.
|
|
45
|
+
- Existing `.godpowers/` state remains compatible.
|
|
52
46
|
|
|
53
47
|
## Notes
|
|
54
48
|
|
|
55
|
-
- GitHub release creation for `v2.
|
|
49
|
+
- GitHub release creation for `v2.5.0`.
|
|
56
50
|
- The tag should match the npm package version.
|
|
57
|
-
- The `v2.
|
|
58
|
-
`godpowers@2.
|
|
51
|
+
- The `v2.5.0` tag should point to the release commit that matches the npm
|
|
52
|
+
`godpowers@2.5.0` package.
|
package/SKILL.md
CHANGED
|
@@ -53,6 +53,11 @@ Each tier gates on a verified artifact from the prior tier. You cannot build
|
|
|
53
53
|
without architecture. You cannot deploy without a build. You cannot launch with
|
|
54
54
|
unresolved Critical security findings.
|
|
55
55
|
|
|
56
|
+
For PRD, design, architecture, roadmap, stack, repo, build, and harden, run
|
|
57
|
+
`npx godpowers gate --tier=<tier> --project=.` after the tier artifact is
|
|
58
|
+
created and before starting downstream tier work. A non-zero exit blocks
|
|
59
|
+
progress until the artifact or evidence is repaired.
|
|
60
|
+
|
|
56
61
|
### 5. Context Isolation
|
|
57
62
|
Every execution agent gets a fresh context window. The orchestrator is thin; it
|
|
58
63
|
spawns workers with full context budgets. This defeats context rot.
|
package/bin/install.js
CHANGED
|
@@ -20,6 +20,7 @@ const {
|
|
|
20
20
|
const { describeProfiles } = require('../lib/install-profiles');
|
|
21
21
|
const commandFamilies = require('../lib/command-families');
|
|
22
22
|
const identity = require('../lib/package-identity');
|
|
23
|
+
const cliDispatch = require('../lib/cli-dispatch');
|
|
23
24
|
|
|
24
25
|
const VERSION = identity.PACKAGE_VERSION;
|
|
25
26
|
|
|
@@ -50,6 +51,7 @@ function showHelp() {
|
|
|
50
51
|
log('Commands:');
|
|
51
52
|
log(' status Show the Godpowers Dashboard for a project');
|
|
52
53
|
log(' next Show the dashboard and recommended next command');
|
|
54
|
+
log(' gate Check a tier artifact gate');
|
|
53
55
|
log(' quick-proof Show a runnable proof from the shipped fixture');
|
|
54
56
|
log(' automation-status Show host automation provider support');
|
|
55
57
|
log(' automation-setup Show an opt-in automation setup plan');
|
|
@@ -63,6 +65,7 @@ function showHelp() {
|
|
|
63
65
|
log('');
|
|
64
66
|
log('Options:');
|
|
65
67
|
log(' --project=<path> Project root for status, next, proof, or automation commands');
|
|
68
|
+
log(' --tier=<name> Tier for gate: prd, design, arch, roadmap, stack, repo, build, or harden');
|
|
66
69
|
log(' --json Emit JSON for status, next, proof, or automation commands');
|
|
67
70
|
log(' --brief Render compact output for status, next, or proof');
|
|
68
71
|
log(' --name=<scope/name> Extension package name for extension-scaffold');
|
|
@@ -96,6 +99,7 @@ function showHelp() {
|
|
|
96
99
|
log('Examples:');
|
|
97
100
|
log(' npx godpowers status --project=.');
|
|
98
101
|
log(' npx godpowers next --project=.');
|
|
102
|
+
log(' npx godpowers gate --tier=prd --project=.');
|
|
99
103
|
log(' npx godpowers quick-proof --project=.');
|
|
100
104
|
log(' npx godpowers automation-status --project=.');
|
|
101
105
|
log(' npx godpowers automation-setup --project=.');
|
|
@@ -107,105 +111,6 @@ function showHelp() {
|
|
|
107
111
|
log(' npx godpowers --codex --cursor');
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
function runAutomationCommand(opts) {
|
|
111
|
-
const automation = require('../lib/automation-providers');
|
|
112
|
-
const result = opts.command === 'automation-setup'
|
|
113
|
-
? automation.setupPlan(opts.project)
|
|
114
|
-
: automation.detect(opts.project);
|
|
115
|
-
if (opts.json) {
|
|
116
|
-
console.log(JSON.stringify(result, null, 2));
|
|
117
|
-
} else if (opts.command === 'automation-setup') {
|
|
118
|
-
console.log(automation.renderSetupPlan(result));
|
|
119
|
-
} else {
|
|
120
|
-
console.log(automation.render(result));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function runDashboardCommand(opts) {
|
|
125
|
-
const dashboard = require('../lib/dashboard');
|
|
126
|
-
const result = dashboard.compute(opts.project);
|
|
127
|
-
if (opts.json) {
|
|
128
|
-
console.log(JSON.stringify(result, null, 2));
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
console.log(dashboard.render(result, { brief: opts.brief }));
|
|
133
|
-
if (opts.command === 'next') {
|
|
134
|
-
console.log('');
|
|
135
|
-
console.log('Suggested next command:');
|
|
136
|
-
console.log(` ${result.next && result.next.command ? result.next.command : 'describe the next intent'}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function runDogfoodCommand(opts) {
|
|
141
|
-
const dogfood = require('../lib/dogfood-runner');
|
|
142
|
-
const result = dogfood.runAll();
|
|
143
|
-
if (opts.json) {
|
|
144
|
-
console.log(JSON.stringify(result, null, 2));
|
|
145
|
-
} else {
|
|
146
|
-
console.log(dogfood.render(result));
|
|
147
|
-
}
|
|
148
|
-
if (result.status !== 'pass') process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function runQuickProofCommand(opts) {
|
|
152
|
-
const quickProof = require('../lib/quick-proof');
|
|
153
|
-
const result = quickProof.compute(opts.project);
|
|
154
|
-
if (opts.json) {
|
|
155
|
-
console.log(JSON.stringify(result, null, 2));
|
|
156
|
-
} else {
|
|
157
|
-
console.log(quickProof.render(result, { brief: opts.brief }));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function runExtensionScaffoldCommand(opts) {
|
|
162
|
-
const authoring = require('../lib/extension-authoring');
|
|
163
|
-
if (!opts.extensionName) {
|
|
164
|
-
error('extension-scaffold requires --name=@scope/package');
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
167
|
-
const result = authoring.scaffold(opts.extensionOutput, {
|
|
168
|
-
name: opts.extensionName,
|
|
169
|
-
skill: opts.extensionSkill || undefined,
|
|
170
|
-
agent: opts.extensionAgent || undefined,
|
|
171
|
-
workflow: opts.extensionWorkflow || undefined,
|
|
172
|
-
runtimeVersion: VERSION
|
|
173
|
-
});
|
|
174
|
-
if (opts.json) {
|
|
175
|
-
console.log(JSON.stringify(result, null, 2));
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
success(`Scaffolded ${result.name} at ${result.path}`);
|
|
180
|
-
if (result.written.length > 0) {
|
|
181
|
-
log(`Wrote ${result.written.length} file(s): ${result.written.join(', ')}`);
|
|
182
|
-
}
|
|
183
|
-
if (result.validation.length > 0) {
|
|
184
|
-
warn(`Validation warnings: ${result.validation.join('; ')}`);
|
|
185
|
-
} else {
|
|
186
|
-
success('Extension manifest validates');
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const COMMAND_RUNNERS = {
|
|
191
|
-
status: runDashboardCommand,
|
|
192
|
-
next: runDashboardCommand,
|
|
193
|
-
'quick-proof': runQuickProofCommand,
|
|
194
|
-
'automation-status': runAutomationCommand,
|
|
195
|
-
'automation-setup': runAutomationCommand,
|
|
196
|
-
dogfood: runDogfoodCommand,
|
|
197
|
-
'extension-scaffold': runExtensionScaffoldCommand
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
function runCommand(opts) {
|
|
201
|
-
const runner = COMMAND_RUNNERS[opts.command];
|
|
202
|
-
if (runner) {
|
|
203
|
-
runner(opts);
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
114
|
function applyDefaultRuntimeSelection(opts) {
|
|
210
115
|
if (opts.runtimes.length > 0 || opts.all) return;
|
|
211
116
|
if (!process.stdin.isTTY) {
|
|
@@ -277,7 +182,7 @@ function main() {
|
|
|
277
182
|
process.exit(0);
|
|
278
183
|
}
|
|
279
184
|
|
|
280
|
-
if (runCommand(opts)) return;
|
|
185
|
+
if (cliDispatch.runCommand(opts)) return;
|
|
281
186
|
|
|
282
187
|
console.log(BANNER);
|
|
283
188
|
const srcDir = path.resolve(__dirname, '..');
|
|
@@ -300,13 +205,14 @@ if (require.main === module) {
|
|
|
300
205
|
|
|
301
206
|
module.exports = {
|
|
302
207
|
showHelp,
|
|
303
|
-
COMMAND_RUNNERS,
|
|
304
|
-
runCommand,
|
|
305
|
-
runAutomationCommand,
|
|
306
|
-
runDashboardCommand,
|
|
307
|
-
runDogfoodCommand,
|
|
308
|
-
runQuickProofCommand,
|
|
309
|
-
runExtensionScaffoldCommand,
|
|
208
|
+
COMMAND_RUNNERS: cliDispatch.COMMAND_RUNNERS,
|
|
209
|
+
runCommand: cliDispatch.runCommand,
|
|
210
|
+
runAutomationCommand: cliDispatch.runAutomationCommand,
|
|
211
|
+
runDashboardCommand: cliDispatch.runDashboardCommand,
|
|
212
|
+
runDogfoodCommand: cliDispatch.runDogfoodCommand,
|
|
213
|
+
runQuickProofCommand: cliDispatch.runQuickProofCommand,
|
|
214
|
+
runExtensionScaffoldCommand: cliDispatch.runExtensionScaffoldCommand,
|
|
215
|
+
runGateCommand: cliDispatch.runGateCommand,
|
|
310
216
|
applyDefaultRuntimeSelection,
|
|
311
217
|
runInstall,
|
|
312
218
|
runUninstall,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Build State
|
|
2
|
+
|
|
3
|
+
## Delivered
|
|
4
|
+
|
|
5
|
+
[DECISION] The build gate fixture records a completed implementation slice for executable gate tests.
|
|
6
|
+
|
|
7
|
+
## Verification Commands
|
|
8
|
+
|
|
9
|
+
- [DECISION] `npm test`: passed for the fixture project.
|
|
10
|
+
- [DECISION] `npm run lint`: passed for the fixture project.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Findings
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
[DECISION] The harden fixture contains no unresolved Critical findings.
|
|
6
|
+
|
|
7
|
+
| Severity | Count |
|
|
8
|
+
|---|---:|
|
|
9
|
+
| Critical | 0 |
|
|
10
|
+
| High | 0 |
|
|
11
|
+
| Medium | 0 |
|
|
12
|
+
| Low | 0 |
|
|
13
|
+
|
|
14
|
+
[DECISION] Launch gate: PASSED.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Repo Audit
|
|
2
|
+
|
|
3
|
+
## Scaffold Evidence
|
|
4
|
+
|
|
5
|
+
[DECISION] The repo gate fixture records that the scaffold artifact exists and can be linted by the Phase 1 gate.
|
|
6
|
+
|
|
7
|
+
## Verification
|
|
8
|
+
|
|
9
|
+
[DECISION] The fixture is intentionally minimal because Phase 1 does not run scaffold CI commands.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tier artifact map for dashboard, gates, and documentation checks.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const TIER_ARTIFACTS = {
|
|
6
|
+
prd: [
|
|
7
|
+
{ path: '.godpowers/prd/PRD.md', required: true, lint: true }
|
|
8
|
+
],
|
|
9
|
+
design: [
|
|
10
|
+
{ path: 'DESIGN.md', required: true, lint: true },
|
|
11
|
+
{ path: 'PRODUCT.md', required: false, lint: true },
|
|
12
|
+
{ path: '.godpowers/design/STATE.md', required: true, lint: true }
|
|
13
|
+
],
|
|
14
|
+
arch: [
|
|
15
|
+
{ path: '.godpowers/arch/ARCH.md', required: true, lint: true }
|
|
16
|
+
],
|
|
17
|
+
roadmap: [
|
|
18
|
+
{ path: '.godpowers/roadmap/ROADMAP.md', required: true, lint: true }
|
|
19
|
+
],
|
|
20
|
+
stack: [
|
|
21
|
+
{ path: '.godpowers/stack/DECISION.md', required: true, lint: true }
|
|
22
|
+
],
|
|
23
|
+
repo: [
|
|
24
|
+
{ path: '.godpowers/repo/AUDIT.md', required: true, lint: true }
|
|
25
|
+
],
|
|
26
|
+
build: [
|
|
27
|
+
{ path: '.godpowers/build/STATE.md', required: true, lint: true }
|
|
28
|
+
],
|
|
29
|
+
harden: [
|
|
30
|
+
{ path: '.godpowers/harden/FINDINGS.md', required: true, lint: true }
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function normalizeTier(tier) {
|
|
35
|
+
if (!tier) return null;
|
|
36
|
+
return String(tier).replace(/^\/?god-/, '').toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function tiers() {
|
|
40
|
+
return Object.keys(TIER_ARTIFACTS);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function artifactsForTier(tier) {
|
|
44
|
+
const key = normalizeTier(tier);
|
|
45
|
+
if (!key || !TIER_ARTIFACTS[key]) return null;
|
|
46
|
+
return TIER_ARTIFACTS[key].map((artifact) => ({ ...artifact }));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function requiredArtifactsForTier(tier) {
|
|
50
|
+
const artifacts = artifactsForTier(tier);
|
|
51
|
+
return artifacts ? artifacts.filter((artifact) => artifact.required) : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
TIER_ARTIFACTS,
|
|
56
|
+
normalizeTier,
|
|
57
|
+
tiers,
|
|
58
|
+
artifactsForTier,
|
|
59
|
+
requiredArtifactsForTier
|
|
60
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer CLI command dispatch.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const gate = require('./gate');
|
|
6
|
+
const identity = require('./package-identity');
|
|
7
|
+
|
|
8
|
+
const VERSION = identity.PACKAGE_VERSION;
|
|
9
|
+
|
|
10
|
+
function log(msg) {
|
|
11
|
+
console.log(` ${msg}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function success(msg) {
|
|
15
|
+
console.log(` \x1b[32m+\x1b[0m ${msg}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function warn(msg) {
|
|
19
|
+
console.log(` \x1b[33m!\x1b[0m ${msg}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function error(msg) {
|
|
23
|
+
console.error(` \x1b[31mx\x1b[0m ${msg}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function runAutomationCommand(opts) {
|
|
27
|
+
const automation = require('./automation-providers');
|
|
28
|
+
const result = opts.command === 'automation-setup'
|
|
29
|
+
? automation.setupPlan(opts.project)
|
|
30
|
+
: automation.detect(opts.project);
|
|
31
|
+
if (opts.json) {
|
|
32
|
+
console.log(JSON.stringify(result, null, 2));
|
|
33
|
+
} else if (opts.command === 'automation-setup') {
|
|
34
|
+
console.log(automation.renderSetupPlan(result));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(automation.render(result));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function runDashboardCommand(opts) {
|
|
41
|
+
const dashboard = require('./dashboard');
|
|
42
|
+
const result = dashboard.compute(opts.project);
|
|
43
|
+
if (opts.json) {
|
|
44
|
+
console.log(JSON.stringify(result, null, 2));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(dashboard.render(result, { brief: opts.brief }));
|
|
49
|
+
if (opts.command === 'next') {
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log('Suggested next command:');
|
|
52
|
+
console.log(` ${result.next && result.next.command ? result.next.command : 'describe the next intent'}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runDogfoodCommand(opts) {
|
|
57
|
+
const dogfood = require('./dogfood-runner');
|
|
58
|
+
const result = dogfood.runAll();
|
|
59
|
+
if (opts.json) {
|
|
60
|
+
console.log(JSON.stringify(result, null, 2));
|
|
61
|
+
} else {
|
|
62
|
+
console.log(dogfood.render(result));
|
|
63
|
+
}
|
|
64
|
+
if (result.status !== 'pass') process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function runQuickProofCommand(opts) {
|
|
68
|
+
const quickProof = require('./quick-proof');
|
|
69
|
+
const result = quickProof.compute(opts.project);
|
|
70
|
+
if (opts.json) {
|
|
71
|
+
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
} else {
|
|
73
|
+
console.log(quickProof.render(result, { brief: opts.brief }));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function runExtensionScaffoldCommand(opts) {
|
|
78
|
+
const authoring = require('./extension-authoring');
|
|
79
|
+
if (!opts.extensionName) {
|
|
80
|
+
error('extension-scaffold requires --name=@scope/package');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const result = authoring.scaffold(opts.extensionOutput, {
|
|
84
|
+
name: opts.extensionName,
|
|
85
|
+
skill: opts.extensionSkill || undefined,
|
|
86
|
+
agent: opts.extensionAgent || undefined,
|
|
87
|
+
workflow: opts.extensionWorkflow || undefined,
|
|
88
|
+
runtimeVersion: VERSION
|
|
89
|
+
});
|
|
90
|
+
if (opts.json) {
|
|
91
|
+
console.log(JSON.stringify(result, null, 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
success(`Scaffolded ${result.name} at ${result.path}`);
|
|
96
|
+
if (result.written.length > 0) {
|
|
97
|
+
log(`Wrote ${result.written.length} file(s): ${result.written.join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
if (result.validation.length > 0) {
|
|
100
|
+
warn(`Validation warnings: ${result.validation.join('; ')}`);
|
|
101
|
+
} else {
|
|
102
|
+
success('Extension manifest validates');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function runGateCommand(opts) {
|
|
107
|
+
if (!opts.tier) {
|
|
108
|
+
const result = {
|
|
109
|
+
tier: null,
|
|
110
|
+
verdict: 'fail',
|
|
111
|
+
artifacts: [],
|
|
112
|
+
checks: [{ id: 'tier-required', status: 'fail', artifact: null, reason: 'gate requires --tier=<name>' }],
|
|
113
|
+
findings: [{ id: 'tier-required', severity: 'error', artifact: null, reason: 'gate requires --tier=<name>' }],
|
|
114
|
+
summary: { errors: 1, warnings: 0, infos: 0, missing: 0, checkedArtifacts: 0 }
|
|
115
|
+
};
|
|
116
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
117
|
+
else console.log(gate.render(result));
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = gate.check({ tier: opts.tier, projectRoot: opts.project });
|
|
123
|
+
if (opts.json) {
|
|
124
|
+
console.log(JSON.stringify(result, null, 2));
|
|
125
|
+
} else {
|
|
126
|
+
console.log(gate.render(result));
|
|
127
|
+
}
|
|
128
|
+
if (gate.exitCode(result) !== 0) {
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const COMMAND_RUNNERS = {
|
|
134
|
+
status: runDashboardCommand,
|
|
135
|
+
next: runDashboardCommand,
|
|
136
|
+
'quick-proof': runQuickProofCommand,
|
|
137
|
+
'automation-status': runAutomationCommand,
|
|
138
|
+
'automation-setup': runAutomationCommand,
|
|
139
|
+
dogfood: runDogfoodCommand,
|
|
140
|
+
'extension-scaffold': runExtensionScaffoldCommand,
|
|
141
|
+
gate: runGateCommand
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
function runCommand(opts) {
|
|
145
|
+
const runner = COMMAND_RUNNERS[opts.command];
|
|
146
|
+
if (runner) {
|
|
147
|
+
runner(opts);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
COMMAND_RUNNERS,
|
|
155
|
+
runCommand,
|
|
156
|
+
runAutomationCommand,
|
|
157
|
+
runDashboardCommand,
|
|
158
|
+
runDogfoodCommand,
|
|
159
|
+
runQuickProofCommand,
|
|
160
|
+
runExtensionScaffoldCommand,
|
|
161
|
+
runGateCommand
|
|
162
|
+
};
|
package/lib/dashboard.js
CHANGED
|
@@ -16,10 +16,11 @@ const automationProviders = require('./automation-providers');
|
|
|
16
16
|
const repoDocSync = require('./repo-doc-sync');
|
|
17
17
|
const repoSurfaceSync = require('./repo-surface-sync');
|
|
18
18
|
const hostCapabilities = require('./host-capabilities');
|
|
19
|
+
const artifactMap = require('./artifact-map');
|
|
19
20
|
|
|
20
21
|
const GOD_DIR = '.godpowers';
|
|
21
|
-
const PRD_PATH = '
|
|
22
|
-
const ROADMAP_PATH = '
|
|
22
|
+
const PRD_PATH = artifactMap.requiredArtifactsForTier('prd')[0].path;
|
|
23
|
+
const ROADMAP_PATH = artifactMap.requiredArtifactsForTier('roadmap')[0].path;
|
|
23
24
|
const CHECKPOINT_PATH = '.godpowers/CHECKPOINT.md';
|
|
24
25
|
const SYNC_LOG_PATH = '.godpowers/SYNC-LOG.md';
|
|
25
26
|
const REVIEW_PATH = '.godpowers/REVIEW-REQUIRED.md';
|
package/lib/gate.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executable Godpowers tier gates.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 gates are intentionally mechanical. They check expected artifacts on
|
|
5
|
+
* disk, run the shared artifact linter, and apply narrow tier-specific checks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const artifactMap = require('./artifact-map');
|
|
12
|
+
const linter = require('./artifact-linter');
|
|
13
|
+
const router = require('./router');
|
|
14
|
+
|
|
15
|
+
function relToAbs(projectRoot, relPath) {
|
|
16
|
+
return path.join(projectRoot, relPath);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function makeCheck(id, status, artifact, reason) {
|
|
20
|
+
return { id, status, artifact, reason };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeFinding(id, severity, artifact, reason, extra = {}) {
|
|
24
|
+
return { id, severity, artifact, reason, ...extra };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function emptySummary() {
|
|
28
|
+
return {
|
|
29
|
+
errors: 0,
|
|
30
|
+
warnings: 0,
|
|
31
|
+
infos: 0,
|
|
32
|
+
missing: 0,
|
|
33
|
+
checkedArtifacts: 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function addFindingSummary(summary, severity) {
|
|
38
|
+
if (severity === 'error') summary.errors++;
|
|
39
|
+
else if (severity === 'warning') summary.warnings++;
|
|
40
|
+
else summary.infos++;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function lintArtifact(projectRoot, relPath, opts = {}) {
|
|
44
|
+
return linter.lintFile(relToAbs(projectRoot, relPath), {
|
|
45
|
+
projectRoot,
|
|
46
|
+
today: opts.today
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function checkArtifacts(projectRoot, tier, artifacts, opts, result) {
|
|
51
|
+
for (const artifact of artifacts) {
|
|
52
|
+
const exists = fs.existsSync(relToAbs(projectRoot, artifact.path));
|
|
53
|
+
const artifactResult = {
|
|
54
|
+
path: artifact.path,
|
|
55
|
+
required: artifact.required,
|
|
56
|
+
exists,
|
|
57
|
+
lint: null
|
|
58
|
+
};
|
|
59
|
+
result.artifacts.push(artifactResult);
|
|
60
|
+
|
|
61
|
+
if (!exists) {
|
|
62
|
+
const status = artifact.required ? 'fail' : 'skipped';
|
|
63
|
+
result.checks.push(makeCheck(
|
|
64
|
+
`artifact:${tier}:${artifact.path}`,
|
|
65
|
+
status,
|
|
66
|
+
artifact.path,
|
|
67
|
+
artifact.required ? 'Required artifact is missing.' : 'Optional artifact is absent.'
|
|
68
|
+
));
|
|
69
|
+
if (artifact.required) {
|
|
70
|
+
result.summary.missing++;
|
|
71
|
+
const finding = makeFinding(
|
|
72
|
+
`missing-artifact:${tier}:${artifact.path}`,
|
|
73
|
+
'error',
|
|
74
|
+
artifact.path,
|
|
75
|
+
'Required artifact is missing.'
|
|
76
|
+
);
|
|
77
|
+
result.findings.push(finding);
|
|
78
|
+
addFindingSummary(result.summary, finding.severity);
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
result.checks.push(makeCheck(
|
|
84
|
+
`artifact:${tier}:${artifact.path}`,
|
|
85
|
+
'pass',
|
|
86
|
+
artifact.path,
|
|
87
|
+
'Artifact exists on disk.'
|
|
88
|
+
));
|
|
89
|
+
|
|
90
|
+
if (!artifact.lint) continue;
|
|
91
|
+
const lintResult = lintArtifact(projectRoot, artifact.path, opts);
|
|
92
|
+
artifactResult.lint = {
|
|
93
|
+
type: lintResult.type,
|
|
94
|
+
summary: lintResult.summary
|
|
95
|
+
};
|
|
96
|
+
result.summary.checkedArtifacts++;
|
|
97
|
+
for (const finding of lintResult.findings) {
|
|
98
|
+
result.findings.push({
|
|
99
|
+
...finding,
|
|
100
|
+
id: `lint:${artifact.path}:${finding.code}:${finding.line}`,
|
|
101
|
+
artifact: artifact.path,
|
|
102
|
+
reason: finding.message
|
|
103
|
+
});
|
|
104
|
+
addFindingSummary(result.summary, finding.severity);
|
|
105
|
+
}
|
|
106
|
+
result.checks.push(makeCheck(
|
|
107
|
+
`lint:${tier}:${artifact.path}`,
|
|
108
|
+
lintResult.summary.errors > 0 ? 'fail' : 'pass',
|
|
109
|
+
artifact.path,
|
|
110
|
+
lintResult.summary.errors > 0
|
|
111
|
+
? `${lintResult.summary.errors} lint error(s) block this gate.`
|
|
112
|
+
: `${lintResult.summary.warnings} warning(s), ${lintResult.summary.infos} info finding(s).`
|
|
113
|
+
));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function extractPassedCommands(text) {
|
|
118
|
+
const commands = [];
|
|
119
|
+
for (const line of text.split(/\r?\n/)) {
|
|
120
|
+
const backtick = line.match(/`([^`\n]+)`/);
|
|
121
|
+
const labeled = line.match(/\bcommand\s*:\s*([^;]+?)(?:\s{2,}|\s+status\s*:|\s+result\s*:|$)/i);
|
|
122
|
+
const command = backtick ? backtick[1].trim() : (labeled ? labeled[1].trim() : null);
|
|
123
|
+
if (!command) continue;
|
|
124
|
+
if (/\b(pass|passed|green|success|succeeded|ok)\b/i.test(line)) {
|
|
125
|
+
commands.push(command);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return [...new Set(commands)];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function checkBuildEvidence(projectRoot, result) {
|
|
132
|
+
const relPath = '.godpowers/build/STATE.md';
|
|
133
|
+
const file = relToAbs(projectRoot, relPath);
|
|
134
|
+
if (!fs.existsSync(file)) return;
|
|
135
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
136
|
+
const passedCommands = extractPassedCommands(text);
|
|
137
|
+
if (passedCommands.length === 0) {
|
|
138
|
+
const finding = makeFinding(
|
|
139
|
+
'build-verification-evidence',
|
|
140
|
+
'error',
|
|
141
|
+
relPath,
|
|
142
|
+
'Build state does not record exact project verification commands that passed.'
|
|
143
|
+
);
|
|
144
|
+
result.findings.push(finding);
|
|
145
|
+
addFindingSummary(result.summary, finding.severity);
|
|
146
|
+
result.checks.push(makeCheck(
|
|
147
|
+
'build-verification-evidence',
|
|
148
|
+
'fail',
|
|
149
|
+
relPath,
|
|
150
|
+
finding.reason
|
|
151
|
+
));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
result.checks.push(makeCheck(
|
|
155
|
+
'build-verification-evidence',
|
|
156
|
+
'pass',
|
|
157
|
+
relPath,
|
|
158
|
+
`Build state records ${passedCommands.length} passed verification command(s).`
|
|
159
|
+
));
|
|
160
|
+
result.summary.buildVerificationCommands = passedCommands;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function checkHardenCriticals(projectRoot, result) {
|
|
164
|
+
const relPath = '.godpowers/harden/FINDINGS.md';
|
|
165
|
+
const file = relToAbs(projectRoot, relPath);
|
|
166
|
+
if (!fs.existsSync(file)) return;
|
|
167
|
+
const pass = router.hasNoCriticalFindings(projectRoot);
|
|
168
|
+
if (!pass) {
|
|
169
|
+
const finding = makeFinding(
|
|
170
|
+
'harden-critical-findings',
|
|
171
|
+
'error',
|
|
172
|
+
relPath,
|
|
173
|
+
'Harden findings contain unresolved Critical findings or a blocked launch gate.'
|
|
174
|
+
);
|
|
175
|
+
result.findings.push(finding);
|
|
176
|
+
addFindingSummary(result.summary, finding.severity);
|
|
177
|
+
}
|
|
178
|
+
result.checks.push(makeCheck(
|
|
179
|
+
'harden-critical-findings',
|
|
180
|
+
pass ? 'pass' : 'fail',
|
|
181
|
+
relPath,
|
|
182
|
+
pass
|
|
183
|
+
? 'No unresolved Critical findings or blocked launch gate found.'
|
|
184
|
+
: 'Unresolved Critical findings or a blocked launch gate block this gate.'
|
|
185
|
+
));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function finalize(result) {
|
|
189
|
+
result.verdict = result.findings.some((finding) => finding.severity === 'error')
|
|
190
|
+
? 'fail'
|
|
191
|
+
: 'pass';
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function check(opts = {}) {
|
|
196
|
+
const projectRoot = path.resolve(opts.projectRoot || opts.project || process.cwd());
|
|
197
|
+
const tier = artifactMap.normalizeTier(opts.tier);
|
|
198
|
+
const artifacts = artifactMap.artifactsForTier(tier);
|
|
199
|
+
const result = {
|
|
200
|
+
tier,
|
|
201
|
+
verdict: 'fail',
|
|
202
|
+
project: projectRoot,
|
|
203
|
+
artifacts: [],
|
|
204
|
+
checks: [],
|
|
205
|
+
findings: [],
|
|
206
|
+
summary: emptySummary()
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
if (!tier || !artifacts) {
|
|
210
|
+
const supported = artifactMap.tiers().join(', ');
|
|
211
|
+
const finding = makeFinding(
|
|
212
|
+
'unknown-tier',
|
|
213
|
+
'error',
|
|
214
|
+
null,
|
|
215
|
+
`Unknown gate tier. Supported tiers: ${supported}.`
|
|
216
|
+
);
|
|
217
|
+
result.findings.push(finding);
|
|
218
|
+
addFindingSummary(result.summary, finding.severity);
|
|
219
|
+
result.checks.push(makeCheck('tier-supported', 'fail', null, finding.reason));
|
|
220
|
+
return finalize(result);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
checkArtifacts(projectRoot, tier, artifacts, opts, result);
|
|
224
|
+
if (tier === 'build') checkBuildEvidence(projectRoot, result);
|
|
225
|
+
if (tier === 'harden') checkHardenCriticals(projectRoot, result);
|
|
226
|
+
return finalize(result);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function checkAsync(opts = {}) {
|
|
230
|
+
return check(opts);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function exitCode(result) {
|
|
234
|
+
return result.verdict === 'pass' ? 0 : 1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function render(result) {
|
|
238
|
+
const lines = [];
|
|
239
|
+
lines.push(`Godpowers Gate: ${result.tier || 'unknown'}`);
|
|
240
|
+
lines.push(`Verdict: ${result.verdict}`);
|
|
241
|
+
lines.push('');
|
|
242
|
+
lines.push('Artifacts:');
|
|
243
|
+
for (const artifact of result.artifacts) {
|
|
244
|
+
const marker = artifact.exists ? '+' : (artifact.required ? 'x' : '-');
|
|
245
|
+
lines.push(` ${marker} ${artifact.path}${artifact.required ? '' : ' (optional)'}`);
|
|
246
|
+
}
|
|
247
|
+
lines.push('');
|
|
248
|
+
lines.push('Checks:');
|
|
249
|
+
for (const checkResult of result.checks) {
|
|
250
|
+
lines.push(` ${checkResult.status.toUpperCase()} ${checkResult.id}: ${checkResult.reason}`);
|
|
251
|
+
}
|
|
252
|
+
if (result.findings.length > 0) {
|
|
253
|
+
lines.push('');
|
|
254
|
+
lines.push('Findings:');
|
|
255
|
+
for (const finding of result.findings) {
|
|
256
|
+
const where = finding.artifact ? `${finding.artifact}: ` : '';
|
|
257
|
+
lines.push(` ${finding.severity.toUpperCase()} ${finding.id}: ${where}${finding.reason}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
lines.push('');
|
|
261
|
+
lines.push(`Summary: ${result.summary.errors} error(s), ${result.summary.warnings} warning(s), ${result.summary.infos} info finding(s)`);
|
|
262
|
+
return lines.join('\n');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = {
|
|
266
|
+
check,
|
|
267
|
+
checkAsync,
|
|
268
|
+
extractPassedCommands,
|
|
269
|
+
exitCode,
|
|
270
|
+
render
|
|
271
|
+
};
|
package/lib/installer-args.js
CHANGED
|
@@ -8,7 +8,8 @@ const COMMANDS = new Set([
|
|
|
8
8
|
'automation-status',
|
|
9
9
|
'automation-setup',
|
|
10
10
|
'dogfood',
|
|
11
|
-
'extension-scaffold'
|
|
11
|
+
'extension-scaffold',
|
|
12
|
+
'gate'
|
|
12
13
|
]);
|
|
13
14
|
|
|
14
15
|
function parseArgs(argv, cwd = process.cwd()) {
|
|
@@ -23,6 +24,7 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
23
24
|
extensionSkill: null,
|
|
24
25
|
extensionAgent: null,
|
|
25
26
|
extensionWorkflow: null,
|
|
27
|
+
tier: null,
|
|
26
28
|
runtimes: [],
|
|
27
29
|
global: false,
|
|
28
30
|
local: false,
|
|
@@ -46,6 +48,12 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
46
48
|
case '--brief':
|
|
47
49
|
opts.brief = true;
|
|
48
50
|
break;
|
|
51
|
+
case '--tier':
|
|
52
|
+
if (args[i + 1]) {
|
|
53
|
+
opts.tier = args[i + 1];
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
49
57
|
case '--project':
|
|
50
58
|
if (args[i + 1]) {
|
|
51
59
|
opts.project = path.resolve(args[i + 1]);
|
|
@@ -93,6 +101,8 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
93
101
|
opts.extensionAgent = arg.slice('--agent='.length);
|
|
94
102
|
} else if (arg.startsWith('--workflow=')) {
|
|
95
103
|
opts.extensionWorkflow = arg.slice('--workflow='.length);
|
|
104
|
+
} else if (arg.startsWith('--tier=')) {
|
|
105
|
+
opts.tier = arg.slice('--tier='.length);
|
|
96
106
|
} else if (arg.startsWith('--profile=')) {
|
|
97
107
|
opts.profile = arg.slice('--profile='.length);
|
|
98
108
|
} else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
|
@@ -89,6 +89,17 @@ const STANDARDS_EXEMPT_COMMANDS = new Set([
|
|
|
89
89
|
'/god-tech-debt'
|
|
90
90
|
]);
|
|
91
91
|
|
|
92
|
+
const TIER_GATE_COMMANDS = new Set([
|
|
93
|
+
'/god-prd',
|
|
94
|
+
'/god-design',
|
|
95
|
+
'/god-arch',
|
|
96
|
+
'/god-roadmap',
|
|
97
|
+
'/god-stack',
|
|
98
|
+
'/god-repo',
|
|
99
|
+
'/god-build',
|
|
100
|
+
'/god-harden'
|
|
101
|
+
]);
|
|
102
|
+
|
|
92
103
|
function read(projectRoot, relPath) {
|
|
93
104
|
const file = path.join(projectRoot, relPath);
|
|
94
105
|
if (!fs.existsSync(file)) return '';
|
|
@@ -166,6 +177,7 @@ function detect(projectRoot) {
|
|
|
166
177
|
let typedOutcomeCount = 0;
|
|
167
178
|
let standardsExemptCount = 0;
|
|
168
179
|
let traceEventMissingCount = 0;
|
|
180
|
+
let gateCommandCount = 0;
|
|
169
181
|
|
|
170
182
|
for (const routePath of routes) {
|
|
171
183
|
const route = parseRoute(projectRoot, routePath);
|
|
@@ -253,6 +265,24 @@ function detect(projectRoot) {
|
|
|
253
265
|
);
|
|
254
266
|
}
|
|
255
267
|
}
|
|
268
|
+
|
|
269
|
+
if (TIER_GATE_COMMANDS.has(command)) {
|
|
270
|
+
const tierName = command.replace('/god-', '');
|
|
271
|
+
const expected = `npx godpowers gate --tier=${tierName} --project=.`;
|
|
272
|
+
const actual = route.standards && route.standards['gate-command'];
|
|
273
|
+
if (actual === expected) {
|
|
274
|
+
gateCommandCount++;
|
|
275
|
+
} else {
|
|
276
|
+
addCheck(
|
|
277
|
+
checks,
|
|
278
|
+
`missing-gate-command-${command.replace(/[^a-z0-9]+/gi, '-')}`,
|
|
279
|
+
'stale',
|
|
280
|
+
routePath,
|
|
281
|
+
`${command} must declare standards.gate-command as ${expected}.`,
|
|
282
|
+
{ spawn: 'god-auditor' }
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
256
286
|
}
|
|
257
287
|
|
|
258
288
|
if (symbolicCount === 0) {
|
|
@@ -287,6 +317,14 @@ function detect(projectRoot) {
|
|
|
287
317
|
: `${traceEventMissingCount} agent-spawning routes are missing required trace events.`,
|
|
288
318
|
{ spawn: traceEventMissingCount === 0 ? null : 'god-auditor' }
|
|
289
319
|
);
|
|
320
|
+
addCheck(
|
|
321
|
+
checks,
|
|
322
|
+
'gate-command-policy',
|
|
323
|
+
checks.some((check) => check.id.startsWith('missing-gate-command-')) ? 'stale' : 'fresh',
|
|
324
|
+
'routing/',
|
|
325
|
+
`${gateCommandCount} tier routes declare executable gate commands.`,
|
|
326
|
+
{ spawn: checks.some((check) => check.id.startsWith('missing-gate-command-')) ? 'god-auditor' : null }
|
|
327
|
+
);
|
|
290
328
|
|
|
291
329
|
const stale = checks.filter((check) => check.status !== 'fresh');
|
|
292
330
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godpowers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "AI-powered development system: 112 slash commands and 40 specialist agents that take a project from raw idea to hardened production. Runs inside Claude Code, Codex, Cursor, Windsurf, Gemini, and 10+ other AI coding tools.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"godpowers": "./bin/install.js"
|
|
@@ -124,6 +124,25 @@ quality drift before declaring complete.
|
|
|
124
124
|
### --skip-hygiene
|
|
125
125
|
Default. Skip the hygiene pass. Use when iterating quickly.
|
|
126
126
|
|
|
127
|
+
## Tier transition gates
|
|
128
|
+
|
|
129
|
+
After each tier skill returns, run the matching executable gate before starting
|
|
130
|
+
the downstream tier:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npx godpowers gate --tier=prd --project=.
|
|
134
|
+
npx godpowers gate --tier=design --project=.
|
|
135
|
+
npx godpowers gate --tier=arch --project=.
|
|
136
|
+
npx godpowers gate --tier=roadmap --project=.
|
|
137
|
+
npx godpowers gate --tier=stack --project=.
|
|
138
|
+
npx godpowers gate --tier=repo --project=.
|
|
139
|
+
npx godpowers gate --tier=build --project=.
|
|
140
|
+
npx godpowers gate --tier=harden --project=.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Run the design gate only when the project requires design. A non-zero gate exit
|
|
144
|
+
pauses the project run for repair and blocks downstream tier dispatch.
|
|
145
|
+
|
|
127
146
|
## Mandatory final sync
|
|
128
147
|
|
|
129
148
|
Regardless of flags, `/god-mode` always runs `/god-sync` before declaring
|
package/routing/god-arch.yaml
CHANGED
|
@@ -34,6 +34,7 @@ standards:
|
|
|
34
34
|
three-label-test: true
|
|
35
35
|
have-nots: [A-01, A-02, A-03, A-04, A-05, A-06, A-07, A-08, A-09, A-10, A-11, A-12, A-13]
|
|
36
36
|
gate-on-failure: pause-for-user
|
|
37
|
+
gate-command: npx godpowers gate --tier=arch --project=.
|
|
37
38
|
|
|
38
39
|
success-path:
|
|
39
40
|
next-recommended: /god-roadmap
|
package/routing/god-build.yaml
CHANGED
|
@@ -29,6 +29,7 @@ standards:
|
|
|
29
29
|
three-label-test: true
|
|
30
30
|
have-nots: [B-01, B-02, B-03, B-04, B-05, B-06, B-07, B-08, B-09, B-10, B-11, B-12]
|
|
31
31
|
gate-on-failure: pause-for-user
|
|
32
|
+
gate-command: npx godpowers gate --tier=build --project=.
|
|
32
33
|
|
|
33
34
|
success-path:
|
|
34
35
|
next-recommended: /god-deploy
|
package/routing/god-design.yaml
CHANGED
|
@@ -50,6 +50,7 @@ standards:
|
|
|
50
50
|
- "purple-blue gradients" # impeccable anti-pattern
|
|
51
51
|
- "Inter everywhere" # impeccable anti-pattern
|
|
52
52
|
- "cards in cards" # impeccable anti-pattern
|
|
53
|
+
gate-command: npx godpowers gate --tier=design --project=.
|
|
53
54
|
|
|
54
55
|
success-path:
|
|
55
56
|
next-recommended: /god-arch
|
package/routing/god-harden.yaml
CHANGED
|
@@ -33,6 +33,7 @@ standards:
|
|
|
33
33
|
three-label-test: true
|
|
34
34
|
have-nots: [H-01, H-02, H-03, H-04, H-05, H-06, H-07, H-08, H-09, H-10, H-11]
|
|
35
35
|
gate-on-failure: pause-for-user
|
|
36
|
+
gate-command: npx godpowers gate --tier=harden --project=.
|
|
36
37
|
|
|
37
38
|
success-path:
|
|
38
39
|
next-recommended: /god-launch
|
package/routing/god-prd.yaml
CHANGED
|
@@ -35,6 +35,7 @@ standards:
|
|
|
35
35
|
three-label-test: true
|
|
36
36
|
have-nots: [P-01, P-02, P-03, P-04, P-05, P-06, P-07, P-08, P-09, P-10, P-11, P-12, P-13, P-14, P-15]
|
|
37
37
|
gate-on-failure: pause-for-user
|
|
38
|
+
gate-command: npx godpowers gate --tier=prd --project=.
|
|
38
39
|
|
|
39
40
|
success-path:
|
|
40
41
|
next-recommended: /god-arch
|
package/routing/god-repo.yaml
CHANGED
package/routing/god-roadmap.yaml
CHANGED
package/routing/god-stack.yaml
CHANGED
package/skills/god-arch.md
CHANGED
|
@@ -26,7 +26,8 @@ Spawn the **god-architect** agent in a fresh context via the host platform's nat
|
|
|
26
26
|
After god-architect returns:
|
|
27
27
|
1. Verify ARCH.md and ADRs exist on disk
|
|
28
28
|
2. Spawn god-auditor to verify have-nots pass
|
|
29
|
-
3.
|
|
29
|
+
3. Run `npx godpowers gate --tier=arch --project=.` and do not proceed on a non-zero exit
|
|
30
|
+
4. Update `.godpowers/PROGRESS.md`: Architecture status = done
|
|
30
31
|
|
|
31
32
|
## Pause Format
|
|
32
33
|
|
package/skills/god-build.md
CHANGED
|
@@ -76,8 +76,10 @@ After all waves:
|
|
|
76
76
|
decompose, prune, or escalate. Pass the exact failing diagnostics, rerun the
|
|
77
77
|
command, and repeat until green or until the same root failure exhausts the
|
|
78
78
|
repair budget.
|
|
79
|
-
5.
|
|
80
|
-
6.
|
|
79
|
+
5. Record the exact verification commands that passed in `.godpowers/build/STATE.md`
|
|
80
|
+
6. Run `npx godpowers gate --tier=build --project=.` and do not proceed on a non-zero exit
|
|
81
|
+
7. Update PROGRESS.md: Build status = done
|
|
82
|
+
8. If the build plan or implementation establishes durable conventions, plan
|
|
81
83
|
pillar updates through `lib/pillars.planArtifactSync`. Under
|
|
82
84
|
`/god-mode --yolo`, apply those updates immediately and log the decision.
|
|
83
85
|
|
package/skills/god-design.md
CHANGED
|
@@ -92,6 +92,14 @@ When DESIGN.md or PRODUCT.md change as a result of any subcommand:
|
|
|
92
92
|
This pattern mirrors code review (god-spec-reviewer + god-quality-reviewer)
|
|
93
93
|
applied to design.
|
|
94
94
|
|
|
95
|
+
## Verification
|
|
96
|
+
|
|
97
|
+
After god-designer and god-design-reviewer return:
|
|
98
|
+
1. Verify `DESIGN.md` exists on disk
|
|
99
|
+
2. Verify `.godpowers/design/STATE.md` exists on disk
|
|
100
|
+
3. Run `npx godpowers gate --tier=design --project=.` and do not proceed on a non-zero exit
|
|
101
|
+
4. Update `.godpowers/PROGRESS.md`: Design status = done when the project requires design
|
|
102
|
+
|
|
95
103
|
## Output
|
|
96
104
|
|
|
97
105
|
Project root:
|
package/skills/god-harden.md
CHANGED
|
@@ -21,7 +21,8 @@ Spawn the **god-harden-auditor** agent in a fresh context via the host platform'
|
|
|
21
21
|
|
|
22
22
|
After god-harden-auditor returns:
|
|
23
23
|
1. Verify FINDINGS.md exists on disk
|
|
24
|
-
2.
|
|
24
|
+
2. Run `npx godpowers gate --tier=harden --project=.` and do not proceed on a non-zero exit
|
|
25
|
+
3. Read findings classification:
|
|
25
26
|
- If any Critical: PROGRESS.md status = failed, launch is BLOCKED
|
|
26
27
|
- If only High/Medium/Low: PROGRESS.md status = done
|
|
27
28
|
|
package/skills/god-mode.md
CHANGED
|
@@ -121,6 +121,10 @@ workflow.
|
|
|
121
121
|
`safe-sync-clear` fails, run
|
|
122
122
|
`/god-reconcile Release Truth And Safe Sync` before deploy, observe,
|
|
123
123
|
harden, launch, broad migration, or resume work.
|
|
124
|
+
- Instruction to run `npx godpowers gate --tier=<tier> --project=.`
|
|
125
|
+
after each tier skill returns and before starting the downstream tier
|
|
126
|
+
for PRD, design when required, architecture, roadmap, stack, repo,
|
|
127
|
+
build, and harden. A non-zero exit pauses the project run for repair.
|
|
124
128
|
- Instruction that `--yolo` cannot bypass safe sync blockers or
|
|
125
129
|
unresolved Critical harden findings. These are release-truth gates, not
|
|
126
130
|
preference pauses.
|
package/skills/god-prd.md
CHANGED
|
@@ -30,7 +30,8 @@ Spawn the **god-pm** agent in a fresh context via the host platform's native age
|
|
|
30
30
|
After god-pm returns:
|
|
31
31
|
1. Verify `.godpowers/prd/PRD.md` exists on disk
|
|
32
32
|
2. Spawn god-auditor briefly to verify have-nots pass
|
|
33
|
-
3.
|
|
33
|
+
3. Run `npx godpowers gate --tier=prd --project=.` and do not proceed on a non-zero exit
|
|
34
|
+
4. Update `.godpowers/PROGRESS.md`: PRD status = done
|
|
34
35
|
|
|
35
36
|
## Pause Format
|
|
36
37
|
|
package/skills/god-repo.md
CHANGED
|
@@ -22,7 +22,8 @@ Spawn the **god-repo-scaffolder** agent in a fresh context via the host platform
|
|
|
22
22
|
After god-repo-scaffolder returns:
|
|
23
23
|
1. Verify AUDIT.md exists on disk
|
|
24
24
|
2. Verify CI passes on the empty scaffold
|
|
25
|
-
3.
|
|
25
|
+
3. Run `npx godpowers gate --tier=repo --project=.` and do not proceed on a non-zero exit
|
|
26
|
+
4. Update `.godpowers/PROGRESS.md`: Repo status = done
|
|
26
27
|
|
|
27
28
|
## On Completion
|
|
28
29
|
|
package/skills/god-roadmap.md
CHANGED
|
@@ -23,7 +23,8 @@ Spawn the **god-roadmapper** agent in a fresh context via the host platform's na
|
|
|
23
23
|
After god-roadmapper returns:
|
|
24
24
|
1. Verify ROADMAP.md exists on disk
|
|
25
25
|
2. Spawn god-auditor to verify have-nots pass
|
|
26
|
-
3.
|
|
26
|
+
3. Run `npx godpowers gate --tier=roadmap --project=.` and do not proceed on a non-zero exit
|
|
27
|
+
4. Update `.godpowers/PROGRESS.md`: Roadmap status = done
|
|
27
28
|
|
|
28
29
|
## On Completion
|
|
29
30
|
|
package/skills/god-stack.md
CHANGED
|
@@ -21,7 +21,8 @@ Spawn the **god-stack-selector** agent in a fresh context via the host platform'
|
|
|
21
21
|
|
|
22
22
|
After god-stack-selector returns:
|
|
23
23
|
1. Verify DECISION.md exists on disk
|
|
24
|
-
2.
|
|
24
|
+
2. Run `npx godpowers gate --tier=stack --project=.` and do not proceed on a non-zero exit
|
|
25
|
+
3. Update `.godpowers/PROGRESS.md`: Stack status = done
|
|
25
26
|
|
|
26
27
|
## On Completion
|
|
27
28
|
|