@kontourai/flow-agents 1.0.0 → 1.1.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/.github/workflows/ci.yml +110 -0
- package/CHANGELOG.md +24 -0
- package/build/src/cli/console-learning-projection.js +19 -2
- package/build/src/cli/effective-backlog-settings.js +18 -2
- package/build/src/cli/fixture-retirement-audit.js +19 -2
- package/build/src/cli/flow-kit.js +135 -5
- package/build/src/cli/init.js +19 -2
- package/build/src/cli/promote-workflow-artifact.js +19 -2
- package/build/src/cli/publish-change-helper.js +19 -2
- package/build/src/cli/pull-work-provider.js +19 -2
- package/build/src/cli/runtime-adapter.js +20 -2
- package/build/src/cli/usage-feedback.js +19 -2
- package/build/src/cli/utterance-check.js +19 -2
- package/build/src/cli/validate-hook-influence.js +19 -2
- package/build/src/cli/validate-source-tree.js +19 -2
- package/build/src/cli/veritas-governance.js +19 -2
- package/build/src/cli/workflow-artifact-cleanup-audit.js +19 -2
- package/build/src/runtime-adapters.js +56 -25
- package/build/src/tools/build-universal-bundles.js +19 -2
- package/build/src/tools/generate-context-map.js +19 -2
- package/build/src/tools/validate-package.js +19 -2
- package/build/src/tools/validate-source-tree.js +20 -3
- package/context/scripts/telemetry/console-presets.sh +1 -1
- package/docs/fixture-ownership.md +1 -1
- package/docs/kit-authoring-guide.md +20 -3
- package/evals/ci/run-baseline.sh +55 -8
- package/evals/integration/test_activate_npx_context.sh +134 -0
- package/evals/integration/test_flow_kit_install_git.sh +163 -0
- package/evals/integration/test_runtime_adapter_activation.sh +138 -17
- package/evals/run.sh +2 -0
- package/evals/static/test_console_presets.sh +49 -0
- package/package.json +1 -1
- package/scripts/telemetry/console-presets.sh +1 -1
- package/src/cli/console-learning-projection.ts +7 -1
- package/src/cli/effective-backlog-settings.ts +6 -1
- package/src/cli/fixture-retirement-audit.ts +7 -1
- package/src/cli/flow-kit.ts +123 -4
- package/src/cli/init.ts +7 -1
- package/src/cli/promote-workflow-artifact.ts +7 -1
- package/src/cli/publish-change-helper.ts +7 -1
- package/src/cli/pull-work-provider.ts +7 -1
- package/src/cli/runtime-adapter.ts +8 -1
- package/src/cli/usage-feedback.ts +7 -1
- package/src/cli/utterance-check.ts +7 -1
- package/src/cli/validate-hook-influence.ts +7 -1
- package/src/cli/validate-source-tree.ts +7 -1
- package/src/cli/veritas-governance.ts +7 -1
- package/src/cli/workflow-artifact-cleanup-audit.ts +7 -1
- package/src/runtime-adapters.ts +55 -27
- package/src/tools/build-universal-bundles.ts +7 -1
- package/src/tools/generate-context-map.ts +7 -1
- package/src/tools/validate-package.ts +7 -1
- package/src/tools/validate-source-tree.ts +8 -2
package/src/cli/flow-kit.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import * as child_process from "node:child_process";
|
|
1
2
|
import * as crypto from "node:crypto";
|
|
2
3
|
import * as fs from "node:fs";
|
|
4
|
+
import * as os from "node:os";
|
|
3
5
|
import * as path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
4
7
|
import { parseArgs, flagBool, flagString } from "../lib/args.js";
|
|
5
8
|
import { assertPathContained, copyDir, isoNow, readJson, walkFiles, writeJson } from "../lib/fs.js";
|
|
6
9
|
import { assertKitRepository, deriveKitTargets } from "../flow-kit/validate.js";
|
|
@@ -31,6 +34,22 @@ function contentHash(root: string): string {
|
|
|
31
34
|
return `sha256:${hash.digest("hex")}`;
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
/** Content hash that excludes .git and other VCS/cache directories (for install-git clones). */
|
|
38
|
+
function kitContentHash(root: string): string {
|
|
39
|
+
const EXCLUDE_DIRS = new Set([".git", "__pycache__", ".pytest_cache"]);
|
|
40
|
+
const hash = crypto.createHash("sha256");
|
|
41
|
+
for (const file of walkFiles(root)) {
|
|
42
|
+
const parts = path.relative(root, file).split(path.sep);
|
|
43
|
+
if (parts.some((p) => EXCLUDE_DIRS.has(p))) continue;
|
|
44
|
+
const rel = parts.join("/");
|
|
45
|
+
hash.update(rel);
|
|
46
|
+
hash.update("\0");
|
|
47
|
+
hash.update(fs.readFileSync(file));
|
|
48
|
+
hash.update("\0");
|
|
49
|
+
}
|
|
50
|
+
return `sha256:${hash.digest("hex")}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
34
53
|
function installLocal(argv: string[]): number {
|
|
35
54
|
const args = parseArgs(argv);
|
|
36
55
|
const source = path.resolve(args.positionals[0] ?? "");
|
|
@@ -39,12 +58,10 @@ function installLocal(argv: string[]): number {
|
|
|
39
58
|
try {
|
|
40
59
|
manifest = assertKitRepository(source);
|
|
41
60
|
} catch (error) {
|
|
42
|
-
console.log("warning: Flow validation surface unavailable; local kit check uses the minimal Flow Definition fallback");
|
|
43
61
|
console.log("Flow Kit repository validation failed:");
|
|
44
62
|
for (const diagnostic of ((error as Error & { diagnostics?: string[] }).diagnostics ?? [(error as Error).message])) console.log(` - ${diagnostic}`);
|
|
45
63
|
return 1;
|
|
46
64
|
}
|
|
47
|
-
console.log("warning: Flow validation surface unavailable; local kit check uses the minimal Flow Definition fallback");
|
|
48
65
|
const kitId = String(manifest.id);
|
|
49
66
|
const hash = contentHash(source);
|
|
50
67
|
const registry = loadRegistry(dest);
|
|
@@ -169,15 +186,117 @@ function inspect(argv: string[]): number {
|
|
|
169
186
|
return result.conformance.k0 ? 0 : 1;
|
|
170
187
|
}
|
|
171
188
|
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* install-git <repo-url>[#ref] [--ref <branch|tag|sha>] [--dest <path>] [--force] [--update]
|
|
192
|
+
*
|
|
193
|
+
* Shallow-clones a remote git repository to a temporary directory, validates the kit
|
|
194
|
+
* container with the same logic used by install-local, then delegates to the existing
|
|
195
|
+
* install path. Supports an optional #ref fragment in the URL or a separate --ref flag.
|
|
196
|
+
*
|
|
197
|
+
* Implements kontourai/flow-agents#56 (git-ref install surface).
|
|
198
|
+
*/
|
|
199
|
+
function installGit(argv: string[]): number {
|
|
200
|
+
const args = parseArgs(argv);
|
|
201
|
+
const rawUrl = args.positionals[0] ?? "";
|
|
202
|
+
if (!rawUrl) {
|
|
203
|
+
console.error("install-git: missing <repo-url> argument");
|
|
204
|
+
console.error("usage: flow-kit install-git <repo-url>[#ref] [--ref <branch|tag|sha>] [--dest <path>]");
|
|
205
|
+
return 2;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Parse ref: #fragment in URL takes precedence over --ref flag.
|
|
209
|
+
let repoUrl = rawUrl;
|
|
210
|
+
let ref: string | null = null;
|
|
211
|
+
const hashIdx = rawUrl.indexOf("#");
|
|
212
|
+
if (hashIdx !== -1) {
|
|
213
|
+
repoUrl = rawUrl.slice(0, hashIdx);
|
|
214
|
+
ref = rawUrl.slice(hashIdx + 1) || null;
|
|
215
|
+
}
|
|
216
|
+
if (!ref) ref = flagString(args.flags, "ref") ?? null;
|
|
217
|
+
|
|
218
|
+
const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
|
|
219
|
+
const force = flagBool(args.flags, "force") ?? false;
|
|
220
|
+
const update = flagBool(args.flags, "update") ?? false;
|
|
221
|
+
|
|
222
|
+
// Shallow-clone into a temporary directory.
|
|
223
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "flow-kit-git-"));
|
|
224
|
+
try {
|
|
225
|
+
const cloneArgs = ["clone", "--depth", "1"];
|
|
226
|
+
if (ref) cloneArgs.push("--branch", ref);
|
|
227
|
+
cloneArgs.push("--", repoUrl, tmpBase);
|
|
228
|
+
try {
|
|
229
|
+
child_process.execFileSync("git", cloneArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
230
|
+
} catch (err) {
|
|
231
|
+
const msg = err instanceof Error && (err as NodeJS.ErrnoException & { stderr?: Buffer }).stderr
|
|
232
|
+
? ((err as NodeJS.ErrnoException & { stderr?: Buffer }).stderr as Buffer).toString().trim()
|
|
233
|
+
: String(err);
|
|
234
|
+
console.error(`install-git: git clone failed: ${msg}`);
|
|
235
|
+
return 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Validate the cloned kit using the same logic as install-local.
|
|
239
|
+
let manifest: Record<string, unknown>;
|
|
240
|
+
try {
|
|
241
|
+
manifest = assertKitRepository(tmpBase);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.log("Flow Kit repository validation failed:");
|
|
244
|
+
for (const diagnostic of ((error as Error & { diagnostics?: string[] }).diagnostics ?? [(error as Error).message])) {
|
|
245
|
+
console.log(` - ${diagnostic}`);
|
|
246
|
+
}
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Delegate to the shared install logic (copy + registry update).
|
|
251
|
+
const kitId = String(manifest.id);
|
|
252
|
+
const hash = kitContentHash(tmpBase);
|
|
253
|
+
const registry = loadRegistry(dest);
|
|
254
|
+
const existing = registry.kits.find((entry) => entry.id === kitId);
|
|
255
|
+
const target = installedPath(dest, kitId);
|
|
256
|
+
assertPathContained(dest, target);
|
|
257
|
+
const sourceText = repoUrl + (ref ? `#${ref}` : "");
|
|
258
|
+
if (existing && existing.source !== sourceText && !update) {
|
|
259
|
+
console.log(`conflict: kit '${kitId}' is already installed from ${existing.source}; rerun with --update to replace it`);
|
|
260
|
+
return 2;
|
|
261
|
+
}
|
|
262
|
+
if (existing && existing.source === sourceText && existing.hash === hash && fs.existsSync(target) && !force) {
|
|
263
|
+
console.log(`kit '${kitId}' is already installed from ${sourceText}`);
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
copyDir(tmpBase, target);
|
|
267
|
+
const entry: Record<string, unknown> = {
|
|
268
|
+
id: kitId,
|
|
269
|
+
source: sourceText,
|
|
270
|
+
hash,
|
|
271
|
+
installed_at: existing && existing.source === sourceText && !update ? existing.installed_at : isoNow(),
|
|
272
|
+
installed_path: target,
|
|
273
|
+
state: "installed",
|
|
274
|
+
};
|
|
275
|
+
if (typeof manifest.version === "string" && manifest.version) entry.version = manifest.version;
|
|
276
|
+
registry.kits = existing ? registry.kits.map((item) => item.id === kitId ? entry : item) : [...registry.kits, entry];
|
|
277
|
+
writeJson(registryPath(dest), registry);
|
|
278
|
+
console.log(`${existing ? "updated" : "installed"} git kit '${kitId}' from ${sourceText} at ${target}`);
|
|
279
|
+
return 0;
|
|
280
|
+
} finally {
|
|
281
|
+
fs.rmSync(tmpBase, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
172
285
|
export function main(argv = process.argv.slice(2)): number {
|
|
173
286
|
const [command, ...rest] = argv;
|
|
174
287
|
if (command === "install-local") return installLocal(rest);
|
|
288
|
+
if (command === "install-git") return installGit(rest);
|
|
175
289
|
if (command === "list") return list(rest);
|
|
176
290
|
if (command === "status") return status(rest);
|
|
177
291
|
if (command === "activate") return activate(rest);
|
|
178
292
|
if (command === "inspect") return inspect(rest);
|
|
179
|
-
console.error("usage: flow-kit <install-local|list|status|activate|inspect> ...");
|
|
293
|
+
console.error("usage: flow-kit <install-local|install-git|list|status|activate|inspect> ...");
|
|
180
294
|
return 2;
|
|
181
295
|
}
|
|
182
296
|
|
|
183
|
-
|
|
297
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
298
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
299
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
300
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
301
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
302
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
package/src/cli/init.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import * as os from "node:os";
|
|
4
5
|
import * as path from "node:path";
|
|
5
6
|
import { createInterface } from "node:readline/promises";
|
|
@@ -458,4 +459,9 @@ export async function mainDogfood(argv = process.argv.slice(2)): Promise<number>
|
|
|
458
459
|
}
|
|
459
460
|
}
|
|
460
461
|
|
|
461
|
-
|
|
462
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
463
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
464
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
465
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
466
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
467
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = await main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { parseArgs, flagString } from "../lib/args.js";
|
|
4
5
|
import { isoNow, relPath } from "../lib/fs.js";
|
|
@@ -61,4 +62,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
61
62
|
return 0;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
66
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
67
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
68
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
69
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
70
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { parseArgs, flagString } from "../lib/args.js";
|
|
4
5
|
import { readJson } from "../lib/fs.js";
|
|
@@ -140,4 +141,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
145
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
146
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
147
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
148
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
149
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { parseArgs, flagList, flagString } from "../lib/args.js";
|
|
3
4
|
|
|
4
5
|
const FLOW_ARTIFACT_PATTERN = /(?<path>\.flow-agents\/[^\s`'")]+)/g;
|
|
@@ -478,4 +479,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
478
479
|
return 0;
|
|
479
480
|
}
|
|
480
481
|
|
|
481
|
-
|
|
482
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
483
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
484
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
485
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
486
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
487
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
1
3
|
import * as path from "node:path";
|
|
2
4
|
import { parseArgs, flagString } from "../lib/args.js";
|
|
3
5
|
import { activateCodexLocal, activateStrandsLocal } from "../runtime-adapters.js";
|
|
@@ -26,4 +28,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
26
28
|
return Array.isArray(result.errors) && result.errors.length ? 1 : 0;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
32
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
33
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
34
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
35
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
36
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { parseArgs, flagBool, flagList, flagString } from "../lib/args.js";
|
|
@@ -415,4 +416,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
415
416
|
}
|
|
416
417
|
}
|
|
417
418
|
|
|
418
|
-
|
|
419
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
420
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
421
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
422
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
423
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
424
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { flagBool, flagString, parseArgs } from "../lib/args.js";
|
|
4
5
|
|
|
@@ -321,4 +322,9 @@ export async function main(argv = process.argv.slice(2)): Promise<number> {
|
|
|
321
322
|
return runCheck(rest);
|
|
322
323
|
}
|
|
323
324
|
|
|
324
|
-
|
|
325
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
326
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
327
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
328
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
329
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
330
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = await main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
|
|
3
4
|
const validTiers = new Set(["adapter", "design-target", "installed-command", "live-acceptance", "documented-runtime-gap"]);
|
|
4
5
|
const validRuntimes = new Set(["codex", "claude-code", "kiro-cli"]);
|
|
@@ -116,4 +117,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
121
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
122
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
123
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
124
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
125
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -27,4 +27,10 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
27
27
|
return 0;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
31
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS).
|
|
32
|
+
import * as _fsVST from "node:fs";
|
|
33
|
+
import { fileURLToPath as _ftpVST } from "node:url";
|
|
34
|
+
const _selfVST = (() => { try { return _fsVST.realpathSync(_ftpVST(import.meta.url)); } catch { return _ftpVST(import.meta.url); } })();
|
|
35
|
+
const _argv1VST = (() => { try { return _fsVST.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
36
|
+
if (_selfVST === _argv1VST) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { spawnSync } from "node:child_process";
|
|
4
5
|
import { flagBool, flagString, parseArgs } from "../lib/args.js";
|
|
@@ -319,4 +320,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
319
320
|
return options ? runEvidence(options) : 2;
|
|
320
321
|
}
|
|
321
322
|
|
|
322
|
-
|
|
323
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
324
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
325
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
326
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
327
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
328
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { flagBool, flagString, parseArgs } from "../lib/args.js";
|
|
4
5
|
|
|
@@ -278,4 +279,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
278
279
|
return 0;
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
|
|
282
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
283
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
284
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
285
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
286
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
287
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
package/src/runtime-adapters.ts
CHANGED
|
@@ -86,7 +86,7 @@ export function readKitInventory(sourceRoot: string, dest: string): KitInventory
|
|
|
86
86
|
const errors: string[] = [];
|
|
87
87
|
const assets: KitAsset[] = [];
|
|
88
88
|
const catalogPath = path.join(sourceRoot, "kits", "catalog.json");
|
|
89
|
-
if (!fs.existsSync(catalogPath))
|
|
89
|
+
if (!fs.existsSync(catalogPath)) warnings.push(`${catalogPath}: built-in Kit Catalog not found; skipping built-in kits (this is normal when running outside a flow-agents checkout)`);
|
|
90
90
|
else {
|
|
91
91
|
const catalog = readJson(catalogPath) as Record<string, unknown>;
|
|
92
92
|
const kits = catalog.kits;
|
|
@@ -126,31 +126,47 @@ function safeSegment(value: string): string {
|
|
|
126
126
|
return value.replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "") || "asset";
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// Asset classes that are directly activated (copied to the runtime directory) by both adapters.
|
|
130
|
+
// flows: gate definitions read by the adapter's flow-routing layer.
|
|
131
|
+
// skills: agent guidance markdown copied to skills/<kit-id>/ for agent discovery.
|
|
132
|
+
// docs: documentation markdown copied to docs/<kit-id>/ for agent reference.
|
|
133
|
+
const ACTIVATED_ASSET_CLASSES = new Set(["flows", "skills", "docs"]);
|
|
134
|
+
|
|
129
135
|
export function activateCodexLocal(sourceRoot: string, dest: string): Record<string, unknown> {
|
|
130
136
|
const inventory = readKitInventory(sourceRoot, dest);
|
|
131
137
|
const runtimeDir = path.join(dest, ".flow-agents", "runtime", "codex");
|
|
132
138
|
const generated: Record<string, string>[] = [];
|
|
133
139
|
const skipped: Record<string, string | null>[] = [];
|
|
134
140
|
for (const asset of inventory.assets) {
|
|
135
|
-
if (asset.asset_class
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
if (asset.asset_class === "flows") {
|
|
142
|
+
if (!asset.asset_id) {
|
|
143
|
+
skipped.push({ asset_class: asset.asset_class, path: asset.relative_path, kit_id: asset.kit_id, asset_id: null, reason: "flow asset is missing an id" });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
|
|
147
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
148
|
+
fs.copyFileSync(asset.source_path, output);
|
|
149
|
+
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id, source_path: asset.source_path.split(path.sep).join("/") });
|
|
150
|
+
} else if (asset.asset_class === "skills" || asset.asset_class === "docs") {
|
|
151
|
+
// Copy skills and docs to runtime/<adapter>/<class>/<kit-id>/<filename> so the
|
|
152
|
+
// agent's guidance index (AGENTS.md) can reference them and they are co-located
|
|
153
|
+
// with flow definitions for the same kit.
|
|
154
|
+
const filename = path.basename(asset.source_path);
|
|
155
|
+
const output = path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
|
|
156
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
157
|
+
fs.copyFileSync(asset.source_path, output);
|
|
158
|
+
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id ?? "", source_path: asset.source_path.split(path.sep).join("/") });
|
|
159
|
+
} else {
|
|
160
|
+
skipped.push({ asset_class: asset.asset_class, path: asset.relative_path, kit_id: asset.kit_id, asset_id: asset.asset_id, reason: "asset class is not activated by codex-local" });
|
|
142
161
|
}
|
|
143
|
-
const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
|
|
144
|
-
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
145
|
-
fs.copyFileSync(asset.source_path, output);
|
|
146
|
-
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id, source_path: asset.source_path.split(path.sep).join("/") });
|
|
147
162
|
}
|
|
148
163
|
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
149
|
-
const
|
|
164
|
+
const supportedClasses = Array.from(ACTIVATED_ASSET_CLASSES);
|
|
165
|
+
const manifest = { schema_version: "1.0", adapter: "codex-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
|
|
150
166
|
const manifestPath = path.join(runtimeDir, "activation.json");
|
|
151
167
|
writeJson(manifestPath, manifest);
|
|
152
168
|
generated.push({ asset_class: "activation-manifest", path: relPath(dest, manifestPath), kit_id: "runtime", asset_id: "codex-local.activation", source_path: manifestPath.split(path.sep).join("/") });
|
|
153
|
-
return { selected_adapter: "codex-local", supported_asset_classes:
|
|
169
|
+
return { selected_adapter: "codex-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
// Decision Q3 (Issue #32): Option (a) — new adapter id "strands-local" rather than
|
|
@@ -163,27 +179,39 @@ export function activateStrandsLocal(sourceRoot: string, dest: string): Record<s
|
|
|
163
179
|
const inventory = readKitInventory(sourceRoot, dest);
|
|
164
180
|
// Runtime flows land at .flow-agents/runtime/strands/flows/<kit-id>/<asset-id>.flow.json
|
|
165
181
|
// so the Strands steering context can glob for *.flow.json under this path.
|
|
182
|
+
// Runtime skills land at .flow-agents/runtime/strands/skills/<kit-id>/<filename> and
|
|
183
|
+
// docs at .flow-agents/runtime/strands/docs/<kit-id>/<filename> for system-prompt injection.
|
|
166
184
|
const runtimeDir = path.join(dest, ".flow-agents", "runtime", "strands");
|
|
167
185
|
const generated: Record<string, string>[] = [];
|
|
168
186
|
const skipped: Record<string, string | null>[] = [];
|
|
169
187
|
for (const asset of inventory.assets) {
|
|
170
|
-
if (asset.asset_class
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
if (asset.asset_class === "flows") {
|
|
189
|
+
if (!asset.asset_id) {
|
|
190
|
+
skipped.push({ asset_class: asset.asset_class, path: asset.relative_path, kit_id: asset.kit_id, asset_id: null, reason: "flow asset is missing an id" });
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
|
|
194
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
195
|
+
fs.copyFileSync(asset.source_path, output);
|
|
196
|
+
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id, source_path: asset.source_path.split(path.sep).join("/") });
|
|
197
|
+
} else if (asset.asset_class === "skills" || asset.asset_class === "docs") {
|
|
198
|
+
// Mirror the codex-local layout: strands/<class>/<kit-id>/<filename>.
|
|
199
|
+
// The Strands system-prompt injection layer can glob for all *.md files under
|
|
200
|
+
// .flow-agents/runtime/strands/skills/ to include agent guidance in the context.
|
|
201
|
+
const filename = path.basename(asset.source_path);
|
|
202
|
+
const output = path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
|
|
203
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
204
|
+
fs.copyFileSync(asset.source_path, output);
|
|
205
|
+
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id ?? "", source_path: asset.source_path.split(path.sep).join("/") });
|
|
206
|
+
} else {
|
|
207
|
+
skipped.push({ asset_class: asset.asset_class, path: asset.relative_path, kit_id: asset.kit_id, asset_id: asset.asset_id, reason: "asset class is not activated by strands-local" });
|
|
177
208
|
}
|
|
178
|
-
const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
|
|
179
|
-
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
180
|
-
fs.copyFileSync(asset.source_path, output);
|
|
181
|
-
generated.push({ asset_class: asset.asset_class, path: relPath(dest, output), kit_id: asset.kit_id, asset_id: asset.asset_id, source_path: asset.source_path.split(path.sep).join("/") });
|
|
182
209
|
}
|
|
183
210
|
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
184
|
-
const
|
|
211
|
+
const supportedClasses = Array.from(ACTIVATED_ASSET_CLASSES);
|
|
212
|
+
const manifest = { schema_version: "1.0", adapter: "strands-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
|
|
185
213
|
const manifestPath = path.join(runtimeDir, "activation.json");
|
|
186
214
|
writeJson(manifestPath, manifest);
|
|
187
215
|
generated.push({ asset_class: "activation-manifest", path: relPath(dest, manifestPath), kit_id: "runtime", asset_id: "strands-local.activation", source_path: manifestPath.split(path.sep).join("/") });
|
|
188
|
-
return { selected_adapter: "strands-local", supported_asset_classes:
|
|
216
|
+
return { selected_adapter: "strands-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
|
|
189
217
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { loadJson, readText, root, walkFiles, writeText } from "./common.js";
|
|
5
6
|
|
|
@@ -646,4 +647,9 @@ export function main(): number {
|
|
|
646
647
|
}
|
|
647
648
|
return 0;
|
|
648
649
|
}
|
|
649
|
-
|
|
650
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
651
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
652
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
653
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
654
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
655
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { exists, loadJson, markdownTable, oneLine, readText, rel, root, writeText } from "./common.js";
|
|
5
6
|
|
|
@@ -196,4 +197,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
196
197
|
return 0;
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
201
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
202
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
203
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
204
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
205
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
5
6
|
export function main(argv = process.argv.slice(2)): number {
|
|
@@ -54,4 +55,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
54
55
|
console.log(errors === 0 ? "Result: PASS" : `Result: FAIL (${errors} error(s))`);
|
|
55
56
|
return errors === 0 ? 0 : 1;
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
59
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
60
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
61
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
62
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
63
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { spawnSync } from "node:child_process";
|
|
5
6
|
import { loadJson, readText, rel, root, walkFiles } from "./common.js";
|
|
@@ -84,7 +85,7 @@ const fixtureOwnerPolicies = new Map<string, { owners: string[]; classification:
|
|
|
84
85
|
["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
|
|
85
86
|
["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
|
|
86
87
|
["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
|
|
87
|
-
["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
|
|
88
|
+
["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/integration/test_activate_npx_context.sh", "evals/integration/test_flow_kit_install_git.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
|
|
88
89
|
["evals/fixtures/kit-conformance-levels", { owners: ["evals/integration/test_kit_conformance_levels.sh"], classification: "K-level conformance and consumer-target derivation fixtures" }],
|
|
89
90
|
["evals/fixtures/hook-influence", { owners: ["evals/integration/test_hook_influence_cases.sh", "evals/static/test_workflow_skills.sh", "scripts/validate-hook-influence-cases.js"], classification: "hook influence behavioral cases" }],
|
|
90
91
|
["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
|
|
@@ -491,4 +492,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
491
492
|
console.log("Source tree validation passed.");
|
|
492
493
|
return 0;
|
|
493
494
|
}
|
|
494
|
-
|
|
495
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
496
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
497
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
498
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
499
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
500
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|