@henryqw/pi-subagent 27.5.2 → 27.5.3
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/docs/orchestration.md
CHANGED
|
@@ -375,7 +375,7 @@ The report lists removed, skipped, and failed resources. After complete cleanup,
|
|
|
375
375
|
|
|
376
376
|
### Prune leftover worktrees
|
|
377
377
|
|
|
378
|
-
Run `/subagent prune` to clean up leftover worker and integration checkouts under `.worktrees/`. Their branches have the name `pi-subagent/subagent-` plus 24 hex characters. The command also cleans up such branches that have no checkout. The review
|
|
378
|
+
Run `/subagent prune` to clean up leftover worker and integration checkouts under `.worktrees/`. Their branches have the name `pi-subagent/subagent-` plus 24 hex characters. The command also cleans up such branches that have no checkout. The review starts with the number of removable and blocked items. It groups the items by the exact reason that the command can remove them, or by their exact blockers, and shows each reason once. Under each reason, it identifies each item by the first 8 of its 24 hex characters. The command can remove an item only when all of these conditions are true:
|
|
379
379
|
|
|
380
380
|
- Its branch tip is already in Main's HEAD or in the local `origin` default-branch ref. The command does not fetch.
|
|
381
381
|
- Its checkout has no tracked or untracked changes. No merge, rebase, cherry-pick, revert, or bisect is in progress.
|
|
@@ -387,7 +387,7 @@ One confirmation removes every listed removable item. When the command removes a
|
|
|
387
387
|
|
|
388
388
|
If a top-level ignored entry is new or changed, the command skips the checkout. If the full list of ignored entries of a checkout does not fit in that dialog, the command excludes the checkout. The command blocks checkouts with initialized submodules. It also blocks owned checkouts that are detached or on another branch. Remove them manually.
|
|
389
389
|
|
|
390
|
-
The list shows removable items first. The command never cuts a long list silently. The list ends with `+N more not shown`, and the command does not remove the items that it did not show in that run. Run `/subagent prune` again to review them.
|
|
390
|
+
The list shows removable items first. The command never cuts a long list silently. The list ends with `+N more not shown`, where `N` counts items, and the command does not remove the items that it did not show in that run. Run `/subagent prune` again to review them.
|
|
391
391
|
|
|
392
392
|
The command checks each item again immediately before removal and skips an item that changed. If the session or branch changes during removal, the command skips the remaining steps. The command deletes a branch only if the branch still points at the reviewed tip. Then the command reconciles each aborted request that recorded a removed resource. It uses the same guarded path as a repeated `subagent_abort`, so the cleanup of that request no longer shows as pending.
|
|
393
393
|
|
|
@@ -100,13 +100,12 @@ const errorText = (error: unknown): string => clean(error instanceof Error ? err
|
|
|
100
100
|
const completed = (status: string) => ["completed", "success", "failed", "failure", "aborted", "integrated"].includes(status);
|
|
101
101
|
const priority = (status: string) => /attention|blocked|retained|failed/i.test(status) ? 0 : completed(status) ? 2 : 1;
|
|
102
102
|
|
|
103
|
-
const ignoredSummary = ({ ignored }: PruneItem) => !ignored.length ? "none"
|
|
104
|
-
: `${ignored.length} (${ignored.slice(0, 5).join(", ")}${ignored.length > 5 ? `, +${ignored.length - 5} more` : ""})`;
|
|
105
103
|
const PRUNE_IGNORED_CONFIRM_CODE_POINTS = 4_000;
|
|
106
104
|
const NOT_SHOWN = "they are not removed in this run; run /subagent prune again after this run.";
|
|
107
105
|
|
|
108
106
|
/** Fit whole item lines within `max` code points; never cut silently, and report exactly which items were shown. */
|
|
109
|
-
function fitItems<T>(header: string, items: readonly T[], line: (item: T) => string, max: number, omitted = NOT_SHOWN
|
|
107
|
+
function fitItems<T>(header: string, items: readonly T[], line: (item: T) => string, max: number, omitted = NOT_SHOWN,
|
|
108
|
+
count: (item: T) => number = () => 1): { text: string; shown: T[] } {
|
|
110
109
|
const lines = [header];
|
|
111
110
|
const shown: T[] = [];
|
|
112
111
|
let size = [...header].length;
|
|
@@ -119,18 +118,30 @@ function fitItems<T>(header: string, items: readonly T[], line: (item: T) => str
|
|
|
119
118
|
size += length;
|
|
120
119
|
shown.push(item);
|
|
121
120
|
}
|
|
122
|
-
|
|
121
|
+
const hidden = items.slice(shown.length).reduce((sum, item) => sum + count(item), 0);
|
|
122
|
+
if (hidden) lines.push(`+${hidden} more not shown; ${omitted}`);
|
|
123
123
|
return { text: lines.join("\n"), shown };
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
/**
|
|
126
|
+
/** Groups items by exact reason, prunable groups first, in lines of 10 IDs so a long list is cut at whole lines.
|
|
127
|
+
* Only shown items may be removed. IDs are display-only; removal uses the reviewed items. */
|
|
127
128
|
function pruneReview({ root, items }: PrunePlan): { text: string; shown: PruneItem[] } {
|
|
128
129
|
if (!items.length) return { text: `No pi-subagent worktrees or branches in ${root}.`, shown: [] };
|
|
130
|
+
const groups = new Map<string, PruneItem[]>();
|
|
131
|
+
for (const item of [...items.filter((item) => item.prunable), ...items.filter((item) => !item.prunable)]) {
|
|
132
|
+
const title = item.prunable ? `✓ Prunable: ${item.reason}` : `✗ Blocked: ${item.path ? item.reason : `${item.reason}; no checkout`}`;
|
|
133
|
+
if (groups.has(title)) groups.get(title)!.push(item);
|
|
134
|
+
else groups.set(title, [item]);
|
|
135
|
+
}
|
|
136
|
+
const rows = [...groups].flatMap(([title, group]) => Array.from({ length: Math.ceil(group.length / 10) }, (_, index) => {
|
|
137
|
+
const chunk = group.slice(index * 10, index * 10 + 10);
|
|
138
|
+
const ids = chunk.map(({ branch }) => clean(branch.replace(/^pi-subagent\/subagent-/, "").slice(0, 8))).join(", ");
|
|
139
|
+
return { items: chunk, text: `${index ? "" : `${clean(title, 600)} (${group.length}):\n`} ${ids}` };
|
|
140
|
+
}));
|
|
129
141
|
const prunable = items.filter((item) => item.prunable).length;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
ROLES_REPORT_CODE_POINTS);
|
|
142
|
+
const reviewed = fitItems(`Subagent prune review in ${root}: ${prunable} prunable, ${items.length - prunable} blocked. IDs are the first 8 characters after pi-subagent/subagent-.`,
|
|
143
|
+
rows, (row) => row.text, ROLES_REPORT_CODE_POINTS, NOT_SHOWN, (row) => row.items.length);
|
|
144
|
+
return { text: reviewed.text, shown: reviewed.shown.flatMap((row) => row.items) };
|
|
134
145
|
}
|
|
135
146
|
|
|
136
147
|
function discardReview(plan: DiscardPlan) {
|