@ikon85/agent-workflow-kit 0.43.0 → 0.44.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/.agents/skills/grill-me/SKILL.md +3 -1
- package/.agents/skills/grill-with-docs/SKILL.md +3 -1
- package/.agents/skills/kit-update/SKILL.md +9 -11
- package/.agents/skills/orchestrate-wave/SKILL.md +8 -8
- package/.agents/skills/setup-workflow/SKILL.md +20 -18
- package/.agents/skills/setup-workflow/board-sync.md +4 -1
- package/.agents/skills/setup-workflow/workflow-advisories.md +3 -2
- package/.agents/skills/setup-workflow/worktree-lifecycle.md +36 -31
- package/.agents/skills/to-issues/SKILL.md +43 -0
- package/.agents/skills/wrapup/SKILL.md +98 -54
- package/.claude/skills/grill-me/SKILL.md +3 -1
- package/.claude/skills/grill-me-codex/SKILL.md +3 -1
- package/.claude/skills/grill-with-docs/SKILL.md +3 -1
- package/.claude/skills/grill-with-docs-codex/SKILL.md +3 -1
- package/.claude/skills/kit-update/SKILL.md +9 -11
- package/.claude/skills/orchestrate-wave/SKILL.md +8 -8
- package/.claude/skills/setup-workflow/SKILL.md +20 -18
- package/.claude/skills/setup-workflow/board-sync.md +4 -1
- package/.claude/skills/setup-workflow/workflow-advisories.md +3 -2
- package/.claude/skills/setup-workflow/worktree-lifecycle.md +36 -31
- package/.claude/skills/to-issues/SKILL.md +43 -0
- package/.claude/skills/wrapup/SKILL.md +98 -54
- package/README.md +249 -7
- package/agent-workflow-kit.package.json +42 -42
- package/package.json +16 -12
- package/scripts/marker_lib.py +3 -0
- package/scripts/profile_globs.py +26 -45
- package/scripts/release-delta-guard.mjs +26 -9
- package/scripts/worktree-lifecycle/README.md +57 -160
- package/scripts/worktree-lifecycle/capabilities.json +2 -3
- package/scripts/worktree-lifecycle/classify.py +480 -0
- package/scripts/worktree-lifecycle/cleanup.py +9 -29
- package/scripts/worktree-lifecycle/core.py +68 -1429
- package/scripts/worktree-lifecycle/ignore_seed.py +1 -1
- package/scripts/worktree-lifecycle/profile.py +46 -29
- package/scripts/worktree-lifecycle/setup.py +7 -19
- package/scripts/wrapup-land.py +1292 -545
- package/src/consumer-migrations.json +1 -16
- package/src/lib/bundle.mjs +31 -1
- package/docs/adr/0001-consumer-divergence-policy.md +0 -53
- package/docs/adr/0002-capability-gated-orchestration.md +0 -70
- package/docs/adr/0003-kit-core-and-project-extension-lifecycle.md +0 -63
- package/docs/adr/0004-release-intent-is-a-version-tag.md +0 -64
- package/docs/adr/0005-to-issues-is-the-planning-facade.md +0 -42
- package/docs/adr/0006-routing-knowledge-access-and-policy-are-separate.md +0 -95
- package/docs/adr/0007-session-teardown-requires-provenance-bound-ownership.md +0 -88
- package/docs/adr/0008-planning-ignore-rules-are-offered-never-installed.md +0 -84
- package/docs/adr/0009-teardown-authority-is-stateless-repository-classification.md +0 -101
- package/docs/adr/0010-model-roster-replaces-the-optimization-dial.md +0 -87
- package/docs/agents/board-sync.md +0 -74
- package/docs/agents/code-review.md +0 -14
- package/docs/agents/workflow-capabilities.json +0 -28
- package/docs/research/agent-task-taxonomy-benchmark-coverage.md +0 -451
- package/docs/research/benchlm-routing-source.md +0 -198
- package/docs/research/consumer-owned-protocol-files.md +0 -238
- package/docs/research/frontend-agent-benchmarks.md +0 -282
- package/docs/research/model-effort-routing-benchmarks.md +0 -261
- package/docs/research/provider-neutral-agent-routing.md +0 -207
- package/docs/research/wave-152-consumer-acceptance.md +0 -98
- package/docs/research/wave-43-script-hook-census.md +0 -128
- package/scripts/build-kit.mjs +0 -117
- package/scripts/build-kit.test.mjs +0 -207
- package/scripts/census/census.test.mjs +0 -252
- package/scripts/census/state.test.mjs +0 -15
- package/scripts/census/transaction.test.mjs +0 -103
- package/scripts/census-contract.test.mjs +0 -227
- package/scripts/check-kit-staleness.mjs +0 -45
- package/scripts/check-kit-staleness.test.mjs +0 -19
- package/scripts/codex-exec-scenarios/fake-codex.mjs +0 -165
- package/scripts/codex-exec.test.mjs +0 -871
- package/scripts/grill-census-wiring-guard.mjs +0 -87
- package/scripts/grill-census-wiring-guard.test.mjs +0 -107
- package/scripts/kit-release.test.mjs +0 -273
- package/scripts/kit-update-pr.test.mjs +0 -151
- package/scripts/lib/audit-refs.mjs +0 -146
- package/scripts/lib/scrub.mjs +0 -113
- package/scripts/lib/scrub.test.mjs +0 -187
- package/scripts/memory-lifecycle/memory-lifecycle.test.mjs +0 -170
- package/scripts/portability_profile_scan.py +0 -53
- package/scripts/release-delta-guard.test.mjs +0 -174
- package/scripts/release-parity.test.mjs +0 -90
- package/scripts/release-state.test.mjs +0 -360
- package/scripts/test_anchor_table.py +0 -69
- package/scripts/test_board_bootstrap.py +0 -348
- package/scripts/test_board_sync.py +0 -208
- package/scripts/test_board_sync_create_idempotency.py +0 -117
- package/scripts/test_board_sync_wave_title.py +0 -120
- package/scripts/test_census_backstop.py +0 -712
- package/scripts/test_census_forward_contract.py +0 -91
- package/scripts/test_census_update_contract.test.mjs +0 -454
- package/scripts/test_codex_adapter_sync_contract.py +0 -169
- package/scripts/test_dist_kit_smoke.py +0 -220
- package/scripts/test_drift_guard_diagnostics.py +0 -295
- package/scripts/test_issue_claim_contract.py +0 -169
- package/scripts/test_kit_docs_language_census.py +0 -178
- package/scripts/test_marker_lib.py +0 -162
- package/scripts/test_orchestrate_wave_contract.py +0 -381
- package/scripts/test_pr_body_check.py +0 -264
- package/scripts/test_profile_globs.py +0 -280
- package/scripts/test_program_planning_contract.py +0 -133
- package/scripts/test_release_authorization_contract.py +0 -101
- package/scripts/test_render_anchor.py +0 -267
- package/scripts/test_retro_wrapup_contract.py +0 -153
- package/scripts/test_skill_code_review_seed.py +0 -89
- package/scripts/test_skill_codex_exec_lifecycle.py +0 -123
- package/scripts/test_skill_frontmatter_lint.py +0 -234
- package/scripts/test_skill_gh_lint.py +0 -124
- package/scripts/test_skill_language_census.py +0 -254
- package/scripts/test_skill_optional_readiness.py +0 -171
- package/scripts/test_skill_portability_lint.py +0 -866
- package/scripts/test_skill_precommit_template.py +0 -30
- package/scripts/test_skill_publish_audit.py +0 -358
- package/scripts/test_skill_readiness_contract.py +0 -264
- package/scripts/test_skill_readiness_preflight.py +0 -180
- package/scripts/test_skill_required_readiness.py +0 -233
- package/scripts/test_skill_selfcontainment_lint.py +0 -255
- package/scripts/test_skill_setup_workflow_seeds.py +0 -691
- package/scripts/test_skill_stale_name_lint.py +0 -126
- package/scripts/test_skill_surface_refs.py +0 -300
- package/scripts/test_skill_trailing_artifact_lint.py +0 -71
- package/scripts/test_tdd_contract.py +0 -78
- package/scripts/test_worktree_ignore_seed.py +0 -320
- package/scripts/test_worktree_setup_base_guard.py +0 -140
- package/scripts/test_worktree_wrapup_contract.py +0 -1737
- package/scripts/test_wrapup_land.py +0 -428
- package/scripts/worktree-lifecycle/session.py +0 -1857
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { execFile } from 'node:child_process';
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
6
|
-
import { tmpdir } from 'node:os';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
-
import {
|
|
10
|
-
CENSUS_BUILDER_VERSION,
|
|
11
|
-
activateCensus,
|
|
12
|
-
diffCensus,
|
|
13
|
-
scanCensus,
|
|
14
|
-
} from './census/index.mjs';
|
|
15
|
-
|
|
16
|
-
const exec = promisify(execFile);
|
|
17
|
-
const FIXTURES = new URL('../test/fixtures/census-consumers/', import.meta.url);
|
|
18
|
-
const REPO = fileURLToPath(new URL('../', import.meta.url));
|
|
19
|
-
|
|
20
|
-
async function consumer(name, tracked = ['.']) {
|
|
21
|
-
const root = await mkdtemp(join(tmpdir(), `awk-census-${name}-`));
|
|
22
|
-
await cp(new URL(`${name}/`, FIXTURES), root, { recursive: true });
|
|
23
|
-
await exec('git', ['init', '--quiet'], { cwd: root });
|
|
24
|
-
await exec('git', ['add', ...tracked], { cwd: root });
|
|
25
|
-
return root;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function cleanup(root) {
|
|
29
|
-
await rm(root, { recursive: true, force: true });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function json(path) {
|
|
33
|
-
return JSON.parse(await readFile(path, 'utf8'));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function backstopStatus(root) {
|
|
37
|
-
const { stdout } = await exec('python3', [
|
|
38
|
-
join(REPO, '.claude/hooks/drift-guard.py'), '--census-status',
|
|
39
|
-
], { cwd: root });
|
|
40
|
-
return JSON.parse(stdout);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
test('brownfield reports real X of Y surfaces separately from behaviors', async () => {
|
|
44
|
-
const root = await consumer('brownfield');
|
|
45
|
-
try {
|
|
46
|
-
const profile = await json(join(root, '.census/profile.json'));
|
|
47
|
-
const census = await scanCensus({
|
|
48
|
-
repoRoot: root,
|
|
49
|
-
enabled: profile.enabled,
|
|
50
|
-
hasActive: true,
|
|
51
|
-
behaviorFamilies: profile.decisions.map(({ family, status }) => ({ name: family, status })),
|
|
52
|
-
});
|
|
53
|
-
const covered = census.families.surfaces.filter(({ status }) => status === 'abgedeckt').length;
|
|
54
|
-
|
|
55
|
-
assert.equal(census.state, 'current');
|
|
56
|
-
assert.equal(`${covered} of ${census.families.surfaces.length}`, '3 of 3');
|
|
57
|
-
assert.deepEqual(census.families.behaviors, [
|
|
58
|
-
{ name: 'authentication', status: 'abgedeckt', type: 'behavior' },
|
|
59
|
-
{ name: 'email-delivery', status: 'nicht relevant', type: 'behavior' },
|
|
60
|
-
]);
|
|
61
|
-
assert.ok(census.families.surfaces.every(({ type }) => type === 'surface'));
|
|
62
|
-
} finally { await cleanup(root); }
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('greenfield without an active snapshot remains honest bootstrap', async () => {
|
|
66
|
-
const root = await consumer('greenfield');
|
|
67
|
-
try {
|
|
68
|
-
const census = await scanCensus({ repoRoot: root, enabled: true, hasActive: false });
|
|
69
|
-
assert.equal(census.state, 'bootstrap');
|
|
70
|
-
assert.ok(census.denominator.length > 0, 'bootstrap still reports discovered facts');
|
|
71
|
-
} finally { await cleanup(root); }
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('unknown pattern stays open until its repository-local scanner test passes', async () => {
|
|
75
|
-
const root = await consumer('unknown-pattern', ['package.json', 'src', '.census']);
|
|
76
|
-
try {
|
|
77
|
-
const activePath = join(root, '.census/active.json');
|
|
78
|
-
const profilePath = join(root, '.census/profile.json');
|
|
79
|
-
const surface = 'services/payments/src';
|
|
80
|
-
const scannerRecord = {
|
|
81
|
-
surface,
|
|
82
|
-
module: 'scripts/census-local/scan-services.mjs',
|
|
83
|
-
export: 'scanServices',
|
|
84
|
-
test: 'test/census-services.test.mjs',
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const untracked = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
88
|
-
assert.equal(untracked.state, 'refresh_required');
|
|
89
|
-
assert.deepEqual(diffCensus(untracked, untracked).open, [surface]);
|
|
90
|
-
|
|
91
|
-
// Establish real active history before the repository grows the unknown surface.
|
|
92
|
-
await rm(join(root, 'services'), { recursive: true });
|
|
93
|
-
await rm(join(root, 'scripts'), { recursive: true });
|
|
94
|
-
await rm(join(root, 'test'), { recursive: true });
|
|
95
|
-
const known = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
96
|
-
await writeFile(activePath, `${JSON.stringify({
|
|
97
|
-
...known,
|
|
98
|
-
profileReport: { decisions: [], localScanners: [], overrides: [] },
|
|
99
|
-
})}\n`);
|
|
100
|
-
|
|
101
|
-
await cp(new URL('unknown-pattern/services/', FIXTURES), join(root, 'services'), { recursive: true });
|
|
102
|
-
await exec('git', ['add', 'services'], { cwd: root });
|
|
103
|
-
const trackedOnly = await backstopStatus(root);
|
|
104
|
-
assert.equal(trackedOnly.state, 'refresh_required');
|
|
105
|
-
assert.ok(trackedOnly.reasons.includes('topology'));
|
|
106
|
-
|
|
107
|
-
const profile = await json(profilePath);
|
|
108
|
-
profile.localScanners.push(scannerRecord);
|
|
109
|
-
await writeFile(profilePath, `${JSON.stringify(profile)}\n`);
|
|
110
|
-
const missingProof = await backstopStatus(root);
|
|
111
|
-
assert.equal(missingProof.state, 'refresh_required');
|
|
112
|
-
assert.ok(missingProof.reasons.includes(`proof:${surface}`));
|
|
113
|
-
|
|
114
|
-
await cp(new URL('unknown-pattern/scripts/', FIXTURES), join(root, 'scripts'), { recursive: true });
|
|
115
|
-
await cp(new URL('unknown-pattern/test/', FIXTURES), join(root, 'test'), { recursive: true });
|
|
116
|
-
await writeFile(
|
|
117
|
-
join(root, scannerRecord.test),
|
|
118
|
-
"import { test } from 'node:test'; test('failing local proof', () => { throw new Error('no'); });\n",
|
|
119
|
-
);
|
|
120
|
-
await exec('git', ['add', 'scripts', 'test', '.census/profile.json'], { cwd: root });
|
|
121
|
-
const failedProof = await backstopStatus(root);
|
|
122
|
-
assert.equal(failedProof.state, 'refresh_required');
|
|
123
|
-
assert.ok(failedProof.reasons.includes(`proof:${surface}`));
|
|
124
|
-
|
|
125
|
-
await cp(
|
|
126
|
-
new URL('unknown-pattern/test/census-services.test.mjs', FIXTURES),
|
|
127
|
-
join(root, scannerRecord.test),
|
|
128
|
-
);
|
|
129
|
-
const candidate = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
130
|
-
assert.equal(candidate.state, 'current');
|
|
131
|
-
await activateCensus({
|
|
132
|
-
activePath,
|
|
133
|
-
candidate: {
|
|
134
|
-
...candidate,
|
|
135
|
-
profileReport: { decisions: [], localScanners: [scannerRecord], overrides: [] },
|
|
136
|
-
},
|
|
137
|
-
verify: async () => {
|
|
138
|
-
await exec('node', ['--test', scannerRecord.test], { cwd: root });
|
|
139
|
-
const scanner = await import(`${pathToFileURL(join(root, scannerRecord.module)).href}?proof=${Date.now()}`);
|
|
140
|
-
return scanner[scannerRecord.export]().includes(surface);
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const proven = await backstopStatus(root);
|
|
145
|
-
assert.equal(proven.state, 'current');
|
|
146
|
-
assert.deepEqual(proven.reasons, []);
|
|
147
|
-
const active = await json(activePath);
|
|
148
|
-
assert.equal(active.profileReport.localScanners[0].test, scannerRecord.test);
|
|
149
|
-
} finally { await cleanup(root); }
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('scan and compact delta do not overwrite a consumer-local modification', async () => {
|
|
153
|
-
const root = await consumer('local-modified');
|
|
154
|
-
try {
|
|
155
|
-
const sourcePath = join(root, 'src/index.mjs');
|
|
156
|
-
const activePath = join(root, '.census/active.json');
|
|
157
|
-
const sourceBefore = await readFile(sourcePath);
|
|
158
|
-
const activeBefore = await readFile(activePath);
|
|
159
|
-
const previous = { denominator: [], families: { surfaces: [], behaviors: [] } };
|
|
160
|
-
const current = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
161
|
-
const delta = diffCensus(previous, current);
|
|
162
|
-
|
|
163
|
-
assert.deepEqual(Object.keys(delta), ['added', 'changed', 'open', 'removed']);
|
|
164
|
-
assert.ok(JSON.stringify(delta).length < JSON.stringify(current).length);
|
|
165
|
-
assert.deepEqual(await readFile(sourcePath), sourceBefore);
|
|
166
|
-
assert.deepEqual(await readFile(activePath), activeBefore);
|
|
167
|
-
} finally { await cleanup(root); }
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('secret canary is absent from scan, delta, and file reads', async () => {
|
|
171
|
-
const root = await consumer('secret-canary');
|
|
172
|
-
try {
|
|
173
|
-
const reads = [];
|
|
174
|
-
const scan = await scanCensus({
|
|
175
|
-
repoRoot: root,
|
|
176
|
-
readText: async (path) => {
|
|
177
|
-
reads.push(path);
|
|
178
|
-
return readFile(path, 'utf8');
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
const output = JSON.stringify({ scan, delta: diffCensus({ denominator: [] }, scan) });
|
|
182
|
-
assert.doesNotMatch(output, /CENSUS_SECRET_CANARY/);
|
|
183
|
-
assert.ok(reads.every((path) => !path.includes('credentials')));
|
|
184
|
-
} finally { await cleanup(root); }
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('interrupted activation preserves the active census byte-for-byte', async () => {
|
|
188
|
-
const root = await consumer('interrupted');
|
|
189
|
-
try {
|
|
190
|
-
const activePath = join(root, '.census/active.json');
|
|
191
|
-
const before = await readFile(activePath);
|
|
192
|
-
const candidate = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
193
|
-
await assert.rejects(activateCensus({
|
|
194
|
-
activePath,
|
|
195
|
-
candidate,
|
|
196
|
-
verify: async () => true,
|
|
197
|
-
renameCandidate: async () => { throw new Error('fixture interruption'); },
|
|
198
|
-
}), (error) => error.state === 'failed');
|
|
199
|
-
assert.deepEqual(await readFile(activePath), before);
|
|
200
|
-
} finally { await cleanup(root); }
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test('consumer update reports a newer builder without silently mutating its census', async () => {
|
|
204
|
-
const root = await consumer('consumer-update');
|
|
205
|
-
try {
|
|
206
|
-
const activePath = join(root, '.census/active.json');
|
|
207
|
-
const profilePath = join(root, '.census/profile.json');
|
|
208
|
-
const fresh = await scanCensus({ repoRoot: root, enabled: true, hasActive: true });
|
|
209
|
-
await writeFile(activePath, `${JSON.stringify({
|
|
210
|
-
...fresh,
|
|
211
|
-
fingerprints: { ...fresh.fingerprints, builder: 'older-builder-fingerprint' },
|
|
212
|
-
profileReport: { decisions: [], localScanners: [], overrides: [] },
|
|
213
|
-
})}\n`);
|
|
214
|
-
const activeBefore = await readFile(activePath);
|
|
215
|
-
const profileBefore = await readFile(profilePath);
|
|
216
|
-
const { stdout } = await exec('python3', [
|
|
217
|
-
join(REPO, '.claude/hooks/drift-guard.py'), '--census-status',
|
|
218
|
-
], { cwd: root });
|
|
219
|
-
const status = JSON.parse(stdout);
|
|
220
|
-
|
|
221
|
-
assert.equal(status.state, 'refresh_required');
|
|
222
|
-
assert.equal(status.detail, `builder ${CENSUS_BUILDER_VERSION}`);
|
|
223
|
-
assert.ok(status.reasons.includes('builder'));
|
|
224
|
-
assert.deepEqual(await readFile(activePath), activeBefore);
|
|
225
|
-
assert.deepEqual(await readFile(profilePath), profileBefore);
|
|
226
|
-
} finally { await cleanup(root); }
|
|
227
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/** Verify that the checked-in install manifest matches a fresh public-SSOT build. */
|
|
2
|
-
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { buildKit } from './build-kit.mjs';
|
|
7
|
-
|
|
8
|
-
const index = (manifest) => new Map(manifest.files.map((file) => [file.path, file.sha256]));
|
|
9
|
-
|
|
10
|
-
export function diffManifests(checked, built) {
|
|
11
|
-
const before = index(checked);
|
|
12
|
-
const after = index(built);
|
|
13
|
-
const added = [...after.keys()].filter((path) => !before.has(path)).sort();
|
|
14
|
-
const removed = [...before.keys()].filter((path) => !after.has(path)).sort();
|
|
15
|
-
const changed = [...after.keys()].filter(
|
|
16
|
-
(path) => before.has(path) && before.get(path) !== after.get(path),
|
|
17
|
-
).sort();
|
|
18
|
-
return { added, removed, changed };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function checkKitStaleness({ repoRoot } = {}) {
|
|
22
|
-
repoRoot ??= join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
-
const distDir = await mkdtemp(join(tmpdir(), 'awkit-staleness-'));
|
|
24
|
-
try {
|
|
25
|
-
await buildKit({ repoRoot, distDir });
|
|
26
|
-
const checked = JSON.parse(await readFile(join(repoRoot, 'agent-workflow-kit.package.json'), 'utf8'));
|
|
27
|
-
const built = JSON.parse(await readFile(join(distDir, 'agent-workflow-kit.package.json'), 'utf8'));
|
|
28
|
-
const diff = diffManifests(checked, built);
|
|
29
|
-
const drift = Object.values(diff).some((paths) => paths.length);
|
|
30
|
-
return { status: drift ? 'drift' : 'ok', ...diff };
|
|
31
|
-
} finally {
|
|
32
|
-
await rm(distDir, { recursive: true, force: true });
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
37
|
-
checkKitStaleness().then((result) => {
|
|
38
|
-
if (result.status === 'ok') return console.log('kit:staleness — OK');
|
|
39
|
-
console.error('kit:staleness — DRIFT');
|
|
40
|
-
for (const key of ['added', 'removed', 'changed']) {
|
|
41
|
-
if (result[key].length) console.error(` ${key}: ${result[key].join(', ')}`);
|
|
42
|
-
}
|
|
43
|
-
process.exitCode = 1;
|
|
44
|
-
}).catch((error) => { console.error(error.message); process.exitCode = 1; });
|
|
45
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { diffManifests } from './check-kit-staleness.mjs';
|
|
7
|
-
|
|
8
|
-
test('manifest parity reports no drift for identical path hashes', () => {
|
|
9
|
-
const manifest = { files: [{ path: 'a', sha256: 'one' }] };
|
|
10
|
-
assert.deepEqual(diffManifests(manifest, manifest), { added: [], removed: [], changed: [] });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('manifest parity counts added, removed, and changed files', () => {
|
|
14
|
-
const checked = { files: [{ path: 'gone', sha256: '1' }, { path: 'same', sha256: '1' }, { path: 'changed', sha256: '1' }] };
|
|
15
|
-
const built = { files: [{ path: 'new', sha256: '1' }, { path: 'same', sha256: '1' }, { path: 'changed', sha256: '2' }] };
|
|
16
|
-
assert.deepEqual(diffManifests(checked, built), {
|
|
17
|
-
added: ['new'], removed: ['gone'], changed: ['changed'],
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { appendFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
|
|
5
|
-
const args = process.argv.slice(2);
|
|
6
|
-
const scenario = process.env.FAKE_CODEX_SCENARIO ?? 'ok';
|
|
7
|
-
const pause = Number(process.env.FAKE_CODEX_PAUSE_MS ?? 1_000);
|
|
8
|
-
|
|
9
|
-
if (args.includes('--version') || args[0] === '--version') {
|
|
10
|
-
console.log(`codex-cli ${process.env.FAKE_CODEX_VERSION ?? '0.144.6'}`);
|
|
11
|
-
process.exit(0);
|
|
12
|
-
}
|
|
13
|
-
if (args[0] === 'login' && args[1] === 'status') {
|
|
14
|
-
if (scenario === 'auth-fail') {
|
|
15
|
-
console.error('Not logged in');
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
console.log('Logged in using fake credentials');
|
|
19
|
-
process.exit(0);
|
|
20
|
-
}
|
|
21
|
-
if (args.includes('--help')) {
|
|
22
|
-
const resumeHelp = args[0] === 'exec' && args[1] === 'resume';
|
|
23
|
-
if (process.env.FAKE_CODEX_MISSING_CAPABILITY === '1'
|
|
24
|
-
|| (resumeHelp && process.env.FAKE_CODEX_RESUME_MISSING_CAPABILITY === '1')) {
|
|
25
|
-
console.log('Usage: codex exec [PROMPT]');
|
|
26
|
-
} else if (resumeHelp) {
|
|
27
|
-
console.log('Usage: codex exec resume [--config key=value] [--json] THREAD PROMPT');
|
|
28
|
-
} else {
|
|
29
|
-
console.log('Usage: codex exec [--json] [--sandbox MODE] resume THREAD');
|
|
30
|
-
}
|
|
31
|
-
process.exit(0);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (process.env.FAKE_CODEX_LAUNCH_LOG) {
|
|
35
|
-
appendFileSync(process.env.FAKE_CODEX_LAUNCH_LOG, `${JSON.stringify(args)}\n`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (args[0] === 'exec' && args[1] === 'resume') {
|
|
39
|
-
const configIndex = args.indexOf('-c');
|
|
40
|
-
const expected = `sandbox_mode=${process.env.FAKE_EXPECTED_SANDBOX ?? 'read-only'}`;
|
|
41
|
-
if (args.includes('--sandbox') || configIndex < 0 || args[configIndex + 1] !== expected) {
|
|
42
|
-
console.error('invalid resume sandbox contract');
|
|
43
|
-
process.exit(64);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const emit = (value) => console.log(JSON.stringify(value));
|
|
48
|
-
const thread = process.env.FAKE_CODEX_THREAD ?? 'fake-thread-1';
|
|
49
|
-
const started = () => emit({ type: 'thread.started', thread_id: thread });
|
|
50
|
-
const verdict = () => emit({
|
|
51
|
-
type: 'item.completed',
|
|
52
|
-
item: { id: 'fake-item', type: 'agent_message', text: 'fake verdict' },
|
|
53
|
-
});
|
|
54
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
|
-
const waitForSignal = () => new Promise((resolve) => {
|
|
56
|
-
const keepAlive = setInterval(() => {}, 2_147_483_647);
|
|
57
|
-
const done = () => {
|
|
58
|
-
clearInterval(keepAlive);
|
|
59
|
-
resolve();
|
|
60
|
-
};
|
|
61
|
-
process.once('SIGTERM', done);
|
|
62
|
-
process.once('SIGINT', done);
|
|
63
|
-
process.once('SIGHUP', done);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
switch (scenario) {
|
|
67
|
-
case 'silent':
|
|
68
|
-
await sleep(pause);
|
|
69
|
-
break;
|
|
70
|
-
case 'startup-byte-silence':
|
|
71
|
-
process.stderr.write('x');
|
|
72
|
-
await sleep(pause);
|
|
73
|
-
break;
|
|
74
|
-
case 'quiet-post-thread':
|
|
75
|
-
started();
|
|
76
|
-
await sleep(pause);
|
|
77
|
-
verdict();
|
|
78
|
-
break;
|
|
79
|
-
case 'malformed':
|
|
80
|
-
console.log('{definitely-not-json');
|
|
81
|
-
break;
|
|
82
|
-
case 'json-null':
|
|
83
|
-
emit(null);
|
|
84
|
-
break;
|
|
85
|
-
case 'json-array':
|
|
86
|
-
emit([]);
|
|
87
|
-
break;
|
|
88
|
-
case 'json-scalar':
|
|
89
|
-
emit('scalar');
|
|
90
|
-
break;
|
|
91
|
-
case 'item-null':
|
|
92
|
-
emit({ type: 'item.completed', item: null });
|
|
93
|
-
break;
|
|
94
|
-
case 'item-array':
|
|
95
|
-
emit({ type: 'item.completed', item: [] });
|
|
96
|
-
break;
|
|
97
|
-
case 'event-type-null':
|
|
98
|
-
emit({ type: null });
|
|
99
|
-
break;
|
|
100
|
-
case 'event-type-array':
|
|
101
|
-
emit({ type: [] });
|
|
102
|
-
break;
|
|
103
|
-
case 'item-type-null':
|
|
104
|
-
emit({ type: 'item.completed', item: { type: null } });
|
|
105
|
-
break;
|
|
106
|
-
case 'item-type-array':
|
|
107
|
-
emit({ type: 'item.completed', item: { type: [] } });
|
|
108
|
-
break;
|
|
109
|
-
case 'thread-non-string':
|
|
110
|
-
emit({ type: 'thread.started', thread_id: 42 });
|
|
111
|
-
break;
|
|
112
|
-
case 'verdict-non-string':
|
|
113
|
-
started();
|
|
114
|
-
emit({ type: 'item.completed', item: { type: 'agent_message', text: 42 } });
|
|
115
|
-
break;
|
|
116
|
-
case 'missing-thread':
|
|
117
|
-
verdict();
|
|
118
|
-
break;
|
|
119
|
-
case 'missing-verdict':
|
|
120
|
-
started();
|
|
121
|
-
emit({ type: 'turn.completed' });
|
|
122
|
-
break;
|
|
123
|
-
case 'exec-fail':
|
|
124
|
-
started();
|
|
125
|
-
console.error('fake execution failed token=super-secret');
|
|
126
|
-
process.exit(23);
|
|
127
|
-
break;
|
|
128
|
-
case 'split-secret':
|
|
129
|
-
started();
|
|
130
|
-
writeFileSync(2, `${'x'.repeat(65_530)}token=super-secret`);
|
|
131
|
-
process.exit(24);
|
|
132
|
-
break;
|
|
133
|
-
case 'signal':
|
|
134
|
-
process.kill(process.pid, 'SIGTERM');
|
|
135
|
-
break;
|
|
136
|
-
case 'group-hang': {
|
|
137
|
-
started();
|
|
138
|
-
const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)']);
|
|
139
|
-
if (process.env.FAKE_CODEX_CHILD_PID) {
|
|
140
|
-
writeFileSync(process.env.FAKE_CODEX_CHILD_PID, String(child.pid));
|
|
141
|
-
}
|
|
142
|
-
if (process.env.FAKE_CODEX_GROUP_HANG_READY) {
|
|
143
|
-
writeFileSync(process.env.FAKE_CODEX_GROUP_HANG_READY, 'ready\n');
|
|
144
|
-
}
|
|
145
|
-
await waitForSignal();
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case 'orphan-group': {
|
|
149
|
-
started();
|
|
150
|
-
const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'inherit' });
|
|
151
|
-
if (process.env.FAKE_CODEX_CHILD_PID) {
|
|
152
|
-
writeFileSync(process.env.FAKE_CODEX_CHILD_PID, String(child.pid));
|
|
153
|
-
}
|
|
154
|
-
child.unref();
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case 'pipe-burst':
|
|
158
|
-
process.stderr.write('x'.repeat(2 * 1024 * 1024));
|
|
159
|
-
started();
|
|
160
|
-
verdict();
|
|
161
|
-
break;
|
|
162
|
-
default:
|
|
163
|
-
started();
|
|
164
|
-
verdict();
|
|
165
|
-
}
|