godpowers 2.2.1 → 2.3.1

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 (81) hide show
  1. package/CHANGELOG.md +65 -7
  2. package/INSPIRATION.md +4 -2
  3. package/README.md +35 -22
  4. package/RELEASE.md +44 -23
  5. package/SKILL.md +2 -2
  6. package/agents/god-auditor.md +1 -1
  7. package/agents/god-debugger.md +44 -8
  8. package/agents/god-deps-auditor.md +11 -0
  9. package/agents/god-executor.md +50 -5
  10. package/agents/god-greenfieldifier.md +1 -1
  11. package/agents/god-orchestrator.md +2 -2
  12. package/agents/god-planner.md +14 -0
  13. package/agents/god-pm.md +1 -1
  14. package/agents/god-quality-reviewer.md +23 -1
  15. package/agents/god-reconciler.md +1 -1
  16. package/agents/god-roadmapper.md +1 -1
  17. package/agents/god-spec-reviewer.md +16 -0
  18. package/agents/god-stack-selector.md +4 -0
  19. package/agents/god-updater.md +1 -1
  20. package/bin/install.js +9 -3
  21. package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/manifest.json +2 -2
  22. package/lib/README.md +9 -2
  23. package/lib/agent-cache.js +25 -3
  24. package/lib/atomic-write.js +85 -0
  25. package/lib/checkpoint.js +2 -1
  26. package/lib/code-intelligence.js +161 -0
  27. package/lib/context-writer.js +1 -1
  28. package/lib/dashboard.js +54 -20
  29. package/lib/executor-repair.js +65 -0
  30. package/lib/extensions.js +6 -13
  31. package/lib/feature-awareness.js +2 -2
  32. package/lib/fs-async.js +2 -1
  33. package/lib/host-capabilities.js +4 -0
  34. package/lib/install-profiles.js +155 -0
  35. package/lib/installer-args.js +12 -0
  36. package/lib/installer-core.js +43 -8
  37. package/lib/linkage.js +2 -1
  38. package/lib/package-identity.js +38 -0
  39. package/lib/package-legitimacy.js +158 -0
  40. package/lib/planning-systems.js +16 -13
  41. package/lib/quick-proof.js +5 -2
  42. package/lib/repo-surface-sync.js +1 -1
  43. package/lib/requirements.js +2 -1
  44. package/lib/reverse-sync.js +2 -1
  45. package/lib/route-quality-sync.js +2 -0
  46. package/lib/source-grounding.js +177 -0
  47. package/lib/source-sync.js +6 -5
  48. package/lib/state.js +2 -1
  49. package/package.json +2 -2
  50. package/references/HAVE-NOTS.md +1 -1
  51. package/references/orchestration/MODE-DETECTION.md +2 -2
  52. package/references/shared/ORCHESTRATORS.md +3 -3
  53. package/routing/god-design.yaml +1 -1
  54. package/routing/god-extension-scaffold.yaml +25 -0
  55. package/routing/god-migrate.yaml +3 -3
  56. package/routing/god-stack.yaml +1 -1
  57. package/routing/recipes/add-feature-mid-arc-pause.yaml +6 -0
  58. package/routing/recipes/brownfield-onboarding.yaml +5 -2
  59. package/routing/recipes/extension-authoring.yaml +32 -0
  60. package/routing/recipes/greenfield-fast.yaml +3 -0
  61. package/routing/recipes/production-broken.yaml +4 -0
  62. package/routing/recipes/release-maintenance.yaml +3 -0
  63. package/routing/recipes/returning-after-break.yaml +3 -0
  64. package/routing/recipes/weekly-health-check.yaml +2 -0
  65. package/schema/state.v1.json +1 -1
  66. package/skills/god-build.md +17 -3
  67. package/skills/god-discuss.md +10 -5
  68. package/skills/god-doctor.md +9 -3
  69. package/skills/god-dogfood.md +2 -2
  70. package/skills/god-extension-scaffold.md +66 -0
  71. package/skills/god-init.md +6 -6
  72. package/skills/god-migrate.md +10 -10
  73. package/skills/god-sync.md +2 -2
  74. package/skills/god-update-deps.md +4 -2
  75. package/skills/god-version.md +2 -2
  76. package/skills/god.md +8 -11
  77. package/templates/IMPORTED-CONTEXT.md +1 -1
  78. package/templates/INITIAL-FINDINGS.md +2 -2
  79. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/PROJECT.md +0 -0
  80. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/REQUIREMENTS.md +0 -0
  81. /package/fixtures/dogfood/{half-migrated-gsd → half-migrated-planning}/.planning/ROADMAP.md +0 -0
@@ -63,6 +63,26 @@ Your job: would you ship this code in production?
63
63
  - Flag missing or inaccurate annotations: the deliverable ledger derives
64
64
  requirement status from them, so a gap here understates delivered work
65
65
 
66
+ ### 8. Comment Quality and Style Fidelity
67
+ - Required traceability comments such as `// Implements: P-...` remain present
68
+ and accurate.
69
+ - Comments explain non-obvious why, constraints, hazards, or tradeoffs. Flag
70
+ comments that narrate obvious code, duplicate names, go stale, or use generic
71
+ AI-assistant prose.
72
+ - Avoid decorative section banners or chatty explanation unless the surrounding
73
+ codebase already uses that convention.
74
+ - If `CODEDNA.md` exists, compare naming, comment voice, extraction threshold,
75
+ error style, test style, and common idioms against the profile.
76
+ - Absence of `CODEDNA.md` is not a failure. When no profile exists, judge style
77
+ against nearby code and repository conventions.
78
+
79
+ ### 9. Optional Code Intelligence
80
+ - When `ast-grep`, `sg`, or LSP tools are available, use them to support
81
+ maintainability review for structural matches, impacted references, or
82
+ diagnostics that plain grep can miss.
83
+ - Absence of these tools is not a failure. Treat them as extra evidence when
84
+ present.
85
+
66
86
  ## Output
67
87
 
68
88
  Return verdict to orchestrator:
@@ -78,6 +98,8 @@ Return verdict to orchestrator:
78
98
  - [PASS/FAIL] Maintainability: [evidence]
79
99
  - [PASS/FAIL] Simplicity and surgicality: [evidence]
80
100
  - [PASS/FAIL] Requirement traceability: [evidence]
101
+ - [PASS/FAIL] Comment quality and style fidelity: [evidence]
102
+ - [PASS/FAIL] Optional code intelligence: [evidence or not applicable]
81
103
 
82
104
  ### Verdict: PASS / FAIL
83
105
 
@@ -86,7 +108,7 @@ Return verdict to orchestrator:
86
108
 
87
109
  ## Pass Criteria
88
110
 
89
- ALL seven dimensions must PASS. Any FAIL blocks the commit.
111
+ ALL nine dimensions must PASS. Any FAIL blocks the commit.
90
112
 
91
113
  If FAIL: orchestrator returns the slice to god-executor.
92
114
  If PASS: orchestrator commits the slice atomically.
@@ -144,7 +144,7 @@ For each artifact below, check (in parallel where possible):
144
144
  - If imported planning systems have low confidence or conflicts: recommend
145
145
  `god-greenfieldifier`.
146
146
 
147
- #### SOURCE SYSTEM SYNC-BACK (GSD, BMAD, Superpowers)
147
+ #### SOURCE SYSTEM SYNC-BACK (legacy planning, BMAD, Superpowers)
148
148
  - Did migrated source-system summaries need managed sync-back?
149
149
  - Verdict: not-applicable / fresh / needs-sync-back / blocked-by-conflict
150
150
  - If conflicts exist: recommend greenfieldifier review before writes.
@@ -36,7 +36,7 @@ If `.godpowers/prep/INITIAL-FINDINGS.md` exists, read it first for repo risks,
36
36
  existing tests, docs, CI, deploy, and suggested sequencing implications.
37
37
 
38
38
  If `.godpowers/prep/IMPORTED-CONTEXT.md` exists, read its delivery signals
39
- before sequencing work. Use imported GSD, Superpowers, BMAD, or similar
39
+ before sequencing work. Use imported legacy planning, Superpowers, BMAD, or similar
40
40
  stories and plans as hypothesis-level input only. Convert imported terminology
41
41
  into Godpowers vocabulary. Do not preserve imported methodology terminology in
42
42
  Godpowers artifacts unless the user explicitly asked for it.
@@ -57,6 +57,20 @@ Answer each with EVIDENCE from the code:
57
57
  - A planned requirement id with no annotation is a finding, because the
58
58
  deliverable ledger derives requirement status from those annotations.
59
59
 
60
+ 7. **Did the implementation honor source grounding and package legitimacy?**
61
+ - Did every existing file or symbol cited by the slice plan exist before
62
+ execution?
63
+ - Did new files appear only under the plan's new artifacts or expected file
64
+ list?
65
+ - If the slice added a dependency, is there package legitimacy evidence or
66
+ an explicit accepted-risk note?
67
+
68
+ 8. **Did available code intelligence support the review?**
69
+ - If `ast-grep`, `sg`, or LSP tools are available, use them to verify
70
+ impacted references, structural matches, or diagnostics for the touched
71
+ language when relevant.
72
+ - If unavailable or irrelevant, do not fail the slice for absence alone.
73
+
60
74
  ## Output
61
75
 
62
76
  Return verdict to orchestrator:
@@ -83,6 +97,8 @@ Return verdict to orchestrator:
83
97
  - Every touched file has request-trace evidence
84
98
  - No speculative flexibility or unrelated cleanup entered the diff
85
99
  - Every planned requirement id carries its `// Implements: P-...` annotation
100
+ - Source-grounding failures are resolved or explicitly accepted
101
+ - New dependencies have legitimacy evidence or accepted-risk notes
86
102
 
87
103
  If FAIL: orchestrator returns the slice to god-executor with the failures.
88
104
  If PASS: orchestrator spawns god-quality-reviewer next.
@@ -65,6 +65,9 @@ Rules:
65
65
  3. For each category:
66
66
  - List 2-3 viable candidates
67
67
  - Score on: fit-for-requirements, maturity, ecosystem health, team familiarity, total cost
68
+ - For package-backed choices, run or request the package legitimacy gate:
69
+ registry existence, package age, repository signal, maintainer signal,
70
+ typo-squat risk, and known vulnerability status where available
68
71
  - Document the **flip point**: condition under which you'd reverse this choice
69
72
  - Document the **lock-in cost**: how hard is it to switch (Low/Medium/High)
70
73
  4. Verify pairing compatibility (e.g., chosen ORM works with chosen DB)
@@ -103,6 +106,7 @@ Stack DECISION FAILS if:
103
106
  - High lock-in choice with likely flip point in <6 months
104
107
  - Pairing incompatibility (chosen ORM doesn't support chosen DB, etc.)
105
108
  - "Best practice" rationale without specific rationale tied to ARCH
109
+ - Package-backed choice without legitimacy evidence or an accepted-risk note
106
110
 
107
111
  ## Pause Conditions
108
112
 
@@ -174,7 +174,7 @@ After feature work, every artifact that was impacted needs to reflect reality.
174
174
 
175
175
  ### Source-system sync-back
176
176
  - Call `lib/source-sync.run(projectRoot)` when `.godpowers/state.json`
177
- declares GSD, BMAD, Superpowers, or other source-system records.
177
+ declares legacy planning, BMAD, Superpowers, or other source-system records.
178
178
  - Write only managed Godpowers summary sections back to source systems.
179
179
  - Preserve user-authored source-system content outside managed sections.
180
180
  - If source-system confidence is low or conflicts are present, recommend
package/bin/install.js CHANGED
@@ -17,8 +17,10 @@ const {
17
17
  uninstallForRuntime,
18
18
  countInstalledSurface
19
19
  } = require('../lib/installer-core');
20
+ const { describeProfiles } = require('../lib/install-profiles');
21
+ const identity = require('../lib/package-identity');
20
22
 
21
- const VERSION = require('../package.json').version;
23
+ const VERSION = identity.PACKAGE_VERSION;
22
24
 
23
25
  const BANNER = `
24
26
  GODPOWERS v${VERSION}
@@ -80,6 +82,8 @@ function showHelp() {
80
82
  log(' --codebuddy Install for CodeBuddy');
81
83
  log(' --pi Install for Pi');
82
84
  log(' --all Install for all 15 runtimes');
85
+ log(' --profile=<name> Install profile: core, builder, maintainer, suite, or full');
86
+ log(' --minimal Alias for --profile=core');
83
87
  log(' -u, --uninstall Uninstall Godpowers');
84
88
  log(' -h, --help Show this help message');
85
89
  log('');
@@ -92,6 +96,7 @@ function showHelp() {
92
96
  log(' npx godpowers dogfood');
93
97
  log(' npx godpowers extension-scaffold --name=@godpowers/my-pack --output=.');
94
98
  log(' npx godpowers --claude --global');
99
+ log(' npx godpowers --claude --global --profile=core');
95
100
  log(' npx godpowers --all');
96
101
  log(' npx godpowers --codex --cursor');
97
102
  }
@@ -243,10 +248,11 @@ function runInstall(opts, srcDir) {
243
248
  process.exit(1);
244
249
  }
245
250
 
246
- const surface = countInstalledSurface(srcDir);
251
+ const surface = countInstalledSurface(srcDir, opts);
247
252
  log('');
248
253
  log(`\x1b[32mDone!\x1b[0m Installed Godpowers v${VERSION} for ${installed} runtime(s).`);
249
254
  log('');
255
+ log(`\x1b[36mProfile:\x1b[0m ${describeProfiles(opts.profile)}`);
250
256
  log(`\x1b[36mInstalled:\x1b[0m`);
251
257
  log(` ${surface.skills} slash commands (try: /god-mode, /god-next, /god-status, /god-progress)`);
252
258
  log(` ${surface.agents} specialist agents`);
@@ -258,7 +264,7 @@ function runInstall(opts, srcDir) {
258
264
  log(` Or: \x1b[36m/god-next\x1b[0m to see what to run next`);
259
265
  log(` Or: \x1b[36m/god-init\x1b[0m to start a new project`);
260
266
  log('');
261
- log(`\x1b[36mDocs:\x1b[0m https://github.com/godpowers/godpowers`);
267
+ log(`\x1b[36mDocs:\x1b[0m ${identity.HOMEPAGE_URL}`);
262
268
  log('');
263
269
  }
264
270
 
@@ -1,12 +1,12 @@
1
1
  {
2
- "name": "Half migrated GSD project",
2
+ "name": "Half migrated planning project",
3
3
  "kind": "planning-migration",
4
4
  "actions": [
5
5
  "import-planning-context",
6
6
  "sync-back"
7
7
  ],
8
8
  "expectedSystems": [
9
- "gsd"
9
+ "legacy-planning"
10
10
  ],
11
11
  "expectedFiles": [
12
12
  ".godpowers/prep/IMPORTED-CONTEXT.md",
package/lib/README.md CHANGED
@@ -13,7 +13,8 @@ package-level integrations.
13
13
  | `intent.js` | Read and validate `intent.yaml` from project roots or `.godpowers/`. |
14
14
  | `checkpoint.js` | Create and inspect resumable checkpoint artifacts. |
15
15
  | `feature-awareness.js` | Detect and refresh existing-project awareness after runtime upgrades. |
16
- | `host-capabilities.js` | Detect host guarantees for shell, git, npm, agent spawning, extension authoring, and suite dry-runs. |
16
+ | `code-intelligence.js` | Detect optional `ast-grep`, `sg`, and LSP tooling for structural search, rewrite, and diagnostics guidance. |
17
+ | `host-capabilities.js` | Detect host guarantees for shell, git, npm, agent spawning, optional code intelligence, extension authoring, and suite dry-runs. |
17
18
  | `repo-doc-sync.js` | Detect and refresh mechanical repository documentation surfaces. |
18
19
  | `repo-surface-sync.js` | Detect structural drift across commands, routes, packages, agents, workflows, recipes, extensions, and release policy. |
19
20
  | `route-quality-sync.js` | Detect symbolic route spawns, unresolved agent targets, and unapproved contextual route exits. |
@@ -22,6 +23,7 @@ package-level integrations.
22
23
  | `dogfood-runner.js` | Run deterministic messy-repo scenarios against migration, host, extension, and suite release behavior. |
23
24
  | `budget.js` | Read and enforce configured budget controls. |
24
25
  | `cost-tracker.js` | Track token and cost estimates from event streams. |
26
+ | `atomic-write.js` | Write load-bearing files through temp-file validation and atomic rename. |
25
27
  | `fs-async.js` | Promise-based file read/write helpers for non-blocking runtime paths. |
26
28
 
27
29
  ## Events and observability
@@ -45,6 +47,7 @@ package-level integrations.
45
47
  | `agent-cache.js` | Cache agent metadata for faster routing. |
46
48
  | `agent-validator.js` | Validate agent frontmatter and contracts. |
47
49
  | `agent-refs.js` | Validate workflow agent references and scan skill/agent prose for phantom references. |
50
+ | `executor-repair.js` | Classify executor repair decisions as retry, decompose, prune, or escalate. |
48
51
  | `skill-surface.js` | Derive slash-command metadata from the individual `skills/` files. |
49
52
 
50
53
  ## Artifact quality
@@ -63,7 +66,7 @@ package-level integrations.
63
66
  |--------|---------|
64
67
  | `context-writer.js` | Produce tool-specific context files. |
65
68
  | `context-budget.js` | Keep generated context within budget. |
66
- | `planning-systems.js` | Detect and import GSD, BMAD, and Superpowers planning context. |
69
+ | `planning-systems.js` | Detect and import legacy planning, BMAD, and Superpowers planning context. |
67
70
  | `source-sync.js` | Write managed Godpowers progress back to source-system companion files. |
68
71
  | `design-detector.js` | Detect design-system conventions. |
69
72
  | `design-spec.js` | Normalize design specifications. |
@@ -74,6 +77,7 @@ package-level integrations.
74
77
  | `impeccable-bridge.js` | Bridge runtime checks into impeccable quality workflows. |
75
78
  | `extensions.js` | Load and validate extension packs. |
76
79
  | `extension-authoring.js` | Scaffold publishable extension packs with manifest, package, README, skill, agent, and workflow files. |
80
+ | `package-legitimacy.js` | Assess third-party package metadata for existence, typo risk, recency, and repository signals. |
77
81
  | `pillars.js` | Manage the Pillars project-context layer (`AGENTS.md` plus routed `agents/*.md`). |
78
82
 
79
83
  ## Repository and graph helpers
@@ -81,6 +85,7 @@ package-level integrations.
81
85
  | Module | Purpose |
82
86
  |--------|---------|
83
87
  | `code-scanner.js` | Scan source trees for routing and quality evidence. |
88
+ | `source-grounding.js` | Check that build plans cite existing files and symbols before execution starts. |
84
89
  | `cross-artifact-impact.js` | Detect relationships between changed artifacts. |
85
90
  | `cross-repo-linkage.js` | Track suite-level repository relationships. |
86
91
  | `drift-detector.js` | Detect context drift between artifacts and implementation. |
@@ -98,7 +103,9 @@ package-level integrations.
98
103
  | `installer-core.js` | Install and uninstall the Godpowers surface for each runtime. |
99
104
  | `installer-files.js` | File-copy helpers shared by the installer and its tests. |
100
105
  | `installer-args.js` | Parse `bin/install.js` arguments and subcommands. |
106
+ | `install-profiles.js` | Select smaller role-specific slash-command install surfaces. |
101
107
  | `installer-runtimes.js` | Map supported runtimes to their config directories. |
108
+ | `package-identity.js` | Centralize package name, version, repository, docs, and command identity. |
102
109
  | `automation-providers.js` | Detect and configure host-native automation providers. |
103
110
  | `dashboard.js` | Compute the next-step action brief and host guarantee line. |
104
111
  | `quick-proof.js` | Render the shipped proof fixture for `godpowers quick-proof`. |
@@ -134,9 +134,29 @@ function clear(projectRoot, opts = {}) {
134
134
  const now = Date.now();
135
135
  for (const shard of fs.readdirSync(dir)) {
136
136
  const shardPath = path.join(dir, shard);
137
- if (!fs.statSync(shardPath).isDirectory()) continue;
137
+ const shardStat = fs.lstatSync(shardPath);
138
+ if (shardStat.isSymbolicLink()) {
139
+ if (opts.all) {
140
+ fs.unlinkSync(shardPath);
141
+ removed++;
142
+ } else {
143
+ kept++;
144
+ }
145
+ continue;
146
+ }
147
+ if (!shardStat.isDirectory()) continue;
138
148
  for (const fname of fs.readdirSync(shardPath)) {
139
149
  const fpath = path.join(shardPath, fname);
150
+ const fileStat = fs.lstatSync(fpath);
151
+ if (fileStat.isSymbolicLink()) {
152
+ if (opts.all) { fs.unlinkSync(fpath); removed++; }
153
+ else { kept++; }
154
+ continue;
155
+ }
156
+ if (!fileStat.isFile()) {
157
+ kept++;
158
+ continue;
159
+ }
140
160
  let entry;
141
161
  try { entry = JSON.parse(fs.readFileSync(fpath, 'utf8')); }
142
162
  catch (e) {
@@ -173,10 +193,12 @@ function stats(projectRoot) {
173
193
  let oldestTs = null;
174
194
  for (const shard of fs.readdirSync(dir)) {
175
195
  const shardPath = path.join(dir, shard);
176
- if (!fs.statSync(shardPath).isDirectory()) continue;
196
+ const shardStat = fs.lstatSync(shardPath);
197
+ if (shardStat.isSymbolicLink() || !shardStat.isDirectory()) continue;
177
198
  for (const fname of fs.readdirSync(shardPath)) {
178
199
  const fpath = path.join(shardPath, fname);
179
- const stat = fs.statSync(fpath);
200
+ const stat = fs.lstatSync(fpath);
201
+ if (stat.isSymbolicLink() || !stat.isFile()) continue;
180
202
  totalBytes += stat.size;
181
203
  count += 1;
182
204
  try {
@@ -0,0 +1,85 @@
1
+ const fs = require('fs');
2
+ const fsp = require('fs/promises');
3
+ const path = require('path');
4
+
5
+ function tempPathFor(filePath) {
6
+ const dir = path.dirname(filePath);
7
+ const base = path.basename(filePath);
8
+ return path.join(dir, `.${base}.${process.pid}.${Date.now()}.tmp`);
9
+ }
10
+
11
+ function validateContent(content, validate) {
12
+ if (typeof validate === 'function') validate(content);
13
+ }
14
+
15
+ function writeFileAtomic(filePath, content, opts = {}) {
16
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
17
+ const tmp = tempPathFor(filePath);
18
+ try {
19
+ fs.writeFileSync(tmp, content);
20
+ validateContent(content, opts.validateContent);
21
+ if (typeof opts.validateFile === 'function') opts.validateFile(tmp);
22
+ fs.renameSync(tmp, filePath);
23
+ return filePath;
24
+ } catch (error) {
25
+ try {
26
+ fs.rmSync(tmp, { force: true });
27
+ } catch (_) {
28
+ // Best-effort cleanup. The original file is still untouched.
29
+ }
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ async function writeFileAtomicAsync(filePath, content, opts = {}) {
35
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
36
+ const tmp = tempPathFor(filePath);
37
+ try {
38
+ await fsp.writeFile(tmp, content);
39
+ validateContent(content, opts.validateContent);
40
+ if (typeof opts.validateFile === 'function') await opts.validateFile(tmp);
41
+ await fsp.rename(tmp, filePath);
42
+ return filePath;
43
+ } catch (error) {
44
+ try {
45
+ await fsp.rm(tmp, { force: true });
46
+ } catch (_) {
47
+ // Best-effort cleanup. The original file is still untouched.
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+
53
+ function jsonContent(value) {
54
+ return JSON.stringify(value, null, 2) + '\n';
55
+ }
56
+
57
+ function writeJsonAtomic(filePath, value, opts = {}) {
58
+ const content = jsonContent(value);
59
+ return writeFileAtomic(filePath, content, {
60
+ ...opts,
61
+ validateContent: text => {
62
+ JSON.parse(text);
63
+ validateContent(text, opts.validateContent);
64
+ }
65
+ });
66
+ }
67
+
68
+ async function writeJsonAtomicAsync(filePath, value, opts = {}) {
69
+ const content = jsonContent(value);
70
+ return writeFileAtomicAsync(filePath, content, {
71
+ ...opts,
72
+ validateContent: text => {
73
+ JSON.parse(text);
74
+ validateContent(text, opts.validateContent);
75
+ }
76
+ });
77
+ }
78
+
79
+ module.exports = {
80
+ tempPathFor,
81
+ writeFileAtomic,
82
+ writeFileAtomicAsync,
83
+ writeJsonAtomic,
84
+ writeJsonAtomicAsync
85
+ };
package/lib/checkpoint.js CHANGED
@@ -49,6 +49,7 @@
49
49
  const fs = require('fs');
50
50
  const path = require('path');
51
51
  const crypto = require('crypto');
52
+ const atomic = require('./atomic-write');
52
53
 
53
54
  const MAX_ACTIONS = 20;
54
55
  const MAX_FACTS = 10;
@@ -224,7 +225,7 @@ function write(projectRoot, state) {
224
225
  ''
225
226
  ].filter(line => line !== null).join('\n');
226
227
 
227
- fs.writeFileSync(file, fm + body);
228
+ atomic.writeFileAtomic(file, fm + body);
228
229
  return file;
229
230
  }
230
231
 
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Optional code intelligence capability detection.
3
+ *
4
+ * These tools sharpen refactors and reviews when present, but Godpowers must
5
+ * keep working through grep-backed workflows when they are absent.
6
+ */
7
+
8
+ const cp = require('child_process');
9
+
10
+ const AST_GREP_CANDIDATES = [
11
+ { id: 'ast-grep', command: 'ast-grep', args: ['--version'], match: /ast-grep/i },
12
+ { id: 'sg', command: 'sg', args: ['--version'], match: /\b(ast-grep|sg\s+\d)/i }
13
+ ];
14
+
15
+ const LSP_CANDIDATES = [
16
+ { id: 'omo-lsp', command: 'omo-lsp', args: ['--version'], languages: ['multi'] },
17
+ { id: 'typescript-language-server', command: 'typescript-language-server', args: ['--version'], languages: ['javascript', 'typescript'] },
18
+ { id: 'vscode-json-language-server', command: 'vscode-json-language-server', args: ['--version'], languages: ['json'] },
19
+ { id: 'yaml-language-server', command: 'yaml-language-server', args: ['--version'], languages: ['yaml'] },
20
+ { id: 'pyright-langserver', command: 'pyright-langserver', args: ['--version'], languages: ['python'] },
21
+ { id: 'pylsp', command: 'pylsp', args: ['--version'], languages: ['python'] },
22
+ { id: 'gopls', command: 'gopls', args: ['version'], languages: ['go'] },
23
+ { id: 'rust-analyzer', command: 'rust-analyzer', args: ['--version'], languages: ['rust'] },
24
+ { id: 'clangd', command: 'clangd', args: ['--version'], languages: ['c', 'cpp'] },
25
+ { id: 'jdtls', command: 'jdtls', args: ['--version'], languages: ['java'] },
26
+ { id: 'ruby-lsp', command: 'ruby-lsp', args: ['--version'], languages: ['ruby'] },
27
+ { id: 'solargraph', command: 'solargraph', args: ['--version'], languages: ['ruby'] },
28
+ { id: 'terraform-ls', command: 'terraform-ls', args: ['version'], languages: ['terraform'] }
29
+ ];
30
+
31
+ function firstLine(value) {
32
+ if (!value) return null;
33
+ const text = String(value).trim();
34
+ if (!text) return null;
35
+ return text.split(/\r?\n/)[0] || 'installed';
36
+ }
37
+
38
+ function commandVersion(command, args, opts = {}) {
39
+ try {
40
+ const out = cp.execFileSync(command, args, {
41
+ cwd: opts.cwd || process.cwd(),
42
+ encoding: 'utf8',
43
+ stdio: ['ignore', 'pipe', 'ignore'],
44
+ timeout: opts.timeout || 800
45
+ });
46
+ return firstLine(out) || 'installed';
47
+ } catch (err) {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function checkCandidate(candidate, opts) {
53
+ const version = firstLine(opts.commandVersion(candidate.command, candidate.args, {
54
+ cwd: opts.cwd,
55
+ timeout: opts.timeout
56
+ }));
57
+ if (!version) return null;
58
+ if (candidate.match && !candidate.match.test(version)) return null;
59
+ return {
60
+ id: candidate.id,
61
+ command: candidate.command,
62
+ version,
63
+ languages: candidate.languages || []
64
+ };
65
+ }
66
+
67
+ function detectFirst(candidates, opts) {
68
+ for (const candidate of candidates) {
69
+ const found = checkCandidate(candidate, opts);
70
+ if (found) return found;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ function detectAll(candidates, opts) {
76
+ const found = [];
77
+ const limit = opts.maxTools || 5;
78
+ for (const candidate of candidates) {
79
+ const result = checkCandidate(candidate, opts);
80
+ if (result) found.push(result);
81
+ if (found.length >= limit) break;
82
+ }
83
+ return found;
84
+ }
85
+
86
+ function detect(projectRoot, opts = {}) {
87
+ const root = projectRoot || process.cwd();
88
+ const probe = opts.commandVersion || commandVersion;
89
+ const probeOpts = {
90
+ cwd: root,
91
+ commandVersion: probe,
92
+ timeout: opts.timeout || 800,
93
+ maxTools: opts.maxTools || 5
94
+ };
95
+
96
+ const astGrep = detectFirst(opts.astGrepCandidates || AST_GREP_CANDIDATES, probeOpts);
97
+ const lspTools = detectAll(opts.lspCandidates || LSP_CANDIDATES, probeOpts);
98
+ const gaps = [];
99
+ if (!astGrep) gaps.push('ast-grep not detected');
100
+ if (lspTools.length === 0) gaps.push('LSP tools not detected');
101
+
102
+ return {
103
+ level: astGrep || lspTools.length > 0 ? 'available' : 'not-detected',
104
+ astGrep: astGrep
105
+ ? { available: true, command: astGrep.command, version: astGrep.version }
106
+ : { available: false, command: null, version: null },
107
+ lsp: {
108
+ available: lspTools.length > 0,
109
+ primary: lspTools[0] || null,
110
+ tools: lspTools
111
+ },
112
+ gaps
113
+ };
114
+ }
115
+
116
+ function lspSummary(lsp) {
117
+ if (!lsp || !lsp.available) return 'not detected';
118
+ return lsp.tools.map(tool => tool.command).join(', ');
119
+ }
120
+
121
+ function summary(report) {
122
+ if (!report) return 'not detected';
123
+ const parts = [];
124
+ if (report.astGrep && report.astGrep.available) {
125
+ parts.push(`ast-grep via ${report.astGrep.command}`);
126
+ }
127
+ if (report.lsp && report.lsp.available) {
128
+ parts.push(`LSP via ${lspSummary(report.lsp)}`);
129
+ }
130
+ return parts.length > 0 ? parts.join('; ') : 'not detected';
131
+ }
132
+
133
+ function render(report) {
134
+ const ast = report && report.astGrep && report.astGrep.available
135
+ ? `${report.astGrep.command} (${report.astGrep.version})`
136
+ : 'not detected';
137
+ const lsp = report && report.lsp && report.lsp.available
138
+ ? lspSummary(report.lsp)
139
+ : 'not detected';
140
+ const gaps = report && report.gaps && report.gaps.length > 0
141
+ ? report.gaps.join('; ')
142
+ : 'none';
143
+ return [
144
+ 'Code intelligence:',
145
+ ` Structural search: ${ast}`,
146
+ ` LSP tools: ${lsp}`,
147
+ ` Gaps: ${gaps}`
148
+ ].join('\n');
149
+ }
150
+
151
+ module.exports = {
152
+ detect,
153
+ summary,
154
+ render,
155
+ _private: {
156
+ AST_GREP_CANDIDATES,
157
+ LSP_CANDIDATES,
158
+ commandVersion,
159
+ firstLine
160
+ }
161
+ };
@@ -99,7 +99,7 @@ function buildCanonicalContent(state, opts = {}) {
99
99
  lines.push('- `/god-next` - what to run next, with reason');
100
100
  lines.push('- `/god-mode` - run the full autonomous project run');
101
101
  lines.push('- `/god-sync` - refresh artifacts, context, and source-system sync-back');
102
- lines.push('- `/god-migrate` - import or sync GSD, BMAD, or Superpowers context');
102
+ lines.push('- `/god-migrate` - import or sync legacy planning, BMAD, or Superpowers context');
103
103
  lines.push('- `/god-context refresh` - refresh AI-tool awareness for this project');
104
104
  lines.push('');
105
105