@webpresso/agent-kit 3.2.0 → 3.3.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/bin/_run.js +4 -1
- package/catalog/agent/rules/pre-implementation.md +15 -0
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +3 -13
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +6 -1
- package/dist/esm/blueprint/core/parser.js +61 -4
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/lifecycle/audit.js +22 -10
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +39 -8
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/promotion.js +12 -0
- package/dist/esm/build/cli-mcp-parity.js +5 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +9 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +2 -0
- package/dist/esm/cli/commands/agent-launch.js +31 -20
- package/dist/esm/cli/commands/audit.js +4 -0
- package/dist/esm/cli/commands/blueprint/abandon.d.ts +21 -0
- package/dist/esm/cli/commands/blueprint/abandon.js +178 -0
- package/dist/esm/cli/commands/blueprint/router-dispatch.js +21 -0
- package/dist/esm/cli/commands/blueprint/router-output.js +1 -0
- package/dist/esm/cli/commands/blueprint/router.d.ts +2 -0
- package/dist/esm/cli/commands/blueprint/router.js +5 -2
- package/dist/esm/cli/commands/ci-preflight.js +24 -3
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +87 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +189 -23
- package/dist/esm/cli/commands/init/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/index.js +2 -2
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +31 -30
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +3 -1
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +17 -2
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +189 -17
- package/dist/esm/cli/commands/opencode-probe.d.ts +118 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +23 -0
- package/dist/esm/cli/commands/opencode-rotate.js +23 -11
- package/dist/esm/cli/commands/quality-runner.js +8 -4
- package/dist/esm/cli/commands/review.js +27 -5
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +14 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +138 -2
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/hooks/doctor.d.ts +2 -0
- package/dist/esm/hooks/doctor.js +37 -1
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/types.js +27 -1
- package/dist/esm/hooks/stop/qa-changed-files.js +18 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +31 -7
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/audit.d.ts +2 -1
- package/dist/esm/mcp/tools/audit.js +12 -0
- package/dist/esm/mcp/tools/audits.d.ts +1 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +14 -9
- package/dist/esm/review/availability.d.ts +11 -0
- package/dist/esm/review/availability.js +18 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +10 -1
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/review-checkout.d.ts +6 -0
- package/dist/esm/review/execution/review-checkout.js +61 -16
- package/dist/esm/review/execution/supervisor.js +31 -4
- package/dist/esm/review/execution/types.d.ts +1 -0
- package/dist/esm/review/subject.d.ts +13 -0
- package/dist/esm/review/subject.js +42 -4
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +2 -6
- package/dist/esm/test-helpers/global-setup.d.ts +0 -1
- package/dist/esm/test-helpers/global-setup.js +18 -15
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/dist/esm/worktrees/owner-dirt.d.ts +40 -0
- package/dist/esm/worktrees/owner-dirt.js +104 -0
- package/package.json +14 -12
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem-level orphan scan for `wp worktree prune --orphans`.
|
|
3
|
+
*
|
|
4
|
+
* Complements the existing registry-only `wp worktree prune --all`: that
|
|
5
|
+
* command only drops stale bookkeeping rows from `registry.json`. This module
|
|
6
|
+
* instead walks the physical managed-worktree roots
|
|
7
|
+
* (`~/.webpresso/worktrees`, legacy `~/.agent/worktrees`) directly, because a
|
|
8
|
+
* large share of stale directories on disk were never registered at all
|
|
9
|
+
* (crashed sessions, `gh pr merge --delete-branch` deregistering the git
|
|
10
|
+
* worktree out from under the directory, manual `rm` of only part of a
|
|
11
|
+
* worktree). Git metadata and the registry can both be perfectly clean while
|
|
12
|
+
* the `~/.webpresso` tree still accumulates dozens of dead directories.
|
|
13
|
+
*
|
|
14
|
+
* Classification (see `catalog blueprint worktree-prune-stale-dirs`):
|
|
15
|
+
*
|
|
16
|
+
* - `alive` — path is listed by `git worktree list` for its resolved
|
|
17
|
+
* primary repo. Never removable.
|
|
18
|
+
* - `orphan` — a resolvable, existing primary repo's `git worktree
|
|
19
|
+
* list` output does NOT include this path. Removable only
|
|
20
|
+
* when a `git status --porcelain` probe inside the
|
|
21
|
+
* directory succeeds AND reports no changes.
|
|
22
|
+
* - `broken` — a primary repo pointer was resolved, but the target
|
|
23
|
+
* repo itself is missing on disk, or its `git worktree
|
|
24
|
+
* list` probe failed outright (bounded, non-retrying).
|
|
25
|
+
* Never removable — the directory's true state cannot be
|
|
26
|
+
* proven.
|
|
27
|
+
* - `unrecognized` — no primary repo pointer could be resolved at all
|
|
28
|
+
* (neither `registry.json` nor the directory's own
|
|
29
|
+
* `.git` file yields one), or the directory's `.git`
|
|
30
|
+
* pointer resolves but `git status --porcelain` cannot
|
|
31
|
+
* even run inside it (not a functioning checkout).
|
|
32
|
+
* Reported for manual review; never removable.
|
|
33
|
+
*/
|
|
34
|
+
import { readdirSync, statSync } from "node:fs";
|
|
35
|
+
import { join, relative, sep } from "node:path";
|
|
36
|
+
import { canonicalizeWorktreePath } from "./identity.js";
|
|
37
|
+
function measureSizeSafely(path, deps) {
|
|
38
|
+
try {
|
|
39
|
+
return deps.measureSize(path);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return "unknown";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function finalizeRow(candidate, classification, reason, deps) {
|
|
46
|
+
if (classification !== "orphan") {
|
|
47
|
+
return {
|
|
48
|
+
...candidate,
|
|
49
|
+
classification,
|
|
50
|
+
dirty: "unknown",
|
|
51
|
+
sizeBytes: "unknown",
|
|
52
|
+
removable: false,
|
|
53
|
+
reason,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const dirty = deps.probeDirty(candidate.path);
|
|
57
|
+
if (dirty === "unknown") {
|
|
58
|
+
return {
|
|
59
|
+
...candidate,
|
|
60
|
+
classification: "unrecognized",
|
|
61
|
+
dirty,
|
|
62
|
+
sizeBytes: "unknown",
|
|
63
|
+
removable: false,
|
|
64
|
+
reason: "git status probe failed — not a functioning git checkout",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Size is decision-relevant only once a row is a confirmed orphan, so it is
|
|
68
|
+
// measured here — never for alive/broken/unrecognized rows above.
|
|
69
|
+
const sizeBytes = measureSizeSafely(candidate.path, deps);
|
|
70
|
+
return {
|
|
71
|
+
...candidate,
|
|
72
|
+
classification,
|
|
73
|
+
dirty,
|
|
74
|
+
sizeBytes,
|
|
75
|
+
removable: dirty === false,
|
|
76
|
+
reason: dirty ? "uncommitted/untracked changes present" : reason,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Classify a single on-disk candidate. Pure aside from the injected deps. */
|
|
80
|
+
export function classifyOrphanCandidate(candidate, deps) {
|
|
81
|
+
const repoRoot = deps.resolveRepoRoot(candidate);
|
|
82
|
+
if (!repoRoot) {
|
|
83
|
+
return finalizeRow(candidate, "unrecognized", "no resolvable primary repo pointer", deps);
|
|
84
|
+
}
|
|
85
|
+
if (!deps.repoRootExists(repoRoot)) {
|
|
86
|
+
return finalizeRow(candidate, "broken", `target repo missing: ${repoRoot}`, deps);
|
|
87
|
+
}
|
|
88
|
+
const inventory = deps.buildInventory(repoRoot);
|
|
89
|
+
if (!inventory.complete) {
|
|
90
|
+
return finalizeRow(candidate, "broken", "git worktree list unavailable for target repo", deps);
|
|
91
|
+
}
|
|
92
|
+
const canonical = canonicalizeWorktreePath(candidate.path);
|
|
93
|
+
if (inventory.canonicalPaths.has(canonical)) {
|
|
94
|
+
return finalizeRow(candidate, "alive", "present in git worktree list", deps);
|
|
95
|
+
}
|
|
96
|
+
return finalizeRow(candidate, "orphan", "absent from git worktree list", deps);
|
|
97
|
+
}
|
|
98
|
+
/** Namespace-keyed lookup of a known-live `repoRoot`, first-registered-wins. */
|
|
99
|
+
export function buildRegistryRepoRootMap(entries) {
|
|
100
|
+
const map = new Map();
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (!map.has(entry.repoNamespace))
|
|
103
|
+
map.set(entry.repoNamespace, entry.repoRoot);
|
|
104
|
+
}
|
|
105
|
+
return map;
|
|
106
|
+
}
|
|
107
|
+
const WORKTREES_GITDIR_MARKER = "/.git/worktrees/";
|
|
108
|
+
/**
|
|
109
|
+
* Fallback primary-repo resolver for candidates the registry never recorded:
|
|
110
|
+
* a linked worktree's `.git` is a file containing `gitdir: <repoRoot>/.git/
|
|
111
|
+
* worktrees/<name>`. Returns `undefined` when unreadable or unparseable
|
|
112
|
+
* (e.g. `.git` is a directory, or the pointer target was already reaped).
|
|
113
|
+
*/
|
|
114
|
+
export function resolveRepoRootFromGitPointer(path, readGitPointer) {
|
|
115
|
+
const content = readGitPointer(join(path, ".git"));
|
|
116
|
+
if (!content)
|
|
117
|
+
return undefined;
|
|
118
|
+
const match = /^gitdir:\s*(.+)$/mu.exec(content.trim());
|
|
119
|
+
const gitdir = match?.[1]?.trim();
|
|
120
|
+
if (!gitdir)
|
|
121
|
+
return undefined;
|
|
122
|
+
const markerIndex = gitdir.lastIndexOf(WORKTREES_GITDIR_MARKER);
|
|
123
|
+
if (markerIndex === -1)
|
|
124
|
+
return undefined;
|
|
125
|
+
return gitdir.slice(0, markerIndex);
|
|
126
|
+
}
|
|
127
|
+
const MAX_SCAN_DEPTH = 8;
|
|
128
|
+
const SKIP_DIR_NAMES = new Set([".git", "node_modules"]);
|
|
129
|
+
function repoNamespaceFromPath(root, path) {
|
|
130
|
+
const segments = relative(root, path).split(sep).filter(Boolean);
|
|
131
|
+
const reposIndex = segments.indexOf("repos");
|
|
132
|
+
if (reposIndex === -1)
|
|
133
|
+
return undefined;
|
|
134
|
+
return segments[reposIndex + 1];
|
|
135
|
+
}
|
|
136
|
+
function walkForCandidates(root, dir, depth, out) {
|
|
137
|
+
if (depth > MAX_SCAN_DEPTH)
|
|
138
|
+
return;
|
|
139
|
+
let entries;
|
|
140
|
+
try {
|
|
141
|
+
entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (entries.some((entry) => entry.name === ".git")) {
|
|
147
|
+
const repoNamespace = repoNamespaceFromPath(root, dir);
|
|
148
|
+
if (repoNamespace)
|
|
149
|
+
out.push({ path: dir, repoNamespace });
|
|
150
|
+
return; // never descend into a worktree checkout's own contents
|
|
151
|
+
}
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
if (!entry.isDirectory() || SKIP_DIR_NAMES.has(entry.name))
|
|
154
|
+
continue;
|
|
155
|
+
walkForCandidates(root, join(dir, entry.name), depth + 1, out);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Walk every known managed-worktree root and collect worktree-shaped leaf dirs. */
|
|
159
|
+
export function discoverOrphanScanCandidates(roots, options = {}) {
|
|
160
|
+
const dirExists = options.dirExists ??
|
|
161
|
+
((path) => {
|
|
162
|
+
try {
|
|
163
|
+
return statSync(path).isDirectory();
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const out = [];
|
|
170
|
+
for (const root of roots) {
|
|
171
|
+
if (!dirExists(root))
|
|
172
|
+
continue;
|
|
173
|
+
walkForCandidates(root, root, 0, out);
|
|
174
|
+
}
|
|
175
|
+
return options.repoNamespaceFilter
|
|
176
|
+
? out.filter((candidate) => candidate.repoNamespace === options.repoNamespaceFilter)
|
|
177
|
+
: out;
|
|
178
|
+
}
|
|
179
|
+
/** Bounded recursive directory size, symlinks excluded, unreadable entries skipped. */
|
|
180
|
+
export function measureDirectorySizeBytes(path) {
|
|
181
|
+
let total = 0;
|
|
182
|
+
const stack = [path];
|
|
183
|
+
while (stack.length > 0) {
|
|
184
|
+
const current = stack.pop();
|
|
185
|
+
if (current === undefined)
|
|
186
|
+
break;
|
|
187
|
+
let entries;
|
|
188
|
+
try {
|
|
189
|
+
entries = readdirSync(current, { withFileTypes: true, encoding: "utf8" });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
if (entry.isSymbolicLink())
|
|
196
|
+
continue;
|
|
197
|
+
const full = join(current, entry.name);
|
|
198
|
+
if (entry.isDirectory()) {
|
|
199
|
+
stack.push(full);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
total += statSync(full).size;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// unreadable file: skip rather than fail the whole scan
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return total;
|
|
211
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner-worktree dirt classification for scaffold abandon + merge-cleanup hints.
|
|
3
|
+
*
|
|
4
|
+
* Machine-parse `git status --porcelain=v1 -z` only (NUL records; renames are
|
|
5
|
+
* destination then origin with no `->`). Do not use line-oriented porcelain for
|
|
6
|
+
* path classification — special characters and renames mis-parse.
|
|
7
|
+
*
|
|
8
|
+
* Parity with `isDirty`: ignored files are not considered (no `--ignored`).
|
|
9
|
+
*/
|
|
10
|
+
export type OwnerDirtKind = "clean" | "scaffold-only" | "other";
|
|
11
|
+
export interface OwnerDirtClassification {
|
|
12
|
+
readonly kind: OwnerDirtKind;
|
|
13
|
+
/** Repo-relative paths contributing to dirt (bounded callers may slice). */
|
|
14
|
+
readonly paths: readonly string[];
|
|
15
|
+
/** Paths under the draft scaffold prefix, when slug is provided. */
|
|
16
|
+
readonly scaffoldPaths: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
export interface PorcelainPathRecord {
|
|
19
|
+
readonly statusCode: string;
|
|
20
|
+
readonly paths: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** Blueprint draft tree for a slug (no leading/trailing slash on slug). */
|
|
23
|
+
export declare function draftScaffoldPrefix(slug: string): string;
|
|
24
|
+
export declare function isUnderDraftScaffold(relativePath: string, slug: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Parse `git status --porcelain=v1 -z` stdout into path records.
|
|
27
|
+
* Rename/copy: first path is destination, second (next record) is origin.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parsePorcelainV1Z(output: string): readonly PorcelainPathRecord[];
|
|
30
|
+
/**
|
|
31
|
+
* Classify dirt relative to an optional draft slug.
|
|
32
|
+
* Without slug, any non-empty porcelain is `other` (or `clean` if empty).
|
|
33
|
+
*/
|
|
34
|
+
export declare function classifyOwnerDirt(porcelainZ: string, slug?: string): OwnerDirtClassification;
|
|
35
|
+
export declare function formatBoundedPaths(paths: readonly string[], limit?: number): string;
|
|
36
|
+
export declare function formatTargetDirtyRefusal(input: {
|
|
37
|
+
readonly targetPath: string;
|
|
38
|
+
readonly porcelainZ: string;
|
|
39
|
+
readonly slug?: string;
|
|
40
|
+
}): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner-worktree dirt classification for scaffold abandon + merge-cleanup hints.
|
|
3
|
+
*
|
|
4
|
+
* Machine-parse `git status --porcelain=v1 -z` only (NUL records; renames are
|
|
5
|
+
* destination then origin with no `->`). Do not use line-oriented porcelain for
|
|
6
|
+
* path classification — special characters and renames mis-parse.
|
|
7
|
+
*
|
|
8
|
+
* Parity with `isDirty`: ignored files are not considered (no `--ignored`).
|
|
9
|
+
*/
|
|
10
|
+
/** Blueprint draft tree for a slug (no leading/trailing slash on slug). */
|
|
11
|
+
export function draftScaffoldPrefix(slug) {
|
|
12
|
+
return `blueprints/draft/${slug}`;
|
|
13
|
+
}
|
|
14
|
+
export function isUnderDraftScaffold(relativePath, slug) {
|
|
15
|
+
const prefix = draftScaffoldPrefix(slug);
|
|
16
|
+
return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse `git status --porcelain=v1 -z` stdout into path records.
|
|
20
|
+
* Rename/copy: first path is destination, second (next record) is origin.
|
|
21
|
+
*/
|
|
22
|
+
export function parsePorcelainV1Z(output) {
|
|
23
|
+
const records = output.split("\0").filter((record) => record.length > 0);
|
|
24
|
+
const parsed = [];
|
|
25
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
26
|
+
const record = records[index];
|
|
27
|
+
if (record.length < 3)
|
|
28
|
+
continue;
|
|
29
|
+
const statusCode = record.slice(0, 2);
|
|
30
|
+
const firstPath = record.slice(3);
|
|
31
|
+
const paths = [firstPath];
|
|
32
|
+
if (/[RC]/u.test(statusCode)) {
|
|
33
|
+
const originalPath = records[index + 1];
|
|
34
|
+
if (originalPath !== undefined) {
|
|
35
|
+
paths.push(originalPath);
|
|
36
|
+
index += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
parsed.push({ statusCode, paths });
|
|
40
|
+
}
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Classify dirt relative to an optional draft slug.
|
|
45
|
+
* Without slug, any non-empty porcelain is `other` (or `clean` if empty).
|
|
46
|
+
*/
|
|
47
|
+
export function classifyOwnerDirt(porcelainZ, slug) {
|
|
48
|
+
const records = parsePorcelainV1Z(porcelainZ);
|
|
49
|
+
if (records.length === 0) {
|
|
50
|
+
return { kind: "clean", paths: [], scaffoldPaths: [] };
|
|
51
|
+
}
|
|
52
|
+
const paths = new Set();
|
|
53
|
+
const scaffoldPaths = new Set();
|
|
54
|
+
let allScaffold = slug !== undefined && slug.length > 0;
|
|
55
|
+
for (const record of records) {
|
|
56
|
+
for (const relativePath of record.paths) {
|
|
57
|
+
if (relativePath.length === 0)
|
|
58
|
+
continue;
|
|
59
|
+
paths.add(relativePath);
|
|
60
|
+
if (slug && isUnderDraftScaffold(relativePath, slug)) {
|
|
61
|
+
scaffoldPaths.add(relativePath);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
allScaffold = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const pathList = [...paths].toSorted();
|
|
69
|
+
const scaffoldList = [...scaffoldPaths].toSorted();
|
|
70
|
+
if (pathList.length === 0) {
|
|
71
|
+
return { kind: "clean", paths: [], scaffoldPaths: [] };
|
|
72
|
+
}
|
|
73
|
+
if (allScaffold && scaffoldList.length > 0) {
|
|
74
|
+
return { kind: "scaffold-only", paths: pathList, scaffoldPaths: scaffoldList };
|
|
75
|
+
}
|
|
76
|
+
return { kind: "other", paths: pathList, scaffoldPaths: scaffoldList };
|
|
77
|
+
}
|
|
78
|
+
export function formatBoundedPaths(paths, limit = 10) {
|
|
79
|
+
if (paths.length === 0)
|
|
80
|
+
return "(none)";
|
|
81
|
+
const head = paths.slice(0, limit);
|
|
82
|
+
const more = paths.length - head.length;
|
|
83
|
+
return more > 0 ? `${head.join("\n")}\n(and ${more} more)` : head.join("\n");
|
|
84
|
+
}
|
|
85
|
+
export function formatTargetDirtyRefusal(input) {
|
|
86
|
+
const classification = classifyOwnerDirt(input.porcelainZ, input.slug);
|
|
87
|
+
const pathBlock = formatBoundedPaths(classification.paths);
|
|
88
|
+
const header = `wp worktree merge-cleanup refused: ${input.targetPath} has uncommitted changes`;
|
|
89
|
+
if (classification.kind === "scaffold-only" && input.slug) {
|
|
90
|
+
return [
|
|
91
|
+
header,
|
|
92
|
+
"Owner has only uncommitted draft scaffold dirt:",
|
|
93
|
+
pathBlock,
|
|
94
|
+
`fix: wp blueprint abandon ${input.slug} --confirm`,
|
|
95
|
+
" or: commit the draft and land it with the PR",
|
|
96
|
+
].join("\n");
|
|
97
|
+
}
|
|
98
|
+
return [
|
|
99
|
+
header,
|
|
100
|
+
"Paths:",
|
|
101
|
+
pathBlock,
|
|
102
|
+
`fix: commit or stash work in ${input.targetPath}, then re-run merge-cleanup`,
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpresso/agent-kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TypeScript-first agent harness for guarded develop/deploy workflows: wp CLI gates, MCP tools, hooks, memory, worktrees, secrets, audits, and evidence checks.",
|
|
6
6
|
"keywords": [
|
|
@@ -142,6 +142,8 @@
|
|
|
142
142
|
"#compiler/*": "./src/compiler/*.ts",
|
|
143
143
|
"#review/*.js": "./src/review/*.ts",
|
|
144
144
|
"#review/*": "./src/review/*.ts",
|
|
145
|
+
"#rust/*.js": "./src/rust/*.ts",
|
|
146
|
+
"#rust/*": "./src/rust/*.ts",
|
|
145
147
|
"#telemetry/*.js": "./src/telemetry/*.ts",
|
|
146
148
|
"#telemetry/*": "./src/telemetry/*.ts",
|
|
147
149
|
"#codex/app-server/client": "./src/codex/app-server/client.ts",
|
|
@@ -505,16 +507,16 @@
|
|
|
505
507
|
"setupWpActionRef": "c2c71a7a4be446fc6858e6b57bf55a11ccfa2d88"
|
|
506
508
|
},
|
|
507
509
|
"optionalDependencies": {
|
|
508
|
-
"@webpresso/agent-kit-runtime-darwin-arm64": "3.
|
|
509
|
-
"@webpresso/agent-kit-runtime-darwin-x64": "3.
|
|
510
|
-
"@webpresso/agent-kit-runtime-linux-x64": "3.
|
|
511
|
-
"@webpresso/agent-kit-runtime-linux-arm64": "3.
|
|
512
|
-
"@webpresso/agent-kit-runtime-windows-x64": "3.
|
|
513
|
-
"@webpresso/agent-kit-session-memory-darwin-x64": "3.
|
|
514
|
-
"@webpresso/agent-kit-session-memory-darwin-arm64": "3.
|
|
515
|
-
"@webpresso/agent-kit-session-memory-linux-x64": "3.
|
|
516
|
-
"@webpresso/agent-kit-session-memory-linux-arm64": "3.
|
|
517
|
-
"@webpresso/agent-kit-session-memory-win32-x64": "3.
|
|
518
|
-
"@webpresso/agent-kit-session-memory-win32-arm64": "3.
|
|
510
|
+
"@webpresso/agent-kit-runtime-darwin-arm64": "3.3.1",
|
|
511
|
+
"@webpresso/agent-kit-runtime-darwin-x64": "3.3.1",
|
|
512
|
+
"@webpresso/agent-kit-runtime-linux-x64": "3.3.1",
|
|
513
|
+
"@webpresso/agent-kit-runtime-linux-arm64": "3.3.1",
|
|
514
|
+
"@webpresso/agent-kit-runtime-windows-x64": "3.3.1",
|
|
515
|
+
"@webpresso/agent-kit-session-memory-darwin-x64": "3.3.1",
|
|
516
|
+
"@webpresso/agent-kit-session-memory-darwin-arm64": "3.3.1",
|
|
517
|
+
"@webpresso/agent-kit-session-memory-linux-x64": "3.3.1",
|
|
518
|
+
"@webpresso/agent-kit-session-memory-linux-arm64": "3.3.1",
|
|
519
|
+
"@webpresso/agent-kit-session-memory-win32-x64": "3.3.1",
|
|
520
|
+
"@webpresso/agent-kit-session-memory-win32-arm64": "3.3.1"
|
|
519
521
|
}
|
|
520
522
|
}
|