create-agentic-workspace 0.9.0 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-workspace",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "The pre-session bootstrap wizard for an Agentic Foundry workspace: declares (never grants) the permission floor, absorbs foundry-bootstrap.sh's out-of-session identity wiring, and scaffolds a seven-file schema-valid workspace. Zero dependencies, no lifecycle scripts, no telemetry.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "marketplace_name": "agentic-foundry",
35
35
  "marketplace_repo": "lukasrepublic/agentic-foundry",
36
36
  "plugin_name": "foundry",
37
- "plugin_version": "1.9.0",
37
+ "plugin_version": "1.10.0",
38
38
  "pins_researched": "2026-08-02"
39
39
  }
40
40
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "plugin_root_glob": "~/.claude/plugins/cache/*/foundry/*",
4
- "generated_for_plugin_version": "1.9.0",
4
+ "generated_for_plugin_version": "1.10.0",
5
5
  "entries": [
6
6
  {
7
7
  "rule": "Bash(~/.claude/plugins/cache/*/foundry/*/scripts/foundry-acceptance-contract-validate.py:*)",
@@ -268,6 +268,11 @@
268
268
  "tier": "allow",
269
269
  "rationale": "read-only programme resolution + ready-set/idle/wake derivation; writes nothing (asserted byte-level over a whole fixture tree by AC-CDW-6) and issues no merge, dispatch, or authorization. NOT write-free means side-effect-free: via foundry_release it runs read-only git plumbing (log/rev-parse) in a repo dir, and it exec_module's the in-tree wave planner. Manifest-named spec_ref/contract_ref are confinement-checked BEFORE any derivation. NOT covered: the dir git runs in comes from the contract's own target_repo via _resolve_repo, whose unrecognized-key fallback can leave the corpus - pre-existing shipped behaviour, reached only through an already-confined, authorized contract"
270
270
  },
271
+ {
272
+ "rule": "Bash(~/.claude/plugins/cache/*/foundry/*/scripts/foundry_command_deck_watch.py:*)",
273
+ "tier": "allow",
274
+ "rationale": "the command-deck watcher lifecycle: renders the tick prompt and keeps the watcher record. Reads are the read-only derivation it delegates to foundry_command_deck.py (same reach, same confinement). WRITES EXACTLY ONE PATH SHAPE: .foundry/watchers/<programme>.json under the project dir, where <programme> is re-checked against a [a-z0-9-]+ fullmatch BEFORE any path join, so a traversing or absolute identifier cannot reach os.path.join at all; forget removes only that same path. The record is inert data - no derivation reads it, it grants nothing and gates nothing. Manifest-derived text reaching the rendered prompt is passed through the shipped as_data sanitiser. NOT covered: it inherits foundry_command_deck.py's own pre-existing target_repo git-dir reach, unchanged by this script"
275
+ },
271
276
  {
272
277
  "rule": "Bash(~/.claude/plugins/cache/*/foundry/*/scripts/foundry_release.py accept:*)",
273
278
  "tier": "ask",
@@ -201,13 +201,40 @@ export function migrationActions(trigger, { scope, marketplaceName, marketplaceR
201
201
  * `before` carried survives with its ORIGINAL value except `extraKnownMarketplaces` — the one key
202
202
  * the migration exists to change — and `enabledPlugins` is unconditionally guaranteed to still name
203
203
  * the plugin, regardless of what either side carried. */
204
- export function repairScopeSettings(before, after, pluginKey) {
204
+ /** ALLOWLIST, not "everything except `source`". The migration replaces the whole registration
205
+ * entry, and Claude Code publishes no schema for what may live in it — so a skip-`source` denylist
206
+ * carries an UNKNOWN key forward by default, and the dangerous shape is a second pin-like key. The
207
+ * shipped `claude` binary carries `ref`, `commit`, `scope` and `lastUpdated` strings in marketplace
208
+ * context: restoring a stale `commit` would be pin resurrection under a different name, defeating
209
+ * the very migration this repair serves. So the carry-forward set is enumerated, and a key nobody
210
+ * anticipated is DROPPED rather than restored — the platform re-adds what it still wants. Adding a
211
+ * key here is a deliberate one-line decision with this paragraph attached. */
212
+ const CARRY_FORWARD = ['autoUpdate'];
213
+
214
+ export function repairScopeSettings(before, after, pluginKey, marketplaceName) {
205
215
  const repaired = { ...after };
206
216
  for (const key of Object.keys(before)) {
207
217
  if (key === 'extraKnownMarketplaces') continue;
208
218
  repaired[key] = before[key];
209
219
  }
210
220
  repaired.enabledPlugins = { ...(repaired.enabledPlugins || {}), [pluginKey]: true };
221
+
222
+ // `!Array.isArray` on both sides: `typeof [] === 'object'`, so an array in this slot would merge
223
+ // its INDICES in as keys (`{"0": "...", source: {...}}`). Only a plain object is a registration.
224
+ const isEntry = (v) => v && typeof v === 'object' && !Array.isArray(v);
225
+ const beforeEntry = (before || {}).extraKnownMarketplaces?.[marketplaceName];
226
+ const afterEntry = repaired.extraKnownMarketplaces?.[marketplaceName];
227
+ if (isEntry(beforeEntry) && isEntry(afterEntry)) {
228
+ const merged = { ...afterEntry };
229
+ for (const key of CARRY_FORWARD) {
230
+ if (!Object.prototype.hasOwnProperty.call(beforeEntry, key)) continue;
231
+ // hasOwnProperty.call, NOT `key in merged`: `in` is true for INHERITED names, so a key
232
+ // literally called `toString` / `constructor` would read as "the platform already declared
233
+ // it" and be skipped. Own-property is the question actually being asked.
234
+ if (!Object.prototype.hasOwnProperty.call(merged, key)) merged[key] = beforeEntry[key];
235
+ }
236
+ repaired.extraKnownMarketplaces = { ...repaired.extraKnownMarketplaces, [marketplaceName]: merged };
237
+ }
211
238
  return repaired;
212
239
  }
213
240
 
@@ -236,7 +263,7 @@ export function migrateScope({ scopeSnap, trigger, marketplaceName, marketplaceR
236
263
  }
237
264
  if (fs.existsSync(scopeSnap.settingsPath)) {
238
265
  const after = readScopeSettings(scopeSnap).obj;
239
- const repaired = repairScopeSettings(scopeSnap.obj, after, pluginKey);
266
+ const repaired = repairScopeSettings(scopeSnap.obj, after, pluginKey, marketplaceName);
240
267
  writeTargetAtomically(scopeSnap.settingsPath, repaired);
241
268
  }
242
269
  if (caught) throw caught;