@infinitedusky/indusk-mcp 1.44.0 → 1.44.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.
- package/admin/.next/BUILD_ID +1 -1
- package/admin/.next/build-manifest.json +3 -3
- package/admin/.next/fallback-build-manifest.json +3 -3
- package/admin/.next/prerender-manifest.json +3 -3
- package/admin/.next/server/app/_global-error.html +1 -1
- package/admin/.next/server/app/_global-error.rsc +1 -1
- package/admin/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/admin/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/admin/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/admin/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/admin/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/admin/.next/server/middleware-build-manifest.js +3 -3
- package/admin/.next/server/pages/500.html +1 -1
- package/admin/.next/server/server-reference-manifest.js +1 -1
- package/admin/.next/server/server-reference-manifest.json +1 -1
- package/admin/.next/trace +1 -1
- package/admin/.next/trace-build +1 -1
- package/dist/bin/commands/agent.js +3 -3
- package/dist/bin/commands/run.js +21 -0
- package/dist/bin/commands/workbench.d.ts +0 -8
- package/dist/bin/commands/workbench.js +19 -5
- package/dist/bin/commands/worktree.d.ts +10 -0
- package/dist/bin/commands/worktree.js +12 -5
- package/dist/lib/cleanup/oversized.js +16 -4
- package/dist/lib/eval/evaluator-runner.d.ts +3 -2
- package/dist/lib/stray-state-audit.js +5 -2
- package/dist/lib/trajectory/state-ops.d.ts +0 -6
- package/dist/lib/trajectory/state-ops.js +0 -15
- package/dist/lib/verify/roots.js +5 -2
- package/dist/lib/worktree/layout.js +9 -3
- package/dist/lib/worktree/repos.d.ts +10 -1
- package/dist/lib/worktree/repos.js +13 -2
- package/extensions/worktree/hooks/on_enable.sh +17 -4
- package/extensions/worktree/scripts/lib/workbench-helpers.sh +58 -4
- package/extensions/worktree/scripts/preflight.sh +10 -46
- package/extensions/worktree/scripts/refresh-worktree.sh +18 -10
- package/extensions/worktree/scripts/setup-worktree.sh +11 -3
- package/extensions/worktree/templates/worktree-config.template.json +1 -1
- package/hooks/_hook-paths.js +129 -17
- package/hooks/_impl-phases.js +88 -0
- package/hooks/check-gates.js +3 -70
- package/hooks/eval-trigger.js +31 -5
- package/hooks/gate-reminder.js +92 -187
- package/package.json +1 -1
- package/skills/cleanup.md +1 -1
- package/skills/retrospective.md +1 -0
- /package/admin/.next/static/{4Mqj7sc33tNR898aibslD → GeLihB_OOFljBvLKtTR-j}/_buildManifest.js +0 -0
- /package/admin/.next/static/{4Mqj7sc33tNR898aibslD → GeLihB_OOFljBvLKtTR-j}/_clientMiddlewareManifest.js +0 -0
- /package/admin/.next/static/{4Mqj7sc33tNR898aibslD → GeLihB_OOFljBvLKtTR-j}/_ssgManifest.js +0 -0
|
@@ -175,7 +175,7 @@ function worktreeCreateResolved(slug, baseBranch, repo) {
|
|
|
175
175
|
if (provisionWorktreeEnv(workbenchRoot, worktreeDir)) {
|
|
176
176
|
console.info(` doppler: auto-provisioned env for ${slug}`);
|
|
177
177
|
}
|
|
178
|
-
for (const cmd of readPostCreate(workbenchRoot)) {
|
|
178
|
+
for (const cmd of readPostCreate(workbenchRoot, repo)) {
|
|
179
179
|
console.info(` post_create: ${cmd}`);
|
|
180
180
|
const pc = spawnSync(cmd, { cwd: worktreeDir, stdio: "inherit", shell: true });
|
|
181
181
|
if (pc.status !== 0) {
|
|
@@ -192,9 +192,16 @@ function worktreeCreateResolved(slug, baseBranch, repo) {
|
|
|
192
192
|
}
|
|
193
193
|
process.exit(code);
|
|
194
194
|
}
|
|
195
|
-
/**
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Read `post_create` commands from the worktree config of THE repo being
|
|
197
|
+
* created — run in each new worktree after create.
|
|
198
|
+
*
|
|
199
|
+
* It read `repos[0]`'s config for every repo, so in a multi-repo workbench
|
|
200
|
+
* `worktree create beta <slug>` ran alpha's install steps in beta's worktree
|
|
201
|
+
* (workbench-trust-fixes, F6 / A15). The caller names the repo; with none
|
|
202
|
+
* named the bash script has already refused, so there is nothing to run.
|
|
203
|
+
*/
|
|
204
|
+
export function readPostCreate(workbenchRoot, repo) {
|
|
198
205
|
if (!repo)
|
|
199
206
|
return [];
|
|
200
207
|
const p = join(workbenchRoot, ".indusk", "worktree-configs", `${repo}.json`);
|
|
@@ -210,7 +217,7 @@ function readPostCreate(workbenchRoot) {
|
|
|
210
217
|
return [];
|
|
211
218
|
}
|
|
212
219
|
}
|
|
213
|
-
/** Walk up from `start` to the nearest workbench root
|
|
220
|
+
/** Walk up from `start` to the nearest workbench root — the one `isWorkbench` rule, so a shapeless `repos[]` config counts too. */
|
|
214
221
|
function resolveWorkbenchRoot(start) {
|
|
215
222
|
let dir = resolve(start);
|
|
216
223
|
for (let i = 0; i < 40; i++) {
|
|
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { getCleanupConfig, resolveCapForPath } from "../config.js";
|
|
5
|
+
import { isWorkbench, readWorkbenchRepos, repoDir } from "../worktree/repos.js";
|
|
5
6
|
/** Run git; swallow errors to an empty string (callers tolerate absence).
|
|
6
7
|
* stderr is ignored — execFileSync forwards child stderr to the parent's
|
|
7
8
|
* terminal by default, which leaked raw `git diff` usage spam (round-2 F1). */
|
|
@@ -86,15 +87,26 @@ function isGeneratedOrVendored(rel) {
|
|
|
86
87
|
* history) it falls back to `baseRef` directly.
|
|
87
88
|
*/
|
|
88
89
|
export function listOversizedChangedFiles(projectRoot, baseRef = "origin/main") {
|
|
89
|
-
// Fail loudly on a non-git root
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// the same statePath/gitPath split the eval-rail hit in 1.31.7/1.31.12).
|
|
90
|
+
// Fail loudly on a non-git root — silently returning [] would make the
|
|
91
|
+
// ritual report "nothing to clean" for a tree it never examined (round-2
|
|
92
|
+
// F1, the same statePath/gitPath split the eval rail hit in 1.31.7/1.31.12).
|
|
93
93
|
if (!git(projectRoot, ["rev-parse", "--show-toplevel"])) {
|
|
94
94
|
throw new Error(`listOversizedChangedFiles: ${projectRoot} is not a git repo. ` +
|
|
95
95
|
"In workbench mode pass the wrapped repo/worktree path (where the code lives), " +
|
|
96
96
|
"not the workbench root (where .indusk/ lives).");
|
|
97
97
|
}
|
|
98
|
+
// A versioned workbench root IS a git repo, so the guard above passes and
|
|
99
|
+
// the diff it would examine is the plan documents — the code lives in repos
|
|
100
|
+
// the workbench's git ignores. Returning [] there is "checked and clean" for
|
|
101
|
+
// code that was never seen. `verify` had the identical gap and refuses by
|
|
102
|
+
// declaration (`resolveVerifyRoots`); this is cleanup's maintained refusal
|
|
103
|
+
// (workbench-trust-fixes, F3).
|
|
104
|
+
if (isWorkbench(projectRoot)) {
|
|
105
|
+
const dirs = readWorkbenchRepos(projectRoot).map(repoDir);
|
|
106
|
+
throw new Error(`listOversizedChangedFiles: ${projectRoot} is a workbench — its code lives in ${dirs.join(", ") || "repos it does not declare"}, ` +
|
|
107
|
+
"not in this repository, so a diff here cannot contain it. Refusing rather than reporting nothing to clean. " +
|
|
108
|
+
"Run the cleanup scan inside the repository the plan's code lives in.");
|
|
109
|
+
}
|
|
98
110
|
const mergeBase = resolveMergeBase(projectRoot, baseRef);
|
|
99
111
|
const names = new Set();
|
|
100
112
|
const ranges = [[`${mergeBase}..HEAD`], ["--cached"], []];
|
|
@@ -10,8 +10,9 @@ export interface EvaluatorRunOptions {
|
|
|
10
10
|
/**
|
|
11
11
|
* Where `.indusk/` lives — the InDusk state root. All state paths
|
|
12
12
|
* (results.log, evaluator-session.json, config.json, highlights) hang off
|
|
13
|
-
* this. In workbench mode this is the workbench root (
|
|
14
|
-
* in single-repo mode it
|
|
13
|
+
* this. In workbench mode this is the workbench root (whose own git
|
|
14
|
+
* history is plan documents, never the code); in single-repo mode it
|
|
15
|
+
* equals `gitRoot`.
|
|
15
16
|
*
|
|
16
17
|
* CONTRACT: `hooks/eval-trigger.js` MUST pass this key as `projectRoot`.
|
|
17
18
|
* It passed `statePath` between 1.31.7 and 1.31.11, leaving `projectRoot`
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { existsSync, readdirSync } from "node:fs";
|
|
23
23
|
import { join } from "node:path";
|
|
24
|
-
import { readWorkbenchRepos } from "./worktree/repos.js";
|
|
24
|
+
import { readWorkbenchRepos, repoDir } from "./worktree/repos.js";
|
|
25
25
|
/**
|
|
26
26
|
* Find stray `.indusk/` directories in a workbench-shaped project.
|
|
27
27
|
*
|
|
@@ -45,7 +45,10 @@ export function findStrayState(workbenchRoot) {
|
|
|
45
45
|
return [];
|
|
46
46
|
const findings = [];
|
|
47
47
|
for (const repo of repos) {
|
|
48
|
-
|
|
48
|
+
// At the declared location (`path`, else name): by name, a repo declared
|
|
49
|
+
// elsewhere was a directory that did not exist, silently skipped, and
|
|
50
|
+
// the audit reported clean (workbench-trust-fixes, F6 / A12).
|
|
51
|
+
const wrappedRepoPath = join(workbenchRoot, repoDir(repo));
|
|
49
52
|
if (!existsSync(wrappedRepoPath))
|
|
50
53
|
continue;
|
|
51
54
|
const strayInsideWrapped = walkForStrayIndusk(wrappedRepoPath, 2);
|
|
@@ -35,12 +35,6 @@ export declare function computePhaseCloseBlockers(body: string, phase: number):
|
|
|
35
35
|
row: TrajectoryRow;
|
|
36
36
|
message: string;
|
|
37
37
|
}[];
|
|
38
|
-
/**
|
|
39
|
-
* Convenience: nudge text for phase start. Returns a string listing rows
|
|
40
|
-
* whose `Writable at` equals the phase and are not yet written, or null
|
|
41
|
-
* if nothing needs authoring.
|
|
42
|
-
*/
|
|
43
|
-
export declare function getPhaseStartNudge(body: string, phase: number): string | null;
|
|
44
38
|
/**
|
|
45
39
|
* Convenience: nudge text for near-phase-close. Returns a string listing
|
|
46
40
|
* rows whose `Passes at` equals the phase but state is not yet `passing`
|
|
@@ -100,21 +100,6 @@ export function computePhaseCloseBlockers(body, phase) {
|
|
|
100
100
|
message: ` [${row.id}] ${row.asserts} — state: ${row.state} (must be 'passing' or 'skipped' to close Phase ${phase})`,
|
|
101
101
|
}));
|
|
102
102
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Convenience: nudge text for phase start. Returns a string listing rows
|
|
105
|
-
* whose `Writable at` equals the phase and are not yet written, or null
|
|
106
|
-
* if nothing needs authoring.
|
|
107
|
-
*/
|
|
108
|
-
export function getPhaseStartNudge(body, phase) {
|
|
109
|
-
const trajectory = parseTrajectory(body);
|
|
110
|
-
if (!trajectory.present)
|
|
111
|
-
return null;
|
|
112
|
-
const rows = getRowsWritableAt(trajectory, phase);
|
|
113
|
-
if (rows.length === 0)
|
|
114
|
-
return null;
|
|
115
|
-
const lines = rows.map((r) => ` [${r.id}] ${r.asserts}`);
|
|
116
|
-
return `Phase ${phase} opens with these tests to author (commit as failing before implementation work):\n${lines.join("\n")}`;
|
|
117
|
-
}
|
|
118
103
|
/**
|
|
119
104
|
* Convenience: nudge text for near-phase-close. Returns a string listing
|
|
120
105
|
* rows whose `Passes at` equals the phase but state is not yet `passing`
|
package/dist/lib/verify/roots.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { isWorkbench, readWorkbenchRepos } from "../worktree/repos.js";
|
|
2
|
+
import { isWorkbench, readWorkbenchRepos, repoDir, resolveReposRoot } from "../worktree/repos.js";
|
|
3
3
|
/**
|
|
4
4
|
* Resolve both roots, or refuse.
|
|
5
5
|
*
|
|
@@ -46,7 +46,10 @@ export function resolveVerifyRoots(planRoot) {
|
|
|
46
46
|
error: `${planRoot} is a workbench: its plan documents and its code (${declared}) live in different repositories, ` +
|
|
47
47
|
"and the verify ledger records a baseline from the plan repo that has no meaning in the code repo. " +
|
|
48
48
|
"Refusing rather than judging code against a diff that cannot contain it. " +
|
|
49
|
-
|
|
49
|
+
// The checkout's real location — `repos_root` + declared `path` — not
|
|
50
|
+
// `<planRoot>/<name>`, which exists only on the flat legacy layout
|
|
51
|
+
// (workbench-trust-fixes, A13).
|
|
52
|
+
`Run verify inside ${join(resolveReposRoot(planRoot), repoDir(repos[0]))} instead; cross-repo verification is a named follow-on.`,
|
|
50
53
|
};
|
|
51
54
|
}
|
|
52
55
|
export function isRefusal(r) {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* one, and a trunk link that was not made reports as one that was.
|
|
17
17
|
*/
|
|
18
18
|
import { spawnSync } from "node:child_process";
|
|
19
|
-
import { existsSync, lstatSync, readdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, } from "node:fs";
|
|
20
|
-
import { join, relative } from "node:path";
|
|
19
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, } from "node:fs";
|
|
20
|
+
import { dirname, join, relative } from "node:path";
|
|
21
21
|
/**
|
|
22
22
|
* Root entries that are never worktrees.
|
|
23
23
|
*
|
|
@@ -120,7 +120,12 @@ export function isDanglingLink(p) {
|
|
|
120
120
|
*/
|
|
121
121
|
export function linkTrunk(workbenchRoot, name, target) {
|
|
122
122
|
const link = join(workbenchRoot, name);
|
|
123
|
-
|
|
123
|
+
// A symlink's relative target resolves from the link's OWN directory, not
|
|
124
|
+
// the workbench root — the two differ the moment `name` has more than one
|
|
125
|
+
// segment (`code/alpha`), and a link computed from the root pointed at
|
|
126
|
+
// itself. The parent must also exist before the link can (falsification
|
|
127
|
+
// A22; nothing in a flat layout could reach either).
|
|
128
|
+
const rel = relative(dirname(link), target);
|
|
124
129
|
if (existsSync(link) || isDanglingLink(link)) {
|
|
125
130
|
// A correct link needs nothing doing — report it as linked.
|
|
126
131
|
if (isSymlink(link) && readlinkSync(link) === rel)
|
|
@@ -131,6 +136,7 @@ export function linkTrunk(workbenchRoot, name, target) {
|
|
|
131
136
|
else
|
|
132
137
|
return false;
|
|
133
138
|
}
|
|
139
|
+
mkdirSync(dirname(link), { recursive: true });
|
|
134
140
|
symlinkSync(rel, link);
|
|
135
141
|
return true;
|
|
136
142
|
}
|
|
@@ -17,7 +17,16 @@ export type { WorkbenchRepo } from "../config.js";
|
|
|
17
17
|
* "not a workbench" exactly as they read a missing `wrapped_repo` today.
|
|
18
18
|
*/
|
|
19
19
|
export declare function readWorkbenchRepos(root: string): WorkbenchRepo[];
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Whether this project is workbench-shaped at all.
|
|
22
|
+
*
|
|
23
|
+
* `shape: "workbench"` says so explicitly; declaring repos says so just as
|
|
24
|
+
* loudly. A config with `repos[]` and no `shape` flag used to read as a flat
|
|
25
|
+
* project here while every other reader (`readWorkbenchRepos`, the bash lane,
|
|
26
|
+
* the eval hook) treated it as a workbench — so `verify` would have judged the
|
|
27
|
+
* wrapper repo's plan documents as code (workbench-trust-fixes, F6 / A14). One
|
|
28
|
+
* rule, and every refusal inherits it through this function.
|
|
29
|
+
*/
|
|
21
30
|
export declare function isWorkbench(root: string): boolean;
|
|
22
31
|
/** The parent directory the sibling clones live in, or null when undeclared. */
|
|
23
32
|
export declare function readSiblingParent(root: string): string | null;
|
|
@@ -64,9 +64,20 @@ export function readWorkbenchRepos(root) {
|
|
|
64
64
|
}
|
|
65
65
|
return repos;
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Whether this project is workbench-shaped at all.
|
|
69
|
+
*
|
|
70
|
+
* `shape: "workbench"` says so explicitly; declaring repos says so just as
|
|
71
|
+
* loudly. A config with `repos[]` and no `shape` flag used to read as a flat
|
|
72
|
+
* project here while every other reader (`readWorkbenchRepos`, the bash lane,
|
|
73
|
+
* the eval hook) treated it as a workbench — so `verify` would have judged the
|
|
74
|
+
* wrapper repo's plan documents as code (workbench-trust-fixes, F6 / A14). One
|
|
75
|
+
* rule, and every refusal inherits it through this function.
|
|
76
|
+
*/
|
|
68
77
|
export function isWorkbench(root) {
|
|
69
|
-
|
|
78
|
+
if (readRawConfig(root)?.worktree?.shape === "workbench")
|
|
79
|
+
return true;
|
|
80
|
+
return readWorkbenchRepos(root).length > 0;
|
|
70
81
|
}
|
|
71
82
|
/** The parent directory the sibling clones live in, or null when undeclared. */
|
|
72
83
|
export function readSiblingParent(root) {
|
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
# `indusk update` re-copies if upstream changes)
|
|
10
10
|
# 3. Registers `wt`, `wt:pm2`, `preflight` scripts in the workbench's
|
|
11
11
|
# package.json (merges, doesn't duplicate)
|
|
12
|
-
# 4.
|
|
13
|
-
#
|
|
14
|
-
#
|
|
12
|
+
# 4. Ships `.indusk/worktree-configs/config.schema.json` (the editor
|
|
13
|
+
# schema the starter config's `$schema` points at), refreshed on every
|
|
14
|
+
# enable; then materializes a starter
|
|
15
|
+
# `.indusk/worktree-configs/<wrapped_repo>.json` from the template if
|
|
16
|
+
# absent — substituting WRAPPED_REPO_NAME with the actual repo name
|
|
17
|
+
# from worktree.wrapped_repo config
|
|
15
18
|
#
|
|
16
19
|
# Requires:
|
|
17
20
|
# - jq installed
|
|
@@ -76,8 +79,18 @@ else
|
|
|
76
79
|
echo " WARN: no package.json at $PKG_JSON; skipping script registration"
|
|
77
80
|
fi
|
|
78
81
|
|
|
79
|
-
# 3.
|
|
82
|
+
# 3. Ship the config schema beside the configs, refreshed on every enable.
|
|
83
|
+
# The starter config's `$schema` points at `./config.schema.json`; enabling
|
|
84
|
+
# an extension copies only its manifest into the project, so nothing else
|
|
85
|
+
# puts the schema anywhere a relative pointer could reach. A config that
|
|
86
|
+
# already exists is left alone below; the schema is always refreshed so a
|
|
87
|
+
# package upgrade carries schema changes into the project.
|
|
80
88
|
CONFIG_DIR="$WORKBENCH_ROOT/.indusk/worktree-configs"
|
|
89
|
+
mkdir -p "$CONFIG_DIR"
|
|
90
|
+
cp "$EXT_DIR/config.schema.json" "$CONFIG_DIR/config.schema.json"
|
|
91
|
+
echo " schema: $CONFIG_DIR/config.schema.json"
|
|
92
|
+
|
|
93
|
+
# 4. Materialize starter worktree-config if absent.
|
|
81
94
|
for REPO_NAME in ${DECLARED_REPOS+"${DECLARED_REPOS[@]}"}; do
|
|
82
95
|
CONFIG_FILE="$CONFIG_DIR/${REPO_NAME}.json"
|
|
83
96
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
@@ -268,6 +268,57 @@ _read_workbench_repo_paths() {
|
|
|
268
268
|
' "$config" 2>/dev/null || echo ""
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
# The worktrees directory <repo> DECLARES, relative to the workbench root, or
|
|
272
|
+
# empty when it declares none (worktrees then live at the root). One reader so
|
|
273
|
+
# setup-worktree.sh run directly agrees with the TS wrapper that passes
|
|
274
|
+
# --worktrees-dir (workbench-trust-fixes, F5).
|
|
275
|
+
_wt_declared_worktrees_dir() {
|
|
276
|
+
local repo="$1" name dir
|
|
277
|
+
while IFS=$'\t' read -r name dir; do
|
|
278
|
+
[[ "$name" == "$repo" ]] || continue
|
|
279
|
+
echo "$dir"
|
|
280
|
+
return 0
|
|
281
|
+
done < <(_read_workbench_worktree_dirs)
|
|
282
|
+
echo ""
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Every directory that could be a worktree: the workbench root's entries minus
|
|
286
|
+
# the reserved names, plus every entry of every DECLARED worktrees dir. Callers
|
|
287
|
+
# that need "all worktrees" (refresh --all) iterate this rather than scanning
|
|
288
|
+
# the root alone, which could not see a declared worktrees dir at all.
|
|
289
|
+
_wt_list_worktree_dirs() {
|
|
290
|
+
local entry name dir _name path
|
|
291
|
+
# Root entries that are declared LOCATIONS rather than worktrees: a declared
|
|
292
|
+
# worktrees dir itself, and the first segment of a declared repo path
|
|
293
|
+
# (`code` for `code/alpha`). Listing them as candidates produced a SKIP line
|
|
294
|
+
# per layout element on every `refresh --all` (falsification A23).
|
|
295
|
+
local -a declared_dirs=()
|
|
296
|
+
while IFS=$'\t' read -r _name dir; do
|
|
297
|
+
[[ -n "$dir" ]] && declared_dirs+=("${dir%%/*}")
|
|
298
|
+
done < <(_read_workbench_worktree_dirs)
|
|
299
|
+
while IFS=$'\t' read -r _name path; do
|
|
300
|
+
[[ -n "$path" ]] && declared_dirs+=("${path%%/*}")
|
|
301
|
+
done < <(_read_workbench_repo_paths)
|
|
302
|
+
for entry in "$WORKBENCH_ROOT"/*; do
|
|
303
|
+
[[ -d "$entry" ]] || continue
|
|
304
|
+
name="$(basename "$entry")"
|
|
305
|
+
_wt_is_reserved_name "$name" && continue
|
|
306
|
+
local skip=0 d
|
|
307
|
+
for d in "${declared_dirs[@]+"${declared_dirs[@]}"}"; do
|
|
308
|
+
[[ "$name" == "$d" ]] && skip=1
|
|
309
|
+
done
|
|
310
|
+
[[ "$skip" -eq 1 ]] && continue
|
|
311
|
+
echo "$entry"
|
|
312
|
+
done
|
|
313
|
+
while IFS=$'\t' read -r _name dir; do
|
|
314
|
+
[[ -n "$dir" && -d "$WORKBENCH_ROOT/$dir" ]] || continue
|
|
315
|
+
for entry in "$WORKBENCH_ROOT/$dir"/*; do
|
|
316
|
+
[[ -d "$entry" ]] || continue
|
|
317
|
+
echo "$entry"
|
|
318
|
+
done
|
|
319
|
+
done < <(_read_workbench_worktree_dirs)
|
|
320
|
+
}
|
|
321
|
+
|
|
271
322
|
# Root entries that are never worktrees. ONE definition for the bash lane —
|
|
272
323
|
# wt.sh and wt-pm2.sh each carried a copy, and they had already drifted from
|
|
273
324
|
# RESERVED_ROOT_DIRS in src/lib/worktree/layout.ts (`docs` was missing from
|
|
@@ -283,7 +334,7 @@ _wt_is_reserved_name() {
|
|
|
283
334
|
# 1. The declared workbench-side location: <workbench>/<path-or-name>,
|
|
284
335
|
# which is the trunk symlink in a linked layout or the real directory in
|
|
285
336
|
# a nested one (`repos_root: "."`).
|
|
286
|
-
# 2. Else <repos_root>/<name> — the checkout itself, for a workbench whose
|
|
337
|
+
# 2. Else <repos_root>/<path-or-name> — the checkout itself, for a workbench whose
|
|
287
338
|
# trunk link was never made. Routing there is what "the config says where
|
|
288
339
|
# things are" means; failing because a symlink is missing is scan-luck.
|
|
289
340
|
_wt_resolve_trunk_dir() {
|
|
@@ -301,11 +352,14 @@ _wt_resolve_trunk_dir() {
|
|
|
301
352
|
fi
|
|
302
353
|
local repos_root
|
|
303
354
|
repos_root="$(_read_repos_root)" || return 1
|
|
304
|
-
|
|
305
|
-
|
|
355
|
+
# The checkout itself, at its DECLARED location under repos_root — not by
|
|
356
|
+
# name, which missed every repo declared at a `path` on a sibling layout
|
|
357
|
+
# (workbench-trust-fixes, F5).
|
|
358
|
+
if [[ -d "$repos_root/${declared_path:-$repo}" ]]; then
|
|
359
|
+
echo "$repos_root/${declared_path:-$repo}"
|
|
306
360
|
return 0
|
|
307
361
|
fi
|
|
308
|
-
echo "Error: trunk for repo '$repo' not found — looked at $side and $repos_root/$repo. Run \`indusk workbench restore\`." >&2
|
|
362
|
+
echo "Error: trunk for repo '$repo' not found — looked at $side and $repos_root/${declared_path:-$repo}. Run \`indusk workbench restore\`." >&2
|
|
309
363
|
return 1
|
|
310
364
|
}
|
|
311
365
|
|
|
@@ -61,55 +61,19 @@ if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
|
61
61
|
exit 1
|
|
62
62
|
fi
|
|
63
63
|
|
|
64
|
-
#
|
|
65
|
-
|
|
64
|
+
# One resolution surface — the root and every declared worktrees dir, exact
|
|
65
|
+
# match first, `-<slug>` suffix as fallback, ambiguity refused by name — the
|
|
66
|
+
# same resolver behind `wt` (workbench-trust-fixes, F5). The private root-only
|
|
67
|
+
# scan that stood here could not see a declared worktrees dir and carried its
|
|
68
|
+
# own reserved-name list, already drifted from the shared one.
|
|
69
|
+
WORKTREE_PATH="$(_wt_resolve_target "$SLUG")" || exit 1
|
|
70
|
+
TRUNK_DIR="$(_wt_resolve_trunk_dir "$REPO")" || exit 1
|
|
71
|
+
# Reject preflighting the trunk — preflight is a feature-branch concept. By
|
|
72
|
+
# resolved path: under a declared `path` the trunk's basename is not the name.
|
|
73
|
+
if [[ "$(cd "$WORKTREE_PATH" && pwd -P)" == "$(cd "$TRUNK_DIR" && pwd -P)" ]]; then
|
|
66
74
|
echo "Error: preflight cannot target the trunk; pick a worktree slug" >&2
|
|
67
75
|
exit 1
|
|
68
76
|
fi
|
|
69
|
-
|
|
70
|
-
# Slug resolution (single-pass against workbench root; same as wt.sh).
|
|
71
|
-
_is_reserved_name() {
|
|
72
|
-
case "$1" in
|
|
73
|
-
.indusk | .claude | .vscode | .cursor | node_modules | dist | build | .git | .next | scripts | env) return 0 ;;
|
|
74
|
-
*) return 1 ;;
|
|
75
|
-
esac
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
exact_paths=()
|
|
79
|
-
suffix_paths=()
|
|
80
|
-
for entry in "$WORKBENCH_ROOT"/*; do
|
|
81
|
-
[[ -d "$entry" ]] || continue
|
|
82
|
-
name="$(basename "$entry")"
|
|
83
|
-
_is_reserved_name "$name" && continue
|
|
84
|
-
# Skip the trunk symlink — preflight is for feature worktrees.
|
|
85
|
-
[[ "$name" == "$REPO" ]] && continue
|
|
86
|
-
if [[ "$name" == "$SLUG" ]]; then
|
|
87
|
-
exact_paths+=("$entry")
|
|
88
|
-
elif [[ "$name" == *"-$SLUG" ]]; then
|
|
89
|
-
suffix_paths+=("$entry")
|
|
90
|
-
fi
|
|
91
|
-
done
|
|
92
|
-
|
|
93
|
-
candidate_paths=()
|
|
94
|
-
if [[ ${#exact_paths[@]} -gt 0 ]]; then
|
|
95
|
-
candidate_paths=("${exact_paths[@]}")
|
|
96
|
-
elif [[ ${#suffix_paths[@]} -gt 0 ]]; then
|
|
97
|
-
candidate_paths=("${suffix_paths[@]}")
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
if [[ ${#candidate_paths[@]} -eq 0 ]]; then
|
|
101
|
-
echo "Error: no worktree matching slug '$SLUG' at $WORKBENCH_ROOT" >&2
|
|
102
|
-
exit 1
|
|
103
|
-
fi
|
|
104
|
-
if [[ ${#candidate_paths[@]} -gt 1 ]]; then
|
|
105
|
-
echo "Error: multiple worktrees match slug '$SLUG':" >&2
|
|
106
|
-
for p in "${candidate_paths[@]}"; do
|
|
107
|
-
printf ' %s\n' "$(basename "$p")" >&2
|
|
108
|
-
done
|
|
109
|
-
exit 1
|
|
110
|
-
fi
|
|
111
|
-
|
|
112
|
-
WORKTREE_PATH="${candidate_paths[0]}"
|
|
113
77
|
WORKTREE_NAME="$(basename "$WORKTREE_PATH")"
|
|
114
78
|
|
|
115
79
|
# Read preflight[] from config. Each entry: { name, command, when? }.
|
|
@@ -36,7 +36,9 @@ export WORKBENCH_ROOT
|
|
|
36
36
|
REPO="$(_resolve_workbench_repo "${REPO_ARG:-}")"
|
|
37
37
|
|
|
38
38
|
SIBLING_PARENT="$(_read_repos_root)"
|
|
39
|
-
|
|
39
|
+
# Trunk from CONFIG (declared `path` honored) — workbench-trust-fixes, F5.
|
|
40
|
+
CLIENT_ROOT="$(_wt_resolve_trunk_dir "$REPO")"
|
|
41
|
+
CLIENT_ROOT_REAL="$(cd "$CLIENT_ROOT" && pwd -P)"
|
|
40
42
|
|
|
41
43
|
CONFIG_FILE="$WORKBENCH_ROOT/.indusk/worktree-configs/${REPO}.json"
|
|
42
44
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
@@ -54,8 +56,10 @@ refresh_one() {
|
|
|
54
56
|
echo " SKIP: $slug — directory not found"
|
|
55
57
|
return 0
|
|
56
58
|
fi
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
# The trunk is excluded by resolved path, not by name — under a declared
|
|
60
|
+
# `path` its basename need not be the repo name.
|
|
61
|
+
if [[ "$(cd "$worktree_path" && pwd -P)" == "$CLIENT_ROOT_REAL" ]]; then
|
|
62
|
+
return 0 # the trunk checkout, not a worktree
|
|
59
63
|
fi
|
|
60
64
|
# Check this is actually a git worktree (has a .git file pointing into
|
|
61
65
|
# the canonical clone's worktrees/ dir).
|
|
@@ -65,7 +69,7 @@ refresh_one() {
|
|
|
65
69
|
fi
|
|
66
70
|
|
|
67
71
|
echo ""
|
|
68
|
-
echo "Refreshing: $slug"
|
|
72
|
+
echo "Refreshing: $slug ($worktree_path)"
|
|
69
73
|
|
|
70
74
|
local gitdir
|
|
71
75
|
gitdir="$(cd "$worktree_path" && git rev-parse --git-dir)"
|
|
@@ -180,13 +184,17 @@ refresh_one() {
|
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
if [[ "$ARG" == "--all" ]]; then
|
|
183
|
-
echo "Refreshing all worktrees in $WORKBENCH_ROOT (excluding the trunk
|
|
184
|
-
|
|
185
|
-
[[ -
|
|
186
|
-
|
|
187
|
-
done
|
|
187
|
+
echo "Refreshing all worktrees in $WORKBENCH_ROOT and its declared worktrees dirs (excluding the trunk)..."
|
|
188
|
+
while IFS= read -r d; do
|
|
189
|
+
[[ -n "$d" ]] && refresh_one "$d"
|
|
190
|
+
done < <(_wt_list_worktree_dirs)
|
|
188
191
|
else
|
|
189
|
-
|
|
192
|
+
# One resolution surface for a slug — the root and every declared worktrees
|
|
193
|
+
# dir, exact match first, `-<slug>` suffix as fallback, ambiguity refused by
|
|
194
|
+
# name — the resolver behind `wt` (workbench-trust-fixes, F5). A slug that
|
|
195
|
+
# resolves to nothing is an error, not a SKIP that exits 0.
|
|
196
|
+
target="$(_wt_resolve_target "$ARG")" || exit 1
|
|
197
|
+
refresh_one "$target"
|
|
190
198
|
fi
|
|
191
199
|
|
|
192
200
|
echo ""
|
|
@@ -66,11 +66,19 @@ if [[ -z "$SIBLING_PARENT" ]]; then
|
|
|
66
66
|
exit 1
|
|
67
67
|
fi
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
# The trunk checkout from CONFIG — declared `path` honored — through the one
|
|
70
|
+
# resolver `wt` has used since 1.42.0. Building "$SIBLING_PARENT/$REPO" by
|
|
71
|
+
# name failed outright on any repo declared at a path (workbench-trust-fixes, F5).
|
|
72
|
+
CLIENT_ROOT="$(_wt_resolve_trunk_dir "$REPO")"
|
|
73
|
+
if [[ ! -e "$CLIENT_ROOT/.git" ]]; then
|
|
74
|
+
echo "Error: $CLIENT_ROOT is not a git checkout" >&2
|
|
72
75
|
exit 1
|
|
73
76
|
fi
|
|
77
|
+
# A declared worktrees location applies whether or not the TS wrapper passed
|
|
78
|
+
# --worktrees-dir — this script is also run directly.
|
|
79
|
+
if [[ -z "$WORKTREES_DIR" ]]; then
|
|
80
|
+
WORKTREES_DIR="$(_wt_declared_worktrees_dir "$REPO")"
|
|
81
|
+
fi
|
|
74
82
|
|
|
75
83
|
CONFIG_FILE="$WORKBENCH_ROOT/.indusk/worktree-configs/${REPO}.json"
|
|
76
84
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|