godpowers 3.13.1 → 3.13.2
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 +44 -0
- package/README.md +32 -29
- package/RELEASE.md +18 -19
- package/lib/README.md +1 -0
- package/lib/evidence.js +4 -0
- package/lib/have-nots-validator.js +5 -1
- package/lib/intent.js +9 -4
- package/lib/pillars.js +13 -0
- package/lib/planning-systems.js +1 -6
- package/lib/recipe-coverage-sync.js +3 -12
- package/lib/release-surface-sync.js +3 -12
- package/lib/repo-surface-sync.js +3 -31
- package/lib/requirements.js +4 -12
- package/lib/reverse-sync.js +9 -2
- package/lib/route-quality-sync.js +3 -21
- package/lib/source-sync.js +0 -4
- package/lib/sync-check.js +56 -0
- package/lib/sync-fs.js +13 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.13.2] - 2026-06-17
|
|
11
|
+
|
|
12
|
+
Maintenance release that drives a third self-audit (`codeaudit.md`) to zero: one
|
|
13
|
+
Medium and twelve Low findings across de-duplication, error handling, security
|
|
14
|
+
hardening, the test gate, and docs. No public command/agent/workflow/recipe
|
|
15
|
+
surface change (counts stay 120 / 40 / 13 / 44); lib module count 90 -> 91.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **Shared sync check-builder (ARC-001):** the four `*-sync` modules no longer
|
|
19
|
+
copy-paste `addCheck`/`listFiles`; they share `lib/sync-check.js` (full
|
|
20
|
+
`addCheck` for the aggregator, area-bound `makeAddCheck` for the rest).
|
|
21
|
+
- **Per-file coverage floor (TEST-001):** `coverage:lib` now emits a json-summary
|
|
22
|
+
and `scripts/check-per-file-coverage.js` (in `release:check`) fails any lib
|
|
23
|
+
module below 70% lines (excluding the two environment-bound browser drivers),
|
|
24
|
+
so a single file can no longer rot while the aggregate stays green.
|
|
25
|
+
- **De-duplication and cleanup (QUAL-001/002/003):** removed dead helpers
|
|
26
|
+
(two unused `rel()`, an unused `sha`), added `sync-fs.readTextOrNull` adopted by
|
|
27
|
+
`requirements.js` (which now sources PRD/ROADMAP paths from `artifact-map`), and
|
|
28
|
+
fixed a boolean/string status wart in `repo-surface-sync`.
|
|
29
|
+
- **Pillars delineation (ARC-002):** `pillars.js` now has section dividers
|
|
30
|
+
separating the model and artifact-sync halves (a full split was deferred; the
|
|
31
|
+
halves share construction functions that are public API).
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **Reverse-sync error visibility (ERR-001):** the requirements step now writes
|
|
35
|
+
state before the ledger and surfaces a caught error as `requirementsError`
|
|
36
|
+
instead of silently nulling it.
|
|
37
|
+
|
|
38
|
+
### Security
|
|
39
|
+
- **MCP module-name guard (SEC-001):** `requireRuntime` rejects any name that is
|
|
40
|
+
not a plain lib basename (defense-in-depth).
|
|
41
|
+
- **YAML recursion cap (SEC-002):** `intent.cleanArrays` caps recursion depth so a
|
|
42
|
+
pathologically deep file cannot overflow the stack.
|
|
43
|
+
|
|
44
|
+
### Performance
|
|
45
|
+
- **have-nots regex (PERF-001):** `findPositions` compiles its regex once instead
|
|
46
|
+
of per line. The whole-ledger read in `evidence.readJsonl` is documented as
|
|
47
|
+
bounded/acceptable with an opt-in prune noted for the future (PERF-002).
|
|
48
|
+
|
|
49
|
+
### Docs
|
|
50
|
+
- **Absolute README doc links (DOC-001):** `docs/` is deliberately excluded from
|
|
51
|
+
the package, so the README's `docs/*` links are now absolute GitHub URLs that
|
|
52
|
+
resolve on the npm page and in the tarball.
|
|
53
|
+
|
|
10
54
|
## [3.13.1] - 2026-06-16
|
|
11
55
|
|
|
12
56
|
Maintenance release that drives a full self-audit (`codeaudit.md`) to zero: one
|
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.**
|
|
@@ -12,30 +12,33 @@ idea to hardened production. It runs as **slash commands inside your AI coding
|
|
|
12
12
|
tool** (Claude Code, Codex, Cursor, etc.) that orchestrate **specialist agents**
|
|
13
13
|
in fresh contexts to do the work.
|
|
14
14
|
|
|
15
|
-
Want the short proof first? Start with [Quick Proof](docs/quick-proof.md) to
|
|
15
|
+
Want the short proof first? Start with [Quick Proof](https://github.com/aihxp/godpowers/blob/main/docs/quick-proof.md) to
|
|
16
16
|
run `npx godpowers quick-proof --project=. --brief`, see outcome metrics, pick
|
|
17
17
|
a starter command set, and understand runtime expectations before reading the
|
|
18
|
-
full reference. The [First 10 Minute Proof Case Study](docs/case-studies/first-10-minute-proof.md)
|
|
18
|
+
full reference. The [First 10 Minute Proof Case Study](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/first-10-minute-proof.md)
|
|
19
19
|
shows the same evidence as a before-and-after adoption story. External
|
|
20
|
-
CLI-verifiable canaries now cover [sindresorhus/is](docs/case-studies/sindresorhus-is-adoption-canary.md),
|
|
21
|
-
[expressjs/cors](docs/case-studies/expressjs-cors-adoption-canary.md), and
|
|
22
|
-
[tinyhttp/tinyhttp](docs/case-studies/tinyhttp-adoption-canary.md), with host
|
|
20
|
+
CLI-verifiable canaries now cover [sindresorhus/is](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/sindresorhus-is-adoption-canary.md),
|
|
21
|
+
[expressjs/cors](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/expressjs-cors-adoption-canary.md), and
|
|
22
|
+
[tinyhttp/tinyhttp](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/tinyhttp-adoption-canary.md), with host
|
|
23
23
|
slash-command gaps called out rather than hidden.
|
|
24
|
-
Host-run proof studies now cover [slugify-cli](docs/case-studies/run-a.md),
|
|
25
|
-
[Countdown](docs/case-studies/run-b.md), and
|
|
26
|
-
[react-github-readme-button](docs/case-studies/run-c.md), including one
|
|
24
|
+
Host-run proof studies now cover [slugify-cli](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/run-a.md),
|
|
25
|
+
[Countdown](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/run-b.md), and
|
|
26
|
+
[react-github-readme-button](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/run-c.md), including one
|
|
27
27
|
blocked harden run recorded as evidence instead of hidden as success.
|
|
28
28
|
|
|
29
29
|
Godpowers makes AI coding accountable: every serious run should leave disk
|
|
30
30
|
state, artifacts, validation gates, host guarantees, and a next action. Code is
|
|
31
31
|
only one output. The project memory and proof trail matter too.
|
|
32
32
|
|
|
33
|
-
Version 3.13.
|
|
34
|
-
the
|
|
35
|
-
the
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
Version 3.13.2 is a maintenance release that drives a third self-audit to zero:
|
|
34
|
+
the `*-sync` modules now share one check-builder, the coverage gate enforces a
|
|
35
|
+
per-file floor, the corrupt-state error is typed, dead helpers are removed, and
|
|
36
|
+
the MCP module loader and YAML parser gain defense-in-depth guards. Version
|
|
37
|
+
3.13.1 drove a full self-audit to zero: the runtime ledger no longer loses
|
|
38
|
+
verification records under concurrent writes, the `outcome check` verifier and
|
|
39
|
+
the pre-tool-use hook are hardened, the `*-sync` modules share one filesystem
|
|
40
|
+
helper, the argument parser is table-driven, and branch coverage is gated.
|
|
41
|
+
Version 3.13.0 makes the default greenfield arc (`/god-mode`) miss
|
|
39
42
|
less. The one-shot `full-arc` workflow now runs a whole-codebase code audit after the
|
|
40
43
|
build (so it catches what the per-slice reviews missed in AI-generated code) and
|
|
41
44
|
a documentation pass after harden (so the shipped product has docs verified
|
|
@@ -183,7 +186,7 @@ instead of pretending a background agent ran.
|
|
|
183
186
|
| Other install targets | Skills and agent contracts install, while host-native spawning depends on the tool. |
|
|
184
187
|
| Degraded hosts | Godpowers must report local-only or simulated agent behavior instead of hiding the limitation. |
|
|
185
188
|
|
|
186
|
-
See [Host capabilities](docs/host-capabilities.md) for the detailed guarantee
|
|
189
|
+
See [Host capabilities](https://github.com/aihxp/godpowers/blob/main/docs/host-capabilities.md) for the detailed guarantee
|
|
187
190
|
model.
|
|
188
191
|
|
|
189
192
|
## Usage
|
|
@@ -334,17 +337,17 @@ dependency to the main `godpowers` package:
|
|
|
334
337
|
|
|
335
338
|
```bash
|
|
336
339
|
npx godpowers mcp-info --project=.
|
|
337
|
-
npx -y -p godpowers@3.13.
|
|
340
|
+
npx -y -p godpowers@3.13.2 -p @godpowers/mcp@3.13.2 godpowers-mcp serve --project=.
|
|
338
341
|
```
|
|
339
342
|
|
|
340
343
|
The companion exposes `status`, `next`, `gate_check`, `lint_artifact`, and
|
|
341
344
|
`trace_requirement`. Host registration is opt-in:
|
|
342
345
|
|
|
343
346
|
```bash
|
|
344
|
-
npx -y -p godpowers@3.13.
|
|
347
|
+
npx -y -p godpowers@3.13.2 -p @godpowers/mcp@3.13.2 godpowers-mcp setup --host=codex --project=. --write
|
|
345
348
|
```
|
|
346
349
|
|
|
347
|
-
See [MCP Companion](docs/mcp.md) for package boundaries and setup details.
|
|
350
|
+
See [MCP Companion](https://github.com/aihxp/godpowers/blob/main/docs/mcp.md) for package boundaries and setup details.
|
|
348
351
|
|
|
349
352
|
### Slash Commands
|
|
350
353
|
|
|
@@ -618,16 +621,16 @@ Pi. T3 Code inherits from the underlying agent (Codex / Claude / OpenCode).
|
|
|
618
621
|
|
|
619
622
|
## Full reference
|
|
620
623
|
|
|
621
|
-
- [Getting Started](docs/getting-started.md)
|
|
622
|
-
- [Quick Proof](docs/quick-proof.md)
|
|
623
|
-
- [First 10 Minute Proof Case Study](docs/case-studies/first-10-minute-proof.md)
|
|
624
|
-
- [Concepts](docs/concepts.md)
|
|
625
|
-
- [Command reference (all 120 skills + 40 agents)](docs/reference.md)
|
|
626
|
-
- [Feature awareness](docs/feature-awareness.md)
|
|
627
|
-
- [Adoption Canary](docs/adoption-canary.md)
|
|
628
|
-
- [Repository documentation sync](docs/repo-doc-sync.md)
|
|
629
|
-
- [Repository surface sync](docs/repo-surface-sync.md)
|
|
630
|
-
- [Roadmap](docs/ROADMAP.md)
|
|
624
|
+
- [Getting Started](https://github.com/aihxp/godpowers/blob/main/docs/getting-started.md)
|
|
625
|
+
- [Quick Proof](https://github.com/aihxp/godpowers/blob/main/docs/quick-proof.md)
|
|
626
|
+
- [First 10 Minute Proof Case Study](https://github.com/aihxp/godpowers/blob/main/docs/case-studies/first-10-minute-proof.md)
|
|
627
|
+
- [Concepts](https://github.com/aihxp/godpowers/blob/main/docs/concepts.md)
|
|
628
|
+
- [Command reference (all 120 skills + 40 agents)](https://github.com/aihxp/godpowers/blob/main/docs/reference.md)
|
|
629
|
+
- [Feature awareness](https://github.com/aihxp/godpowers/blob/main/docs/feature-awareness.md)
|
|
630
|
+
- [Adoption Canary](https://github.com/aihxp/godpowers/blob/main/docs/adoption-canary.md)
|
|
631
|
+
- [Repository documentation sync](https://github.com/aihxp/godpowers/blob/main/docs/repo-doc-sync.md)
|
|
632
|
+
- [Repository surface sync](https://github.com/aihxp/godpowers/blob/main/docs/repo-surface-sync.md)
|
|
633
|
+
- [Roadmap](https://github.com/aihxp/godpowers/blob/main/docs/ROADMAP.md)
|
|
631
634
|
- [Release Notes](RELEASE.md)
|
|
632
635
|
- [Changelog](CHANGELOG.md)
|
|
633
636
|
- [Inspiration](INSPIRATION.md)
|
package/RELEASE.md
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
|
-
# Godpowers 3.13.
|
|
1
|
+
# Godpowers 3.13.2 Release
|
|
2
2
|
|
|
3
3
|
> Status: Prepared
|
|
4
|
-
> Date: 2026-06-
|
|
4
|
+
> Date: 2026-06-17
|
|
5
5
|
|
|
6
|
-
[DECISION] Godpowers 3.13.
|
|
7
|
-
[DECISION] No new skill, agent, workflow, or recipe surface is added or removed. Surface counts are unchanged from 3.13.
|
|
8
|
-
[DECISION] This release keeps `core` as the omitted installer profile, keeps `--profile=full` as the complete compatibility surface, and keeps the full 3.1.0-3.13.
|
|
6
|
+
[DECISION] Godpowers 3.13.2 is a maintenance release that drives a third self-audit (`codeaudit.md`, codeauditor-grade, nine weighted dimensions) to zero. It fixes one Medium finding and twelve Low findings across de-duplication, error handling, security hardening, the test gate, and documentation.
|
|
7
|
+
[DECISION] No new skill, agent, workflow, or recipe surface is added or removed. Surface counts are unchanged from 3.13.1: 120 slash commands, 40 specialist agents, 13 workflows, 44 recipes. The lib module count rises from 90 to 91 (`lib/sync-check.js`).
|
|
8
|
+
[DECISION] This release keeps `core` as the omitted installer profile, keeps `--profile=full` as the complete compatibility surface, and keeps the full 3.1.0-3.13.1 surface.
|
|
9
9
|
|
|
10
10
|
## What's in this release
|
|
11
11
|
|
|
12
|
-
- [DECISION]
|
|
13
|
-
- [DECISION]
|
|
14
|
-
- [DECISION]
|
|
15
|
-
- [DECISION]
|
|
16
|
-
- [DECISION]
|
|
17
|
-
- [DECISION] Re-audit follow-ups: a fresh self-audit confirmed no regressions and closed the residual gaps it found - `installer-core.js` imports the shared logger (QUAL-003); `dashboard.js`/`planning-systems.js` consume `sync-fs` (ARC-003); the `lib/README` module catalog is complete and now guarded by a completeness check (DOC-004); the ledger-append comment is corrected (DOC-005); the corrupt-state error is typed rather than message-matched (ERR-004); and the hook tests assert each warning's text (TEST-005).
|
|
12
|
+
- [DECISION] De-duplication (ARC-001, QUAL-001/002/003): the four `*-sync` modules share `lib/sync-check.js` (`addCheck`/`makeAddCheck`/`listFiles`) instead of copy-pasting them; removed dead helpers (two unused `rel()`, an unused `sha`); added `sync-fs.readTextOrNull` adopted by `requirements.js`, which now sources PRD/ROADMAP paths from `artifact-map`; fixed a boolean/string status wart in `repo-surface-sync`.
|
|
13
|
+
- [DECISION] Test gate (TEST-001, TEST-002): `coverage:lib` now emits a json-summary and `scripts/check-per-file-coverage.js` (in `release:check`) fails any lib module below 70% lines, excluding the two environment-bound browser drivers, so a single file can no longer rot while the aggregate stays green; the `run()`/`appendLog()` write path of the three sync siblings is now tested for the no-banned-dash invariant.
|
|
14
|
+
- [DECISION] Error handling (ERR-001): reverse-sync writes state before the ledger and surfaces a caught error as `requirementsError` instead of silently nulling it.
|
|
15
|
+
- [DECISION] Security hardening (SEC-001, SEC-002): the MCP `requireRuntime` rejects any module name that is not a plain lib basename; `intent.cleanArrays` caps recursion depth so a pathologically deep YAML cannot overflow the stack.
|
|
16
|
+
- [DECISION] Performance and docs (PERF-001/002, DOC-001, ARC-002): `have-nots` `findPositions` compiles its regex once per call; the bounded whole-ledger read is documented with an opt-in prune noted; the README's `docs/*` links are now absolute GitHub URLs (docs are deliberately excluded from the package); and `pillars.js` is delineated into its model and artifact-sync halves (a full split was deferred because the halves share public-API construction functions).
|
|
18
17
|
|
|
19
18
|
## Changes
|
|
20
19
|
|
|
21
|
-
- [DECISION] `package.json`, `package-lock.json`, and `packages/mcp/package.json` now publish the 3.13.
|
|
22
|
-
- [DECISION] New runtime
|
|
23
|
-
- [DECISION] CHANGELOG, RELEASE notes, README, roadmap, reference, architecture, and the architecture map now reflect 3.13.
|
|
20
|
+
- [DECISION] `package.json`, `package-lock.json`, and `packages/mcp/package.json` now publish the 3.13.2 version.
|
|
21
|
+
- [DECISION] New runtime module `lib/sync-check.js` (lib module count 90 -> 91). No public command/agent/workflow/recipe surface change.
|
|
22
|
+
- [DECISION] CHANGELOG, RELEASE notes, README, roadmap, reference, architecture, and the architecture map now reflect 3.13.2. The SECURITY supported-version table already carries the `3.13.x` row.
|
|
24
23
|
|
|
25
24
|
## Validation
|
|
26
25
|
|
|
27
26
|
- [DECISION] `npm test` passed all command groups.
|
|
28
|
-
- [DECISION] `npm run release:check` passed `coverage:lib` above the 90 percent line floor and the
|
|
27
|
+
- [DECISION] `npm run release:check` passed `coverage:lib` above the 90 percent line floor and the 75 percent branch floor, and the new per-file floor (>= 70 percent lines across 88 lib modules).
|
|
29
28
|
- [DECISION] `npm run release:check` passed `npm audit --omit=dev` with 0 vulnerabilities and `git diff --check`.
|
|
30
|
-
- [DECISION] `npm run release:check` passed public surface docs for version 3.13.
|
|
29
|
+
- [DECISION] `npm run release:check` passed public surface docs for version 3.13.2 with 120 skills, 40 agents, 13 workflows, and 44 recipes.
|
|
31
30
|
- [DECISION] `npm run release:check` passed root and `@godpowers/mcp` package contents.
|
|
32
31
|
|
|
33
32
|
## Upgrade
|
|
34
33
|
|
|
35
|
-
- [DECISION] Use `npm install -g godpowers@3.13.
|
|
36
|
-
- [DECISION] No migration is required.
|
|
34
|
+
- [DECISION] Use `npm install -g godpowers@3.13.2` or `npx godpowers@3.13.2`.
|
|
35
|
+
- [DECISION] No migration is required. The changes are internal de-duplication, error-visibility, security, test-gate, and documentation improvements with no surface change.
|
|
37
36
|
|
|
38
37
|
## Notes
|
|
39
38
|
|
|
40
|
-
- [DECISION] The publish targets are npm `godpowers@3.13.
|
|
39
|
+
- [DECISION] The publish targets are npm `godpowers@3.13.2`, npm `@godpowers/mcp@3.13.2`, and GitHub release `https://github.com/aihxp/godpowers/releases/tag/v3.13.2`.
|
|
41
40
|
- [DECISION] The tag-triggered GitHub publish workflow remains the preferred npm path because it publishes with provenance. This release has not been tagged or published to npm yet.
|
package/lib/README.md
CHANGED
|
@@ -29,6 +29,7 @@ package-level integrations.
|
|
|
29
29
|
| `atomic-write.js` | Write load-bearing files through temp-file validation and atomic rename. |
|
|
30
30
|
| `fs-async.js` | Promise-based file read/write helpers for non-blocking runtime paths. |
|
|
31
31
|
| `sync-fs.js` | Shared project-relative read/write/exists/readJson helpers for the `*-sync` modules. |
|
|
32
|
+
| `sync-check.js` | Shared check-builder (`addCheck`/`makeAddCheck`) and file-lister for the `*-sync` modules. |
|
|
32
33
|
|
|
33
34
|
## Events and observability
|
|
34
35
|
|
package/lib/evidence.js
CHANGED
|
@@ -195,6 +195,10 @@ function appendJsonlAtomic(file, record) {
|
|
|
195
195
|
return file;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// Reads the whole ledger into memory. This is bounded and acceptable for a CLI:
|
|
199
|
+
// each record caps its stdout/stderr tails (TAIL_CHARS) so growth is slow, and
|
|
200
|
+
// every consumer reads it once per command, never in a loop. If a long-lived
|
|
201
|
+
// project's ledger ever grows large, add an opt-in prune/size cap here (PERF-002).
|
|
198
202
|
function readJsonl(file) {
|
|
199
203
|
let raw;
|
|
200
204
|
try {
|
|
@@ -53,9 +53,13 @@ const LABEL_TAGS = ['DECISION', 'HYPOTHESIS', 'OPEN QUESTION', 'OPEN-QUESTION'];
|
|
|
53
53
|
function findPositions(content, regex) {
|
|
54
54
|
const positions = [];
|
|
55
55
|
const lines = content.split('\n');
|
|
56
|
+
// Compile once and reuse across lines (PERF-001); reset lastIndex per line so
|
|
57
|
+
// matching is identical to a fresh per-line regex.
|
|
58
|
+
const flags = regex.flags.includes('g') ? regex.flags : regex.flags + 'g';
|
|
59
|
+
const localRegex = new RegExp(regex.source, flags);
|
|
56
60
|
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
localRegex.lastIndex = 0;
|
|
57
62
|
let match;
|
|
58
|
-
const localRegex = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g');
|
|
59
63
|
while ((match = localRegex.exec(lines[i])) !== null) {
|
|
60
64
|
positions.push({ line: i + 1, column: match.index + 1, matched: match[0] });
|
|
61
65
|
}
|
package/lib/intent.js
CHANGED
|
@@ -332,15 +332,20 @@ function splitInlineArray(text) {
|
|
|
332
332
|
return parts;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
// Far beyond any legitimate config nesting; caps recursion so a hostile,
|
|
336
|
+
// thousands-deep YAML file cannot overflow the stack (SEC-002).
|
|
337
|
+
const MAX_CLEAN_DEPTH = 200;
|
|
338
|
+
|
|
339
|
+
function cleanArrays(obj, depth = 0) {
|
|
340
|
+
if (depth > MAX_CLEAN_DEPTH) return obj;
|
|
341
|
+
if (Array.isArray(obj)) return obj.map((v) => cleanArrays(v, depth + 1));
|
|
337
342
|
if (obj && typeof obj === 'object') {
|
|
338
343
|
// Detect array container (legacy or new)
|
|
339
|
-
if (obj.__items__) return obj.__items__.map(cleanArrays);
|
|
344
|
+
if (obj.__items__) return obj.__items__.map((v) => cleanArrays(v, depth + 1));
|
|
340
345
|
if (obj.__pending_array__) return obj.__pending_array__;
|
|
341
346
|
const cleaned = {};
|
|
342
347
|
for (const [k, v] of Object.entries(obj)) {
|
|
343
|
-
cleaned[k] = cleanArrays(v);
|
|
348
|
+
cleaned[k] = cleanArrays(v, depth + 1);
|
|
344
349
|
}
|
|
345
350
|
return cleaned;
|
|
346
351
|
}
|
package/lib/pillars.js
CHANGED
|
@@ -114,6 +114,13 @@ const GODPOWERS_ARTIFACTS = [
|
|
|
114
114
|
'.godpowers/design/PRODUCT.md'
|
|
115
115
|
];
|
|
116
116
|
|
|
117
|
+
// ===========================================================================
|
|
118
|
+
// Pillar model: parse pillar files, detect installed pillars, compute the
|
|
119
|
+
// per-task load set, and construct/initialize pillar files. Shared with the
|
|
120
|
+
// artifact-sync workflow below (init/ensurePillar/pillarStub/detect are also
|
|
121
|
+
// part of the public API).
|
|
122
|
+
// ===========================================================================
|
|
123
|
+
|
|
117
124
|
function stripQuotes(value) {
|
|
118
125
|
return String(value).trim().replace(/^['"]|['"]$/g, '');
|
|
119
126
|
}
|
|
@@ -401,6 +408,12 @@ function writeFenced(filePath, begin, end, content) {
|
|
|
401
408
|
fs.writeFileSync(filePath, next);
|
|
402
409
|
}
|
|
403
410
|
|
|
411
|
+
// ===========================================================================
|
|
412
|
+
// Artifact-sync workflow: turn Godpowers artifacts (PRD/ARCH/...) into durable
|
|
413
|
+
// pillar signals and write them into the routed pillar files. Builds on the
|
|
414
|
+
// model above (init/ensurePillar/pillarStub/detect/buildProtocolContent).
|
|
415
|
+
// ===========================================================================
|
|
416
|
+
|
|
404
417
|
function artifactToPillars(artifactPath) {
|
|
405
418
|
const normalized = artifactPath.replace(/\\/g, '/');
|
|
406
419
|
const pillars = [];
|
package/lib/planning-systems.js
CHANGED
|
@@ -100,10 +100,6 @@ function ensureDir(filePath) {
|
|
|
100
100
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function sha(input) {
|
|
104
|
-
return crypto.createHash('sha256').update(input).digest('hex');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
103
|
function hashFiles(projectRoot, files) {
|
|
108
104
|
const h = crypto.createHash('sha256');
|
|
109
105
|
for (const file of files.map((f) => f.path).sort()) {
|
|
@@ -473,7 +469,6 @@ module.exports = {
|
|
|
473
469
|
_private: {
|
|
474
470
|
classifyFile,
|
|
475
471
|
extractSignals,
|
|
476
|
-
filesForKinds
|
|
477
|
-
sha
|
|
472
|
+
filesForKinds
|
|
478
473
|
}
|
|
479
474
|
};
|
|
@@ -10,6 +10,9 @@ const path = require('path');
|
|
|
10
10
|
|
|
11
11
|
const recipes = require('./recipes');
|
|
12
12
|
const { read, write } = require('./sync-fs');
|
|
13
|
+
const { makeAddCheck } = require('./sync-check');
|
|
14
|
+
|
|
15
|
+
const addCheck = makeAddCheck('recipe-coverage');
|
|
13
16
|
|
|
14
17
|
const LOG_PATH = '.godpowers/surface/RECIPE-COVERAGE-SYNC.md';
|
|
15
18
|
|
|
@@ -42,18 +45,6 @@ const REQUIRED_COVERAGE = [
|
|
|
42
45
|
];
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
46
|
-
checks.push({
|
|
47
|
-
area: 'recipe-coverage',
|
|
48
|
-
id,
|
|
49
|
-
status,
|
|
50
|
-
path: relPath,
|
|
51
|
-
message,
|
|
52
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
53
|
-
spawn: opts.spawn || null
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
48
|
function recipePath(projectRoot, name) {
|
|
58
49
|
const rel = `routing/recipes/${name}.yaml`;
|
|
59
50
|
return fs.existsSync(path.join(projectRoot, rel)) ? rel : 'routing/recipes/';
|
|
@@ -10,6 +10,9 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
12
|
const { read, write, readJson } = require('./sync-fs');
|
|
13
|
+
const { makeAddCheck } = require('./sync-check');
|
|
14
|
+
|
|
15
|
+
const addCheck = makeAddCheck('release-surface');
|
|
13
16
|
|
|
14
17
|
const LOG_PATH = '.godpowers/surface/RELEASE-SURFACE-SYNC.md';
|
|
15
18
|
|
|
@@ -46,18 +49,6 @@ function releaseGateText(projectRoot, pkg) {
|
|
|
46
49
|
].join('\n');
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
50
|
-
checks.push({
|
|
51
|
-
area: 'release-surface',
|
|
52
|
-
id,
|
|
53
|
-
status,
|
|
54
|
-
path: relPath,
|
|
55
|
-
message,
|
|
56
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
57
|
-
spawn: opts.spawn || null
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
52
|
function detect(projectRoot) {
|
|
62
53
|
const checks = [];
|
|
63
54
|
const pkg = readJson(projectRoot, 'package.json') || {};
|
package/lib/repo-surface-sync.js
CHANGED
|
@@ -12,6 +12,7 @@ const path = require('path');
|
|
|
12
12
|
|
|
13
13
|
const { parseSimpleYaml } = require('./intent');
|
|
14
14
|
const { read, write, exists, readJson } = require('./sync-fs');
|
|
15
|
+
const { addCheck, listFiles } = require('./sync-check');
|
|
15
16
|
const extensions = require('./extensions');
|
|
16
17
|
const repoDocSync = require('./repo-doc-sync');
|
|
17
18
|
const routeQualitySync = require('./route-quality-sync');
|
|
@@ -52,19 +53,6 @@ const REQUIRED_PACKAGE_CHECKS = [
|
|
|
52
53
|
'routing/god-export-otel.yaml'
|
|
53
54
|
];
|
|
54
55
|
|
|
55
|
-
function rel(projectRoot, absPath) {
|
|
56
|
-
return path.relative(projectRoot, absPath).split(path.sep).join('/');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function listFiles(projectRoot, relDir, pattern) {
|
|
60
|
-
const dir = path.join(projectRoot, relDir);
|
|
61
|
-
if (!fs.existsSync(dir)) return [];
|
|
62
|
-
return fs.readdirSync(dir)
|
|
63
|
-
.filter((name) => pattern.test(name))
|
|
64
|
-
.sort()
|
|
65
|
-
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
56
|
function releaseGateText(projectRoot, pkg) {
|
|
69
57
|
return [
|
|
70
58
|
JSON.stringify((pkg && pkg.scripts) || {}),
|
|
@@ -81,19 +69,6 @@ function commandForSkill(skillPath) {
|
|
|
81
69
|
return `/${path.basename(skillPath, '.md')}`;
|
|
82
70
|
}
|
|
83
71
|
|
|
84
|
-
function addCheck(checks, area, id, status, relPath, message, opts = {}) {
|
|
85
|
-
checks.push({
|
|
86
|
-
area,
|
|
87
|
-
id,
|
|
88
|
-
status,
|
|
89
|
-
path: relPath,
|
|
90
|
-
message,
|
|
91
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
92
|
-
safeFix: opts.safeFix === true,
|
|
93
|
-
spawn: opts.spawn || null
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
72
|
function routingChecks(projectRoot) {
|
|
98
73
|
const checks = [];
|
|
99
74
|
const skills = listFiles(projectRoot, 'skills', /^god.*\.md$/);
|
|
@@ -485,15 +460,12 @@ function releasePolicyChecks(projectRoot) {
|
|
|
485
460
|
checks,
|
|
486
461
|
'release',
|
|
487
462
|
'release-checklist-surface-sync',
|
|
488
|
-
read(projectRoot, 'docs/RELEASE-CHECKLIST.md').includes('repo-surface-sync'),
|
|
463
|
+
read(projectRoot, 'docs/RELEASE-CHECKLIST.md').includes('repo-surface-sync') ? 'fresh' : 'stale',
|
|
489
464
|
'docs/RELEASE-CHECKLIST.md',
|
|
490
465
|
'Release checklist references repo-surface-sync readiness.',
|
|
491
466
|
{ spawn: 'god-docs-writer' }
|
|
492
467
|
);
|
|
493
|
-
return checks
|
|
494
|
-
...check,
|
|
495
|
-
status: check.status === true ? 'fresh' : (check.status === false ? 'stale' : check.status)
|
|
496
|
-
}));
|
|
468
|
+
return checks;
|
|
497
469
|
}
|
|
498
470
|
|
|
499
471
|
function detect(projectRoot) {
|
package/lib/requirements.js
CHANGED
|
@@ -33,9 +33,11 @@ const linkage = require('./linkage');
|
|
|
33
33
|
const state = require('./state');
|
|
34
34
|
const atomic = require('./atomic-write');
|
|
35
35
|
const textUtil = require('./text-util');
|
|
36
|
+
const artifactMap = require('./artifact-map');
|
|
37
|
+
const { readTextOrNull: readText } = require('./sync-fs');
|
|
36
38
|
|
|
37
|
-
const PRD_PATH = '
|
|
38
|
-
const ROADMAP_PATH = '
|
|
39
|
+
const PRD_PATH = artifactMap.requiredArtifactsForTier('prd')[0].path;
|
|
40
|
+
const ROADMAP_PATH = artifactMap.requiredArtifactsForTier('roadmap')[0].path;
|
|
39
41
|
const LEDGER_PATH = '.godpowers/REQUIREMENTS.md';
|
|
40
42
|
|
|
41
43
|
const PRIORITIES = ['MUST', 'SHOULD', 'COULD'];
|
|
@@ -44,16 +46,6 @@ const REQ_ID_RE_G = /\bP-(MUST|SHOULD|COULD)-\d+\b/g;
|
|
|
44
46
|
const MILESTONE_ID_RE = /\bM-[\w-]+\b/;
|
|
45
47
|
const LABEL_RE = /\[(?:DECISION|HYPOTHESIS|OPEN QUESTION)\]/g;
|
|
46
48
|
|
|
47
|
-
function readText(projectRoot, relPath) {
|
|
48
|
-
const file = path.join(projectRoot, relPath);
|
|
49
|
-
if (!fs.existsSync(file)) return null;
|
|
50
|
-
try {
|
|
51
|
-
return fs.readFileSync(file, 'utf8');
|
|
52
|
-
} catch (e) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
49
|
function pad2(n) {
|
|
58
50
|
return String(n).padStart(2, '0');
|
|
59
51
|
}
|
package/lib/reverse-sync.js
CHANGED
|
@@ -318,23 +318,29 @@ function run(projectRoot, opts = {}) {
|
|
|
318
318
|
// write the file when the PRD actually declares requirements, to avoid
|
|
319
319
|
// littering pre-PRD projects with an empty ledger.
|
|
320
320
|
let requirementsSummary = null;
|
|
321
|
+
let requirementsError = null;
|
|
321
322
|
if (opts.runRequirements !== false) {
|
|
322
323
|
try {
|
|
323
324
|
const derived = requirements.derive(projectRoot);
|
|
324
325
|
if (derived.hasRequirements) {
|
|
325
|
-
requirements.writeLedger(projectRoot, derived);
|
|
326
326
|
const currentState = state.read(projectRoot);
|
|
327
327
|
requirementsSummary = requirements.summarizeForState(
|
|
328
328
|
derived,
|
|
329
329
|
currentState && currentState.deliverables
|
|
330
330
|
);
|
|
331
|
+
// Write state.json first, then the ledger, so a state-write failure
|
|
332
|
+
// cannot leave a REQUIREMENTS.md ledger that state.json never references.
|
|
331
333
|
if (currentState) {
|
|
332
334
|
currentState.deliverables = requirementsSummary;
|
|
333
335
|
state.write(projectRoot, currentState);
|
|
334
336
|
}
|
|
337
|
+
requirements.writeLedger(projectRoot, derived);
|
|
335
338
|
}
|
|
336
339
|
} catch (e) {
|
|
340
|
+
// ERR-001: surface the failure instead of swallowing it, so the caller can
|
|
341
|
+
// tell a genuine requirements-step error apart from "no requirements".
|
|
337
342
|
requirementsSummary = null;
|
|
343
|
+
requirementsError = e.message;
|
|
338
344
|
}
|
|
339
345
|
}
|
|
340
346
|
|
|
@@ -346,7 +352,8 @@ function run(projectRoot, opts = {}) {
|
|
|
346
352
|
footers,
|
|
347
353
|
sourceSyncResult,
|
|
348
354
|
reviewItems,
|
|
349
|
-
requirements: requirementsSummary
|
|
355
|
+
requirements: requirementsSummary,
|
|
356
|
+
requirementsError
|
|
350
357
|
};
|
|
351
358
|
}
|
|
352
359
|
|
|
@@ -11,6 +11,9 @@ const path = require('path');
|
|
|
11
11
|
|
|
12
12
|
const { parseSimpleYaml } = require('./intent');
|
|
13
13
|
const { read, write } = require('./sync-fs');
|
|
14
|
+
const { makeAddCheck, listFiles } = require('./sync-check');
|
|
15
|
+
|
|
16
|
+
const addCheck = makeAddCheck('route-quality');
|
|
14
17
|
|
|
15
18
|
const LOG_PATH = '.godpowers/surface/ROUTE-QUALITY-SYNC.md';
|
|
16
19
|
const CONTEXTUAL_NEXT_VALUES = new Set([
|
|
@@ -101,15 +104,6 @@ const TIER_GATE_COMMANDS = new Set([
|
|
|
101
104
|
'/god-harden'
|
|
102
105
|
]);
|
|
103
106
|
|
|
104
|
-
function listFiles(projectRoot, relDir, pattern) {
|
|
105
|
-
const dir = path.join(projectRoot, relDir);
|
|
106
|
-
if (!fs.existsSync(dir)) return [];
|
|
107
|
-
return fs.readdirSync(dir)
|
|
108
|
-
.filter((name) => pattern.test(name))
|
|
109
|
-
.sort()
|
|
110
|
-
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
107
|
function arr(value) {
|
|
114
108
|
return Array.isArray(value) ? value : [];
|
|
115
109
|
}
|
|
@@ -122,18 +116,6 @@ function parseRoute(projectRoot, routePath) {
|
|
|
122
116
|
}
|
|
123
117
|
}
|
|
124
118
|
|
|
125
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
126
|
-
checks.push({
|
|
127
|
-
area: 'route-quality',
|
|
128
|
-
id,
|
|
129
|
-
status,
|
|
130
|
-
path: relPath,
|
|
131
|
-
message,
|
|
132
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
133
|
-
spawn: opts.spawn || null
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
119
|
function spawnTokens(route) {
|
|
138
120
|
const execution = route.execution || {};
|
|
139
121
|
return normalizeSpawnList([
|
package/lib/source-sync.js
CHANGED
|
@@ -30,10 +30,6 @@ const SYSTEM_TARGETS = {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
function rel(projectRoot, absPath) {
|
|
34
|
-
return path.relative(projectRoot, absPath).split(path.sep).join('/');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
33
|
function sha(input) {
|
|
38
34
|
return `sha256:${crypto.createHash('sha256').update(input).digest('hex')}`;
|
|
39
35
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared check-builder and file-lister for the lib/*-sync.js family (ARC-001).
|
|
3
|
+
*
|
|
4
|
+
* The aggregator (repo-surface-sync) passes a per-call `area` and may mark a
|
|
5
|
+
* check `safeFix`, so it uses the full `addCheck`. The single-area sync modules
|
|
6
|
+
* (recipe-coverage, release-surface, route-quality) bind their area once via
|
|
7
|
+
* `makeAddCheck(area)`; their records intentionally omit `safeFix` (none of
|
|
8
|
+
* their checks are auto-fixable), matching the original per-module builders.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
function severityFor(status, opts) {
|
|
15
|
+
return opts.severity || (status === 'fresh' ? 'info' : 'warning');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Full form: caller supplies `area`; records include the `safeFix` flag.
|
|
19
|
+
function addCheck(checks, area, id, status, relPath, message, opts = {}) {
|
|
20
|
+
checks.push({
|
|
21
|
+
area,
|
|
22
|
+
id,
|
|
23
|
+
status,
|
|
24
|
+
path: relPath,
|
|
25
|
+
message,
|
|
26
|
+
severity: severityFor(status, opts),
|
|
27
|
+
safeFix: opts.safeFix === true,
|
|
28
|
+
spawn: opts.spawn || null
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Area-bound form for single-area modules; records omit `safeFix`.
|
|
33
|
+
function makeAddCheck(area) {
|
|
34
|
+
return function (checks, id, status, relPath, message, opts = {}) {
|
|
35
|
+
checks.push({
|
|
36
|
+
area,
|
|
37
|
+
id,
|
|
38
|
+
status,
|
|
39
|
+
path: relPath,
|
|
40
|
+
message,
|
|
41
|
+
severity: severityFor(status, opts),
|
|
42
|
+
spawn: opts.spawn || null
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function listFiles(projectRoot, relDir, pattern) {
|
|
48
|
+
const dir = path.join(projectRoot, relDir);
|
|
49
|
+
if (!fs.existsSync(dir)) return [];
|
|
50
|
+
return fs.readdirSync(dir)
|
|
51
|
+
.filter((name) => pattern.test(name))
|
|
52
|
+
.sort()
|
|
53
|
+
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { addCheck, makeAddCheck, listFiles };
|
package/lib/sync-fs.js
CHANGED
|
@@ -34,4 +34,16 @@ function readJson(projectRoot, relPath) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// Like read(), but returns null (not '') when the file is missing or unreadable,
|
|
38
|
+
// for callers that distinguish "absent" from "empty".
|
|
39
|
+
function readTextOrNull(projectRoot, relPath) {
|
|
40
|
+
const file = path.join(projectRoot, relPath);
|
|
41
|
+
if (!fs.existsSync(file)) return null;
|
|
42
|
+
try {
|
|
43
|
+
return fs.readFileSync(file, 'utf8');
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { read, write, exists, readJson, readTextOrNull };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godpowers",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.2",
|
|
4
4
|
"description": "AI-powered development system: 120 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"
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"test:e2e": "node tests/integration/full-arc.test.js",
|
|
25
25
|
"test:mcp": "npm --workspace @godpowers/mcp test",
|
|
26
26
|
"coverage": "c8 --reporter=text --reporter=lcov node scripts/run-tests.js",
|
|
27
|
-
"coverage:lib": "c8 --include=lib/**/*.js --check-coverage --lines 90 --branches 75 --reporter=text node scripts/run-tests.js",
|
|
27
|
+
"coverage:lib": "c8 --include=lib/**/*.js --check-coverage --lines 90 --branches 75 --reporter=text --reporter=json-summary node scripts/run-tests.js",
|
|
28
28
|
"test:audit": "npm audit --omit=dev && git diff --check && npm run test:surface",
|
|
29
29
|
"pack:check": "node scripts/check-package-contents.js",
|
|
30
30
|
"pack:mcp:check": "npm --workspace @godpowers/mcp run pack:check",
|
|
31
|
-
"release:check": "npm run coverage:lib && npm run test:audit && npm run pack:check && npm run pack:mcp:check",
|
|
31
|
+
"release:check": "npm run coverage:lib && node scripts/check-per-file-coverage.js && npm run test:audit && npm run pack:check && npm run pack:mcp:check",
|
|
32
32
|
"lint": "node scripts/static-check.js"
|
|
33
33
|
},
|
|
34
34
|
"workspaces": [
|