create-agentic-workspace 0.4.0 → 0.4.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.
@@ -20,7 +20,9 @@ if (rawArgv.length > 0 && !rawArgv[0].startsWith('--')) {
20
20
  argv = ['--dir', rawArgv[0], ...rawArgv.slice(1)];
21
21
  }
22
22
 
23
- const { exitCode, output } = await runCli(argv, {
23
+ // runCli streams every line to `output` as it is produced (ER #95), so the returned string is a
24
+ // transcript for callers, not something to print again here.
25
+ const { exitCode } = await runCli(argv, {
24
26
  cwd: process.cwd(),
25
27
  isTTY: Boolean(process.stdin.isTTY),
26
28
  input: process.stdin,
@@ -29,5 +31,4 @@ const { exitCode, output } = await runCli(argv, {
29
31
  pkgDir,
30
32
  });
31
33
 
32
- process.stdout.write(`${output}\n`);
33
34
  process.exitCode = exitCode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-workspace",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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.4.0",
37
+ "plugin_version": "1.4.1",
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.4.0",
4
+ "generated_for_plugin_version": "1.4.1",
5
5
  "entries": [
6
6
  {
7
7
  "rule": "Bash(~/.claude/plugins/cache/*/foundry/*/scripts/foundry-acceptance-contract-validate.py:*)",
package/src/preview.mjs CHANGED
@@ -35,10 +35,21 @@ export function renderPreview({ plan, machineScopeWrites, map }) {
35
35
  // previewed and nothing else; `git init` would be an un-previewed side effect, and the choice of
36
36
  // remote is genuinely the reader's. `create-vite` prints its next steps the same way, for the same
37
37
  // reason. Telling keeps the promise; doing would quietly break it.
38
- export const TRUST_HANDOFF_TEXT = (dir, { isGitRepo = true } = {}) => `
39
- The workspace has been written. The permission floor above is a declaration, not a grant: the
38
+ // `reconciledExisting` is true when --reconcile-floor actually WROTE rules into a workspace that
39
+ // already existed. That case inverts the sentence below, and the inversion was observed live: an
40
+ // operator reconciled an already-trusted workspace, restarted the session, and 42 wildcarded allow
41
+ // rules took effect with NO trust dialog at all. The dialog is the consent ceremony for a workspace
42
+ // the platform has not yet trusted; for one it already trusts, THIS CLI'S OWN preview-and-confirm is
43
+ // the only consent moment there will be. Saying otherwise points the operator at a review that will
44
+ // not happen — the one claim this tool cannot afford to get wrong, since "declares, never grants" is
45
+ // its whole posture. (bootstrap-cli R8, answered 2026-08-12.)
46
+ export const TRUST_HANDOFF_TEXT = (dir, { isGitRepo = true, reconciledExisting = false } = {}) => `
47
+ ${reconciledExisting ? `The workspace has been written, and the permission-floor rules above were added to a workspace
48
+ that is ALREADY TRUSTED. On that path there is no second consent ceremony: the preview and the
49
+ confirmation you just gave were it, and the added \`allow\` rules are in effect from the next
50
+ session onward with no dialog. Re-read the list above if you have not.` : `The workspace has been written. The permission floor above is a declaration, not a grant: the
40
51
  platform's trust dialog is the consent ceremony, and the \`allow\` rules take effect only after
41
- you accept it — the dialog lists them.
52
+ you accept it — the dialog lists them.`}
42
53
 
43
54
  cd ${dir}${isGitRepo ? '' : `
44
55
  git init && git add -A && git commit -m 'workspace seed'
package/src/run.mjs CHANGED
@@ -93,7 +93,20 @@ function expandPluginRootGlob(glob, homeDir) {
93
93
  * caught and turned into a refusal-shaped exit 1 (or, for a bug, exit 1 with the error message). */
94
94
  export async function runCli(argv, { cwd, isTTY, input, output, homeDir, pkgDir }) {
95
95
  const lines = [];
96
- const print = (s) => lines.push(s);
96
+ // ER #95 — STREAM, don't accumulate. This used to only push into `lines`, which bin printed
97
+ // after runCli returned, so every prompt fired against a blank screen: "Write the workspace as
98
+ // previewed above? [y/N]" was asked with nothing above it, and the preview — the file plan, the
99
+ // machine-scope writes OUTSIDE the target root, and the 62 permission rules — scrolled past
100
+ // afterwards. A confirmation whose subject is not yet on screen cannot be informed consent, and
101
+ // its mere presence claims a review that did not happen.
102
+ //
103
+ // It became load-bearing with --reconcile-floor. R8 was answered live: reconciling an
104
+ // already-trusted workspace fires NO trust dialog, so this prompt is the ONLY consent moment
105
+ // there is. `lines` is still accumulated because runCli's return contract exposes it.
106
+ const print = (s) => {
107
+ lines.push(s);
108
+ output.write(`${s}\n`);
109
+ };
97
110
 
98
111
  try {
99
112
  let parsed;
@@ -254,7 +267,12 @@ export async function runCli(argv, { cwd, isTTY, input, output, homeDir, pkgDir
254
267
  // on the path is the check that covers both — a directory-only test would tell a worktree user
255
268
  // to re-init a repository they already have.
256
269
  const isGitRepo = fs.existsSync(path.join(targetRoot, '.git'));
257
- print(TRUST_HANDOFF_TEXT(targetRoot, { isGitRepo }));
270
+ print(TRUST_HANDOFF_TEXT(targetRoot, {
271
+ isGitRepo,
272
+ // only when a reconcile actually wrote — a dry run, a no-op second run, or a plain scaffold
273
+ // all keep the standard hand-off
274
+ reconciledExisting: Boolean(floorPlan && floorPlan.total > 0),
275
+ }));
258
276
 
259
277
  return { exitCode: exitCodeForPlan(plan), output: lines.join('\n') };
260
278
  } catch (e) {