acstack 0.53.1 → 0.54.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.
Files changed (87) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +11 -11
  3. package/agents/codex/frontend-verifier.toml +8 -7
  4. package/agents/codex/task-checker.toml +1 -1
  5. package/agents/codex/task-executor.toml +1 -1
  6. package/agents/codex/test-writer.toml +1 -1
  7. package/agents/frontend-verifier.md +8 -7
  8. package/agents/task-checker.md +1 -1
  9. package/agents/task-executor.md +1 -1
  10. package/agents/test-writer.md +1 -1
  11. package/assets/playwriter-bridge/doctor.ps1 +1 -1
  12. package/assets/playwriter-bridge/register-tasks.ps1 +4 -4
  13. package/assets/terminal-workspace/config.example.yaml +2 -2
  14. package/assets/terminal-workspace/helpers/acstack-bridge +3 -1
  15. package/assets/terminal-workspace/helpers/acstack-db +1 -1
  16. package/assets/terminal-workspace/helpers/acstack-md +4 -2
  17. package/assets/terminal-workspace/helpers/acstack-md-lib +2 -2
  18. package/assets/terminal-workspace/helpers/acstack-open +1 -1
  19. package/assets/terminal-workspace/lib/apply-herdr-numbers.sh +1 -1
  20. package/assets/terminal-workspace/lib/apply-tools.sh +1 -1
  21. package/assets/terminal-workspace/lib/apply-tuicr.sh +2 -2
  22. package/assets/terminal-workspace/lib/commands.sh +1 -1
  23. package/assets/terminal-workspace/lib/helper-install.sh +1 -1
  24. package/assets/terminal-workspace/setup.sh +6 -1
  25. package/cli/dist/index.js +713 -523
  26. package/cli/dist/index.js.map +4 -4
  27. package/cli/package.json +1 -1
  28. package/docs/config-reference.md +129 -87
  29. package/docs/dev-setups.md +4 -4
  30. package/docs/terminal-workspace.md +7 -6
  31. package/docs/workflows.md +13 -12
  32. package/examples/design-doc/acstack/README.md +3 -3
  33. package/examples/design-doc/acstack/journey.md +1 -1
  34. package/examples/design-doc/acstack/publication.md +1 -1
  35. package/hooks/context-pressure.sh +6 -2
  36. package/hooks/lib/read-guard.mjs +25 -21
  37. package/hooks/project-data.sh +5 -34
  38. package/hooks/session-start-handoff.sh +11 -22
  39. package/hooks/session-start.sh +5 -6
  40. package/orchestrator/AGENTS.md +30 -25
  41. package/package.json +1 -1
  42. package/pi/agents/frontend-verifier.md +8 -7
  43. package/pi/agents/task-checker.md +1 -1
  44. package/pi/agents/task-executor.md +1 -1
  45. package/pi/agents/test-writer.md +1 -1
  46. package/pi/extension.ts +39 -57
  47. package/profiles/fullstack-python.yaml +1 -1
  48. package/profiles/node-typescript.yaml +1 -1
  49. package/profiles/python.yaml +1 -1
  50. package/skills/archive/SKILL.md +20 -18
  51. package/skills/bootstrap/SKILL.md +6 -6
  52. package/skills/bridge/SKILL-VM.md +5 -5
  53. package/skills/bridge/SKILL-WINDOWS.md +1 -1
  54. package/skills/browser-verify/SKILL.md +6 -6
  55. package/skills/commit/SKILL.md +8 -8
  56. package/skills/compact-and-continue/SKILL.md +11 -9
  57. package/skills/design-doc/SKILL.md +8 -5
  58. package/skills/explore/SKILL.md +1 -1
  59. package/skills/grilling/SKILL.md +4 -3
  60. package/skills/handoff/SKILL.md +4 -4
  61. package/skills/host/SKILL.md +2 -2
  62. package/skills/host/detect-host.sh +8 -4
  63. package/skills/host/herdr.md +1 -1
  64. package/skills/host/install-herdr-block.sh +1 -1
  65. package/skills/host/notes.md +5 -4
  66. package/skills/host/questions.md +1 -1
  67. package/skills/host/report.md +2 -2
  68. package/skills/implement/SKILL.md +6 -5
  69. package/skills/implement/decision-log.md +10 -15
  70. package/skills/implement/design-pass.md +2 -2
  71. package/skills/map/SKILL.md +5 -3
  72. package/skills/map/frontend.md +2 -2
  73. package/skills/map/materialising.md +5 -3
  74. package/skills/map/trackers/files.md +6 -4
  75. package/skills/pre-commit-review/SKILL.md +3 -3
  76. package/skills/quick-fix/SKILL.md +1 -1
  77. package/skills/record-context/SKILL.md +6 -6
  78. package/skills/setup/SKILL.md +20 -11
  79. package/skills/setup/browser.md +2 -2
  80. package/skills/setup/first-run.md +14 -16
  81. package/skills/setup/report.md +4 -3
  82. package/skills/setup/row-fixes.md +7 -8
  83. package/skills/sync/SKILL.md +14 -12
  84. package/skills/terminal-workspace/SKILL.md +4 -4
  85. package/skills/terminal-workspace/headless-clipboard.md +1 -1
  86. package/skills/terminal-workspace/turn-off-and-update.md +1 -1
  87. package/skills/test-conventions/SKILL.md +2 -2
package/pi/extension.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * from the SSOT style file (hooks/ac-writing-style-reminder.sh)
15
15
  * agent_before_settle nudge with a continuity-versus-freshness checkpoint
16
16
  * as context fills (hooks/context-pressure.sh)
17
- * session_compact re-inject .acstack/HANDOFF.md after a compaction, and
17
+ * session_compact re-inject <project folder>/HANDOFF.md after a compaction, and
18
18
  * restart the nudge ladder (hooks/session-start-handoff.sh,
19
19
  * hooks/context-pressure.sh)
20
20
  * tool_call block a main agent's first whole read of a large
@@ -33,51 +33,35 @@ import * as fs from "node:fs";
33
33
  import * as os from "node:os";
34
34
  import * as path from "node:path";
35
35
  import { fileURLToPath } from "node:url";
36
- import { execFileSync } from "node:child_process";
36
+ import { spawnSync } from "node:child_process";
37
37
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
38
38
  // @ts-ignore -- plain ESM shared with hooks/read-guard.mjs, no type declarations
39
- import { decide, envSaysNotMain } from "../hooks/lib/read-guard.mjs";
39
+ import { decide, envSaysNotMain, readGuardSettings } from "../hooks/lib/read-guard.mjs";
40
40
 
41
41
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
42
42
  const troubleshoot = path.join(packageRoot, "TROUBLESHOOTING.md");
43
43
 
44
- function hasLegacyProjectData(root: string): boolean {
45
- if (!fs.lstatSync(path.join(root, "acstack"), { throwIfNoEntry: false })?.isDirectory()) return false;
46
- return ["project.yaml", "domain-map.yaml"].some(name => {
47
- const entry = fs.lstatSync(path.join(root, "acstack", name), { throwIfNoEntry: false });
48
- return entry?.isFile() || entry?.isSymbolicLink();
49
- }) || ["alignment-packs", "design-reviews", "maps", "specs", "changes", "archive", "decision-logs"].some(name => {
50
- const entry = fs.lstatSync(path.join(root, "acstack", name), { throwIfNoEntry: false });
51
- return entry?.isDirectory() || entry?.isSymbolicLink();
52
- });
53
- }
54
-
55
- function projectRoot(cwd: string): string {
56
- let root = path.resolve(cwd);
57
- while (true) {
58
- if (fs.existsSync(path.join(root, ".acstack")) || hasLegacyProjectData(root)) return root;
59
- const parent = path.dirname(root);
60
- if (parent === root) break;
61
- root = parent;
62
- }
44
+ /**
45
+ * Ask the bundled CLI for this repository's project folder, the way the
46
+ * Claude Code hooks do. Null outside a Git repository, or when the CLI names a
47
+ * fix (legacy data, orphaned entry, conflicting modes); that message and any
48
+ * legacy-data warning reach the user once per cwd.
49
+ */
50
+ const projectFolders = new Map<string, string | null>();
51
+ function projectFolder(ctx: ExtensionContext | undefined, cwd: string): string | null {
52
+ const key = path.resolve(cwd);
53
+ if (projectFolders.has(key)) return projectFolders.get(key)!;
54
+ let folder: string | null = null;
63
55
  try {
64
- return execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
56
+ const result = spawnSync(process.execPath, [path.join(packageRoot, "cli", "bin", "acstack.js"), "project-data", "path"], { cwd: key, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
57
+ const message = result.stderr.trim();
58
+ if (message && ctx) report(ctx, message.startsWith("acstack:") ? message : `acstack: ${message}`, result.status === 0 ? "warning" : "error");
59
+ if (result.status === 0) folder = result.stdout.trim() || null;
65
60
  } catch {
66
- return path.resolve(cwd);
67
- }
68
- }
69
-
70
- function ensureProjectData(ctx: ExtensionContext, root: string): boolean {
71
- if (hasLegacyProjectData(root)) {
72
- report(ctx, "acstack: legacy project data found; run ~/.claude/acstack/bin/acstack project-data migrate before using it.", "warning");
73
- }
74
- try {
75
- execFileSync(process.execPath, [path.join(packageRoot, "cli", "bin", "acstack.js"), "project-data", "ensure"], { cwd: root, stdio: ["ignore", "pipe", "pipe"] });
76
- return !hasLegacyProjectData(root);
77
- } catch (error) {
78
- report(ctx, `acstack: project-data ensure failed; ignore policy is not confirmed installed. Skipping project activation and private writes. ${error instanceof Error ? error.message : String(error)}`, "error");
79
- return false;
61
+ folder = null;
80
62
  }
63
+ projectFolders.set(key, folder);
64
+ return folder;
81
65
  }
82
66
 
83
67
  /** pi-subagents' background runner sets this in every child process. */
@@ -349,7 +333,7 @@ function flowList(value: string): string[] {
349
333
  }
350
334
 
351
335
  /**
352
- * The workflow.handoff keys from .acstack/project.yaml. A port of read_config
336
+ * The workflow.handoff keys from <project folder>/project.yaml. A port of read_config
353
337
  * in hooks/context-pressure.sh, hand-rolled for the same reason: no YAML
354
338
  * library is guaranteed to be installed.
355
339
  */
@@ -496,12 +480,12 @@ const HANDOFF_PREAMBLE =
496
480
  "wrong assumption taken after compaction is expensive to unwind.\n\n";
497
481
 
498
482
  /**
499
- * Consume .acstack/HANDOFF.md the way hooks/session-start-handoff.sh does:
483
+ * Consume <project folder>/HANDOFF.md the way hooks/session-start-handoff.sh does:
500
484
  * only a file carrying the acstack_handoff marker, moved into a per-map
501
485
  * archive so it is never injected twice. Returns the text to inject.
502
486
  */
503
- export function consumeHandoff(cwd: string, now: Date = new Date()): string | undefined {
504
- const active = path.join(cwd, ".acstack", "HANDOFF.md");
487
+ export function consumeHandoff(folder: string, now: Date = new Date()): string | undefined {
488
+ const active = path.join(folder, "HANDOFF.md");
505
489
  const raw = readText(active);
506
490
  if (!raw?.trim()) return undefined;
507
491
 
@@ -522,11 +506,11 @@ export function consumeHandoff(cwd: string, now: Date = new Date()): string | un
522
506
  let stamp = (meta.created || "").replace(/[^A-Za-z0-9._-]/g, "-");
523
507
  if (!stamp) stamp = now.toISOString().replace(/\.\d+Z$/, "Z").replace(/:/g, "-");
524
508
 
525
- const changeDir = path.join(cwd, ".acstack", "changes", map);
509
+ const changeDir = path.join(folder, "changes", map);
526
510
  const archiveDir =
527
511
  map !== "ad-hoc" && fs.existsSync(changeDir)
528
512
  ? path.join(changeDir, "handoffs")
529
- : path.join(cwd, ".acstack", "handoffs", map);
513
+ : path.join(folder, "handoffs", map);
530
514
  try {
531
515
  fs.mkdirSync(archiveDir, { recursive: true });
532
516
  let dest = path.join(archiveDir, `${stamp}.md`);
@@ -664,18 +648,16 @@ export default function acstack(pi: ExtensionAPI): void {
664
648
 
665
649
  const orchestratorFile = path.join(packageRoot, "orchestrator", "AGENTS.md");
666
650
  const styleFile = path.join(packageRoot, "output-styles", "ac-writing-style.md");
667
- const unsafeProjects = new Set<string>();
668
- const isProject = (cwd: string) => !unsafeProjects.has(projectRoot(cwd)) && fs.existsSync(path.join(projectRoot(cwd), ".acstack", "project.yaml"));
651
+ const isProject = (cwd: string) => {
652
+ const folder = projectFolder(undefined, cwd);
653
+ return folder !== null && fs.existsSync(path.join(folder, "project.yaml"));
654
+ };
669
655
  let warnedSubagents = false;
670
656
 
671
657
  pi.on("session_start", async (event, ctx) => {
672
658
  if (!delegated) linkClis(ctx);
673
- const root = projectRoot(ctx.cwd);
674
- if (ensureProjectData(ctx, root)) unsafeProjects.delete(root);
675
- else {
676
- unsafeProjects.add(root);
677
- return;
678
- }
659
+ projectFolders.delete(path.resolve(ctx.cwd));
660
+ if (projectFolder(ctx, ctx.cwd) === null) return;
679
661
 
680
662
  if (delegated) return;
681
663
  if (!warnedSubagents && !pi.getAllTools().some((tool) => tool.name === "subagent" || tool.name === "Agent" || tool.name === "team_agent")) {
@@ -705,7 +687,7 @@ export default function acstack(pi: ExtensionAPI): void {
705
687
 
706
688
  if (isProject(ctx.cwd)) {
707
689
  const orchestrator = readText(orchestratorFile);
708
- if (orchestrator) sections["acstack-orchestrator"] = orchestratorFor(orchestrator, delegated ? "delegated" : "lead");
690
+ if (orchestrator) sections["acstack-orchestrator"] = `${orchestratorFor(orchestrator, delegated ? "delegated" : "lead")}\n\nThis repository's ACStack project folder: ${projectFolder(undefined, ctx.cwd)}\n`;
709
691
  else report(ctx, `acstack: could not read ${orchestratorFile}`, "warning");
710
692
  }
711
693
 
@@ -742,7 +724,7 @@ export default function acstack(pi: ExtensionAPI): void {
742
724
  const lastText = readText(lastFile)?.trim();
743
725
  const lastRung = lastText && /^\d+$/.test(lastText) ? Number.parseInt(lastText, 10) : undefined;
744
726
 
745
- const cfg = readHandoffConfig(readText(path.join(projectRoot(ctx.cwd), ".acstack", "project.yaml")));
727
+ const cfg = readHandoffConfig(readText(path.join(projectFolder(undefined, ctx.cwd) ?? "/nonexistent", "project.yaml")));
746
728
  const nudge = contextNudge(usage.tokens, usage.contextWindow, cfg, lastRung, process.env.HERDR_ENV === "1");
747
729
  if (!nudge) return;
748
730
 
@@ -773,9 +755,9 @@ export default function acstack(pi: ExtensionAPI): void {
773
755
  }
774
756
  }
775
757
 
776
- const root = projectRoot(ctx.cwd);
777
- if (!ensureProjectData(ctx, root)) return;
778
- const handoff = consumeHandoff(root);
758
+ const folder = projectFolder(ctx, ctx.cwd);
759
+ if (folder === null) return;
760
+ const handoff = consumeHandoff(folder);
779
761
  if (handoff) {
780
762
  pi.sendMessage({ customType: "acstack-handoff", content: handoff, display: true }, { deliverAs: "nextTurn" });
781
763
  }
@@ -795,7 +777,7 @@ export default function acstack(pi: ExtensionAPI): void {
795
777
  if (!main) return;
796
778
  let ledger = readLedgers.get(sessionId);
797
779
  if (!ledger) readLedgers.set(sessionId, (ledger = new Set()));
798
- const result = decide({ toolName: event.toolName, toolInput: event.input, cwd: ctx.cwd, main, ledger });
780
+ const result = decide({ toolName: event.toolName, toolInput: event.input, cwd: ctx.cwd, main, ledger, settings: readGuardSettings(ctx.cwd, projectFolder(ctx, ctx.cwd)) });
799
781
  if (result.block) return { block: true, reason: result.message };
800
782
  });
801
783
 
@@ -1,5 +1,5 @@
1
1
  # Fullstack Python (backend) + Node/TS (frontend) profile for acstack
2
- # Used by /acstack:setup to generate .acstack/project.yaml
2
+ # Used by /acstack:setup to generate project.yaml in the project folder
3
3
 
4
4
  commands:
5
5
  test: "pytest tests/ --tb=short -x"
@@ -1,5 +1,5 @@
1
1
  # Node/TypeScript project profile for acstack
2
- # Used by /acstack:setup to generate .acstack/project.yaml
2
+ # Used by /acstack:setup to generate project.yaml in the project folder
3
3
 
4
4
  commands:
5
5
  test: "npm test"
@@ -1,5 +1,5 @@
1
1
  # Python project profile for acstack
2
- # Used by /acstack:setup to generate .acstack/project.yaml
2
+ # Used by /acstack:setup to generate project.yaml in the project folder
3
3
 
4
4
  commands:
5
5
  test: "pytest tests/ --tb=short -x"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: archive
3
- description: Close out a cleared map – one whose frontier is empty with no fog and no claims left – by verifying the durable residue, meaning what the effort leaves behind in living specs, ADRs and the glossary, was extracted, then closing the root and disposing of the nodes per workflow.maps.persist. Invoke when a map has just been cleared, when the last node on a map resolves, or when the user asks to archive or close out a map. Refuses and asks first if anything is still open. Also archives legacy .acstack/changes/ directories from the pre-map workflow.
3
+ description: Close out a cleared map – one whose frontier is empty with no fog and no claims left – by verifying the durable residue, meaning what the effort leaves behind in living specs, ADRs and the glossary, was extracted, then closing the root and disposing of the nodes per workflow.maps.persist. Invoke when a map has just been cleared, when the last node on a map resolves, or when the user asks to archive or close out a map. Refuses and asks first if anything is still open. Also archives legacy changes/ directories from the pre-map workflow.
4
4
  argument-hint: "[map or legacy change name]"
5
5
  ---
6
6
 
@@ -18,12 +18,15 @@ Archive verifies implemented specs, durable decisions, and retained review evide
18
18
 
19
19
  ## Step 1: Locate the map
20
20
 
21
+ ACStack keeps project data in the project folder, the path
22
+ `~/.claude/acstack/bin/acstack project-data path` prints.
23
+
21
24
  If `$ARGUMENTS` names a map, use it. Otherwise list maps on the configured
22
- tracker (`workflow.maps.tracker`): `ls .acstack/maps/` on the files backend,
25
+ tracker (`workflow.maps.tracker`): `ls "$(~/.claude/acstack/bin/acstack project-data path)/maps/"` on the files backend,
23
26
  `gh label list --search "map:" --json name` on GitHub.
24
27
 
25
28
  If exactly one, use it. If several, ask. If `$ARGUMENTS` names a legacy
26
- `.acstack/changes/` directory instead, jump to Legacy below.
29
+ `<project folder>/changes/` directory instead, jump to Legacy below.
27
30
 
28
31
  ## Step 2: Verify no open work remains
29
32
 
@@ -83,7 +86,7 @@ On GitHub, compose the walk per `trackers/github.md`.
83
86
  Check each resolution left what it should have:
84
87
 
85
88
  1. **Living specs** – behaviour the effort built should already be in
86
- `.acstack/specs/` via commit-time sync. Spot-check the affected domains.
89
+ `<project folder>/specs/` via commit-time sync. Spot-check the affected domains.
87
90
 
88
91
  Verify against source and verification evidence, not proposals copied from map nodes.
89
92
  Fix gaps on sight per the Spec Correction Protocol.
@@ -98,9 +101,8 @@ Check each resolution left what it should have:
98
101
  Preserve enough context to trace implemented behavior back to its decisions without retaining proposals as requirements.
99
102
  Stop if unresolved scope still needs a decision rather than an explicit out-of-scope resolution.
100
103
 
101
- 4. **The decision log** – read `<main checkout>/.acstack/decision-logs/<name>.tsv`
102
- if it exists. The main checkout is `dirname` of `git rev-parse
103
- --path-format=absolute --git-common-dir`.
104
+ 4. **The decision log** – read `<project folder>/decision-logs/<name>.tsv`
105
+ if it exists.
104
106
 
105
107
  afk runs append one row per decision an agent made alone. Hand each
106
108
  lasting decision to `record-context`. A later row supersedes an earlier
@@ -130,12 +132,12 @@ If you reached this skill on your own after a map cleared, name what you
130
132
  will delete or move. Then wait for the user's go-ahead. Deletion is
131
133
  irreversible and they never asked for it.
132
134
 
133
- `workflow.maps.persist` in `.acstack/project.yaml` decides (default `false`):
135
+ `workflow.maps.persist` in `<project folder>/project.yaml` decides (default `false`):
134
136
 
135
137
  | Value | Action |
136
138
  | ----------------- | ------------------------------------------------------------ |
137
- | `false` (default) | delete `.acstack/maps/<name>/` |
138
- | `true` | move the nodes to `.acstack/archive/[YYYY-MM-DD]-[name]/nodes/` |
139
+ | `false` (default) | delete `<project folder>/maps/<name>/` |
140
+ | `true` | move the nodes to `<project folder>/archive/[YYYY-MM-DD]-[name]/nodes/` |
139
141
 
140
142
  Default is delete because keeping the nodes leaves a second copy of every
141
143
  decision, and the two drift. On the GitHub tracker there is nothing to
@@ -145,15 +147,15 @@ Retain these records independently of `workflow.maps.persist`:
145
147
 
146
148
  - Authored design entries and referenced source files
147
149
  - Canonical `review.json` files, posted feedback, reconciliation, and exact-revision approval evidence
148
- - Imported legacy history and original `.acstack/alignment-packs/` artifacts
149
- - Main-checkout `.acstack/decision-logs/<name>.tsv` files
150
+ - Imported legacy history and original `<project folder>/alignment-packs/` artifacts
151
+ - `<project folder>/decision-logs/<name>.tsv` files
150
152
  - Handoffs that contain review evidence or provenance not retained elsewhere
151
153
 
152
154
  Never recursively delete alignment, review, decision-log, or handoff directories as archive cleanup.
153
155
  Do not invoke a cleanup helper that erases those records.
154
156
 
155
157
  If a retained source or review record sits inside the proposed node disposal path, stop and resolve its safe retention first.
156
- Respect native Git ignore rules; never force-add private records.
158
+ Never change the Git index or force-add records to commit them.
157
159
 
158
160
  ## Step 6: Report
159
161
 
@@ -162,19 +164,19 @@ Respect native Git ignore rules; never force-add private records.
162
164
  - Disposal: node deletion or archive path, plus retained review evidence and provenance
163
165
  - Reminder: commit with `/acstack:commit`
164
166
 
165
- ## Legacy: archiving a `.acstack/changes/` directory
167
+ ## Legacy: archiving a `<project folder>/changes/` directory
166
168
 
167
169
  The pre-map workflow created changes that hold proposal.md, design.md and
168
170
  tasks.md. They still archive the old way, so an upgrade never strands work:
169
171
 
170
172
  1. Read the change's artifacts. Derive the affected domains from the file
171
- paths in `tasks.md` via `.acstack/domain-map.yaml`.
173
+ paths in `tasks.md` via `<project folder>/domain-map.yaml`.
172
174
 
173
175
  2. For each domain, merge what the feature ADDED, MODIFIED or REMOVED into
174
- `.acstack/specs/[domain]/spec.md`.
176
+ `<project folder>/specs/[domain]/spec.md`.
175
177
 
176
178
  3. Write a `delta.md` into the change directory.
177
179
 
178
180
  Then run the CLI's `acstack archive <name>`. It moves the artifacts,
179
- delta included, to `.acstack/changes/archive/[YYYY-MM-DD]-[change-name]/`.
180
- It also removes the change from `.acstack/changes/`.
181
+ delta included, to `<project folder>/changes/archive/[YYYY-MM-DD]-[change-name]/`.
182
+ It also removes the change from `<project folder>/changes/`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bootstrap
3
- description: Multi-pass AI crawl to generate initial living specs from the existing codebase. Use when .acstack/specs/ domains need their initial spec.md files bootstrapped from code.
3
+ description: Multi-pass AI crawl to generate initial living specs from the existing codebase. Use when the project folder's specs/ domains need their initial spec.md files bootstrapped from code.
4
4
  argument-hint: "[domain-names (optional)]"
5
5
  disable-model-invocation: true
6
6
  ---
@@ -15,9 +15,9 @@ Consider the user input before you proceed, if it is not empty.
15
15
 
16
16
  ## Purpose
17
17
 
18
- This skill bootstraps cumulative living specs in `.acstack/specs/`. It crawls the existing codebase with multiple independent AI passes. It then merges the passes and validates the results.
18
+ This skill bootstraps cumulative living specs in `<project folder>/specs/`. The project folder is the path `~/.claude/acstack/bin/acstack project-data path` prints; pass its absolute path to every subagent that reads or writes specs. It crawls the existing codebase with multiple independent AI passes. It then merges the passes and validates the results.
19
19
 
20
- **IMPORTANT**: This is a one-time bootstrapping operation. Only run when `.acstack/specs/` domains have no `spec.md` files yet.
20
+ **IMPORTANT**: This is a one-time bootstrapping operation. Only run when `<project folder>/specs/` domains have no `spec.md` files yet.
21
21
 
22
22
  ## Your role: orchestrator only
23
23
 
@@ -33,9 +33,9 @@ Your job is to:
33
33
 
34
34
  ## Pre-flight check
35
35
 
36
- 1. Read `.acstack/domain-map.yaml` to get the list of domains and their file path mappings.
36
+ 1. Read `<project folder>/domain-map.yaml` to get the list of domains and their file path mappings.
37
37
  2. If `$ARGUMENTS` specifies domain names, filter to only those domains.
38
- 3. Check if any `.acstack/specs/*/spec.md` files already exist:
38
+ 3. Check if any `<project folder>/specs/*/spec.md` files already exist:
39
39
  - If specs exist, warn the user. Ask for confirmation before overwriting.
40
40
  - If no specs exist, proceed automatically.
41
41
 
@@ -98,7 +98,7 @@ Launch a single subagent to apply validated corrections from Phase 3.
98
98
 
99
99
  ## Phase 5: human review (pass 9)
100
100
 
101
- Present a summary to the user. Ask: "Review the specs in `.acstack/specs/`. Want to edit any domain before finalizing?"
101
+ Present a summary to the user. Ask: "Review the specs in `<project folder>/specs/`. Want to edit any domain before finalizing?"
102
102
 
103
103
  ## Phase 6: finalization (pass 10)
104
104
 
@@ -10,7 +10,7 @@
10
10
  binds `127.0.0.1:19988` on this host.
11
11
 
12
12
  - `agent-browser` (installed per the `browser-verify` skill) pointed at
13
- that endpoint via `.acstack/agent-browser.json`.
13
+ that endpoint via `agent-browser.json` in the project folder.
14
14
 
15
15
  - `vm-free-port.sh` for clearing a stuck port 19988 after a dropped
16
16
  session.
@@ -42,17 +42,17 @@ and is driving a real Chrome on the laptop.
42
42
 
43
43
  See the `browser-verify` skill for the install command.
44
44
 
45
- ### 2. Create `.acstack/agent-browser.json` in each configured project
45
+ ### 2. Create `agent-browser.json` in each configured project's folder
46
46
 
47
47
  ```json
48
48
  { "cdp": "19988" }
49
49
  ```
50
50
 
51
- Run `~/.claude/acstack/bin/acstack project-data ensure` after creating `.acstack/`.
52
- Select the config explicitly from the project root before browser commands:
51
+ The project folder is the path `~/.claude/acstack/bin/acstack project-data path` prints from the repository.
52
+ Select the config explicitly before browser commands:
53
53
 
54
54
  ```bash
55
- export AGENT_BROWSER_CONFIG="$(pwd)/.acstack/agent-browser.json"
55
+ export AGENT_BROWSER_CONFIG="$(~/.claude/acstack/bin/acstack project-data path)/agent-browser.json"
56
56
  ```
57
57
 
58
58
  For manual CDP checks without project configuration, use `agent-browser --cdp 19988 ...` instead; do not pass a missing config.
@@ -299,7 +299,7 @@ Two tasks run it:
299
299
  Every request from a VM carries a shared secret called the opener token.
300
300
  `register-tasks.ps1` generates it and stores it at
301
301
  `%LOCALAPPDATA%\playwriter-bridge\opener.token`. It then pushes the token over
302
- `ssh` to `~/.config/acstack/opener.token` on each VM.
302
+ `ssh` to `~/.acstack/opener.token` on each VM.
303
303
 
304
304
  The token is not optional. The reverse tunnel makes 19989 reachable by
305
305
  anything running on the VM, and this service puts pages on your screen.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: browser-verify
3
- description: How to interact with a browser for frontend verification using agent-browser CLI and CDP. ALWAYS use when UI or frontend files are modified and workflow.frontend_verification is true in .acstack/project.yaml. Also use before running agent-browser commands, when element refs go stale, when CDP connection fails, or when verifying UI behavior. Covers all commands (snapshot, screenshot, click, fill, type), element ref strategy, DOM staleness rules, selector priority, and remote/headless/local environment troubleshooting.
3
+ description: How to interact with a browser for frontend verification using agent-browser CLI and CDP. ALWAYS use when UI or frontend files are modified and workflow.frontend_verification is true in the project's project.yaml. Also use before running agent-browser commands, when element refs go stale, when CDP connection fails, or when verifying UI behavior. Covers all commands (snapshot, screenshot, click, fill, type), element ref strategy, DOM staleness rules, selector priority, and remote/headless/local environment troubleshooting.
4
4
  ---
5
5
 
6
6
  # Browser helpers
@@ -13,7 +13,7 @@ For setup (installing agent-browser, creating config files, scaffolding knowledg
13
13
 
14
14
  ## Project configuration
15
15
 
16
- Read `.acstack/project.yaml` for:
16
+ ACStack keeps project data in the project folder, the path `~/.claude/acstack/bin/acstack project-data path` prints. Read `<project folder>/project.yaml` for:
17
17
 
18
18
  - `frontend.directory` – frontend source directory
19
19
  - `frontend.dev_server_url` – dev server URL
@@ -52,7 +52,7 @@ Without `--json` it prints the same two facts on one line: the mode, then the re
52
52
 
53
53
  ### How it decides
54
54
 
55
- The host declares which modes this machine can provide. It does so on three axes in the global config at `~/.config/acstack/config.json`:
55
+ The host declares which modes this machine can provide. It does so on three axes in the global config at `~/.acstack/config.json` (`$ACSTACK_HOME/config.json` when that variable is set):
56
56
 
57
57
  - `host.browser.remote` – this machine can reach a browser on another machine
58
58
  - `host.browser.headless` – this machine can launch headless Chromium
@@ -181,13 +181,13 @@ Omit `--headless` to see the browser. Useful during development.
181
181
 
182
182
  ## agent-browser command reference
183
183
 
184
- For configured verification, explicitly select the absolute `.acstack/agent-browser.json` path before running the commands below:
184
+ For configured verification, explicitly select the absolute `<project folder>/agent-browser.json` path before running the commands below:
185
185
 
186
186
  ```bash
187
- export AGENT_BROWSER_CONFIG="$(pwd)/.acstack/agent-browser.json"
187
+ export AGENT_BROWSER_CONFIG="$(~/.claude/acstack/bin/acstack project-data path)/agent-browser.json"
188
188
  ```
189
189
 
190
- - Run this from the project root only when the file exists; alternatively pass `--config /absolute/project/.acstack/agent-browser.json`
190
+ - Run this only when the file exists; alternatively pass `--config` with the same absolute path
191
191
  - Do not rely on root config autodiscovery
192
192
  - Manual CDP quality checks remain available without project configuration or a configured browser mode
193
193
  - For those checks, omit the missing config and use `agent-browser --cdp <port> ...` with the explicitly chosen endpoint
@@ -45,25 +45,25 @@ For each planned commit:
45
45
 
46
46
  **This step is non-negotiable.** After drafting the commit message but BEFORE creating the commit, spec sync runs automatically.
47
47
 
48
- Read `.acstack/project.yaml` – if `workflow.spec_sync` is `true` (or not explicitly `false`), run spec sync:
48
+ The project folder is the path `~/.claude/acstack/bin/acstack project-data path` prints. Read `<project folder>/project.yaml` – if `workflow.spec_sync` is `true` (or not explicitly `false`), run spec sync:
49
49
 
50
- 1. Read `.acstack/domain-map.yaml` (if exists)
50
+ 1. Read `<project folder>/domain-map.yaml` (if exists)
51
51
  2. Map staged files to spec domains
52
- 3. For each affected domain where `.acstack/specs/[domain]/spec.md` exists:
52
+ 3. For each affected domain where `<project folder>/specs/[domain]/spec.md` exists:
53
53
  - Analyze what the staged changes add, modify, or remove
54
54
  - Generate lightweight ADDED/MODIFIED/REMOVED entries
55
55
  - Update the domain's spec.md
56
56
  4. Refresh `DESIGN.md`, per Step 2 of the `sync` skill
57
- 5. Stage updated specs only when native `.gitignore` rules allow tracking
58
- - Keep ignored specs updated locally; never force-add them or change the Git index to bypass this choice
57
+ 5. Stage updated specs only when they sit inside the current working tree
58
+ - That happens only in tracked mode, when you commit in the main checkout
59
+ - Otherwise the specs stay updated in the project folder and out of the commit
59
60
  - Stage `DESIGN.md` and `.impeccable/design.json` too when the refresh ran
60
- 6. Include only opted-in spec updates in the commit
61
- 7. Flag unmapped source files and prompt user to map them
61
+ 6. Flag unmapped source files and prompt user to map them
62
62
 
63
63
  **Skip spec sync ONLY when:**
64
64
 
65
65
  - `workflow.spec_sync` is explicitly `false` in project.yaml
66
- - No `.acstack/domain-map.yaml` exists
66
+ - No `<project folder>/domain-map.yaml` exists
67
67
  - Changed files don't match any domain
68
68
  - Changes are docs/config only (no behavioral changes)
69
69
  - No spec.md exists for affected domains (don't create from scratch here)
@@ -55,7 +55,8 @@ instead – in your final message, or through your host's agent-messaging tool
55
55
  or compacts.
56
56
 
57
57
  A child session would write the lead's quiet marker and the shared
58
- `.acstack/HANDOFF.md` anchor. Both belong to the lead alone.
58
+ `<project folder>/HANDOFF.md` anchor, where the project folder is the path
59
+ `~/.claude/acstack/bin/acstack project-data path` prints. Both belong to the lead alone.
59
60
 
60
61
  Five facts sit behind that command, measured on Claude Code 2.1.241. Each one
61
62
  names a way the command has already been got wrong:
@@ -99,8 +100,8 @@ something by running a command, it should run the command.
99
100
  | Map state and reading order | `~/.claude/acstack/bin/acstack node walk --map <name>` (reading order of the map) |
100
101
  | What can be worked next | `~/.claude/acstack/bin/acstack node frontier --map <name>` (what is workable now) |
101
102
  | Files in flight | `git status --short` and `git diff` |
102
- | Test baseline, last result | `.acstack/.test-baseline` and referenced check logs; reuse valid evidence under the shared policy |
103
- | What the code does | the living specs in `.acstack/specs/` |
103
+ | Test baseline, last result | `<project folder>/.test-baseline` and referenced check logs; reuse valid evidence under the shared policy |
104
+ | What the code does | the living specs in `<project folder>/specs/` |
104
105
 
105
106
  On GitHub, `--map` refuses – pipe `gh issue list` into `--stdin` per the
106
107
  map skill's `trackers/github.md`.
@@ -143,7 +144,7 @@ not optional.
143
144
 
144
145
  ## Write the anchor
145
146
 
146
- Write `.acstack/HANDOFF.md`. The frontmatter is **mandatory and machine-read**.
147
+ Write `"$(~/.claude/acstack/bin/acstack project-data path)/HANDOFF.md"`. The frontmatter is **mandatory and machine-read**.
147
148
  The hook ignores any file without the marker, so it never touches a user's own
148
149
  unrelated `HANDOFF.md`.
149
150
 
@@ -218,9 +219,10 @@ note.
218
219
  Tell them to run that `/compact` line, then to type `continue`. The hook
219
220
  re-injects the anchor and retires it, so the anchor never loads twice.
220
221
 
221
- `.acstack/HANDOFF.md` holds conversation content and stays local by default.
222
- Run `~/.claude/acstack/bin/acstack project-data ensure` after creating `.acstack/`.
223
- Warn if user exceptions expose the anchor or consumed handoffs to tracking.
222
+ `<project folder>/HANDOFF.md` holds conversation content. In global mode, the
223
+ default, it sits outside the repository. In tracked mode it sits in the main
224
+ checkout's committed `.acstack/`, so warn the user not to commit the anchor or
225
+ consumed handoffs.
224
226
 
225
227
  ## Notes
226
228
 
@@ -232,8 +234,8 @@ Warn if user exceptions expose the anchor or consumed handoffs to tracking.
232
234
  skill moves the work to a genuinely new session
233
235
 
234
236
  - **Consume-once.** The hook retires the anchor as it injects it, moving it to
235
- `.acstack/handoffs/<map>/<created>.md` (legacy
236
- `.acstack/changes/<name>/handoffs/` when that directory still exists).
237
+ `<project folder>/handoffs/<map>/<created>.md` (legacy
238
+ `<project folder>/changes/<name>/handoffs/` when that directory still exists).
237
239
  Collisions get a `-2` suffix
238
240
 
239
241
  - **Subagents and teammates have isolated context.** This protects the
@@ -34,7 +34,8 @@ flowchart LR
34
34
 
35
35
  ## Author entry and element files
36
36
 
37
- - Default new project designs to `.acstack/designs/<id>/design.yaml`, unless the user chooses another location
37
+ - Default new project designs to `<project folder>/designs/<id>/design.yaml`, unless the user chooses another location
38
+ - The project folder is the path `~/.claude/acstack/bin/acstack project-data path` prints
38
39
  - Keep the entry and its referenced files in one directory tree
39
40
  - Use stable IDs matching `[a-zA-Z][a-zA-Z0-9_-]{0,79}`
40
41
  - Give the entry `id`, `name`, `summary`, and an `elements` array of relative YAML paths
@@ -172,7 +173,9 @@ relationships:
172
173
  ## Reconcile and approve a map-linked design
173
174
 
174
175
  - Invoke this workflow from map for shared behavior, component boundaries, or data-flow decisions
175
- - Store `Design entry: <repository-relative-path>` in the map root body
176
+ - Store `Design entry: <path>` in the map root body
177
+ - Write the path relative to the project folder, such as `designs/<id>/design.yaml`, or relative to the repository for a design kept in the repository
178
+ - ACStack resolves it against the project folder first, then the current checkout
176
179
  - Add `planning: {tracker: files | github, map: <name>}` to the entry
177
180
  - Use the same contribution format in files and GitHub node bodies
178
181
  - Put one fenced JSON array under `## Design contributions`
@@ -213,7 +216,7 @@ relationships:
213
216
  - ACStack removes `map --map` and `align-viewer` without aliases
214
217
  - Author the new design first; the old planning graph cannot define its architecture
215
218
  - Keep map planning in `node walk`, `node frontier`, and `node diagram`
216
- - Use a nonsymlink legacy directory under `.acstack/alignment-packs/`
219
+ - Use a nonsymlink legacy directory under `<project folder>/alignment-packs/`
217
220
  - Write an explicit JSON mapping array of `{from: {type, id}, to: {type, id}}` pairs
218
221
  - Map every commented legacy target to a valid current target
219
222
  - Choose document context explicitly when a legacy target is ambiguous
@@ -224,7 +227,7 @@ relationships:
224
227
  ```
225
228
 
226
229
  - Supply `--feedback` only for an explicit exported feedback JSON file
227
- - Migration reads saved pack versions, approvals, annotations, and the corresponding main-checkout decision log
230
+ - Migration reads saved pack versions, approvals, annotations, and the corresponding decision log in the project folder
228
231
  - Migration preserves original artifacts and decision logs byte-for-byte
229
232
  - Canonical `review.json` retains imported history, source fingerprints, and original comments, quotes, authors, timestamps, and provenance
230
233
  - Imported comments append to existing feedback; duplicate source comments import once
@@ -235,4 +238,4 @@ relationships:
235
238
  - Reconcile imported feedback and obtain current exact-revision approval before implementation or archive
236
239
  - Never migrate automatically at startup or archive
237
240
  - Retain design sources, canonical reviews, import history, and decision logs independently of `workflow.maps.persist`
238
- - Respect native Git ignore rules; never force-add private review records
241
+ - Never change the Git index or force-add review records
@@ -92,7 +92,7 @@ You have full context of the ACStack system. Use it naturally, don't force it.
92
92
  ### Check for context
93
93
 
94
94
  At the start, quickly check what exists – maps on the configured tracker
95
- (`ls .acstack/maps/` on the files backend) and living specs
95
+ (`ls "$(~/.claude/acstack/bin/acstack project-data path)/maps/"` on the files backend) and living specs
96
96
  (`~/.claude/acstack/bin/acstack list --specs --json`).
97
97
 
98
98
  ### When no map exists
@@ -65,7 +65,8 @@ session.
65
65
 
66
66
  ## Presentation
67
67
 
68
- `workflow.grilling.questions` in `.acstack/project.yaml` picks the mode:
68
+ `workflow.grilling.questions` in `<project folder>/project.yaml` picks the mode.
69
+ The project folder is the path `~/.claude/acstack/bin/acstack project-data path` prints.
69
70
 
70
71
  | Value | Behaviour |
71
72
  | ---------------- | ------------------------------------------------------------------------------------ |
@@ -127,8 +128,8 @@ belong in git:
127
128
  ```markdown
128
129
  ## Answer
129
130
 
130
- Map nodes stay local by default under `.acstack/maps/`.
131
- The user can opt in through native root `.gitignore` exceptions.
131
+ On the files backend, map nodes live under `<project folder>/maps/`.
132
+ In global mode, the default, that folder sits outside the repository, so nodes are never committed.
132
133
 
133
134
  The durable output is the living specs, the architecture decision records and
134
135
  the glossary. The `record-context` skill extracts each one as a node