godpowers 1.6.24 → 2.1.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 (65) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +166 -0
  3. package/README.md +103 -8
  4. package/RELEASE.md +48 -50
  5. package/SKILL.md +9 -1
  6. package/agents/god-design-reviewer.md +6 -6
  7. package/agents/god-designer.md +1 -1
  8. package/agents/god-executor.md +23 -0
  9. package/agents/god-quality-reviewer.md +12 -1
  10. package/agents/god-spec-reviewer.md +10 -0
  11. package/bin/install.js +137 -655
  12. package/extensions/data-pack/manifest.yaml +1 -1
  13. package/extensions/data-pack/package.json +1 -1
  14. package/extensions/launch-pack/README.md +1 -1
  15. package/extensions/launch-pack/manifest.yaml +1 -1
  16. package/extensions/launch-pack/package.json +1 -1
  17. package/extensions/security-pack/manifest.yaml +1 -1
  18. package/extensions/security-pack/package.json +1 -1
  19. package/fixtures/quick-proof/manifest.json +19 -0
  20. package/fixtures/quick-proof/project/.godpowers/prep/INITIAL-FINDINGS.md +5 -0
  21. package/fixtures/quick-proof/project/.godpowers/state.json +69 -0
  22. package/fixtures/quick-proof/project/README.md +5 -0
  23. package/fixtures/quick-proof/project/package.json +6 -0
  24. package/lib/agent-browser-driver.js +13 -13
  25. package/lib/agent-cache.js +8 -1
  26. package/lib/agent-refs.js +161 -0
  27. package/lib/budget.js +25 -11
  28. package/lib/events.js +11 -4
  29. package/lib/extension-authoring.js +27 -0
  30. package/lib/feature-awareness.js +24 -0
  31. package/lib/fs-async.js +28 -0
  32. package/lib/installer-args.js +99 -0
  33. package/lib/installer-core.js +345 -0
  34. package/lib/installer-files.js +80 -0
  35. package/lib/installer-runtimes.js +112 -0
  36. package/lib/intent.js +111 -16
  37. package/lib/quick-proof.js +153 -0
  38. package/lib/release-surface-sync.js +8 -1
  39. package/lib/repo-surface-sync.js +9 -2
  40. package/lib/review-required.js +2 -1
  41. package/lib/router.js +23 -3
  42. package/lib/skill-surface.js +42 -0
  43. package/lib/state-lock.js +10 -0
  44. package/lib/state.js +101 -8
  45. package/lib/workflow-runner.js +42 -5
  46. package/package.json +7 -3
  47. package/references/HAVE-NOTS.md +4 -3
  48. package/references/orchestration/GOD-MODE-RUNBOOK.md +273 -0
  49. package/routing/god-arch.yaml +1 -1
  50. package/routing/god-build.yaml +1 -1
  51. package/skills/god-add-backlog.md +1 -1
  52. package/skills/god-agent-audit.md +2 -2
  53. package/skills/god-build.md +5 -3
  54. package/skills/god-context-scan.md +2 -3
  55. package/skills/god-design.md +2 -2
  56. package/skills/god-doctor.md +2 -2
  57. package/skills/god-extension-info.md +1 -1
  58. package/skills/god-help.md +4 -3
  59. package/skills/god-mode.md +10 -266
  60. package/skills/god-org-context.md +1 -1
  61. package/skills/god-repair.md +3 -3
  62. package/skills/god-review.md +9 -0
  63. package/skills/god-stories.md +1 -1
  64. package/skills/god-test-extension.md +1 -1
  65. package/skills/god-version.md +2 -2
package/bin/install.js CHANGED
@@ -1,137 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Godpowers Installer
4
+ * Godpowers installer and local CLI helpers.
5
5
  *
6
- * Installs Godpowers globally for supported AI coding tools.
7
- * Usage: npx godpowers [options]
6
+ * The executable stays intentionally thin. Runtime definitions, argument
7
+ * parsing, and install/uninstall file operations live in lib/ so they can be
8
+ * tested and reused without shelling through the binary.
8
9
  */
9
10
 
10
- const fs = require('fs');
11
11
  const path = require('path');
12
- const os = require('os');
12
+
13
+ const { parseArgs } = require('../lib/installer-args');
14
+ const { RUNTIMES, runtimeKeys } = require('../lib/installer-runtimes');
15
+ const {
16
+ installForRuntime,
17
+ uninstallForRuntime,
18
+ countInstalledSurface
19
+ } = require('../lib/installer-core');
13
20
 
14
21
  const VERSION = require('../package.json').version;
15
22
 
16
23
  const BANNER = `
17
- ██████╗ ██████╗ ██████╗
18
- ██╔════╝ ██╔═══██╗██╔══██╗
19
- ██║ ███╗██║ ██║██║ ██║
20
- ██║ ██║██║ ██║██║ ██║
21
- ╚██████╔╝╚██████╔╝██████╔╝
22
- ╚═════╝ ╚═════╝ ╚═════╝
23
24
  GODPOWERS v${VERSION}
24
25
  Ship fast. Ship right. Ship everything.
25
26
  `;
26
27
 
27
- const RUNTIMES = {
28
- claude: {
29
- name: 'Claude Code',
30
- configDir: path.join(os.homedir(), '.claude'),
31
- skillsDir: 'skills',
32
- configFile: null,
33
- },
34
- codex: {
35
- name: 'Codex',
36
- configDir: path.join(os.homedir(), '.codex'),
37
- skillsDir: 'skills',
38
- configFile: 'config.toml',
39
- agentMetadata: 'toml',
40
- },
41
- cursor: {
42
- name: 'Cursor',
43
- configDir: path.join(os.homedir(), '.cursor'),
44
- skillsDir: 'rules',
45
- configFile: null,
46
- },
47
- windsurf: {
48
- name: 'Windsurf',
49
- configDir: path.join(os.homedir(), '.windsurf'),
50
- skillsDir: 'rules',
51
- configFile: null,
52
- },
53
- opencode: {
54
- name: 'OpenCode',
55
- configDir: path.join(os.homedir(), '.opencode'),
56
- skillsDir: 'skills',
57
- configFile: null,
58
- },
59
- gemini: {
60
- name: 'Gemini CLI',
61
- configDir: path.join(os.homedir(), '.gemini'),
62
- skillsDir: 'skills',
63
- configFile: null,
64
- },
65
- copilot: {
66
- name: 'GitHub Copilot',
67
- configDir: path.join(os.homedir(), '.copilot'),
68
- skillsDir: 'skills',
69
- configFile: null,
70
- },
71
- augment: {
72
- name: 'Augment',
73
- configDir: path.join(os.homedir(), '.augment'),
74
- skillsDir: 'skills',
75
- configFile: null,
76
- },
77
- trae: {
78
- name: 'Trae',
79
- configDir: path.join(os.homedir(), '.trae'),
80
- skillsDir: 'skills',
81
- configFile: null,
82
- },
83
- cline: {
84
- name: 'Cline',
85
- configDir: path.join(os.homedir(), '.cline'),
86
- skillsDir: 'skills',
87
- configFile: null,
88
- },
89
- kilo: {
90
- name: 'Kilo',
91
- configDir: path.join(os.homedir(), '.kilo'),
92
- skillsDir: 'skills',
93
- configFile: null,
94
- },
95
- antigravity: {
96
- name: 'Antigravity',
97
- configDir: path.join(os.homedir(), '.antigravity'),
98
- skillsDir: 'skills',
99
- configFile: null,
100
- },
101
- qwen: {
102
- name: 'Qwen Code',
103
- configDir: path.join(os.homedir(), '.qwen'),
104
- skillsDir: 'skills',
105
- configFile: null,
106
- },
107
- codebuddy: {
108
- name: 'CodeBuddy',
109
- configDir: path.join(os.homedir(), '.codebuddy'),
110
- skillsDir: 'skills',
111
- configFile: null,
112
- },
113
- pi: {
114
- name: 'Pi',
115
- configDir: path.join(os.homedir(), '.pi'),
116
- skillsDir: 'skills',
117
- configFile: null,
118
- },
119
- };
120
-
121
- function resolveRuntime(runtimeKey, opts = {}) {
122
- const runtime = RUNTIMES[runtimeKey];
123
- if (!runtime) return null;
124
- const resolved = { ...runtime };
125
- if (opts.local && !opts.global) {
126
- resolved.configDir = path.join(process.cwd(), path.basename(runtime.configDir));
127
- }
128
- return resolved;
129
- }
130
-
131
- // ---------------------------------------------------------------------------
132
- // Helpers
133
- // ---------------------------------------------------------------------------
134
-
135
28
  function log(msg) {
136
29
  console.log(` ${msg}`);
137
30
  }
@@ -148,468 +41,29 @@ function error(msg) {
148
41
  console.error(` \x1b[31mx\x1b[0m ${msg}`);
149
42
  }
150
43
 
151
- function ensureDir(dir) {
152
- if (!fs.existsSync(dir)) {
153
- fs.mkdirSync(dir, { recursive: true });
154
- }
155
- }
156
-
157
- function copyRecursive(src, dest) {
158
- ensureDir(dest);
159
- const entries = fs.readdirSync(src, { withFileTypes: true });
160
- for (const entry of entries) {
161
- const srcPath = path.join(src, entry.name);
162
- const destPath = path.join(dest, entry.name);
163
- if (entry.isDirectory()) {
164
- copyRecursive(srcPath, destPath);
165
- } else {
166
- fs.copyFileSync(srcPath, destPath);
167
- }
168
- }
169
- }
170
-
171
- function copyRuntimeBundle(srcDir, destDir) {
172
- ensureDir(destDir);
173
- for (const dir of ['lib', 'routing', 'workflows', 'schema', 'templates', 'references']) {
174
- const src = path.join(srcDir, dir);
175
- if (fs.existsSync(src)) {
176
- copyRecursive(src, path.join(destDir, dir));
177
- }
178
- }
179
- const packageJson = path.join(srcDir, 'package.json');
180
- if (fs.existsSync(packageJson)) {
181
- fs.copyFileSync(packageJson, path.join(destDir, 'package.json'));
182
- }
183
- }
184
-
185
- function installSkillFile(srcFile, skillsDest, runtimeKey, targetName = null) {
186
- const baseName = targetName || path.basename(srcFile, '.md');
187
- if (runtimeKey === 'codex') {
188
- const skillDir = path.join(skillsDest, baseName);
189
- if (fs.existsSync(skillDir)) {
190
- fs.rmSync(skillDir, { recursive: true, force: true });
191
- }
192
- ensureDir(skillDir);
193
- fs.copyFileSync(srcFile, path.join(skillDir, 'SKILL.md'));
194
- return;
195
- }
196
- fs.copyFileSync(srcFile, path.join(skillsDest, `${baseName}.md`));
197
- }
198
-
199
- function parseAgentFrontmatter(content) {
200
- const fallback = { name: null, description: null };
201
- if (!content.startsWith('---\n')) return fallback;
202
-
203
- const end = content.indexOf('\n---', 4);
204
- if (end === -1) return fallback;
205
-
206
- const lines = content.slice(4, end).split('\n');
207
- const parsed = { ...fallback };
208
-
209
- for (let i = 0; i < lines.length; i++) {
210
- const line = lines[i];
211
- const nameMatch = line.match(/^name:\s*(.+)\s*$/);
212
- if (nameMatch) {
213
- parsed.name = nameMatch[1].replace(/^["']|["']$/g, '');
214
- continue;
215
- }
216
-
217
- if (line === 'description: |') {
218
- const desc = [];
219
- i++;
220
- while (i < lines.length && /^ {2}/.test(lines[i])) {
221
- desc.push(lines[i].slice(2));
222
- i++;
223
- }
224
- i--;
225
- parsed.description = desc.join('\n').trim();
226
- continue;
227
- }
228
-
229
- const descMatch = line.match(/^description:\s*(.+)\s*$/);
230
- if (descMatch) {
231
- parsed.description = descMatch[1].replace(/^["']|["']$/g, '');
232
- }
233
- }
234
-
235
- return parsed;
236
- }
237
-
238
- function stripFrontmatter(content) {
239
- if (!content.startsWith('---\n')) return content.trim();
240
- const end = content.indexOf('\n---', 4);
241
- if (end === -1) return content.trim();
242
- return content.slice(end + 4).trim();
243
- }
244
-
245
- function tomlString(value) {
246
- return JSON.stringify(value || '');
247
- }
248
-
249
- function tomlLiteral(value) {
250
- return `'''\n${(value || '').replace(/'''/g, "'''\\'''")}\n'''`;
251
- }
252
-
253
- function writeCodexAgentToml(srcFile, agentsDest) {
254
- const content = fs.readFileSync(srcFile, 'utf8');
255
- const frontmatter = parseAgentFrontmatter(content);
256
- const name = frontmatter.name || path.basename(srcFile, '.md');
257
- const description = frontmatter.description || `Godpowers specialist agent: ${name}.`;
258
- const instructions = stripFrontmatter(content);
259
- const toml = [
260
- `name = ${tomlString(name)}`,
261
- `description = ${tomlString(description)}`,
262
- 'sandbox_mode = "workspace-write"',
263
- `developer_instructions = ${tomlLiteral(instructions)}`,
264
- ''
265
- ].join('\n');
266
-
267
- fs.writeFileSync(path.join(agentsDest, `${name}.toml`), toml);
268
- }
269
-
270
- function installAgentFile(srcFile, agentsDest, runtime) {
271
- fs.copyFileSync(srcFile, path.join(agentsDest, path.basename(srcFile)));
272
- if (runtime.agentMetadata === 'toml') {
273
- writeCodexAgentToml(srcFile, agentsDest);
274
- }
275
- }
276
-
277
- function removeSkillEntry(skillsDir, entry) {
278
- const entryPath = path.join(skillsDir, entry.name);
279
- if (entry.isDirectory()) {
280
- const skillFile = path.join(entryPath, 'SKILL.md');
281
- if (entry.name.startsWith('god-') || entry.name === 'god' || entry.name === 'godpowers') {
282
- if (fs.existsSync(skillFile)) {
283
- fs.rmSync(entryPath, { recursive: true, force: true });
284
- return true;
285
- }
286
- }
287
- return false;
288
- }
289
- if (entry.name.startsWith('god-') || entry.name === 'god.md' || entry.name === 'godpowers.md') {
290
- fs.unlinkSync(entryPath);
291
- return true;
292
- }
293
- return false;
294
- }
295
-
296
- // ---------------------------------------------------------------------------
297
- // Parse args
298
- // ---------------------------------------------------------------------------
299
-
300
- function parseArgs(argv) {
301
- const args = argv.slice(2);
302
- const opts = {
303
- command: null,
304
- project: process.cwd(),
305
- json: false,
306
- brief: false,
307
- extensionName: null,
308
- extensionOutput: process.cwd(),
309
- extensionSkill: null,
310
- extensionAgent: null,
311
- extensionWorkflow: null,
312
- runtimes: [],
313
- global: false,
314
- local: false,
315
- all: false,
316
- help: false,
317
- uninstall: false,
318
- };
319
-
320
- for (let i = 0; i < args.length; i++) {
321
- const arg = args[i];
322
- switch (arg) {
323
- case 'status':
324
- case 'next':
325
- case 'automation-status':
326
- case 'automation-setup':
327
- case 'dogfood':
328
- case 'extension-scaffold':
329
- opts.command = arg;
330
- break;
331
- case '--json':
332
- opts.json = true;
333
- break;
334
- case '--brief':
335
- opts.brief = true;
336
- break;
337
- case '--project':
338
- if (args[i + 1]) {
339
- opts.project = path.resolve(args[i + 1]);
340
- i++;
341
- }
342
- break;
343
- case '-g':
344
- case '--global':
345
- opts.global = true;
346
- break;
347
- case '-l':
348
- case '--local':
349
- opts.local = true;
350
- break;
351
- case '--all':
352
- opts.all = true;
353
- break;
354
- case '-h':
355
- case '--help':
356
- opts.help = true;
357
- break;
358
- case '-u':
359
- case '--uninstall':
360
- opts.uninstall = true;
361
- break;
362
- default:
363
- if (arg.startsWith('--project=')) {
364
- opts.project = path.resolve(arg.slice('--project='.length));
365
- } else if (arg.startsWith('--name=')) {
366
- opts.extensionName = arg.slice('--name='.length);
367
- } else if (arg.startsWith('--output=')) {
368
- opts.extensionOutput = path.resolve(arg.slice('--output='.length));
369
- } else if (arg.startsWith('--skill=')) {
370
- opts.extensionSkill = arg.slice('--skill='.length);
371
- } else if (arg.startsWith('--agent=')) {
372
- opts.extensionAgent = arg.slice('--agent='.length);
373
- } else if (arg.startsWith('--workflow=')) {
374
- opts.extensionWorkflow = arg.slice('--workflow='.length);
375
- } else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
376
- opts.runtimes.push(arg.slice(2));
377
- }
378
- break;
379
- }
380
- }
381
-
382
- return opts;
383
- }
384
-
385
- // ---------------------------------------------------------------------------
386
- // Install
387
- // ---------------------------------------------------------------------------
388
-
389
- function installForRuntime(runtimeKey, srcDir, opts = {}) {
390
- const runtime = resolveRuntime(runtimeKey, opts);
391
- if (!runtime) {
392
- error(`Unknown runtime: ${runtimeKey}`);
393
- return false;
394
- }
395
-
396
- log(`\n Installing for \x1b[36m${runtime.name}\x1b[0m to \x1b[36m${runtime.configDir}\x1b[0m\n`);
397
-
398
- ensureDir(runtime.configDir);
399
-
400
- // 1. Install slash command skills to skills/
401
- // Each skill in skills/ becomes a slash command (e.g. /god-mode)
402
- const skillsSrc = path.join(srcDir, 'skills');
403
- const skillsDest = path.join(runtime.configDir, runtime.skillsDir);
404
- if (fs.existsSync(skillsSrc)) {
405
- ensureDir(skillsDest);
406
- let count = 0;
407
- for (const file of fs.readdirSync(skillsSrc)) {
408
- if (file.endsWith('.md')) {
409
- installSkillFile(path.join(skillsSrc, file), skillsDest, runtimeKey);
410
- count++;
411
- }
412
- }
413
- const shape = runtimeKey === 'codex' ? 'Codex skill directories' : 'skills/';
414
- success(`Installed ${count} slash commands to ${shape}`);
415
- }
416
-
417
- // 2. Install specialist agents to agents/
418
- // Each agent is spawnable from a skill (e.g., god-pm, god-architect, god-executor)
419
- const agentsSrc = path.join(srcDir, 'agents');
420
- const agentsDest = path.join(runtime.configDir, 'agents');
421
- if (fs.existsSync(agentsSrc)) {
422
- ensureDir(agentsDest);
423
- let count = 0;
424
- for (const file of fs.readdirSync(agentsSrc)) {
425
- if (/^god-.*\.md$/.test(file)) {
426
- const srcFile = path.join(agentsSrc, file);
427
- installAgentFile(srcFile, agentsDest, runtime);
428
- count++;
429
- }
430
- }
431
- const shape = runtime.agentMetadata === 'toml' ? 'agents/ with Codex metadata' : 'agents/';
432
- success(`Installed ${count} specialist agents to ${shape}`);
433
- }
434
-
435
- // 3. Install the master SKILL.md (always-on context)
436
- const masterSkill = path.join(srcDir, 'SKILL.md');
437
- if (fs.existsSync(masterSkill)) {
438
- installSkillFile(masterSkill, skillsDest, runtimeKey, 'godpowers');
439
- success('Installed master SKILL.md as godpowers');
440
- }
441
-
442
- // 4. Install templates
443
- const templatesSrc = path.join(srcDir, 'templates');
444
- if (fs.existsSync(templatesSrc)) {
445
- const templatesDest = path.join(runtime.configDir, 'godpowers-templates');
446
- copyRecursive(templatesSrc, templatesDest);
447
- success('Installed templates/');
448
- }
449
-
450
- // 4b. Install references (HAVE-NOTS catalog and per-tier antipatterns)
451
- const referencesSrc = path.join(srcDir, 'references');
452
- if (fs.existsSync(referencesSrc)) {
453
- const referencesDest = path.join(runtime.configDir, 'godpowers-references');
454
- copyRecursive(referencesSrc, referencesDest);
455
- success('Installed references/');
456
- }
457
-
458
- // 4c. Install workflows (declarative YAML for /god-mode and friends)
459
- const workflowsSrc = path.join(srcDir, 'workflows');
460
- if (fs.existsSync(workflowsSrc)) {
461
- const workflowsDest = path.join(runtime.configDir, 'godpowers-workflows');
462
- copyRecursive(workflowsSrc, workflowsDest);
463
- success('Installed workflows/');
464
- }
465
-
466
- // 4d. Install schemas (for validation)
467
- const schemaSrc = path.join(srcDir, 'schema');
468
- if (fs.existsSync(schemaSrc)) {
469
- const schemaDest = path.join(runtime.configDir, 'godpowers-schema');
470
- copyRecursive(schemaSrc, schemaDest);
471
- success('Installed schema/');
472
- }
473
-
474
- // 4e. Install routing definitions (per-command prerequisites + next-suggestions)
475
- const routingSrc = path.join(srcDir, 'routing');
476
- if (fs.existsSync(routingSrc)) {
477
- const routingDest = path.join(runtime.configDir, 'godpowers-routing');
478
- copyRecursive(routingSrc, routingDest);
479
- success('Installed routing/');
480
- }
481
-
482
- // 4f. Install the executable runtime bundle with lib/ next to its data dirs.
483
- const runtimeBundleDest = path.join(runtime.configDir, 'godpowers-runtime');
484
- copyRuntimeBundle(srcDir, runtimeBundleDest);
485
- success('Installed runtime bundle/');
486
-
487
- // 5. Install hooks (Claude Code only for now)
488
- if (runtimeKey === 'claude') {
489
- const hooksSrc = path.join(srcDir, 'hooks');
490
- const hooksDest = path.join(runtime.configDir, 'hooks');
491
- if (fs.existsSync(hooksSrc)) {
492
- ensureDir(hooksDest);
493
- for (const file of fs.readdirSync(hooksSrc)) {
494
- const src = path.join(hooksSrc, file);
495
- const dest = path.join(hooksDest, file);
496
- fs.copyFileSync(src, dest);
497
- try { fs.chmodSync(dest, 0o755); } catch (e) {}
498
- }
499
- success('Installed hooks/');
500
- }
501
- }
502
-
503
- // 6. Write VERSION
504
- fs.writeFileSync(path.join(runtime.configDir, 'GODPOWERS_VERSION'), VERSION);
505
- success(`Wrote GODPOWERS_VERSION (${VERSION})`);
506
-
507
- return true;
508
- }
509
-
510
- // ---------------------------------------------------------------------------
511
- // Uninstall
512
- // ---------------------------------------------------------------------------
513
-
514
- function uninstallForRuntime(runtimeKey, opts = {}) {
515
- const runtime = resolveRuntime(runtimeKey, opts);
516
- if (!runtime) {
517
- error(`Unknown runtime: ${runtimeKey}`);
518
- return false;
519
- }
520
-
521
- log(`\n Uninstalling from \x1b[36m${runtime.name}\x1b[0m at \x1b[36m${runtime.configDir}\x1b[0m\n`);
522
-
523
- const skillsDir = path.join(runtime.configDir, runtime.skillsDir);
524
- const agentsDir = path.join(runtime.configDir, 'agents');
525
- const templatesDir = path.join(runtime.configDir, 'godpowers-templates');
526
- const referencesDir = path.join(runtime.configDir, 'godpowers-references');
527
- const workflowsDir = path.join(runtime.configDir, 'godpowers-workflows');
528
- const schemaDir = path.join(runtime.configDir, 'godpowers-schema');
529
- const routingDir = path.join(runtime.configDir, 'godpowers-routing');
530
- const runtimeBundleDir = path.join(runtime.configDir, 'godpowers-runtime');
531
- const versionFile = path.join(runtime.configDir, 'GODPOWERS_VERSION');
532
-
533
- let removed = 0;
534
-
535
- // Remove all god-* skills
536
- if (fs.existsSync(skillsDir)) {
537
- for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
538
- if (removeSkillEntry(skillsDir, entry)) {
539
- removed++;
540
- }
541
- }
542
- success(`Removed ${removed} god-* skill(s)`);
543
- }
544
-
545
- // Remove all god-* agents
546
- let agentsRemoved = 0;
547
- if (fs.existsSync(agentsDir)) {
548
- for (const file of fs.readdirSync(agentsDir)) {
549
- if (file.startsWith('god-')) {
550
- fs.unlinkSync(path.join(agentsDir, file));
551
- agentsRemoved++;
552
- }
553
- }
554
- success(`Removed ${agentsRemoved} god-* agent(s)`);
555
- }
556
-
557
- // Remove installed data and runtime directories
558
- for (const dir of [
559
- templatesDir,
560
- referencesDir,
561
- workflowsDir,
562
- schemaDir,
563
- routingDir,
564
- runtimeBundleDir
565
- ]) {
566
- if (fs.existsSync(dir)) {
567
- fs.rmSync(dir, { recursive: true, force: true });
568
- success(`Removed ${path.basename(dir)}/`);
569
- }
570
- }
571
-
572
- // Remove hooks (Claude Code only)
573
- if (runtimeKey === 'claude') {
574
- const hooksDir = path.join(runtime.configDir, 'hooks');
575
- for (const hook of ['session-start.sh', 'pre-tool-use.sh']) {
576
- const hookPath = path.join(hooksDir, hook);
577
- if (fs.existsSync(hookPath)) {
578
- fs.unlinkSync(hookPath);
579
- success(`Removed hooks/${hook}`);
580
- }
581
- }
582
- }
583
-
584
- if (fs.existsSync(versionFile)) {
585
- fs.unlinkSync(versionFile);
586
- }
587
-
588
- return true;
589
- }
590
-
591
44
  function showHelp() {
592
45
  console.log(BANNER);
593
46
  log('Usage: npx godpowers [command] [options]\n');
594
47
  log('Commands:');
595
48
  log(' status Show the Godpowers Dashboard for a project');
596
49
  log(' next Show the dashboard and recommended next command');
50
+ log(' quick-proof Show a runnable proof from the shipped fixture');
597
51
  log(' automation-status Show host automation provider support');
598
52
  log(' automation-setup Show an opt-in automation setup plan');
599
53
  log(' dogfood Run built-in messy-repo dogfood scenarios');
600
54
  log(' extension-scaffold Create a publishable extension pack skeleton');
601
55
  log('');
602
56
  log('Options:');
603
- log(' --project=<path> Project root for status, next, or automation commands');
604
- log(' --json Emit JSON for status, next, or automation commands');
605
- log(' --brief Render a compact dashboard for status or next');
57
+ log(' --project=<path> Project root for status, next, proof, or automation commands');
58
+ log(' --json Emit JSON for status, next, proof, or automation commands');
59
+ log(' --brief Render compact output for status, next, or proof');
606
60
  log(' --name=<scope/name> Extension package name for extension-scaffold');
607
61
  log(' --output=<path> Extension output root for extension-scaffold');
608
62
  log(' --skill=<name> Extension skill name for extension-scaffold');
609
63
  log(' --agent=<name> Extension agent name for extension-scaffold');
610
64
  log(' --workflow=<name> Extension workflow name for extension-scaffold');
611
- log(' -g, --global Install globally (to config directory)');
612
- log(' -l, --local Install locally (to current directory)');
65
+ log(' -g, --global Install globally to the config directory');
66
+ log(' -l, --local Install locally to the current directory');
613
67
  log(' --claude Install for Claude Code');
614
68
  log(' --codex Install for Codex');
615
69
  log(' --cursor Install for Cursor');
@@ -632,6 +86,7 @@ function showHelp() {
632
86
  log('Examples:');
633
87
  log(' npx godpowers status --project=.');
634
88
  log(' npx godpowers next --project=.');
89
+ log(' npx godpowers quick-proof --project=.');
635
90
  log(' npx godpowers automation-status --project=.');
636
91
  log(' npx godpowers automation-setup --project=.');
637
92
  log(' npx godpowers dogfood');
@@ -660,13 +115,14 @@ function runDashboardCommand(opts) {
660
115
  const result = dashboard.compute(opts.project);
661
116
  if (opts.json) {
662
117
  console.log(JSON.stringify(result, null, 2));
663
- } else {
664
- console.log(dashboard.render(result, { brief: opts.brief }));
665
- if (opts.command === 'next') {
666
- console.log('');
667
- console.log('Suggested next command:');
668
- console.log(` ${result.next && result.next.command ? result.next.command : 'describe the next intent'}`);
669
- }
118
+ return;
119
+ }
120
+
121
+ console.log(dashboard.render(result, { brief: opts.brief }));
122
+ if (opts.command === 'next') {
123
+ console.log('');
124
+ console.log('Suggested next command:');
125
+ console.log(` ${result.next && result.next.command ? result.next.command : 'describe the next intent'}`);
670
126
  }
671
127
  }
672
128
 
@@ -681,6 +137,16 @@ function runDogfoodCommand(opts) {
681
137
  if (result.status !== 'pass') process.exit(1);
682
138
  }
683
139
 
140
+ function runQuickProofCommand(opts) {
141
+ const quickProof = require('../lib/quick-proof');
142
+ const result = quickProof.compute(opts.project);
143
+ if (opts.json) {
144
+ console.log(JSON.stringify(result, null, 2));
145
+ } else {
146
+ console.log(quickProof.render(result, { brief: opts.brief }));
147
+ }
148
+ }
149
+
684
150
  function runExtensionScaffoldCommand(opts) {
685
151
  const authoring = require('../lib/extension-authoring');
686
152
  if (!opts.extensionName) {
@@ -696,90 +162,75 @@ function runExtensionScaffoldCommand(opts) {
696
162
  });
697
163
  if (opts.json) {
698
164
  console.log(JSON.stringify(result, null, 2));
699
- } else {
700
- success(`Scaffolded ${result.name} at ${result.path}`);
701
- if (result.written.length > 0) {
702
- log(`Wrote ${result.written.length} file(s): ${result.written.join(', ')}`);
703
- }
704
- if (result.validation.length > 0) {
705
- warn(`Validation warnings: ${result.validation.join('; ')}`);
706
- } else {
707
- success('Extension manifest validates');
708
- }
165
+ return;
709
166
  }
710
- }
711
167
 
712
- // ---------------------------------------------------------------------------
713
- // Main
714
- // ---------------------------------------------------------------------------
715
-
716
- function main() {
717
- const opts = parseArgs(process.argv);
718
-
719
- if (opts.help) {
720
- showHelp();
721
- process.exit(0);
168
+ success(`Scaffolded ${result.name} at ${result.path}`);
169
+ if (result.written.length > 0) {
170
+ log(`Wrote ${result.written.length} file(s): ${result.written.join(', ')}`);
171
+ }
172
+ if (result.validation.length > 0) {
173
+ warn(`Validation warnings: ${result.validation.join('; ')}`);
174
+ } else {
175
+ success('Extension manifest validates');
722
176
  }
177
+ }
723
178
 
179
+ function runCommand(opts) {
724
180
  if (opts.command === 'status' || opts.command === 'next') {
725
181
  runDashboardCommand(opts);
726
- return;
182
+ return true;
183
+ }
184
+ if (opts.command === 'quick-proof') {
185
+ runQuickProofCommand(opts);
186
+ return true;
727
187
  }
728
-
729
188
  if (opts.command === 'automation-status' || opts.command === 'automation-setup') {
730
189
  runAutomationCommand(opts);
731
- return;
190
+ return true;
732
191
  }
733
-
734
192
  if (opts.command === 'dogfood') {
735
193
  runDogfoodCommand(opts);
736
- return;
194
+ return true;
737
195
  }
738
-
739
196
  if (opts.command === 'extension-scaffold') {
740
197
  runExtensionScaffoldCommand(opts);
741
- return;
742
- }
743
-
744
- console.log(BANNER);
745
-
746
- const srcDir = path.resolve(__dirname, '..');
747
-
748
- // Detect non-interactive and default to Claude Code.
749
- if (opts.runtimes.length === 0 && !opts.all) {
750
- if (!process.stdin.isTTY) {
751
- warn('Non-interactive terminal detected, defaulting to Claude Code install');
752
- opts.runtimes = ['claude'];
753
- if (!opts.local) opts.global = true;
754
- } else {
755
- // Interactive mode: default to claude
756
- opts.runtimes = ['claude'];
757
- if (!opts.local) opts.global = true;
758
- }
198
+ return true;
759
199
  }
200
+ return false;
201
+ }
760
202
 
761
- if (opts.all) {
762
- opts.runtimes = Object.keys(RUNTIMES);
203
+ function applyDefaultRuntimeSelection(opts) {
204
+ if (opts.runtimes.length > 0 || opts.all) return;
205
+ if (!process.stdin.isTTY) {
206
+ // Refuse to perform a silent global install when there is no human to
207
+ // confirm and no explicit target was given (CI, piped, some npx contexts).
208
+ warn('No runtime selected and stdin is not a TTY.');
209
+ warn('Refusing a silent global install. Re-run with an explicit target,');
210
+ warn('for example: npx godpowers --claude --global (or --all).');
211
+ process.exit(1);
763
212
  }
213
+ opts.runtimes = ['claude'];
214
+ if (!opts.local) opts.global = true;
215
+ }
764
216
 
765
- // Handle uninstall
766
- if (opts.uninstall) {
767
- let removed = 0;
768
- for (const runtime of opts.runtimes) {
769
- if (uninstallForRuntime(runtime, opts)) {
770
- removed++;
771
- }
772
- }
773
- log('');
774
- if (removed > 0) {
775
- log(`\x1b[32mUninstalled\x1b[0m from ${removed} runtime(s).`);
776
- } else {
777
- warn('No runtimes uninstalled.');
217
+ function runUninstall(opts) {
218
+ let removed = 0;
219
+ for (const runtime of opts.runtimes) {
220
+ if (uninstallForRuntime(runtime, opts)) {
221
+ removed++;
778
222
  }
779
- log('');
780
- return;
781
223
  }
224
+ log('');
225
+ if (removed > 0) {
226
+ log(`\x1b[32mUninstalled\x1b[0m from ${removed} runtime(s).`);
227
+ } else {
228
+ warn('No runtimes uninstalled.');
229
+ }
230
+ log('');
231
+ }
782
232
 
233
+ function runInstall(opts, srcDir) {
783
234
  let installed = 0;
784
235
  for (const runtime of opts.runtimes) {
785
236
  if (installForRuntime(runtime, srcDir, opts)) {
@@ -787,31 +238,62 @@ function main() {
787
238
  }
788
239
  }
789
240
 
790
- if (installed > 0) {
791
- // Count slash commands for verification message
792
- const skillsCount = fs.readdirSync(path.join(srcDir, 'skills')).filter(f => f.endsWith('.md')).length;
793
- const agentsCount = fs.readdirSync(path.join(srcDir, 'agents')).filter(f => /^god-.*\.md$/.test(f)).length;
794
-
795
- log('');
796
- log(`\x1b[32mDone!\x1b[0m Installed Godpowers v${VERSION} for ${installed} runtime(s).`);
797
- log('');
798
- log(`\x1b[36mInstalled:\x1b[0m`);
799
- log(` ${skillsCount} slash commands (try: /god-mode, /god-next, /god-status)`);
800
- log(` ${agentsCount} specialist agents`);
801
- log(` Templates and references for artifact discipline`);
802
- log('');
803
- log(`\x1b[36mNext steps:\x1b[0m`);
804
- log(` 1. Open your AI coding tool in any project directory`);
805
- log(` 2. Type: \x1b[36m/god-mode\x1b[0m for the full autonomous project run`);
806
- log(` Or: \x1b[36m/god-next\x1b[0m to see what to run next`);
807
- log(` Or: \x1b[36m/god-init\x1b[0m to start a new project`);
808
- log('');
809
- log(`\x1b[36mDocs:\x1b[0m https://github.com/godpowers/godpowers`);
810
- log('');
811
- } else {
241
+ if (installed === 0) {
812
242
  error('No runtimes installed. Run with --help for usage.');
813
243
  process.exit(1);
814
244
  }
245
+
246
+ const surface = countInstalledSurface(srcDir);
247
+ log('');
248
+ log(`\x1b[32mDone!\x1b[0m Installed Godpowers v${VERSION} for ${installed} runtime(s).`);
249
+ log('');
250
+ log(`\x1b[36mInstalled:\x1b[0m`);
251
+ log(` ${surface.skills} slash commands (try: /god-mode, /god-next, /god-status)`);
252
+ log(` ${surface.agents} specialist agents`);
253
+ log(' Templates and references for artifact discipline');
254
+ log('');
255
+ log(`\x1b[36mNext steps:\x1b[0m`);
256
+ log(' 1. Open your AI coding tool in any project directory');
257
+ log(` 2. Type: \x1b[36m/god-mode\x1b[0m for the full autonomous project run`);
258
+ log(` Or: \x1b[36m/god-next\x1b[0m to see what to run next`);
259
+ log(` Or: \x1b[36m/god-init\x1b[0m to start a new project`);
260
+ log('');
261
+ log(`\x1b[36mDocs:\x1b[0m https://github.com/godpowers/godpowers`);
262
+ log('');
263
+ }
264
+
265
+ function main() {
266
+ const opts = parseArgs(process.argv);
267
+
268
+ if (opts.help) {
269
+ showHelp();
270
+ process.exit(0);
271
+ }
272
+
273
+ if (runCommand(opts)) return;
274
+
275
+ console.log(BANNER);
276
+ const srcDir = path.resolve(__dirname, '..');
277
+
278
+ applyDefaultRuntimeSelection(opts);
279
+ if (opts.all) {
280
+ opts.runtimes = runtimeKeys();
281
+ }
282
+
283
+ if (opts.uninstall) {
284
+ runUninstall(opts);
285
+ } else {
286
+ runInstall(opts, srcDir);
287
+ }
815
288
  }
816
289
 
817
290
  main();
291
+
292
+ module.exports = {
293
+ showHelp,
294
+ runCommand,
295
+ applyDefaultRuntimeSelection,
296
+ runInstall,
297
+ runUninstall,
298
+ RUNTIMES
299
+ };