godpowers 2.4.2 → 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.
Files changed (66) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +22 -6
  3. package/RELEASE.md +29 -35
  4. package/SKILL.md +5 -0
  5. package/agents/god-orchestrator.md +31 -1255
  6. package/bin/install.js +16 -107
  7. package/fixtures/gate/build-pass/.godpowers/build/STATE.md +10 -0
  8. package/fixtures/gate/harden-pass/.godpowers/harden/FINDINGS.md +14 -0
  9. package/fixtures/gate/repo-pass/.godpowers/repo/AUDIT.md +9 -0
  10. package/lib/artifact-map.js +60 -0
  11. package/lib/cli-dispatch.js +162 -0
  12. package/lib/command-families.js +10 -2
  13. package/lib/dashboard.js +3 -2
  14. package/lib/gate.js +271 -0
  15. package/lib/installer-args.js +11 -1
  16. package/lib/route-quality-sync.js +38 -0
  17. package/package.json +3 -2
  18. package/references/orchestration/GOD-MODE-RUNBOOK.md +19 -0
  19. package/references/orchestration/GOD-NEXT-RUNBOOK.md +32 -0
  20. package/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +1259 -0
  21. package/references/orchestration/README.md +6 -0
  22. package/references/shared/DASHBOARD-CONTRACT.md +93 -0
  23. package/references/shared/LOCKING.md +15 -0
  24. package/references/shared/README.md +2 -0
  25. package/routing/god-arch.yaml +1 -0
  26. package/routing/god-build.yaml +1 -0
  27. package/routing/god-design.yaml +1 -0
  28. package/routing/god-harden.yaml +1 -0
  29. package/routing/god-prd.yaml +1 -0
  30. package/routing/god-repo.yaml +1 -0
  31. package/routing/god-roadmap-check.yaml +1 -1
  32. package/routing/god-roadmap.yaml +1 -0
  33. package/routing/god-stack.yaml +1 -0
  34. package/skills/god-arch.md +3 -13
  35. package/skills/god-build.md +5 -14
  36. package/skills/god-deploy.md +1 -12
  37. package/skills/god-design.md +9 -12
  38. package/skills/god-feature.md +1 -12
  39. package/skills/god-harden.md +3 -13
  40. package/skills/god-hotfix.md +1 -12
  41. package/skills/god-launch.md +1 -12
  42. package/skills/god-link.md +1 -12
  43. package/skills/god-migrate.md +1 -3
  44. package/skills/god-mode.md +4 -0
  45. package/skills/god-next.md +34 -410
  46. package/skills/god-observe.md +1 -12
  47. package/skills/god-prd.md +3 -13
  48. package/skills/god-redo.md +1 -12
  49. package/skills/god-refactor.md +1 -12
  50. package/skills/god-repair.md +1 -12
  51. package/skills/god-repo.md +3 -13
  52. package/skills/god-restore.md +1 -12
  53. package/skills/god-roadmap-check.md +5 -0
  54. package/skills/god-roadmap.md +3 -13
  55. package/skills/god-rollback.md +1 -12
  56. package/skills/god-scan.md +1 -12
  57. package/skills/god-skip.md +1 -12
  58. package/skills/god-stack.md +3 -13
  59. package/skills/god-status.md +27 -204
  60. package/skills/god-story-build.md +1 -12
  61. package/skills/god-story-close.md +1 -12
  62. package/skills/god-story.md +1 -12
  63. package/skills/god-sync.md +1 -12
  64. package/skills/god-undo.md +1 -12
  65. package/skills/god-update-deps.md +1 -12
  66. package/skills/god-upgrade.md +1 -12
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
+ };
@@ -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.4.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"
@@ -23,9 +23,10 @@
23
23
  "test:diff": "node scripts/test-artifact-diff.js",
24
24
  "test:e2e": "node tests/integration/full-arc.test.js",
25
25
  "coverage": "c8 --reporter=text --reporter=lcov node scripts/run-tests.js",
26
+ "coverage:lib": "c8 --include=lib/**/*.js --check-coverage --lines 90 --reporter=text node scripts/run-tests.js",
26
27
  "test:audit": "npm audit --omit=dev && git diff --check && npm run test:surface",
27
28
  "pack:check": "node scripts/check-package-contents.js",
28
- "release:check": "npm test && npm run test:audit && npm run pack:check",
29
+ "release:check": "npm run coverage:lib && npm run test:audit && npm run pack:check",
29
30
  "lint": "node scripts/static-check.js"
30
31
  },
31
32
  "keywords": [
@@ -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
@@ -0,0 +1,32 @@
1
+ # God Next Runbook
2
+
3
+ This reference owns the detailed process notes for `/god-next`. The skill file stays a concise dispatch contract.
4
+
5
+ ## Invocation modes
6
+
7
+ 1. Post-completion routing reads `routing/<just-completed>.yaml`, applies success-path and conditional-next rules, runs any configured standards gate, and announces the next command.
8
+ 2. Pre-flight routing reads `routing/<target>.yaml`, evaluates prerequisites, and offers auto-completable prerequisites before the target command runs.
9
+ 3. Standalone routing reads disk state, uses `lib/router.js` for structural next steps, then uses recipes when the project is already in steady state.
10
+ 4. Intent-based routing uses `lib/recipes.js` for fuzzy text, ranked recipes, and state-aware command sequences.
11
+
12
+ ## Data sources
13
+
14
+ Use runtime routing YAML, recipes, `lib/command-families.js`, `lib/router.js`, `lib/dashboard.js`, state.json, PROGRESS.md, CHECKPOINT.md, and completed artifacts.
15
+
16
+ Read `.godpowers/prep/INITIAL-FINDINGS.md` and `.godpowers/prep/IMPORTED-CONTEXT.md` when present. Treat them as preparation context only.
17
+
18
+ ## Route detail
19
+
20
+ When the suggestion is based on state.json, show a three-line path ahead: current state, next command, and the following gate if known.
21
+
22
+ When prerequisites are missing, name the missing prerequisite, the auto-completable command when available, and the exact decision needed from the user.
23
+
24
+ When a standards gate fails, show the failures, suggest `/god-redo` with feedback or `/god-skip` with a reason, and do not auto-progress.
25
+
26
+ ## Edge cases
27
+
28
+ State drift routes to `/god-repair`. Safe sync blockers route to `/god-reconcile Release Truth And Safe Sync` before deploy, observe, harden, launch, broad migration, or resume work.
29
+
30
+ Unresolved Critical harden findings block launch in default mode and under `--yolo`.
31
+
32
+ In steady state, use the work-family ladders before raw route order so feature, hotfix, refactor, docs, dependency, audit, hygiene, and preflight intents resolve to the narrowest useful command.