@nodetool-ai/cli-harness 0.0.0-stage → 0.8.9-beta.2
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/README.md +13 -2
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/affected/affected.d.ts +48 -0
- package/dist/affected/affected.d.ts.map +1 -0
- package/dist/affected/affected.js +167 -0
- package/dist/affected/affected.js.map +1 -0
- package/dist/commands/__tests__/jtbd.test.d.ts +2 -0
- package/dist/commands/__tests__/jtbd.test.d.ts.map +1 -0
- package/dist/commands/__tests__/jtbd.test.js +120 -0
- package/dist/commands/__tests__/jtbd.test.js.map +1 -0
- package/dist/commands/affected.d.ts +9 -0
- package/dist/commands/affected.d.ts.map +1 -0
- package/dist/commands/affected.js +127 -0
- package/dist/commands/affected.js.map +1 -0
- package/dist/commands/eval.d.ts +152 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/eval.js +693 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/harness.d.ts +90 -0
- package/dist/commands/harness.d.ts.map +1 -0
- package/dist/commands/harness.js +883 -0
- package/dist/commands/harness.js.map +1 -0
- package/dist/commands/jtbd.d.ts +38 -0
- package/dist/commands/jtbd.d.ts.map +1 -0
- package/dist/commands/jtbd.js +261 -0
- package/dist/commands/jtbd.js.map +1 -0
- package/dist/commands/reliability.d.ts +14 -0
- package/dist/commands/reliability.d.ts.map +1 -0
- package/dist/commands/reliability.js +81 -0
- package/dist/commands/reliability.js.map +1 -0
- package/dist/evals/graph-runner.d.ts +8 -0
- package/dist/evals/graph-runner.d.ts.map +1 -0
- package/dist/evals/graph-runner.js +136 -0
- package/dist/evals/graph-runner.js.map +1 -0
- package/dist/harness/capability-coverage.d.ts +165 -0
- package/dist/harness/capability-coverage.d.ts.map +1 -0
- package/dist/harness/capability-coverage.js +308 -0
- package/dist/harness/capability-coverage.js.map +1 -0
- package/dist/harness/capability-table.d.ts +17 -0
- package/dist/harness/capability-table.d.ts.map +1 -0
- package/dist/harness/capability-table.js +3941 -0
- package/dist/harness/capability-table.js.map +1 -0
- package/dist/harness/changed-files.d.ts +20 -0
- package/dist/harness/changed-files.d.ts.map +1 -0
- package/dist/harness/changed-files.js +90 -0
- package/dist/harness/changed-files.js.map +1 -0
- package/dist/harness/declared-capabilities.d.ts +12 -0
- package/dist/harness/declared-capabilities.d.ts.map +1 -0
- package/dist/harness/declared-capabilities.js +21 -0
- package/dist/harness/declared-capabilities.js.map +1 -0
- package/dist/harness/registry.d.ts +190 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +2419 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -4
package/README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Repository harness commands
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Harness registration, changed-file selection, evaluation and reliability
|
|
4
|
+
commands for the repository.
|
|
5
|
+
|
|
6
|
+
Install this independent command feature through the default CLI:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
nodetool packs add cli-harness --yes
|
|
10
|
+
nodetool harness list
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Commands do not install missing features automatically. See the
|
|
14
|
+
[command reference](../../docs/cli.md#install-command-features).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026-10-31T10:35:38.418Z
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Packages whose nodes load from compiled dist/ (decorators) — see AGENTS.md. */
|
|
2
|
+
export declare const DECORATOR_PACKAGES: ReadonlySet<string>;
|
|
3
|
+
export interface PackageInfo {
|
|
4
|
+
/** Package name, e.g. "@nodetool-ai/cli". */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Repo-relative directory, e.g. "packages/cli". */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** Internal `@nodetool-ai/*` dependencies declared in package.json. */
|
|
9
|
+
internalDeps: string[];
|
|
10
|
+
/** Extra repo-relative dirs this workspace owns — see EXTRA_WORKSPACE_PATHS. */
|
|
11
|
+
ownedPaths?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Directories that belong to a workspace without living inside it.
|
|
15
|
+
*
|
|
16
|
+
* `reliability/journeys/` holds the journey manifests, fixtures, and goldens
|
|
17
|
+
* the reliability harness runs; editing one changes what
|
|
18
|
+
* `nodetool reliability run` does, but the files sit outside
|
|
19
|
+
* `reliability/harness/`, so a path-prefix match alone reports them as
|
|
20
|
+
* belonging to no workspace. `packages/system-skills/` and
|
|
21
|
+
* `packages/sandbox-packs/` are not workspaces either, for the same reason.
|
|
22
|
+
* Turbo cannot see this ownership: each owner's `test` inputs in turbo.json
|
|
23
|
+
* list the directory, and `scripts/ci-plan.mjs` drops `--affected` for it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const EXTRA_WORKSPACE_PATHS: Readonly<Record<string, readonly string[]>>;
|
|
26
|
+
/**
|
|
27
|
+
* The workspace graph as `computeAffected` wants it, read from the root
|
|
28
|
+
* `package.json` and each workspace manifest — the same reading as
|
|
29
|
+
* `readPackages` in scripts/test-affected.mjs. Only `node:fs`/`node:path`, so
|
|
30
|
+
* the gate planner stays cheap to load.
|
|
31
|
+
*/
|
|
32
|
+
export declare function readWorkspacePackages(repoRoot: string): PackageInfo[];
|
|
33
|
+
export interface AffectedResult {
|
|
34
|
+
/** Workspaces directly containing a changed file. */
|
|
35
|
+
changed: string[];
|
|
36
|
+
/** `changed` plus every workspace that transitively depends on them. */
|
|
37
|
+
affected: string[];
|
|
38
|
+
/** Affected decorator packages — these force a `build:packages`. */
|
|
39
|
+
needsBuild: string[];
|
|
40
|
+
/** Changed files that mapped to no known workspace. */
|
|
41
|
+
unmatched: string[];
|
|
42
|
+
/** Suggested commands, ordered (build first, then per-workspace checks). */
|
|
43
|
+
commands: string[];
|
|
44
|
+
}
|
|
45
|
+
/** Top-level non-package workspaces that consume packages but have no dependents. */
|
|
46
|
+
export declare const TOP_LEVEL_WORKSPACES: readonly ["web", "electron", "mobile"];
|
|
47
|
+
export declare function computeAffected(changedFiles: ReadonlyArray<string>, packages: ReadonlyArray<PackageInfo>): AffectedResult;
|
|
48
|
+
//# sourceMappingURL=affected.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affected.d.ts","sourceRoot":"","sources":["../../src/affected/affected.ts"],"names":[],"mappings":"AAeA,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAMjD,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAC1C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAOlC,CAAC;AASF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CA2BrE;AAED,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oEAAoE;IACpE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,uDAAuD;IACvD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,YAAI,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC;AAyC3E,wBAAgB,eAAe,CAC7B,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,EACnC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,GACnC,cAAc,CAmDhB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a set of changed files to the minimal set of workspaces a coding agent
|
|
3
|
+
* needs to rebuild / typecheck / test — and the exact commands to do it.
|
|
4
|
+
*
|
|
5
|
+
* The monorepo build is dependency-ordered and slow (1–2 min full). Most edits
|
|
6
|
+
* touch one package; this works out the downstream closure of dependents so an
|
|
7
|
+
* agent runs `--workspace=<pkg>` checks instead of the whole tree, and only
|
|
8
|
+
* triggers `npm run build:packages` when a decorator package (which loads from
|
|
9
|
+
* dist/) is in the affected set.
|
|
10
|
+
*
|
|
11
|
+
* Pure and registry-free so it can be unit-tested with a synthetic graph.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
/** Packages whose nodes load from compiled dist/ (decorators) — see AGENTS.md. */
|
|
16
|
+
export const DECORATOR_PACKAGES = new Set([
|
|
17
|
+
"@nodetool-ai/node-sdk",
|
|
18
|
+
"@nodetool-ai/base-nodes",
|
|
19
|
+
"@nodetool-ai/fal-nodes",
|
|
20
|
+
"@nodetool-ai/replicate-nodes",
|
|
21
|
+
"@nodetool-ai/elevenlabs-nodes"
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* Directories that belong to a workspace without living inside it.
|
|
25
|
+
*
|
|
26
|
+
* `reliability/journeys/` holds the journey manifests, fixtures, and goldens
|
|
27
|
+
* the reliability harness runs; editing one changes what
|
|
28
|
+
* `nodetool reliability run` does, but the files sit outside
|
|
29
|
+
* `reliability/harness/`, so a path-prefix match alone reports them as
|
|
30
|
+
* belonging to no workspace. `packages/system-skills/` and
|
|
31
|
+
* `packages/sandbox-packs/` are not workspaces either, for the same reason.
|
|
32
|
+
* Turbo cannot see this ownership: each owner's `test` inputs in turbo.json
|
|
33
|
+
* list the directory, and `scripts/ci-plan.mjs` drops `--affected` for it.
|
|
34
|
+
*/
|
|
35
|
+
export const EXTRA_WORKSPACE_PATHS = {
|
|
36
|
+
"@nodetool-ai/reliability-harness": ["reliability/journeys"],
|
|
37
|
+
// Shipped skills, loaded by `agents` (src/system-skills.ts).
|
|
38
|
+
"@nodetool-ai/agents": ["packages/system-skills"],
|
|
39
|
+
// Guest packs, loaded by node-sdk's pack loader and so by everything above it.
|
|
40
|
+
"@nodetool-ai/node-sdk": ["packages/sandbox-packs"]
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Mobile is deliberately not a root workspace, so its dependency on NodeTool
|
|
44
|
+
* packages is absent from its package.json — see MOBILE_DEPS in
|
|
45
|
+
* scripts/test-affected.mjs, which this mirrors.
|
|
46
|
+
*/
|
|
47
|
+
const MOBILE_DEPS = ["@nodetool-ai/protocol", "@nodetool-ai/app-runtime"];
|
|
48
|
+
/**
|
|
49
|
+
* The workspace graph as `computeAffected` wants it, read from the root
|
|
50
|
+
* `package.json` and each workspace manifest — the same reading as
|
|
51
|
+
* `readPackages` in scripts/test-affected.mjs. Only `node:fs`/`node:path`, so
|
|
52
|
+
* the gate planner stays cheap to load.
|
|
53
|
+
*/
|
|
54
|
+
export function readWorkspacePackages(repoRoot) {
|
|
55
|
+
const readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
56
|
+
const rootPkg = readJson(join(repoRoot, "package.json"));
|
|
57
|
+
const packages = [];
|
|
58
|
+
const dirs = Array.isArray(rootPkg["workspaces"])
|
|
59
|
+
? rootPkg["workspaces"]
|
|
60
|
+
: [];
|
|
61
|
+
for (const dir of dirs) {
|
|
62
|
+
const manifest = join(repoRoot, dir, "package.json");
|
|
63
|
+
if (!existsSync(manifest))
|
|
64
|
+
continue;
|
|
65
|
+
const pkg = readJson(manifest);
|
|
66
|
+
const name = String(pkg["name"]);
|
|
67
|
+
const deps = {
|
|
68
|
+
...pkg["dependencies"],
|
|
69
|
+
...pkg["devDependencies"]
|
|
70
|
+
};
|
|
71
|
+
const owned = EXTRA_WORKSPACE_PATHS[name];
|
|
72
|
+
packages.push({
|
|
73
|
+
name,
|
|
74
|
+
dir,
|
|
75
|
+
internalDeps: Object.keys(deps).filter((d) => d.startsWith("@nodetool-ai/")),
|
|
76
|
+
...(owned && { ownedPaths: [...owned] })
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
packages.push({ name: "mobile", dir: "mobile", internalDeps: MOBILE_DEPS });
|
|
80
|
+
return packages;
|
|
81
|
+
}
|
|
82
|
+
/** Top-level non-package workspaces that consume packages but have no dependents. */
|
|
83
|
+
export const TOP_LEVEL_WORKSPACES = ["web", "electron", "mobile"];
|
|
84
|
+
function matchWorkspace(file, packages) {
|
|
85
|
+
const norm = file.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
86
|
+
const owned = [];
|
|
87
|
+
for (const pkg of packages) {
|
|
88
|
+
owned.push({ path: pkg.dir, name: pkg.name });
|
|
89
|
+
for (const extra of pkg.ownedPaths ?? []) {
|
|
90
|
+
owned.push({ path: extra, name: pkg.name });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Most specific (longest path) first so packages/foo-bar wins over packages/foo.
|
|
94
|
+
owned.sort((a, b) => b.path.length - a.path.length);
|
|
95
|
+
for (const entry of owned) {
|
|
96
|
+
if (norm === entry.path || norm.startsWith(entry.path + "/")) {
|
|
97
|
+
return entry.name;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const top of TOP_LEVEL_WORKSPACES) {
|
|
101
|
+
if (norm === top || norm.startsWith(top + "/"))
|
|
102
|
+
return top;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function buildDependents(packages) {
|
|
107
|
+
const dependents = new Map();
|
|
108
|
+
for (const pkg of packages) {
|
|
109
|
+
for (const dep of pkg.internalDeps) {
|
|
110
|
+
const list = dependents.get(dep) ?? [];
|
|
111
|
+
list.push(pkg.name);
|
|
112
|
+
dependents.set(dep, list);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return dependents;
|
|
116
|
+
}
|
|
117
|
+
export function computeAffected(changedFiles, packages) {
|
|
118
|
+
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
119
|
+
const dependents = buildDependents(packages);
|
|
120
|
+
const changed = new Set();
|
|
121
|
+
const unmatched = [];
|
|
122
|
+
for (const file of changedFiles) {
|
|
123
|
+
const ws = matchWorkspace(file, packages);
|
|
124
|
+
if (ws)
|
|
125
|
+
changed.add(ws);
|
|
126
|
+
else
|
|
127
|
+
unmatched.push(file);
|
|
128
|
+
}
|
|
129
|
+
// Downstream closure: a change to X affects X and everything that depends on
|
|
130
|
+
// X (transitively). Top-level workspaces (web/electron) have no dependents.
|
|
131
|
+
const affected = new Set(changed);
|
|
132
|
+
const queue = [...changed];
|
|
133
|
+
while (queue.length > 0) {
|
|
134
|
+
const name = queue.shift();
|
|
135
|
+
for (const dependent of dependents.get(name) ?? []) {
|
|
136
|
+
if (!affected.has(dependent)) {
|
|
137
|
+
affected.add(dependent);
|
|
138
|
+
queue.push(dependent);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const affectedList = [...affected].sort();
|
|
143
|
+
const needsBuild = affectedList.filter((n) => DECORATOR_PACKAGES.has(n));
|
|
144
|
+
const commands = [];
|
|
145
|
+
if (needsBuild.length > 0) {
|
|
146
|
+
// build:packages is dependency-ordered; run the whole pass once.
|
|
147
|
+
commands.push("npm run build:packages");
|
|
148
|
+
}
|
|
149
|
+
for (const name of affectedList) {
|
|
150
|
+
if (TOP_LEVEL_WORKSPACES.includes(name)) {
|
|
151
|
+
commands.push(`cd ${name} && npm run typecheck && npm test`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const pkg = byName.get(name);
|
|
155
|
+
const dir = pkg?.dir ?? name;
|
|
156
|
+
commands.push(`npm run test --workspace=${dir}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
changed: [...changed].sort(),
|
|
161
|
+
affected: affectedList,
|
|
162
|
+
needsBuild,
|
|
163
|
+
unmatched,
|
|
164
|
+
commands
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=affected.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affected.js","sourceRoot":"","sources":["../../src/affected/affected.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jtbd.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/jtbd.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodetool jtbd` must load the user's saved credentials before it resolves a
|
|
3
|
+
* provider — otherwise a stored key is invisible and the command dies on a
|
|
4
|
+
* provider it could have built.
|
|
5
|
+
*
|
|
6
|
+
* The collaborators arrive through `registerJtbdCommand`'s `deps` parameter, so
|
|
7
|
+
* the fakes below are ordinary objects: nothing here mocks a module.
|
|
8
|
+
*/
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { registerJtbdCommand } from "../jtbd.js";
|
|
15
|
+
/** A job the registry would accept, doing nothing. */
|
|
16
|
+
const TEST_JOB = {
|
|
17
|
+
id: "job",
|
|
18
|
+
statement: "Test job",
|
|
19
|
+
surfaces: [],
|
|
20
|
+
difficulty: "smoke",
|
|
21
|
+
objective: "Do the test job",
|
|
22
|
+
outcomeNames: [],
|
|
23
|
+
start: async () => ({ tools: [], grade: () => [] })
|
|
24
|
+
};
|
|
25
|
+
const originalExitCode = process.exitCode;
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
process.exitCode = originalExitCode;
|
|
28
|
+
});
|
|
29
|
+
let events;
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
events = [];
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* The order the command touches its collaborators in is the whole assertion,
|
|
35
|
+
* so every fake records itself and the run-ending ones throw: the test is about
|
|
36
|
+
* what happened before the provider call, not about completing a suite.
|
|
37
|
+
*/
|
|
38
|
+
function createProgram() {
|
|
39
|
+
const initializeSecrets = vi.fn(async () => {
|
|
40
|
+
events.push("secrets");
|
|
41
|
+
});
|
|
42
|
+
const deps = {
|
|
43
|
+
loadAgents: async () => ({
|
|
44
|
+
JOBS_TO_BE_DONE: [TEST_JOB],
|
|
45
|
+
runJobSuite: async () => {
|
|
46
|
+
events.push("run");
|
|
47
|
+
throw new Error("stop run");
|
|
48
|
+
},
|
|
49
|
+
optimizeFromRun: async () => {
|
|
50
|
+
events.push("optimize");
|
|
51
|
+
throw new Error("stop optimize");
|
|
52
|
+
},
|
|
53
|
+
renderRunForReview: () => ""
|
|
54
|
+
}),
|
|
55
|
+
loadProviders: async () => ({
|
|
56
|
+
createProviderStrict: async () => {
|
|
57
|
+
events.push("provider");
|
|
58
|
+
// SAFETY: the command only hands this to `runJobSuite`, which the fake
|
|
59
|
+
// above throws out of before touching it.
|
|
60
|
+
return {};
|
|
61
|
+
},
|
|
62
|
+
buildConfiguredProviders: async () => ({})
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
const program = new Command();
|
|
66
|
+
program.exitOverride();
|
|
67
|
+
registerJtbdCommand(program, initializeSecrets, deps);
|
|
68
|
+
return { program, initializeSecrets };
|
|
69
|
+
}
|
|
70
|
+
describe("registerJtbdCommand", () => {
|
|
71
|
+
it("initializes saved credentials before a JTBD run resolves its provider", async () => {
|
|
72
|
+
const { program, initializeSecrets } = createProgram();
|
|
73
|
+
await expect(program.parseAsync([
|
|
74
|
+
"node",
|
|
75
|
+
"nodetool",
|
|
76
|
+
"jtbd",
|
|
77
|
+
"run",
|
|
78
|
+
"--provider",
|
|
79
|
+
"missing",
|
|
80
|
+
"--model",
|
|
81
|
+
"test"
|
|
82
|
+
], { from: "node" })).rejects.toThrow("stop run");
|
|
83
|
+
expect(initializeSecrets).toHaveBeenCalledOnce();
|
|
84
|
+
expect(events).toEqual(["secrets", "provider", "run"]);
|
|
85
|
+
});
|
|
86
|
+
it("initializes saved credentials before reviewing a JTBD bundle", async () => {
|
|
87
|
+
const bundle = await mkdtemp(join(tmpdir(), "nodetool-jtbd-"));
|
|
88
|
+
const runDir = join(bundle, "job");
|
|
89
|
+
const { program, initializeSecrets } = createProgram();
|
|
90
|
+
try {
|
|
91
|
+
await mkdir(runDir);
|
|
92
|
+
await writeFile(join(runDir, "report.json"), JSON.stringify({ achieved: false, friction: [], jobId: "job" }));
|
|
93
|
+
await expect(program.parseAsync([
|
|
94
|
+
"node",
|
|
95
|
+
"nodetool",
|
|
96
|
+
"jtbd",
|
|
97
|
+
"optimize",
|
|
98
|
+
"--provider",
|
|
99
|
+
"missing",
|
|
100
|
+
"--model",
|
|
101
|
+
"test",
|
|
102
|
+
"--bundle",
|
|
103
|
+
bundle
|
|
104
|
+
], { from: "node" })).rejects.toThrow("stop optimize");
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await rm(bundle, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
expect(initializeSecrets).toHaveBeenCalledOnce();
|
|
110
|
+
expect(events).toEqual(["secrets", "provider", "optimize"]);
|
|
111
|
+
});
|
|
112
|
+
it("does not initialize saved credentials when listing JTBD jobs", async () => {
|
|
113
|
+
const { program, initializeSecrets } = createProgram();
|
|
114
|
+
await program.parseAsync(["node", "nodetool", "jtbd", "list"], {
|
|
115
|
+
from: "node"
|
|
116
|
+
});
|
|
117
|
+
expect(initializeSecrets).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=jtbd.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jtbd.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/jtbd.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodetool affected` — map changed files to the workspaces that need checking.
|
|
3
|
+
*
|
|
4
|
+
* With no file arguments it reads the working-tree changes from git. Prints the
|
|
5
|
+
* affected packages and the minimal build/test commands; `--json` for tooling.
|
|
6
|
+
*/
|
|
7
|
+
import type { Command } from "commander";
|
|
8
|
+
export declare function registerAffectedCommand(program: Command): void;
|
|
9
|
+
//# sourceMappingURL=affected.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affected.d.ts","sourceRoot":"","sources":["../../src/commands/affected.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6G9D"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { computeAffected, EXTRA_WORKSPACE_PATHS, TOP_LEVEL_WORKSPACES } from "../affected/affected.js";
|
|
2
|
+
export function registerAffectedCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("affected [files...]")
|
|
5
|
+
.description("Map changed files (or git working-tree changes) to the workspaces that must be rebuilt/tested")
|
|
6
|
+
.option("--base <ref>", "Compare against a git ref (e.g. main) instead of the working tree")
|
|
7
|
+
.option("--json", "Print the result as JSON")
|
|
8
|
+
.action(async (files, opts) => {
|
|
9
|
+
try {
|
|
10
|
+
const { readFileSync, readdirSync, existsSync } = await import("node:fs");
|
|
11
|
+
const { join, resolve, dirname } = await import("node:path");
|
|
12
|
+
const { fileURLToPath } = await import("node:url");
|
|
13
|
+
const { execFileSync } = await import("node:child_process");
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
// dist layout: packages/cli-full/dist/commands → repo root is four up.
|
|
16
|
+
const repoRoot = resolve(here, "..", "..", "..", "..");
|
|
17
|
+
// Workspaces come from the root package.json, not from a scan of
|
|
18
|
+
// packages/ — `reliability/harness` is a workspace too, and scanning
|
|
19
|
+
// one directory reported every change under it as belonging to no
|
|
20
|
+
// workspace at all.
|
|
21
|
+
const rootPkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
|
|
22
|
+
const topLevel = new Set(TOP_LEVEL_WORKSPACES);
|
|
23
|
+
const dirs = [];
|
|
24
|
+
for (const entry of rootPkg.workspaces ?? []) {
|
|
25
|
+
const pattern = entry.replace(/\/$/, "");
|
|
26
|
+
if (!pattern.endsWith("/*")) {
|
|
27
|
+
dirs.push(pattern);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const parent = pattern.slice(0, -2);
|
|
31
|
+
if (!existsSync(join(repoRoot, parent)))
|
|
32
|
+
continue;
|
|
33
|
+
for (const child of readdirSync(join(repoRoot, parent), {
|
|
34
|
+
withFileTypes: true
|
|
35
|
+
})) {
|
|
36
|
+
if (child.isDirectory())
|
|
37
|
+
dirs.push(`${parent}/${child.name}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const packages = [];
|
|
41
|
+
for (const dir of dirs) {
|
|
42
|
+
// web/electron get their own command shape in computeAffected.
|
|
43
|
+
if (topLevel.has(dir))
|
|
44
|
+
continue;
|
|
45
|
+
const pkgJsonPath = join(repoRoot, dir, "package.json");
|
|
46
|
+
if (!existsSync(pkgJsonPath))
|
|
47
|
+
continue;
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
50
|
+
if (!pkg.name)
|
|
51
|
+
continue;
|
|
52
|
+
const allDeps = {
|
|
53
|
+
...(pkg.dependencies ?? {}),
|
|
54
|
+
...(pkg.devDependencies ?? {})
|
|
55
|
+
};
|
|
56
|
+
packages.push({
|
|
57
|
+
name: pkg.name,
|
|
58
|
+
dir,
|
|
59
|
+
internalDeps: Object.keys(allDeps).filter((d) => d.startsWith("@nodetool-ai/")),
|
|
60
|
+
ownedPaths: [...(EXTRA_WORKSPACE_PATHS[pkg.name] ?? [])]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// A malformed package.json shouldn't abort the whole mapping.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let changedFiles = files;
|
|
68
|
+
if (changedFiles.length === 0) {
|
|
69
|
+
// argv, never a shell line: `--base` is a git ref, and ref names
|
|
70
|
+
// may carry `;`, `$(…)` and backticks.
|
|
71
|
+
const args = opts.base
|
|
72
|
+
? ["diff", "--name-only", `${opts.base}...HEAD`]
|
|
73
|
+
: ["status", "--porcelain"];
|
|
74
|
+
const out = execFileSync("git", args, {
|
|
75
|
+
cwd: repoRoot,
|
|
76
|
+
encoding: "utf8"
|
|
77
|
+
});
|
|
78
|
+
changedFiles = opts.base
|
|
79
|
+
? out.split("\n").map((l) => l.trim()).filter(Boolean)
|
|
80
|
+
: out
|
|
81
|
+
.split("\n")
|
|
82
|
+
.map((l) => l.slice(3).trim())
|
|
83
|
+
.filter(Boolean);
|
|
84
|
+
}
|
|
85
|
+
const result = computeAffected(changedFiles, packages);
|
|
86
|
+
if (opts.json) {
|
|
87
|
+
console.log(JSON.stringify(result, null, 2));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
printAffected(result, changedFiles.length);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error(String(e));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function printAffected(result, fileCount) {
|
|
100
|
+
if (fileCount === 0) {
|
|
101
|
+
console.log("\nNo changed files detected.");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
console.log(`\n${result.affected.length} affected workspace(s) from ${fileCount} changed file(s):`);
|
|
105
|
+
for (const name of result.affected) {
|
|
106
|
+
const tag = result.changed.includes(name) ? "changed" : "dependent";
|
|
107
|
+
const build = result.needsBuild.includes(name) ? " (rebuild)" : "";
|
|
108
|
+
console.log(` ${tag.padEnd(9)} ${name}${build}`);
|
|
109
|
+
}
|
|
110
|
+
if (result.unmatched.length > 0) {
|
|
111
|
+
console.log(`\n${result.unmatched.length} file(s) outside any workspace:`);
|
|
112
|
+
for (const f of result.unmatched.slice(0, 10))
|
|
113
|
+
console.log(` ${f}`);
|
|
114
|
+
if (result.unmatched.length > 10) {
|
|
115
|
+
console.log(` …and ${result.unmatched.length - 10} more`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (result.commands.length > 0) {
|
|
119
|
+
console.log("\nSuggested commands:");
|
|
120
|
+
for (const cmd of result.commands)
|
|
121
|
+
console.log(` ${cmd}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.log("\nNo workspace commands needed.");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=affected.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affected.js","sourceRoot":"","sources":["../../src/commands/affected.ts"],"names":[],"mappings":"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"}
|