voidforge-build 23.21.0 → 23.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [23.22.0] - 2026-06-24
|
|
10
|
+
|
|
11
|
+
### `update` now auto-activates `/contextmeter` (matches `init`)
|
|
12
|
+
|
|
13
|
+
- **Fixed** — `npx voidforge-build update` now wires the `/contextmeter` status line + `UserPromptSubmit` awareness hook into `.claude/settings.json`, the same default-on way `init` does. Previously `update` copied `scripts/statusline/` but left the meter inactive until you ran `/contextmeter` by hand. `mergeStatuslineSettings` is now shared by `init` and `update`; it stays idempotent and **non-clobbering** — never overwrites a project's own `statusLine`, never duplicates the awareness hook. `--dry-run` / diff now reports the pending `.claude/settings.json` change honestly (reading the snippet from the source so it's accurate even before the scripts are copied). +3 updater tests (suite 1420→1423).
|
|
14
|
+
|
|
15
|
+
Both packages bumped in lockstep (version-consistency gate). Dep `^23.21.0` → `^23.22.0`.
|
|
16
|
+
|
|
9
17
|
## [23.21.0] - 2026-06-24
|
|
10
18
|
|
|
11
19
|
### Triaged field reports #382 / #383 / #384 → `/seal`-hardening + DevOps/QA/orchestration fixes + a pattern-distribution gap
|
package/dist/VERSION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Version
|
|
2
2
|
|
|
3
|
-
**Current:** 23.
|
|
3
|
+
**Current:** 23.22.0
|
|
4
4
|
|
|
5
5
|
## Versioning Scheme
|
|
6
6
|
|
|
@@ -14,6 +14,7 @@ This project uses [Semantic Versioning](https://semver.org/):
|
|
|
14
14
|
|
|
15
15
|
| Version | Date | Summary |
|
|
16
16
|
|---------|------|---------|
|
|
17
|
+
| 23.22.0 | 2026-06-24 | **`update` now auto-activates `/contextmeter` (matches `init`).** `npx voidforge-build update` wires the context-meter status line + `UserPromptSubmit` awareness hook into `.claude/settings.json` the same default-on way `init` does — previously `update` copied `scripts/statusline/` but left the meter inactive until a manual `/contextmeter` run. `mergeStatuslineSettings` is now shared by init + update, idempotent + non-clobbering (never overwrites a project's own `statusLine`, never duplicates the hook); `--dry-run` reports the pending `.claude/settings.json` change. +3 updater tests (1420→1423). Dep `^23.21.0` → `^23.22.0`. |
|
|
17
18
|
| 23.21.0 | 2026-06-24 | **Triaged field reports #382 / #383 / #384 → `/seal`-hardening + DevOps/QA/orchestration fixes + a pattern-distribution gap.** **#384:** release Step 0 unrelated/pre-existing-change detection (`/git`, `/seal`, `RELEASE_MANAGER.md`) — split session-authored vs out-of-scope changes, dependency-manifest scrutiny, never `git add -A` (the `vercel` near-miss, mechanized); creation-time native-collision gate (`BUILD_PROTOCOL.md`, `NATIVE_CAPABILITIES.md`) — check a new command's name + add its row at creation, not release re-audit; `bypass.sh` stale-pointer self-repair via `CLAUDE_CODE_SESSION_ID` (repoints a dead-session pointer on the first try, no re-run; +4 tests, gate 27→31). **#382:** DevOps ACL-traverse enumeration + post-lock prod-FE 200 check; new `egress-sandbox.sh` pattern (`systemd-run --uid/--gid`, uid-independent egress); SUB_AGENTS spend ceiling reserves in-flight child budget; QA coverage-fidelity honesty + per-lane ledger; headless-OAuth bootstrap note. **Distribution:** `prepack.sh`/`copy-assets.sh` now ship every `docs/patterns/` file regardless of extension (`.sh`/`.py`/`.conf` were silently dropped — LRN-11 gap). #383 closed as already-shipped. Build clean, suite 1420. Dep `^23.20.0` → `^23.21.0`. |
|
|
18
19
|
| 23.20.0 | 2026-06-23 | **Triaged 12 upstream field reports (#364–#378) → methodology hardening, + `/seal` and `/contextmeter`.** Applied every accepted fix across ~41 method docs / agents / patterns / commands (throughput/scale gates, ADR concurrency verification, deny-list discipline, runtime-path tracers, render-gate coverage, OAuth/external-claim verification, HTTP two-principal isolation, dall-e→gpt-image-1 currency, `/debrief` gate-gap docs) + 2 new patterns (`post-deploy-probe.sh`, `exclusion-set-invariant.md`); implemented the two wizard reports — non-destructive CLAUDE.md `update` merge (#368) + legacy-marker detection (#369). **New `/seal`** — session closeout (git → debrief → vault → handoff). **New `/contextmeter`** — context-budget meter + `UserPromptSubmit` awareness hook, default-on (warn 80% / crit 92%), `scripts/statusline/` wired through all four distribution paths + npm `files`. Build clean, suite 1392→1420. Dep `^23.19.0` → `^23.20.0`. |
|
|
19
20
|
| 23.19.0 | 2026-06-13 | **Gauntlet acceptance test → 14 fixes (the ADR-067 re-platform, validated by running it on itself).** Ran the new `gauntlet.workflow.js` live on the v23.13–v23.18 platform code (10-agent Surfer roster → 347 agents → 99 distinct claims → 66 confirmed + 24 crossfire, 0 Critical) and fixed the 3-lens-confirmed findings. **Gate (security):** `_paths.sh` reap was missing `-mindepth 1` → could `rm -rf` the entire `sessions/` tree (every live roster/bypass); the reaper now refreshes `$SESSION_DIR` mtime on activity + threshold raised above the TTL, closing the documented reap-vs-fresh-roster/bypass race; `shasum`→`sha256sum` fallback (gate silently broke on Alpine); `bypass.sh` run before the first hook fire now records a repo-scoped *pending* bypass that `check.sh` promotes (was a silent no-op). **Workflows:** strike no longer re-runs the same ≤5-agent roster twice; crossfire `survives:true+REFUTED` verdicts no longer vanish into no bucket (logged in `crossfireRefutedLog`); dedup keeps the **highest** severity + `raisedBy` (was first-write-wins); guarded `JSON.parse(args)`; undefined-domain prompt guard. **Distribution:** `npx voidforge-build init` now copies `.claude/workflows/` + `AGENT_CLASSIFICATION.md`; `update` now propagates `.claude/workflows` + `scripts/surfer-gate` (both were stranded). **Validation:** new `scripts/validate-workflows.sh` (wraps the runtime shape, then `node --check`) wired into `pretest` — corrects the false "scripts pass `node --check`" claim and gates syntax errors from shipping. **Docs:** `WORKFLOWS.md` example `agentType: a.id`→`a.name` + new gotchas; stale `/tmp/voidforge-*` paths fixed in gate README + CLAUDE.md (ADR-060). **CI:** `recover-partial` derives the version from `package.json` not `github.ref_name` (broke on dispatch); Playwright cache key off the committed manifests not the regenerated lockfile. Gate suite 23→27, full suite 1390→1392. Deferred (field-report candidates): concurrent same-repo pointer collision, `workflow_dispatch` branch guard. Dep `^23.18.0` → `^23.19.0`. |
|
|
@@ -21,4 +21,21 @@ export interface ProjectResult {
|
|
|
21
21
|
markerId: string;
|
|
22
22
|
filesCreated: number;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Wire the /contextmeter status line + awareness hook into settings.json (default-on).
|
|
26
|
+
* Mirrors mergeSettingsHook: set `statusLine` only when the project doesn't already have
|
|
27
|
+
* one (never clobber a user's), and append the UserPromptSubmit awareness hook unless an
|
|
28
|
+
* equivalent is already present (idempotent). Defaults (warn 80 / crit 92) live in the
|
|
29
|
+
* scripts, so the wired commands carry no env prefix.
|
|
30
|
+
*
|
|
31
|
+
* Shared by `init` (copyMethodology) and `update` (applyUpdate) so `update` auto-activates
|
|
32
|
+
* the meter the same way `init` does (#384 follow-up). Returns `true` when it makes — or,
|
|
33
|
+
* under `{ dryRun: true }`, WOULD make — a change, so the updater can report the pending
|
|
34
|
+
* settings edit and decide `applied`. `snippetDir` lets the dry-run read the snippet from
|
|
35
|
+
* the methodology SOURCE before the scripts have been copied into the project.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeStatuslineSettings(projectDir: string, opts?: {
|
|
38
|
+
dryRun?: boolean;
|
|
39
|
+
snippetDir?: string;
|
|
40
|
+
}): Promise<boolean>;
|
|
24
41
|
export declare function createProject(config: ProjectConfig): Promise<ProjectResult>;
|
|
@@ -206,17 +206,30 @@ async function mergeSettingsHook(projectDir) {
|
|
|
206
206
|
* one (never clobber a user's), and append the UserPromptSubmit awareness hook unless an
|
|
207
207
|
* equivalent is already present (idempotent). Defaults (warn 80 / crit 92) live in the
|
|
208
208
|
* scripts, so the wired commands carry no env prefix.
|
|
209
|
+
*
|
|
210
|
+
* Shared by `init` (copyMethodology) and `update` (applyUpdate) so `update` auto-activates
|
|
211
|
+
* the meter the same way `init` does (#384 follow-up). Returns `true` when it makes — or,
|
|
212
|
+
* under `{ dryRun: true }`, WOULD make — a change, so the updater can report the pending
|
|
213
|
+
* settings edit and decide `applied`. `snippetDir` lets the dry-run read the snippet from
|
|
214
|
+
* the methodology SOURCE before the scripts have been copied into the project.
|
|
209
215
|
*/
|
|
210
|
-
async function mergeStatuslineSettings(projectDir) {
|
|
211
|
-
const
|
|
216
|
+
export async function mergeStatuslineSettings(projectDir, opts = {}) {
|
|
217
|
+
const snippetDir = opts.snippetDir ?? join(projectDir, 'scripts', 'statusline');
|
|
218
|
+
const snippetPath = join(snippetDir, 'settings-snippet.json');
|
|
212
219
|
const settingsPath = join(projectDir, '.claude', 'settings.json');
|
|
213
220
|
if (!existsSync(snippetPath))
|
|
214
|
-
return;
|
|
215
|
-
|
|
221
|
+
return false;
|
|
222
|
+
let snippet;
|
|
223
|
+
try {
|
|
224
|
+
snippet = JSON.parse(await readFile(snippetPath, 'utf-8'));
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
216
229
|
const snippetStatusLine = snippet?.statusLine;
|
|
217
230
|
const snippetUserPrompt = (snippet?.hooks?.UserPromptSubmit ?? []);
|
|
218
231
|
if (!snippetStatusLine && snippetUserPrompt.length === 0)
|
|
219
|
-
return;
|
|
232
|
+
return false;
|
|
220
233
|
let settings = {};
|
|
221
234
|
if (existsSync(settingsPath)) {
|
|
222
235
|
try {
|
|
@@ -224,15 +237,15 @@ async function mergeStatuslineSettings(projectDir) {
|
|
|
224
237
|
}
|
|
225
238
|
catch {
|
|
226
239
|
// Existing settings.json is unreadable — leave it alone.
|
|
227
|
-
return;
|
|
240
|
+
return false;
|
|
228
241
|
}
|
|
229
242
|
}
|
|
230
|
-
|
|
231
|
-
await mkdir(join(projectDir, '.claude'), { recursive: true });
|
|
232
|
-
}
|
|
243
|
+
let changed = false;
|
|
233
244
|
// statusLine: never clobber a project's existing one.
|
|
234
245
|
if (snippetStatusLine && !settings.statusLine) {
|
|
235
|
-
|
|
246
|
+
if (!opts.dryRun)
|
|
247
|
+
settings.statusLine = snippetStatusLine;
|
|
248
|
+
changed = true;
|
|
236
249
|
}
|
|
237
250
|
// UserPromptSubmit: append the awareness hook unless an equivalent is already present.
|
|
238
251
|
const existingHooks = (settings.hooks ?? {});
|
|
@@ -242,12 +255,19 @@ async function mergeStatuslineSettings(projectDir) {
|
|
|
242
255
|
return hooks.some((h) => typeof h?.command === 'string' && h.command.includes('context-awareness-hook'));
|
|
243
256
|
});
|
|
244
257
|
if (!alreadyHasMeter && snippetUserPrompt.length > 0) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
258
|
+
if (!opts.dryRun) {
|
|
259
|
+
settings.hooks = {
|
|
260
|
+
...existingHooks,
|
|
261
|
+
UserPromptSubmit: [...existingUserPrompt, ...snippetUserPrompt],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
changed = true;
|
|
249
265
|
}
|
|
250
|
-
|
|
266
|
+
if (changed && !opts.dryRun) {
|
|
267
|
+
await mkdir(join(projectDir, '.claude'), { recursive: true });
|
|
268
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
269
|
+
}
|
|
270
|
+
return changed;
|
|
251
271
|
}
|
|
252
272
|
// ── Identity Injection ───────────────────────────────────
|
|
253
273
|
async function injectIdentity(projectDir, config) {
|
|
@@ -8,6 +8,7 @@ import { join } from 'node:path';
|
|
|
8
8
|
import { execSync } from 'node:child_process';
|
|
9
9
|
import { readMarker, writeMarker, DEFAULT_CLAUDE_MD_STRATEGY } from './marker.js';
|
|
10
10
|
import { planClaudeMdUpdate, UPSTREAM_SUFFIX } from './claude-md-strategy.js';
|
|
11
|
+
import { mergeStatuslineSettings } from './project-init.js';
|
|
11
12
|
/**
|
|
12
13
|
* Decide which `update` mode the given argv selects. Pure — no I/O, no exit.
|
|
13
14
|
*
|
|
@@ -103,8 +104,9 @@ export async function diffMethodology(projectDir) {
|
|
|
103
104
|
{ src: 'docs/patterns', dest: 'docs/patterns' },
|
|
104
105
|
{ src: 'scripts/thumper', dest: 'scripts/thumper' },
|
|
105
106
|
{ src: 'scripts/surfer-gate', dest: 'scripts/surfer-gate' },
|
|
106
|
-
// Context-meter status line + awareness hook (/contextmeter). Scripts propagate
|
|
107
|
-
//
|
|
107
|
+
// Context-meter status line + awareness hook (/contextmeter). Scripts propagate here;
|
|
108
|
+
// activation (statusLine + UserPromptSubmit hook in settings.json) is wired below the
|
|
109
|
+
// same way `init` does it, so `update` auto-activates the meter too (#384 follow-up).
|
|
108
110
|
{ src: 'scripts/statusline', dest: 'scripts/statusline' },
|
|
109
111
|
];
|
|
110
112
|
// CLAUDE.md is handled via the non-destructive strategy mechanism (issue #368)
|
|
@@ -179,6 +181,22 @@ export async function diffMethodology(projectDir) {
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}
|
|
184
|
+
// /contextmeter activation (#384 follow-up): `update` now wires the statusLine +
|
|
185
|
+
// awareness hook into .claude/settings.json the same way `init` does. Report the pending
|
|
186
|
+
// settings change here so `--dry-run` shows it. The snippet is read from the SOURCE — the
|
|
187
|
+
// project may not have the statusline scripts yet on this update — and the check is
|
|
188
|
+
// idempotent + non-clobbering (it returns false once the meter is wired, or when the
|
|
189
|
+
// project already has its own statusLine).
|
|
190
|
+
const statuslineNeedsWiring = await mergeStatuslineSettings(projectDir, {
|
|
191
|
+
dryRun: true,
|
|
192
|
+
snippetDir: join(sourceRoot, 'scripts', 'statusline'),
|
|
193
|
+
});
|
|
194
|
+
if (statuslineNeedsWiring) {
|
|
195
|
+
const settingsEntry = '.claude/settings.json';
|
|
196
|
+
if (!plan.modified.includes(settingsEntry) && !plan.added.includes(settingsEntry)) {
|
|
197
|
+
plan.modified.push(settingsEntry);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
182
200
|
return plan;
|
|
183
201
|
}
|
|
184
202
|
// ── Apply Update ─────────────────────────────────────────
|
|
@@ -214,13 +232,20 @@ export async function applyUpdate(projectDir) {
|
|
|
214
232
|
await writeFile(`${claudeMdDestPath}${UPSTREAM_SUFFIX}`, claudeMdPlan.sideFileContent, 'utf-8');
|
|
215
233
|
}
|
|
216
234
|
}
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
235
|
+
// Skip from the generic verbatim copy loop:
|
|
236
|
+
// - CLAUDE.md entries: handled above by the non-destructive strategy.
|
|
237
|
+
// - .claude/settings.json: wired below by mergeStatuslineSettings, not copied verbatim
|
|
238
|
+
// (the methodology source carries no project settings.json — copying it would throw,
|
|
239
|
+
// or in dev clobber the project with the methodology repo's own dogfood settings).
|
|
240
|
+
const skipVerbatim = new Set([
|
|
241
|
+
'CLAUDE.md',
|
|
242
|
+
`CLAUDE.md${UPSTREAM_SUFFIX}`,
|
|
243
|
+
'.claude/settings.json',
|
|
244
|
+
]);
|
|
220
245
|
// Copy added + modified files
|
|
221
246
|
const { mkdir } = await import('node:fs/promises');
|
|
222
247
|
for (const file of [...plan.added, ...plan.modified]) {
|
|
223
|
-
if (
|
|
248
|
+
if (skipVerbatim.has(file))
|
|
224
249
|
continue;
|
|
225
250
|
const srcPath = join(sourceRoot, file);
|
|
226
251
|
const destPath = join(projectDir, file);
|
|
@@ -228,6 +253,12 @@ export async function applyUpdate(projectDir) {
|
|
|
228
253
|
await mkdir(destDir, { recursive: true });
|
|
229
254
|
await cp(srcPath, destPath);
|
|
230
255
|
}
|
|
256
|
+
// /contextmeter activation (#384 follow-up): wire the statusLine + awareness hook into
|
|
257
|
+
// .claude/settings.json so `update` matches `init`'s default-on behavior. The scripts
|
|
258
|
+
// were copied above; this turns the meter on. Idempotent + non-clobbering — it never
|
|
259
|
+
// overwrites a user's own statusLine and never duplicates the awareness hook, so it is
|
|
260
|
+
// safe to run on every update.
|
|
261
|
+
await mergeStatuslineSettings(projectDir);
|
|
231
262
|
// Update marker version
|
|
232
263
|
const marker = await readMarker(projectDir);
|
|
233
264
|
if (marker) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voidforge-build",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.22.0",
|
|
4
4
|
"description": "From nothing, everything. A methodology framework for building with Claude Code.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@aws-sdk/client-rds": "^3.700.0",
|
|
46
46
|
"@aws-sdk/client-s3": "^3.700.0",
|
|
47
47
|
"@aws-sdk/client-sts": "^3.700.0",
|
|
48
|
-
"voidforge-build-methodology": "^23.
|
|
48
|
+
"voidforge-build-methodology": "^23.22.0",
|
|
49
49
|
"node-pty": "^1.2.0-beta.12",
|
|
50
50
|
"ws": "^8.19.0"
|
|
51
51
|
},
|