worclaude 2.10.0 → 2.10.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
CHANGED
|
@@ -4,6 +4,45 @@ All notable changes to worclaude are documented in this file. Format loosely fol
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.10.2] — 2026-04-30
|
|
8
|
+
|
|
9
|
+
Bundles a dogfood catch-up of this repo's own `.claude/` (skipping from v2.8.0 to v2.10.1 in one upgrade run) with a real scaffold bug fix surfaced during that upgrade — `.claude/workflow-ref/` (transient upgrade-time conflict references) was missing from the scaffolder's `updateGitignore` entries, so every user running `worclaude upgrade` had those files pollute their git status. Also closes the BACKLOG "claude --worktree command visibility" item via a docs-only fix: investigation confirmed Claude Code does not create a "minimal `.claude/`" (the worktree's `.claude/` is a normal git checkout from `origin/HEAD`); the perceived missing-commands symptom was caused by main lagging develop, and the existing `subagent-usage` skill already documented the mechanism. The gap was direct `claude --worktree` users with no agent-mediated freshness preamble — filled with a one-paragraph copy-paste reset block.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`.claude/workflow-ref/` now scaffolded into `.gitignore`** (PR #174) — `src/core/scaffolder.js` `updateGitignore` entries list now includes `.claude/workflow-ref/`. Existing users pick up the fix on the next scaffold or upgrade pass that runs `updateGitignore`. Two existing tests updated (`is idempotent` seed input + `writes exactly N entries` count 10 → 11 + expected array).
|
|
14
|
+
|
|
15
|
+
### Docs
|
|
16
|
+
|
|
17
|
+
- **Manual freshness reset for direct `claude --worktree`** (PR #175) — added a copy-paste workaround block (`cd .claude/worktrees/<name> && git fetch origin && git reset --hard origin/<your-branch>`) to the existing "Base-branch gotcha" section of the `subagent-usage` skill, in both `templates/skills/universal/subagent-usage.md` and the dogfood mirror. Closes BACKLOG "claude --worktree command visibility" (the entry's "minimal `.claude/`" framing was wrong; investigation captured in the session summary).
|
|
18
|
+
|
|
19
|
+
### Internal
|
|
20
|
+
|
|
21
|
+
- **Dogfood catch-up v2.8.0 → v2.10.1** (PR #174) — applied 3 release groups' worth of template evolution to this repo's own `.claude/`: sandbox.network block in settings.json (self-testing the v2.10.1 feature), observability hooks for UserPromptSubmit / InstructionsLoaded / SubagentStart / SubagentStop, 3 obs-\*.cjs hook scripts, 3 helper scripts under `.claude/scripts/`, and template-current versions of 5 slash commands plus the git-conventions skill that had drifted behind their templates.
|
|
22
|
+
|
|
23
|
+
Release group: 2 PRs (1 patch, 1 none). v2.10.1 → v2.10.2.
|
|
24
|
+
|
|
25
|
+
## [2.10.1] — 2026-04-29
|
|
26
|
+
|
|
27
|
+
Adds opt-in scaffolding for Claude Code 2.1.113's `sandbox.network` deny/allow lists. Worclaude is a scaffolder, so the new `templates/settings/base.json` ships empty `deniedDomains` and `allowedDomains` stubs rather than an opinionated default list — project owners decide their own network policy. The merge paths for both fresh init (Scenario A) and existing-project init/upgrade (Scenarios B/C) union-merge the new arrays preserving any user-added domains, and a new `worclaude doctor` check warns when the block is missing or malformed (with `worclaude upgrade` as the remediation hint). Also bundles a Dependabot major bump to `commander` 14, which is now Node-20+-only and was unblocked by the v2.10.0 Node 18 drop.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **Sandbox network scaffolding** (PR #172) — `templates/settings/base.json` now scaffolds `sandbox.network.deniedDomains: []` and `allowedDomains: []` between the existing `permissions` and `hooks` blocks. New `mergeSettings` helper `unionStringList(inputs, accessor)` in `src/core/scaffolder.js` handles both `permissions.allow` and the new sandbox arrays uniformly. Backward compatible: a base without `sandbox` produces output without `sandbox`, so legacy callers in tests or downstream consumers don't surface the key spuriously.
|
|
32
|
+
- **`appendUnique(target, key, source)` helper** in `src/core/merger.js` (PR #172) — folds three previously-duplicated union-merge call sites in `mergeSettingsPermissionsAndHooks` (allow / deny / sandbox-arrays) into one-liners. Extracted during a `/simplify` pass after three parallel review agents flagged the duplication.
|
|
33
|
+
- **`checkSandboxBlock` doctor check** (PR #172) — warns when `settings.json` is missing the `sandbox` block (with a `worclaude upgrade` remediation pointer for legacy installs), when `sandbox.network` is malformed, or when either array isn't actually an array.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **`commander` 13.1.0 → 14.0.3** (PR #171) — Dependabot major bump. Commander 14 requires Node 20+ (already satisfied after v2.10.0's Node 18 drop) and adds `helpGroup`/`optionsGroup`/`commandsGroup` APIs plus unescaped negative-number support. Worclaude's CLI surface is unaffected.
|
|
38
|
+
- ⚠ **PR #171 shipped without a `Version bump:` declaration** — Dependabot-generated body, no manual annotation. Treated as `none` per `/sync`'s "missing → none" rule and surfaced here permanently. PR #172's `patch` declaration drove the release.
|
|
39
|
+
|
|
40
|
+
### Tests
|
|
41
|
+
|
|
42
|
+
- 967 → 992 (+25 net). Per-stack sandbox-array assertions across all 16 supported language templates (replaced one all-stacks loop test for individual failure attribution); 3 scaffolder unit tests covering union-merge, dedup, and legacy-passthrough; 2 Scenario B regressions for legacy-install upgrade and user-domain preservation through subsequent merges; 2 doctor checks for missing-block (legacy install) and malformed-block scenarios.
|
|
43
|
+
|
|
44
|
+
Release group: 2 PRs (1 patch, 1 missing-declaration treated as none). v2.10.0 → v2.10.1.
|
|
45
|
+
|
|
7
46
|
## [2.10.0] — 2026-04-29
|
|
8
47
|
|
|
9
48
|
Drops support for Node 18, which reached LTS end-of-life on 2025-04-30 (12 months before this release). The drop unblocks two Dependabot PRs stuck on Node-20-only features (`inquirer 13`'s `util.styleText` and `ora 9`'s regex `v` flag) and ships those bumps in the same release. Also recovers from a Dependabot routing misconfiguration: `.github/dependabot.yml` now declares `target-branch: develop` for both ecosystems, fixing a config gap that caused 5 PRs in the v2.9.3 → v2.10.0 window to be opened against main instead of develop. Their content is preserved across both branches via a recovery sync.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "worclaude",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.2",
|
|
4
4
|
"description": "The Workflow Layer for Claude Code — scaffold agents, commands, skills, hooks, and memory into any project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@sefaertunc/anthropic-watch-client": "^1.0.2",
|
|
68
68
|
"chalk": "^5.4.1",
|
|
69
|
-
"commander": "^
|
|
69
|
+
"commander": "^14.0.3",
|
|
70
70
|
"fs-extra": "^11.3.0",
|
|
71
71
|
"inquirer": "^13.4.2",
|
|
72
72
|
"ora": "^9.4.0",
|
package/src/commands/doctor.js
CHANGED
|
@@ -286,6 +286,36 @@ async function readSettingsJson(projectRoot) {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
async function checkSandboxBlock(projectRoot) {
|
|
290
|
+
const settings = await readSettingsJson(projectRoot);
|
|
291
|
+
if (!settings) return null;
|
|
292
|
+
|
|
293
|
+
if (!settings.sandbox) {
|
|
294
|
+
return result(
|
|
295
|
+
WARN,
|
|
296
|
+
'Sandbox block',
|
|
297
|
+
'settings.json missing `sandbox` block. Run `worclaude upgrade` to scaffold network deny/allow lists.'
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const network = settings.sandbox.network;
|
|
302
|
+
if (!network || typeof network !== 'object') {
|
|
303
|
+
return result(WARN, 'Sandbox block', '`sandbox.network` block missing or malformed');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const issues = [];
|
|
307
|
+
if (!Array.isArray(network.deniedDomains)) {
|
|
308
|
+
issues.push('`deniedDomains` not an array');
|
|
309
|
+
}
|
|
310
|
+
if (!Array.isArray(network.allowedDomains)) {
|
|
311
|
+
issues.push('`allowedDomains` not an array');
|
|
312
|
+
}
|
|
313
|
+
if (issues.length > 0) {
|
|
314
|
+
return result(WARN, 'Sandbox block', issues.join('; '));
|
|
315
|
+
}
|
|
316
|
+
return result(PASS, 'Sandbox block', null);
|
|
317
|
+
}
|
|
318
|
+
|
|
289
319
|
async function checkHookEventNames(projectRoot) {
|
|
290
320
|
const settings = await readSettingsJson(projectRoot);
|
|
291
321
|
if (!settings) {
|
|
@@ -1047,6 +1077,7 @@ export async function doctorCommand(options = {}) {
|
|
|
1047
1077
|
record('core', await checkClaudeMdMemoryGuidance(projectRoot));
|
|
1048
1078
|
record('core', await checkAgentsMd(projectRoot));
|
|
1049
1079
|
record('core', await checkSettingsJson(projectRoot));
|
|
1080
|
+
record('core', await checkSandboxBlock(projectRoot));
|
|
1050
1081
|
record('core', await checkSessions(projectRoot));
|
|
1051
1082
|
spacer();
|
|
1052
1083
|
|
package/src/core/merger.js
CHANGED
|
@@ -241,20 +241,28 @@ export async function mergeSettingsPermissionsAndHooks(
|
|
|
241
241
|
const existing = parseUserJson(existingRaw, '.claude/settings.json');
|
|
242
242
|
|
|
243
243
|
// Merge permissions (Tier 1) — union-merge both allow and deny
|
|
244
|
-
const existingAllow = existing.permissions?.allow || [];
|
|
245
|
-
const workflowAllow = workflowSettings.permissions?.allow || [];
|
|
246
|
-
const newAllow = workflowAllow.filter((p) => !existingAllow.includes(p));
|
|
247
244
|
if (!existing.permissions) existing.permissions = {};
|
|
248
|
-
existing.permissions
|
|
245
|
+
const newAllow = appendUnique(existing.permissions, 'allow', workflowSettings.permissions?.allow);
|
|
249
246
|
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
existing.permissions.deny = [...existingDeny, ...newDeny];
|
|
247
|
+
const existingDenyLen = (existing.permissions.deny ?? []).length;
|
|
248
|
+
const newDeny = appendUnique(existing.permissions, 'deny', workflowSettings.permissions?.deny);
|
|
249
|
+
if (existingDenyLen === 0 && newDeny === 0) {
|
|
250
|
+
delete existing.permissions.deny;
|
|
255
251
|
}
|
|
256
252
|
|
|
257
|
-
report.added.permissions = newAllow
|
|
253
|
+
report.added.permissions = newAllow + newDeny;
|
|
254
|
+
|
|
255
|
+
// Merge sandbox block (Tier 1 — additive, preserves user customizations)
|
|
256
|
+
const workflowSandbox = workflowSettings.sandbox?.network;
|
|
257
|
+
if (workflowSandbox) {
|
|
258
|
+
if (!existing.sandbox) existing.sandbox = {};
|
|
259
|
+
if (!existing.sandbox.network || typeof existing.sandbox.network !== 'object') {
|
|
260
|
+
existing.sandbox.network = {};
|
|
261
|
+
}
|
|
262
|
+
for (const key of ['deniedDomains', 'allowedDomains']) {
|
|
263
|
+
appendUnique(existing.sandbox.network, key, workflowSandbox[key]);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
258
266
|
|
|
259
267
|
// Merge hooks (Tier 1 + Tier 3)
|
|
260
268
|
if (!existing.hooks) existing.hooks = {};
|
|
@@ -360,6 +368,13 @@ async function mergeSettingsJson(projectRoot, existingScan, selections, report)
|
|
|
360
368
|
}
|
|
361
369
|
}
|
|
362
370
|
|
|
371
|
+
function appendUnique(target, key, source) {
|
|
372
|
+
const existing = Array.isArray(target[key]) ? target[key] : [];
|
|
373
|
+
const additions = Array.isArray(source) ? source.filter((d) => !existing.includes(d)) : [];
|
|
374
|
+
target[key] = [...existing, ...additions];
|
|
375
|
+
return additions.length;
|
|
376
|
+
}
|
|
377
|
+
|
|
363
378
|
function countHooks(hooks) {
|
|
364
379
|
if (!hooks) return 0;
|
|
365
380
|
return Object.values(hooks).reduce((sum, entries) => sum + entries.length, 0);
|
package/src/core/scaffolder.js
CHANGED
|
@@ -78,6 +78,7 @@ export async function updateGitignore(projectDir) {
|
|
|
78
78
|
'.claude/cache/',
|
|
79
79
|
'.claude/scratch/',
|
|
80
80
|
'.claude/observability/',
|
|
81
|
+
'.claude/workflow-ref/',
|
|
81
82
|
];
|
|
82
83
|
const header = '# Worclaude (generated workflow files)';
|
|
83
84
|
|
|
@@ -204,16 +205,31 @@ export async function scaffoldMemoryDocs(projectRoot) {
|
|
|
204
205
|
|
|
205
206
|
export function mergeSettings(base, ...stacks) {
|
|
206
207
|
const merged = JSON.parse(JSON.stringify(base));
|
|
207
|
-
const
|
|
208
|
+
const inputs = [base, ...stacks].filter(Boolean);
|
|
208
209
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
merged.permissions.allow = unionStringList(inputs, (i) => i.permissions?.allow);
|
|
211
|
+
|
|
212
|
+
if (merged.sandbox?.network) {
|
|
213
|
+
merged.sandbox.network.deniedDomains = unionStringList(
|
|
214
|
+
inputs,
|
|
215
|
+
(i) => i.sandbox?.network?.deniedDomains
|
|
216
|
+
);
|
|
217
|
+
merged.sandbox.network.allowedDomains = unionStringList(
|
|
218
|
+
inputs,
|
|
219
|
+
(i) => i.sandbox?.network?.allowedDomains
|
|
220
|
+
);
|
|
215
221
|
}
|
|
216
222
|
|
|
217
|
-
merged.permissions.allow = [...new Set(baseAllow)];
|
|
218
223
|
return merged;
|
|
219
224
|
}
|
|
225
|
+
|
|
226
|
+
function unionStringList(inputs, accessor) {
|
|
227
|
+
const set = new Set();
|
|
228
|
+
for (const input of inputs) {
|
|
229
|
+
const list = accessor(input);
|
|
230
|
+
if (Array.isArray(list)) {
|
|
231
|
+
for (const item of list) set.add(item);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return [...set];
|
|
235
|
+
}
|
|
@@ -88,6 +88,14 @@ Run `worclaude doctor` to diagnose. Fix locally with `git remote set-head origin
|
|
|
88
88
|
their worktree to match the parent's current branch automatically; other worktree
|
|
89
89
|
agents do not.
|
|
90
90
|
|
|
91
|
+
If you invoke `claude --worktree` directly (not via an agent), no preamble runs
|
|
92
|
+
— sync the worktree manually before doing real work:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd .claude/worktrees/<name>
|
|
96
|
+
git fetch origin && git reset --hard origin/<your-working-branch>
|
|
97
|
+
```
|
|
98
|
+
|
|
91
99
|
Benefits:
|
|
92
100
|
- Agent's changes don't conflict with your uncommitted work
|
|
93
101
|
- You can review agent changes before merging
|