acstack 0.52.4 → 0.52.6

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "acstack",
3
3
  "description": "Agentic coding stack where planning grows only as far as needed: a map of decisions, tasks, checks, and handoffs behind a pluggable tracker.",
4
- "version": "0.52.4",
4
+ "version": "0.52.6",
5
5
  "author": {
6
6
  "name": "ac8318740"
7
7
  },
package/README.md CHANGED
@@ -284,7 +284,7 @@ What an effort leaves behind is what matters: updated specs, decision notes, and
284
284
  | `/acstack:bridge` | Set up and operate the Playwriter cross-device browser bridge |
285
285
  | `/acstack:visual-docs` | Write docs that lead with a diagram and derive structure from it (Minto pyramid) |
286
286
  | `/acstack:new-worktree` | Create a git worktree off the remote base branch and continue the task inside it |
287
- | `/acstack:teardown-worktree` | Retire finished worktrees and delete their merged local and remote branches |
287
+ | `/acstack:teardown-worktree` | Retire finished worktrees, delete their merged local and remote branches, and clean build outputs in worktrees that stay |
288
288
  | `/acstack:terminal-workspace` | Set up a keyboard-only terminal for driving agents on a dev machine |
289
289
 
290
290
  ## 6. Every agent
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack-cli",
3
- "version": "0.52.4",
3
+ "version": "0.52.6",
4
4
  "description": "CLI for the ACStack agentic coding stack",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -206,10 +206,13 @@ if ! command -v python3 >/dev/null 2>&1; then
206
206
  fi
207
207
 
208
208
  python3 - "$claude_md" <<'PY'
209
- import json, sys
209
+ import json, re, sys
210
210
  path = sys.argv[1]
211
211
  with open(path, 'r', encoding='utf-8') as f:
212
212
  content = f.read()
213
+ # SessionStart reaches the lead session, so it keeps the lead block and drops the delegated one.
214
+ content = re.sub(r'<!-- acstack:delegated:start -->[\s\S]*?<!-- acstack:delegated:end -->\n?', '', content)
215
+ content = re.sub(r'<!-- acstack:lead:(start|end) -->\n?', '', content)
213
216
  print(json.dumps({
214
217
  "hookSpecificOutput": {
215
218
  "hookEventName": "SessionStart",
@@ -2,13 +2,31 @@
2
2
 
3
3
  ACStack keeps meaningful tests and chooses verification effort to fit the change.
4
4
 
5
+ <!-- acstack:lead:start -->
5
6
  ## Your role
6
7
 
7
8
  - Understand the request, investigate the code, implement, and verify the result
8
9
  - Work directly on straightforward tasks, including research, tests, code, and checks
9
- - Delegate when another agent materially improves confidence or handles an independent scope
10
+ - Delegate when another agent materially improves confidence, handles an independent scope, or keeps broad discovery out of your context
10
11
  - Honor explicit user requests for stronger process, including separate agents and tests-first work
11
12
  - Follow the shared Implementation discipline below across implement, quick-fix, and map work
13
+ - Keep the user's session continuous
14
+ - Use `/acstack:handoff` to transfer work to a visible agent
15
+ - Use `/acstack:compact-and-continue` to preserve session state before compaction
16
+ <!-- acstack:lead:end -->
17
+ <!-- acstack:delegated:start -->
18
+ ## Your role
19
+
20
+ - You are a delegated session: the brief your parent gave at launch sets your role, scope, and access
21
+ - You may be an investigator, an implementer, or a coordinator of your own scope
22
+ - The shared guidance below applies wherever the brief does not narrow it
23
+ - Stay inside the assigned scope and access
24
+ - Report work outside them to your parent instead of doing it
25
+ - Treat a no-edit instruction as binding even when the launcher does not enforce it
26
+ - Delegate further only when the brief allows it, and never grant a child more access than you have
27
+ - Report results, evidence, and blockers to your parent
28
+ - Your parent owns synthesis and completion unless the brief assigns them to you
29
+ <!-- acstack:delegated:end -->
12
30
 
13
31
  ## Non-negotiable rules
14
32
 
@@ -40,12 +58,15 @@ ACStack runs on Claude Code, Codex, and pi. The skills name each capability once
40
58
 
41
59
  - **Launch a subagent** – start a child agent with its own context, by its ACStack name (`test-writer`, `task-executor`, `task-checker`, `frontend-verifier`)
42
60
  - Claude Code: the Agent tool, with `subagent_type` set to that name
43
- - pi: `Agent` from `@tintinweb/pi-subagents` (`subagent_type`), or `subagent` from `pi-subagents` (`agent`)
61
+ - pi: `Agent` from `@tintinweb/pi-subagents` (`subagent_type`), `subagent` from `pi-subagents` (`agent`), or `team_agent` from ac-teams
62
+ - `team_agent` does not load ACStack agents by name, so put the role's instructions in the brief
63
+ - ac-teams describes its member access controls in its own `docs/access.md`
44
64
  - **Read-only codebase-search subagent** – `Explore` on Claude Code and pi's `Agent`, or `scout` on pi's `subagent`
45
65
  - `debugger` names a debugging agent
46
66
  - On a host without a matching built-in agent, launch a general subagent with that brief
47
67
  - **Agent team** – parallel full sessions, each owning one scope and launching its own subagents
48
68
  - Claude Code has agent teams, with messaging between teammates and plan approval by the lead
69
+ - pi has ac-teams, through `team_agent`
49
70
  - On a host without agent teams, work directly or delegate scopes sequentially
50
71
  - When the task assigns separate test ownership, keep it separate from implementation
51
72
  - **Ask the user a structured question** – the host's question tool (AskUserQuestion on Claude Code)
@@ -215,6 +236,40 @@ flowchart TD
215
236
  - Check the design gate and load design context once per task when it is on
216
237
  - Follow the same procedure for direct work and delegated implementation
217
238
 
239
+ ### Delegate investigation
240
+
241
+ - Prefer delegated investigation when discovery means broad searches, substantial reading, independent hypotheses, or separable research questions
242
+ - Delegation keeps raw discovery out of your context, even when the investigator runs your own model
243
+ - Work directly when the paths are few and known, or when briefing costs more than searching
244
+ - Launch one investigator per separable question, with no fixed count
245
+ - Launch the host's read-only search agent for navigation questions (see Host tools)
246
+ - For other questions, launch a general agent with the investigator brief below
247
+ - With no launcher available, investigate directly and say that delegation was unavailable
248
+ - Choose the model separately from the role
249
+ - Prefer a configured cheaper, capable model for navigation and fact gathering
250
+ - Use a stronger model for hard causal or ambiguous analysis
251
+ - Never hardcode a provider or model; use what the host or user configured
252
+ - Request enforced read-only access when the launcher reports that it can enforce it
253
+ - Expect prompt-only protection on most hosts today
254
+ - A read-only search agent such as `Explore` lacks edit tools; that limits its tools but is not operating-system enforcement
255
+ - Without enforcement, instruct the investigator not to edit project files, and call that protection prompt-only
256
+ - If an enforced launch fails, report the failure and ask before launching without enforcement
257
+ - Own the synthesis: verify the decisive evidence a conclusion rests on, without repeating the whole investigation
258
+
259
+ The investigator brief states:
260
+
261
+ - The question, and why the answer matters
262
+ - The scope: paths, repositories, or sources to search, and what is out of scope
263
+ - The access: enforced read-only, or a prompt-only instruction not to edit project files
264
+ - The report the parent needs back
265
+
266
+ The investigator reports:
267
+
268
+ - Findings, with file paths and line references
269
+ - Which findings rest on evidence and which are hypotheses
270
+ - What it searched, so the parent knows what it did not cover
271
+ - Uncertainties, and the next checks worth running
272
+
218
273
  ### Use independent review where needed
219
274
 
220
275
  - Require independent review for security boundaries, money, irreversible data changes, or unresolved correctness or impact uncertainty
@@ -414,8 +469,8 @@ Launch each agent by the name below (see Host tools for how your host takes it).
414
469
  | Implement task | `task-executor` | Source and tests unless tests have a separate owner |
415
470
  | Verify implementation | `task-checker` | Independent review, coverage adequacy, regressions |
416
471
  | Verify frontend UI | `frontend-verifier` | Real browser, screenshots, non-negotiable |
417
- | Find/locate something | `Explore` / `scout` | Read-only codebase search; `scout` on pi |
418
- | Debug/investigate | `debugger` | Built-in debugging agent |
472
+ | Investigate a question | `Explore` / `scout`, or a general agent | Read-only; brief per Delegate investigation |
473
+ | Debug | `debugger` | Built-in debugging agent |
419
474
 
420
475
  ---
421
476
 
@@ -436,5 +491,3 @@ Launch each agent by the name below (see Host tools for how your host takes it).
436
491
  - Keep living specs current through commit-time sync
437
492
  - Create a map when unresolved questions need durable tracking
438
493
  - Invoke `bridge` before cross-device browser or tunnel work
439
- - Use `/acstack:handoff` to transfer work to a visible agent
440
- - Use `/acstack:compact-and-continue` to preserve session state before compaction
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack",
3
- "version": "0.52.4",
3
+ "version": "0.52.6",
4
4
  "private": false,
5
5
  "description": "Agentic coding stack with first-class Claude Code and pi support: skills, orchestrator instructions, and verification agents.",
6
6
  "license": "MIT",
package/pi/extension.ts CHANGED
@@ -80,6 +80,8 @@ function ensureProjectData(ctx: ExtensionContext, root: string): boolean {
80
80
  const SUBAGENT_CHILD_ENV = "PI_SUBAGENT_CHILD";
81
81
  /** pi-subagents sets this instead in a child it places in its own herdr pane. */
82
82
  const HERDR_BRIDGE_CHILD_ENV = "PI_SUBAGENTS_HERDR_BRIDGE";
83
+ /** ac-teams sets this in every member it launches. */
84
+ const AC_TEAMS_MEMBER_ENV = "AC_TEAMS_MEMBER";
83
85
 
84
86
  function agentDir(): string {
85
87
  return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent");
@@ -622,6 +624,18 @@ export function skillsSection(skills: Map<string, SkillInfo>): string | undefine
622
624
  return lines.join("\n");
623
625
  }
624
626
 
627
+ /**
628
+ * The orchestrator text for one kind of session. orchestrator/AGENTS.md marks
629
+ * the lead-only and delegated-only blocks; each session keeps its own block
630
+ * and drops the other.
631
+ */
632
+ export function orchestratorFor(text: string, role: "lead" | "delegated"): string {
633
+ const other = role === "lead" ? "delegated" : "lead";
634
+ return text
635
+ .replace(new RegExp(`<!-- acstack:${other}:start -->[\\s\\S]*?<!-- acstack:${other}:end -->\\n?`, "g"), "")
636
+ .replace(new RegExp(`<!-- acstack:${role}:(start|end) -->\\n?`, "g"), "");
637
+ }
638
+
625
639
  /**
626
640
  * The user message for one ACStack skill, in the same shape pi's own
627
641
  * /skill:<name> expansion produces. Built from this package's file, so a
@@ -640,9 +654,9 @@ export function skillInvocation(name: string, file: string, args: string): strin
640
654
  // --------------------------------------------------------------------------
641
655
 
642
656
  export default function acstack(pi: ExtensionAPI): void {
643
- // A background subagent loads every ambient extension. The orchestrator
644
- // prompt and the nudges belong to the lead session, never to a subagent.
645
- if (process.env[SUBAGENT_CHILD_ENV] === "1" || process.env[HERDR_BRIDGE_CHILD_ENV] === "1") return;
657
+ // A delegated session keeps the shared guidance but not the lead's role,
658
+ // CLI symlinks, launcher warning, context nudges, or handoff reload.
659
+ const delegated = process.env[SUBAGENT_CHILD_ENV] === "1" || process.env[HERDR_BRIDGE_CHILD_ENV] === "1" || Boolean(process.env[AC_TEAMS_MEMBER_ENV]);
646
660
 
647
661
  const orchestratorFile = path.join(packageRoot, "orchestrator", "AGENTS.md");
648
662
  const styleFile = path.join(packageRoot, "output-styles", "ac-writing-style.md");
@@ -651,7 +665,7 @@ export default function acstack(pi: ExtensionAPI): void {
651
665
  let warnedSubagents = false;
652
666
 
653
667
  pi.on("session_start", async (event, ctx) => {
654
- linkClis(ctx);
668
+ if (!delegated) linkClis(ctx);
655
669
  const root = projectRoot(ctx.cwd);
656
670
  if (ensureProjectData(ctx, root)) unsafeProjects.delete(root);
657
671
  else {
@@ -659,6 +673,7 @@ export default function acstack(pi: ExtensionAPI): void {
659
673
  return;
660
674
  }
661
675
 
676
+ if (delegated) return;
662
677
  if (!warnedSubagents && !pi.getAllTools().some((tool) => tool.name === "subagent" || tool.name === "Agent" || tool.name === "team_agent")) {
663
678
  warnedSubagents = true;
664
679
  report(
@@ -686,14 +701,19 @@ export default function acstack(pi: ExtensionAPI): void {
686
701
 
687
702
  if (isProject(ctx.cwd)) {
688
703
  const orchestrator = readText(orchestratorFile);
689
- if (orchestrator) sections["acstack-orchestrator"] = orchestrator;
704
+ if (orchestrator) sections["acstack-orchestrator"] = orchestratorFor(orchestrator, delegated ? "delegated" : "lead");
690
705
  else report(ctx, `acstack: could not read ${orchestratorFile}`, "warning");
691
706
  }
692
707
 
693
708
  const styleCfg = writingStyleSettings();
694
709
  if (styleCfg.mode === "off") return;
695
710
  const resolvedStyleFile = styleCfg.mode === "custom" && styleCfg.path ? expandUserPath(styleCfg.path) : styleFile;
696
- const style = readText(resolvedStyleFile);
711
+ let style = readText(resolvedStyleFile);
712
+ // A sandboxed delegated session may not see a custom file outside its read roots.
713
+ if (!style && delegated && resolvedStyleFile !== styleFile) {
714
+ report(ctx, `acstack: could not read writing style at ${resolvedStyleFile}; using the bundled style`, "warning");
715
+ style = readText(styleFile);
716
+ }
697
717
  if (!style) {
698
718
  report(ctx, `acstack: could not read writing style at ${resolvedStyleFile}`, "warning");
699
719
  return;
@@ -705,7 +725,7 @@ export default function acstack(pi: ExtensionAPI): void {
705
725
  return { message: { customType: "acstack-writing-style-reminder", content: reminder, display: false } };
706
726
  });
707
727
 
708
- pi.on("agent_before_settle", async (event, ctx) => {
728
+ if (!delegated) pi.on("agent_before_settle", async (event, ctx) => {
709
729
  if (event.outcome !== "completed") return;
710
730
  const usage = ctx.getContextUsage();
711
731
  if (!usage || usage.tokens === null) return;
@@ -736,7 +756,7 @@ export default function acstack(pi: ExtensionAPI): void {
736
756
  };
737
757
  });
738
758
 
739
- pi.on("session_compact", async (_event, ctx) => {
759
+ if (!delegated) pi.on("session_compact", async (_event, ctx) => {
740
760
  // The compaction threw the old context away, so the ladder starts over.
741
761
  const key = sessionKey(ctx);
742
762
  if (key) {
@@ -1,14 +1,12 @@
1
1
  ---
2
2
  name: teardown-worktree
3
- description: "Retire finished git worktrees – move this session back to the main checkout, move the orchestrator's pane back to the main repo workspace, remove the checkouts, and delete the merged local and remote branches. Scans the whole repo, not just the current worktree. Use whenever the user says \"tear down the worktree\", \"clean up worktrees\", \"remove this worktree\", \"delete the worktree and branch\", \"clean up stale worktrees\", or otherwise wants finished worktrees and their branches gone."
3
+ description: "Retire finished git worktrees – move this session back to the main checkout, move the orchestrator's pane back to the main repo workspace, remove the checkouts, delete the merged local and remote branches, and offer to clean rebuildable build outputs in worktrees that stay. Scans the whole repo, not just the current worktree. Use whenever the user says \"tear down the worktree\", \"clean up worktrees\", \"remove this worktree\", \"delete the worktree and branch\", \"clean up stale worktrees\", \"free disk space from old sessions\", or otherwise wants finished worktrees and their branches gone."
4
4
  argument-hint: "[worktree name, or nothing to scan the whole repo]"
5
5
  ---
6
6
 
7
7
  # Teardown worktree
8
8
 
9
- Retire finished worktrees.
10
-
11
- Move this session out first. Remove the worktrees next. Delete the branches they were on last.
9
+ Retire finished worktrees. Move this session out first. Remove the worktrees next. Delete the branches they were on last.
12
10
 
13
11
  Removing a worktree this session is standing in, or one that still holds a running agent, destroys live work. The order below exists to prevent that, as far as either orchestrator can tell. Follow it.
14
12
 
@@ -24,9 +22,7 @@ One repo per run: the repo that owns the cwd. Resolve its main checkout from any
24
22
  dirname "$(git rev-parse --git-common-dir)"
25
23
  ```
26
24
 
27
- Submodules are separate repos with their own worktrees and their own remote.
28
-
29
- If the repo has submodules carrying worktrees, say so. Offer a second run against each. Never scan them silently.
25
+ Submodules are separate repos with their own worktrees and their own remote. If the repo has submodules carrying worktrees, say so. Offer a second run against each. Never scan them silently.
30
26
 
31
27
  ## 1. Build the plan
32
28
 
@@ -38,11 +34,11 @@ List every worktree of the repo:
38
34
  git worktree list --porcelain
39
35
  ```
40
36
 
41
- Skip the main checkout. First settle who hosts this session and who owns each checkout, then classify each remaining worktree on the four checks that follow.
37
+ Skip the main checkout. First settle who hosts this session and who owns each checkout, then classify each remaining worktree on the five checks that follow.
42
38
 
43
39
  ### Which orchestrator hosts this session, and which one owns each checkout
44
40
 
45
- Settle both before the four checks. A worktree orchestrator is a tool that opens panes and holds worktrees for you – herdr and Orca are the two this skill knows.
41
+ Settle both before the five checks. A worktree orchestrator is a tool that opens panes and holds worktrees for you – herdr and Orca are the two this skill knows.
46
42
 
47
43
  Two different facts drive this skill:
48
44
 
@@ -95,9 +91,11 @@ git -C <path> status --porcelain --ignore-submodules=all
95
91
 
96
92
  Report pointer drift in the plan anyway, from `git -C <path> submodule status`. That way a real pending bump stays visible, and the ignore flag does not hide it.
97
93
 
98
- Any output from the status check means skip.
94
+ Any output from the status check means skip. Do not remove it. Do not force it. List it at the end with what it holds.
95
+
96
+ ### Ignored data
99
97
 
100
- Do not remove it. Do not force it. List it at the end with what it holds.
98
+ Removal deletes every file git ignores, forced or not, and the status check cannot see them. Follow `artifacts.md` step 1. Any preserve path, such as `.env` or Terraform state, means skip unless the user approves losing that named path.
101
99
 
102
100
  ### Merged
103
101
 
@@ -135,7 +133,9 @@ Nothing holds agent state, so nothing can confirm liveness. Classification then
135
133
 
136
134
  ### Show it
137
135
 
138
- Print one table with six columns: worktree, branch, whether the local branch goes, whether the remote branch goes, blocking panes, and the reason. Then ask once.
136
+ Print one table with seven columns: worktree, branch, whether the local branch goes, whether the remote branch goes, blocking panes, ignored data (each preserve path with its size, then the rebuildable total), and the reason.
137
+
138
+ Below the table, list the build outputs step 5 could clean in worktrees that stay. Then ask once: the user approves the removals and picks which cleans to run.
139
139
 
140
140
  The blocking panes column names each pane that blocks the removal, pane id first and process name after – `w26:p2 claude` for example. Leave the cell empty when nothing blocks. The user then knows which pane to close.
141
141
 
@@ -147,9 +147,7 @@ Do this before removing anything, and only after approval.
147
147
 
148
148
  So no tool call walks the session cwd back. `ExitWorktree` only unwinds a worktree this session entered with `EnterWorktree`. It is a no-op for a session that launched inside one.
149
149
 
150
- The removal itself is what moves the session. Run it from the main checkout against an absolute path.
151
-
152
- The harness then resets the session cwd to the main checkout on its own. Confirm with `pwd` afterwards.
150
+ The removal itself is what moves the session. Run it from the main checkout against an absolute path. The harness then resets the session cwd to the main checkout on its own. Confirm with `pwd` afterwards.
153
151
 
154
152
  - Entered with `EnterWorktree`: call `ExitWorktree` with `action: "keep"` first. Keep, not remove: step 3 owns the removal, and `remove` refuses on a worktree entered by path.
155
153
  - Launched inside the worktree: no call needed.
@@ -158,9 +156,7 @@ The harness then resets the session cwd to the main checkout on its own. Confirm
158
156
 
159
157
  That much is the same everywhere. What follows depends on the branch `active` names. This step is the one place `active` still decides anything: how this session moves itself.
160
158
 
161
- When `active` is `herdr`, follow `herdr.md` step 2 before you remove anything.
162
-
163
- When `active` is `orca`, follow `orca.md` step 2. It leaves this session's own checkout in place.
159
+ When `active` is `herdr`, follow `herdr.md` step 2 before you remove anything. When `active` is `orca`, follow `orca.md` step 2. It leaves this session's own checkout in place.
164
160
 
165
161
  ### Host: none
166
162
 
@@ -172,11 +168,9 @@ Every removal through herdr or plain git passes `--force`. Orca is the exception
172
168
 
173
169
  Plain `git worktree remove` refuses on any worktree containing submodules with "working trees containing submodules cannot be moved or removed". That refusal hits every worktree in a repo that has them.
174
170
 
175
- Forcing is safe only because step 1 already proved the tree clean. It is never a way past uncommitted changes.
176
-
177
- The hard rule: never run `worktree remove --force` on a workspace that still holds a pane other than this session's own. Forcing waives herdr's own check, so the pane enumeration is the only guard left.
171
+ Forcing is safe only because step 1 already proved the tree clean and its ignored data rebuildable or approved for loss. It is never a way past uncommitted changes.
178
172
 
179
- Enumerate the panes again immediately before the call. Refuse the removal while any other pane remains.
173
+ The hard rule: never run `worktree remove --force` on a workspace that still holds a pane other than this session's own. Forcing waives herdr's own check, so the pane enumeration is the only guard left. Enumerate the panes again immediately before the call. Refuse the removal while any other pane remains.
180
174
 
181
175
  Take the branch each checkout's `owner` names, not the branch `active` names. Decide per worktree: one checkout in the plan can belong to herdr while the next is plain git.
182
176
 
@@ -210,12 +204,17 @@ Use `branch -d`, never `-D`. If `-d` refuses, the merge test was wrong. Stop and
210
204
 
211
205
  Skip the remote delete when nobody ever pushed the branch, or when merging the pull request already deleted it. The `git fetch origin --prune` from step 1 keeps a branch GitHub already removed from looking like work to do.
212
206
 
213
- ## 5. Report
207
+ ## 5. Clean build outputs in kept worktrees
208
+
209
+ Only for worktrees that stay, whether step 1 skipped them or step 3 kept them, and only for the paths the user picked. Follow `artifacts.md` step 5. This step never retires a worktree, stops an agent, or closes a workspace.
210
+
211
+ ## 6. Report
214
212
 
215
213
  State what went and what stayed:
216
214
 
217
215
  - Worktrees removed, with their branches
218
- - Worktrees skipped, each with its reason: uncommitted work, an unmerged branch, a live agent or terminal, or the checkout this Orca terminal stands in
216
+ - Build outputs cleaned, with the space each freed, and the ones left because something was running
217
+ - Worktrees skipped, each with its reason: uncommitted work, preserve data it holds, an unmerged branch, a live agent or terminal, or the checkout this Orca terminal stands in
219
218
  - Which live-agent checks ran against each worktree, and which did not, so nobody reads a missing check as a passed check
220
219
  - Anything left for the user to decide
221
220
 
@@ -228,6 +227,9 @@ State what went and what stayed:
228
227
  - Remove a workspace that still holds a pane other than your own.
229
228
  - Reach for `EnterWorktree` to get back to the main checkout. It rejects the main working tree.
230
229
  - Force past uncommitted changes. Skip and report instead.
230
+ - Read a clean status as proof the checkout holds nothing. It hides ignored files, and removal deletes them.
231
+ - Clean build outputs in a worktree with a live agent or process. Report the size instead.
232
+ - Remove a worktree, stop an agent, or delete a Docker volume only to reclaim disk space.
231
233
  - Reach for `git branch -D` when `-d` refuses.
232
234
  - Call a branch unmerged on the ancestor check alone. Check the pull request before deciding.
233
235
  - Delete a remote branch with no merged pull request and no ancestor in an integration branch.
@@ -0,0 +1,82 @@
1
+ # Teardown worktree: ignored data and build outputs
2
+
3
+ What to do with the files git ignores. `SKILL.md` step 1 lists them, and step 5 cleans build outputs in worktrees that stay. Each section names the step it belongs to.
4
+
5
+ ## Why ignored data needs its own check
6
+
7
+ `git worktree remove` deletes every ignored file in the checkout, with or without `--force`, and says nothing. The uncommitted-work check cannot see them, because `git status` hides ignored paths.
8
+
9
+ Ignored does not mean disposable. `.env`, local databases, Terraform state, backups, notebooks, and review or design output are often ignored precisely because they are local and unique. Clean, merged, idle, and done are not proof either.
10
+
11
+ ## Step 1 – list and classify
12
+
13
+ List the ignored paths, collapsed to directories, with their sizes:
14
+
15
+ ```bash
16
+ git -C <path> ls-files --others --ignored --exclude-standard --directory -z \
17
+ | xargs -0 -r -I{} du -sh -- "<path>/{}"
18
+ ```
19
+
20
+ Class each path as one of two things:
21
+
22
+ - **Rebuildable**: a tool-native command regenerates it from tracked files. The table below gives the common cases. Each one needs the tracked manifest or lockfile it names to exist in the checkout.
23
+ - **Preserve**: everything else. A generated-looking name or extension alone is not evidence. When unsure, preserve.
24
+
25
+ | Path | Rebuilt by | Needs tracked | Native clean |
26
+ |---|---|---|---|
27
+ | Cargo `target/` | `cargo build` | `Cargo.toml` | `cargo clean --manifest-path <path>/Cargo.toml` |
28
+ | `node_modules/` | `npm ci`, `pnpm install`, `yarn install` | the lockfile | `rm -rf <path>/node_modules` |
29
+ | `.next/`, `dist/`, `build/` | the project's build script | `package.json` naming that output | the project's `clean` script, or `rm -rf` the directory |
30
+ | `.terraform/providers/` | `terraform init` | `.terraform.lock.hcl` | `rm -rf <path>/.terraform/providers` |
31
+ | `__pycache__/`, `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/` | the next run | nothing | `rm -rf` the directory |
32
+
33
+ The listing collapses a wholly ignored directory to one line. Class that line by everything inside it, and list inside any directory that can mix the two classes:
34
+
35
+ ```bash
36
+ du -sh -- <path>/.terraform/*
37
+ ```
38
+
39
+ Only the `providers/` directory under `.terraform/` is rebuildable. The rest of `.terraform/`, `*.tfstate`, and `*.tfstate.backup` are state. Preserve them.
40
+
41
+ A Python virtual environment is preserve by default, even with a tracked requirements file. Packages installed by hand do not show up in it. Call it rebuildable only when the user confirms.
42
+
43
+ Prefer the project's own clean script when `package.json`, a `Makefile`, or `justfile` defines one. Read what it deletes first.
44
+
45
+ ### What the sizes do and do not say
46
+
47
+ `du` reports allocated size. Hardlinked stores, such as pnpm's, free less than that when removed. A symlink costs nothing, and removal deletes the link, never its target.
48
+
49
+ Shared caches outside the checkout, such as `~/.cargo`, `~/.npm`, and the pip cache, serve every project. They are out of scope. Never clean them from a teardown.
50
+
51
+ ### Data outside the checkout
52
+
53
+ Removal leaves behind what the session created elsewhere. Report the parts you can find. Never delete them from a teardown:
54
+
55
+ - Docker Compose containers and their volumes, when Docker is installed:
56
+
57
+ ```bash
58
+ docker ps -a --filter "label=com.docker.compose.project.working_dir=<path>" --format '{{.Names}} {{.Status}}'
59
+ ```
60
+
61
+ - A Cargo target directory moved out by `CARGO_TARGET_DIR` or `.cargo/config.toml`
62
+
63
+ ## Step 1 – what it means for the plan
64
+
65
+ Removing a worktree deletes everything ignored in it, so:
66
+
67
+ - Every ignored path rebuildable: the worktree can go. List the paths and total size in the plan.
68
+ - Any preserve path: skip the worktree. Name each preserve path and its size, so the user can move or delete it deliberately.
69
+
70
+ The user can approve losing a named preserve path in the plan. Only an explicit approval naming that path counts. A general yes to the plan does not.
71
+
72
+ ## Step 5 – clean build outputs in kept worktrees
73
+
74
+ A worktree can be skipped from retirement and still hold gigabytes of build output. Cleaning that output is a separate decision from removing the worktree. It never retires the worktree, stops an agent, or closes a workspace.
75
+
76
+ Offer it for each kept worktree whose rebuildable paths total more than about 100 MB. List the paths, sizes, and the native clean command for each. The user picks which to run.
77
+
78
+ Clean a path only when step 1's live-agent checks found nothing running in that worktree. A live agent, editor, dev server, or build may be using the output, so report the size instead and leave it.
79
+
80
+ Rerun the live-agent checks immediately before each clean. Even then, finding no process is a snapshot, not a lock. Something can start using the path a moment later. Say so in the report rather than calling the clean safe.
81
+
82
+ Run the native command from the table, against absolute paths. Never run `git clean -X` or `git clean -x`. Either one deletes every preserve path along with the build output.