@sabaiway/agent-workflow-kit 10.5.0 → 11.0.0
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/CHANGELOG.md +139 -0
- package/SKILL.md +9 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +6 -6
- package/bridges/antigravity-cli-bridge/capability.json +1 -1
- package/bridges/codex-cli-bridge/SKILL.md +1 -1
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review.sh +6 -6
- package/bridges/codex-cli-bridge/capability.json +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/commit-guard.md +2 -2
- package/references/modes/control-bytes.md +91 -0
- package/references/modes/core-evidence.md +1 -1
- package/references/modes/flow-writer.md +16 -4
- package/references/modes/procedures.md +5 -1
- package/references/modes/review-state.md +3 -3
- package/references/modes/robustness-brief.md +61 -0
- package/references/modes/velocity.md +2 -0
- package/references/robustness-literals.json +107 -0
- package/tools/changed-surface.mjs +1 -1
- package/tools/commands.mjs +14 -0
- package/tools/commit-guard.mjs +24 -12
- package/tools/control-bytes.mjs +264 -0
- package/tools/core-evidence.mjs +33 -20
- package/tools/coverage-check.mjs +1 -1
- package/tools/dispatch-store-read.mjs +269 -0
- package/tools/dispatch-store.mjs +21 -357
- package/tools/flow-check-git-lane.mjs +2 -1
- package/tools/flow-delta-proof.mjs +4 -4
- package/tools/flow-record-shape.mjs +44 -2
- package/tools/flow-round-gates.mjs +151 -0
- package/tools/flow-store-read.mjs +1 -1
- package/tools/flow-writer.mjs +235 -82
- package/tools/fold-scope.mjs +7 -3
- package/tools/fs-read-nofollow.mjs +56 -4
- package/tools/git-env.mjs +120 -0
- package/tools/grounding.mjs +4 -3
- package/tools/held-session.mjs +165 -0
- package/tools/plan-shape-facts.mjs +23 -10
- package/tools/plan-shape.mjs +8 -0
- package/tools/procedures.mjs +47 -4
- package/tools/repo-lex.mjs +29 -0
- package/tools/review-state.mjs +127 -39
- package/tools/robustness-brief.mjs +209 -0
- package/tools/robustness-literals.mjs +101 -0
- package/tools/sandbox-masks.mjs +2 -1
- package/tools/set-flow.mjs +2 -1
- package/tools/source-size-scope.mjs +19 -25
- package/tools/tracked-tree-census.mjs +3 -3
- package/tools/velocity-profile.mjs +5 -2
- package/tools/worktree-handoff-return.mjs +2 -2
- package/tools/worktrees.mjs +2 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
|
|
4
|
+
|
|
5
|
+
export const KINDS = Object.freeze(['env', 'flag', 'ref', 'syscall', 'errno', 'state', 'argv']);
|
|
6
|
+
const KIND_SET = new Set(KINDS);
|
|
7
|
+
const TOP_KEYS = Object.freeze(['schema', 'version', 'classes']);
|
|
8
|
+
const CLASS_KEYS = Object.freeze(['id', 'prove', 'members']);
|
|
9
|
+
const MEMBER_KEYS = Object.freeze(['literal', 'kind', 'note', 'source']);
|
|
10
|
+
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
11
|
+
const ADR = /^AD-\d+$/u;
|
|
12
|
+
const CONTROL = /[\p{Cc}\u2028\u2029\uFFFD]/u;
|
|
13
|
+
const SHIPPED_LIST = fileURLToPath(new URL('../references/robustness-literals.json', import.meta.url));
|
|
14
|
+
|
|
15
|
+
const refuse = (code, detail) => {
|
|
16
|
+
throw Object.assign(new Error(`robustness-literals: ${code}${detail ? ` — ${detail}` : ''}`), { code });
|
|
17
|
+
};
|
|
18
|
+
const requireObject = (value, label) => {
|
|
19
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) refuse('type', `${label} must be an object`);
|
|
20
|
+
};
|
|
21
|
+
const requireKeys = (value, keys, label) => {
|
|
22
|
+
requireObject(value, label);
|
|
23
|
+
const unknown = Object.keys(value).find((key) => !keys.includes(key));
|
|
24
|
+
if (unknown !== undefined) refuse('unknown-key', `${label}.${unknown}`);
|
|
25
|
+
const missing = keys.find((key) => !Object.hasOwn(value, key));
|
|
26
|
+
if (missing !== undefined) refuse('missing-key', `${label}.${missing}`);
|
|
27
|
+
};
|
|
28
|
+
const requireString = (value, label) => {
|
|
29
|
+
if (typeof value !== 'string') refuse('type', `${label} must be a string`);
|
|
30
|
+
if (value.length === 0) refuse('empty-string', label);
|
|
31
|
+
if (CONTROL.test(value)) refuse('control', label);
|
|
32
|
+
};
|
|
33
|
+
const isRepoSource = (source) => !source.startsWith('/') && !source.includes('\\') &&
|
|
34
|
+
!source.split('/').includes('..') && (source.includes('/') || source.includes('.'));
|
|
35
|
+
const freezeList = (list) => Object.freeze({
|
|
36
|
+
...list,
|
|
37
|
+
classes: Object.freeze(list.classes.map((entry) => Object.freeze({
|
|
38
|
+
...entry,
|
|
39
|
+
members: Object.freeze(entry.members.map((member) => Object.freeze(member))),
|
|
40
|
+
}))),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const validateList = (value) => {
|
|
44
|
+
requireKeys(value, TOP_KEYS, 'list');
|
|
45
|
+
if (value.schema !== 1) refuse('schema', 'schema must be 1');
|
|
46
|
+
if (!Number.isSafeInteger(value.version) || value.version < 1) refuse('version', 'version must be an integer >= 1');
|
|
47
|
+
if (!Array.isArray(value.classes)) refuse('type', 'classes must be an array');
|
|
48
|
+
if (value.classes.length === 0) refuse('empty-class', 'classes must not be empty');
|
|
49
|
+
const ids = new Set();
|
|
50
|
+
for (const [classIndex, entry] of value.classes.entries()) {
|
|
51
|
+
const label = `classes[${classIndex}]`;
|
|
52
|
+
requireKeys(entry, CLASS_KEYS, label);
|
|
53
|
+
requireString(entry.id, `${label}.id`);
|
|
54
|
+
if (!SLUG.test(entry.id)) refuse('class-id', entry.id);
|
|
55
|
+
if (ids.has(entry.id)) refuse('duplicate-class', entry.id);
|
|
56
|
+
ids.add(entry.id);
|
|
57
|
+
requireString(entry.prove, `${label}.prove`);
|
|
58
|
+
if (!Array.isArray(entry.members)) refuse('type', `${label}.members must be an array`);
|
|
59
|
+
if (entry.members.length === 0) refuse('empty-class', entry.id);
|
|
60
|
+
const literals = new Set();
|
|
61
|
+
for (const [memberIndex, member] of entry.members.entries()) {
|
|
62
|
+
const memberLabel = `${label}.members[${memberIndex}]`;
|
|
63
|
+
requireKeys(member, MEMBER_KEYS, memberLabel);
|
|
64
|
+
for (const key of MEMBER_KEYS) requireString(member[key], `${memberLabel}.${key}`);
|
|
65
|
+
if (literals.has(member.literal)) refuse('duplicate-literal', `${entry.id}:${member.literal}`);
|
|
66
|
+
literals.add(member.literal);
|
|
67
|
+
if (!KIND_SET.has(member.kind)) refuse('kind', member.kind);
|
|
68
|
+
if (!ADR.test(member.source) && !SLUG.test(member.source) && !isRepoSource(member.source)) refuse('source', member.source);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return freezeList(value);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const readRobustnessLiterals = (path, io = {}) => {
|
|
75
|
+
const read = readRegularFileNoFollow(path, io);
|
|
76
|
+
if (read.outcome !== 'ok') refuse('type', `${path} must be a readable regular file (${read.className ?? read.code ?? read.outcome})`);
|
|
77
|
+
try {
|
|
78
|
+
return validateList(JSON.parse(read.content));
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error?.code) throw error;
|
|
81
|
+
refuse('type', `${path} is not valid JSON (${error.message})`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const readShippedRobustnessLiterals = (deps = {}) =>
|
|
86
|
+
readRobustnessLiterals((deps.resolvePath ?? (() => SHIPPED_LIST))(), deps.io);
|
|
87
|
+
|
|
88
|
+
export const parseRobustTag = (text) => {
|
|
89
|
+
const matches = [...String(text ?? '').matchAll(/robust:(\S*)/gu)];
|
|
90
|
+
if (matches.length === 0) return { classes: [] };
|
|
91
|
+
if (matches.length > 1) return { refusal: 'multiple-tags' };
|
|
92
|
+
if (matches[0][1] === '') return { refusal: 'no-class' };
|
|
93
|
+
const classes = matches[0][1].split(',');
|
|
94
|
+
if (classes.some((entry) => entry === '')) return { refusal: 'empty-class' };
|
|
95
|
+
if (new Set(classes).size !== classes.length) return { refusal: 'duplicate-class' };
|
|
96
|
+
if (classes.some((entry) => !SLUG.test(entry))) return { refusal: 'invalid-class' };
|
|
97
|
+
return { classes };
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const classIds = (list) => Object.freeze(list.classes.map((entry) => entry.id));
|
|
101
|
+
export const digestOf = (list) => createHash('sha256').update(JSON.stringify(list.classes)).digest('hex');
|
package/tools/sandbox-masks.mjs
CHANGED
|
@@ -42,6 +42,7 @@ import { isDirectRun } from './direct-run.mjs';
|
|
|
42
42
|
import { fail } from './orchestration-config.mjs';
|
|
43
43
|
import { isNeverCommittableStat, shellQuoteArg } from './review-state.mjs';
|
|
44
44
|
import { assertContainedRealPath } from './fs-safe.mjs';
|
|
45
|
+
import { GIT_MAX_BUFFER } from './git-env.mjs';
|
|
45
46
|
|
|
46
47
|
export const MASKS_FENCE_START = '# >>> agent-workflow sandbox-masks — managed block, fully REPLACED by the kit sandbox-masks lane; do not hand-edit inside >>>';
|
|
47
48
|
export const MASKS_FENCE_END = '# <<< agent-workflow sandbox-masks <<<';
|
|
@@ -59,7 +60,7 @@ const gitLine = (args, cwd) => {
|
|
|
59
60
|
// The UNFILTERED untracked walk: --others WITHOUT --exclude-standard, so a mask already hidden by
|
|
60
61
|
// an earlier apply stays visible to a rerun (the full-block replace re-derives it, codex).
|
|
61
62
|
const listUntrackedUnfilteredZ = (root) => {
|
|
62
|
-
const r = spawnSync('git', ['ls-files', '--others', '-z'], { cwd: root, encoding: 'utf8', maxBuffer:
|
|
63
|
+
const r = spawnSync('git', ['ls-files', '--others', '-z'], { cwd: root, encoding: 'utf8', maxBuffer: GIT_MAX_BUFFER, windowsHide: true });
|
|
63
64
|
if (r.error || r.status !== 0) return null;
|
|
64
65
|
return r.stdout.split('\0').filter(Boolean);
|
|
65
66
|
};
|
package/tools/set-flow.mjs
CHANGED
|
@@ -50,6 +50,7 @@ import { loadDeclaration } from './run-gates.mjs';
|
|
|
50
50
|
import { compareSemver } from './semver-lite.mjs';
|
|
51
51
|
import { lexicalRepoRelative } from './core-evidence.mjs';
|
|
52
52
|
import { readAuthoritativeVersion } from './manifest/validate.mjs';
|
|
53
|
+
import { GIT_MAX_BUFFER } from './git-env.mjs';
|
|
53
54
|
|
|
54
55
|
const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
55
56
|
const GATES_REL = 'docs/ai/gates.json';
|
|
@@ -197,7 +198,7 @@ const meetsVersionFloor = (version, floor) => {
|
|
|
197
198
|
return cmp !== null && cmp >= 0;
|
|
198
199
|
};
|
|
199
200
|
|
|
200
|
-
const defaultRunGit = (args, cwd) => spawnSync('git', args, { cwd, maxBuffer:
|
|
201
|
+
const defaultRunGit = (args, cwd) => spawnSync('git', args, { cwd, maxBuffer: GIT_MAX_BUFFER, windowsHide: true });
|
|
201
202
|
|
|
202
203
|
const gitToplevel = (cwd, runGit) => {
|
|
203
204
|
const r = runGit(['rev-parse', '--show-toplevel'], cwd);
|
|
@@ -18,37 +18,18 @@ import { readFileSync } from 'node:fs';
|
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
import { spawnSync } from 'node:child_process';
|
|
20
20
|
import { configFail, configPathFor, scopeFail } from './source-size-refusal.mjs';
|
|
21
|
+
import { GIT_MAX_BUFFER, parseIndexEntries, resolveGitLocation, withGitPath } from './git-env.mjs';
|
|
21
22
|
|
|
22
23
|
const LF = 0x0a;
|
|
23
24
|
const CR = 0x0d;
|
|
24
|
-
const TAB = 0x09;
|
|
25
25
|
const SLASH = 0x2f;
|
|
26
26
|
|
|
27
|
-
const GIT_MAX_BUFFER = 256 * 1024 * 1024;
|
|
28
27
|
const SYMLINK_MODE = '120000';
|
|
29
28
|
const GITLINK_MODE = '160000';
|
|
30
29
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// The object id is carried through rather than dropped: the record already holds it, and a consumer
|
|
35
|
-
// that must read what the index HOLDS (not what the worktree happens to show) needs exactly that.
|
|
36
|
-
const parseIndexEntries = (buf) => {
|
|
37
|
-
const entries = [];
|
|
38
|
-
let start = 0;
|
|
39
|
-
while (start < buf.length) {
|
|
40
|
-
let end = buf.indexOf(0, start);
|
|
41
|
-
if (end === -1) end = buf.length;
|
|
42
|
-
const record = buf.subarray(start, end);
|
|
43
|
-
const tab = record.indexOf(TAB);
|
|
44
|
-
if (tab !== -1) {
|
|
45
|
-
const head = record.subarray(0, tab).toString('utf8').split(' ');
|
|
46
|
-
entries.push({ mode: head[0], sha: head[1], stage: Number(head[2]), path: record.subarray(tab + 1) });
|
|
47
|
-
}
|
|
48
|
-
start = end + 1;
|
|
49
|
-
}
|
|
50
|
-
return entries;
|
|
51
|
-
};
|
|
30
|
+
// The `ls-files -s -z` record parser lives in the git leaf (raw bytes in, raw path bytes out, the
|
|
31
|
+
// object id carried through so a consumer reading what the index HOLDS has it) — one home shared
|
|
32
|
+
// with the control-byte gate.
|
|
52
33
|
|
|
53
34
|
// A path whose bytes are not valid UTF-8 round-trips to something DIFFERENT (the replacement char is
|
|
54
35
|
// lossy) — that is the whole test, and it is exact.
|
|
@@ -65,11 +46,24 @@ const bufSegmentPrefix = (path, prefix) =>
|
|
|
65
46
|
const bufEndsWith = (path, suffix) =>
|
|
66
47
|
path.length >= suffix.length && path.subarray(path.length - suffix.length).equals(suffix);
|
|
67
48
|
|
|
49
|
+
// The enumeration-error class (exit 2) also refuses every git LOCATION but a work tree, by its own
|
|
50
|
+
// state name — a redirecting GIT_DIR would otherwise enumerate ANOTHER repository's index in
|
|
51
|
+
// silence — and a runner that throws, which is an answer here, never a raw stack.
|
|
68
52
|
export const enumerateIndex = (cwd, deps = {}) => {
|
|
69
53
|
const spawn = deps.spawn ?? spawnSync;
|
|
70
|
-
const
|
|
54
|
+
const env = deps.env ?? process.env;
|
|
55
|
+
const location = resolveGitLocation(cwd, { spawn, env });
|
|
56
|
+
if (location.state !== 'work-tree') {
|
|
57
|
+
throw configFail(`the git index could not be enumerated in ${cwd} (the git location is ${location.state}: ${location.cause}) — the declared scope is unknown, so nothing is judged`);
|
|
58
|
+
}
|
|
59
|
+
let result;
|
|
60
|
+
try {
|
|
61
|
+
result = spawn('git', ['ls-files', '-s', '-z'], { cwd, env: withGitPath(env), maxBuffer: GIT_MAX_BUFFER, windowsHide: true });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
result = { error: err };
|
|
64
|
+
}
|
|
71
65
|
if (result.error || result.status !== 0) {
|
|
72
|
-
const why = result.error ? result.error.message : `git exited ${result.status}`;
|
|
66
|
+
const why = result.error ? result.error.message : result.signal ? `git was killed by ${result.signal}` : `git exited ${result.status}`;
|
|
73
67
|
throw configFail(`the git index could not be enumerated in ${cwd} (${why}) — the declared scope is unknown, so nothing is judged`);
|
|
74
68
|
}
|
|
75
69
|
return parseIndexEntries(result.stdout);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// tracked-tree-census.mjs — how much of a project's TRACKED tree the changed-line coverage domain
|
|
2
2
|
// can actually assess, in the closed vocabulary that domain already speaks. A LEAF: it imports the
|
|
3
|
-
// classification and
|
|
3
|
+
// changed-surface classification and the shared git-spawn buffer from the git-location leaf, and it spawns exactly one read-only `git ls-files`.
|
|
4
4
|
//
|
|
5
5
|
// Why this exists: the coverage checker's domain is `.mjs/.cjs/.js` by design, and on a TS project
|
|
6
6
|
// that domain is a rounding error of the tree. Certifying it and calling the flow optimal is the
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// Read-only: never writes, never commits. Dependency-free, Node >= 22. No side effects on import.
|
|
11
11
|
|
|
12
12
|
import { spawnSync } from 'node:child_process';
|
|
13
|
-
import { classifyChangedPath } from './changed-surface.mjs';
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
import { classifyChangedPath } from './changed-surface.mjs';
|
|
15
|
+
import { GIT_MAX_BUFFER } from './git-env.mjs';
|
|
16
16
|
|
|
17
17
|
export const CENSUS_VERDICT = Object.freeze({ NARROW: 'domain-narrow', WITHIN_DOMAIN: 'within-domain' });
|
|
18
18
|
|
|
@@ -133,7 +133,7 @@ export const SHELL_READONLY = Object.freeze([
|
|
|
133
133
|
// seed time from the RUNNING tool's own location — resolved-absolute, so a moved or reinstalled
|
|
134
134
|
// skill leaves a stale rule that FAIL-SAFE prompts again (never a silent widening).
|
|
135
135
|
//
|
|
136
|
-
// Membership (
|
|
136
|
+
// Membership (14, frozen): the read-only kit tools plus run-gates.mjs — which is NOT read-only but
|
|
137
137
|
// project-exec (it runs the project's OWN declared gates.json commands), so it seeds as ONE exact
|
|
138
138
|
// byte-string pinned to this project root (`--cwd <resolved root>`): a wildcard would be BROADER
|
|
139
139
|
// than the AD-037 hook boundary (`--cwd <dir>` executes an arbitrary OTHER project's gates.json)
|
|
@@ -163,6 +163,9 @@ export const KIT_READONLY_TOOLS = Object.freeze([
|
|
|
163
163
|
// The round table the procedures advisor names every review round; its path argument is resolved
|
|
164
164
|
// and compared, never opened as a pattern, so it rides the settings-level posture like the rest.
|
|
165
165
|
'tools/review-rounds-cli.mjs',
|
|
166
|
+
// The control-byte gate: a pure reader over the work tree, run by hand before the matrix runs it.
|
|
167
|
+
'tools/control-bytes.mjs',
|
|
168
|
+
'tools/robustness-brief.mjs',
|
|
166
169
|
]);
|
|
167
170
|
// Writer previews: ONLY writers whose ARG-FREE invocation is a documented dry-run ("Default is
|
|
168
171
|
// --dry-run" in their usage) seed an EXACT preview byte-string — every --apply/--write/--yes keeps
|
|
@@ -780,7 +783,7 @@ export const screenAllowlistEntry = (pattern) => {
|
|
|
780
783
|
};
|
|
781
784
|
|
|
782
785
|
/**
|
|
783
|
-
* Derive the opt-in kit-tools tier for a project:
|
|
786
|
+
* Derive the opt-in kit-tools tier for a project: 13 wildcard entries (resolved-absolute script
|
|
784
787
|
* path + args wildcard), ONE exact run-gates entry pinned to the resolved project root, and the
|
|
785
788
|
* writer-preview exact dry-run byte-strings. Pure derivation — a stale path simply prompts again.
|
|
786
789
|
*/
|
|
@@ -34,12 +34,14 @@
|
|
|
34
34
|
import { spawnSync } from 'node:child_process';
|
|
35
35
|
import { lstatSync, readdirSync, realpathSync } from 'node:fs';
|
|
36
36
|
import { createHash } from 'node:crypto';
|
|
37
|
+
|
|
37
38
|
import { findSatelliteEntry, readSatelliteIdentity, WORKTREES_STOP } from './satellite-locator.mjs';
|
|
38
39
|
import { locateProvisionRecordSection, parseProvisionRecord, displayValue } from './worktrees-record.mjs';
|
|
39
40
|
import { readFileBytesNoFollow } from './fs-read-nofollow.mjs';
|
|
40
41
|
import { computeNumerator } from './dispatch-record.mjs';
|
|
41
42
|
import { resolveRepoRoot, formatRatio, buildObservationRecord } from './observation-builder.mjs';
|
|
42
43
|
import { appendDelegationRecord, DELEGATION_STORE_STOP } from './dispatch-store.mjs';
|
|
44
|
+
import { GIT_MAX_BUFFER } from './git-env.mjs';
|
|
43
45
|
|
|
44
46
|
// The worktrees slug grammar, repeated here so the CLI can refuse a malformed slug as USAGE before
|
|
45
47
|
// any probe echoes it — the locator's own refusal interpolates the slug into a terminal message.
|
|
@@ -57,8 +59,6 @@ const PROVENANCE = 'self-reported';
|
|
|
57
59
|
const REGULAR_MODES = new Set(['100644', '100755']);
|
|
58
60
|
const RECORD_HEADING = '## Provision record';
|
|
59
61
|
|
|
60
|
-
const GIT_MAX_BUFFER = 64 * 1024 * 1024;
|
|
61
|
-
|
|
62
62
|
const defaultGit = (args, cwd) => {
|
|
63
63
|
const r = spawnSync('git', args, { cwd, encoding: 'utf8', windowsHide: true, maxBuffer: GIT_MAX_BUFFER });
|
|
64
64
|
return {
|
package/tools/worktrees.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { join, dirname, basename, resolve, relative, isAbsolute, sep } from 'nod
|
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { spawnSync } from 'node:child_process';
|
|
18
18
|
import { randomBytes } from 'node:crypto';
|
|
19
|
+
|
|
19
20
|
import { MCP_JSON_REL, SERVER_NAME, SETTINGS_REL, withoutRegistration } from './mcp-registration.mjs';
|
|
20
21
|
import {
|
|
21
22
|
KIT_OWN_PATHS, KNOWN_FOOTPRINT, expandGlob, normalizeSlashes, isDirPattern, isGlobPattern,
|
|
@@ -25,6 +26,7 @@ import { isDirectRun } from './direct-run.mjs';
|
|
|
25
26
|
import { isScratchPlanName, plansInFlight, PLANS_REL, shellQuoteArg } from './review-state.mjs';
|
|
26
27
|
import { writeContainedFileAtomic } from './atomic-write.mjs';
|
|
27
28
|
import { assertContainedRealPath } from './fs-safe.mjs';
|
|
29
|
+
import { GIT_MAX_BUFFER } from './git-env.mjs';
|
|
28
30
|
import { isFinalCapableDeclaration } from './run-gates.mjs';
|
|
29
31
|
import {
|
|
30
32
|
WORKTREES_STOP, stop, EXIT, handoffBasename, recordValue, hasControlByte, displayValue,
|
|
@@ -76,8 +78,6 @@ const TRACKED_PIN_DECLARATION =
|
|
|
76
78
|
const TRANSFER_EXCLUSIONS = Object.freeze([':!docs/ai', ':!docs/plans']);
|
|
77
79
|
const PREPARE_LOCK_BASENAME = 'aw-prepare-lock';
|
|
78
80
|
|
|
79
|
-
const GIT_MAX_BUFFER = 256 * 1024 * 1024;
|
|
80
|
-
|
|
81
81
|
const USAGE = [
|
|
82
82
|
'usage: worktrees.mjs <subcommand> [args]',
|
|
83
83
|
'',
|