@kontourai/flow-agents 1.0.1 → 1.2.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/.github/workflows/runtime-compat.yml +5 -2
- package/CHANGELOG.md +42 -0
- package/README.md +26 -5
- 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/init.js +19 -2
- package/build/src/cli/{flow-kit.js → kit.js} +122 -108
- 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 +4 -4
- package/build/src/cli/veritas-governance.js +19 -2
- package/build/src/cli/workflow-artifact-cleanup-audit.js +19 -2
- package/build/src/cli.js +3 -3
- package/build/src/flow-kit/validate.js +58 -62
- package/build/src/runtime-adapters.js +55 -24
- package/build/src/tools/build-universal-bundles.js +83 -19
- package/build/src/tools/generate-context-map.js +68 -9
- package/build/src/tools/validate-package.js +19 -2
- package/build/src/tools/validate-source-tree.js +51 -3
- package/context/scripts/telemetry/console-presets.sh +1 -1
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +169 -0
- package/docs/adr/0007-skill-audit.md +112 -0
- package/docs/adr/0008-kit-operation-boundary.md +88 -0
- package/docs/context-map.md +18 -22
- package/docs/flow-kit-repository-contract.md +5 -5
- package/docs/getting-started.md +177 -0
- package/docs/index.md +19 -8
- package/docs/kit-authoring-guide.md +46 -10
- package/docs/knowledge-kit.md +2 -2
- package/docs/spec/runtime-hook-surface.md +1 -1
- package/docs/vision.md +1 -1
- package/docs/workflow-usage-guide.md +1 -1
- package/evals/ci/run-baseline.sh +55 -8
- package/evals/fixtures/builder-kit-workflow-state/happy-path.json +2 -2
- package/evals/fixtures/builder-kit-workflow-state/mid-work-resume.json +2 -2
- package/evals/fixtures/console-learning-projection/artifacts/console-learning-correction/learning.json +1 -1
- package/evals/fixtures/pull-work-provider/github-issues.json +5 -5
- package/evals/integration/test_activate_npx_context.sh +2 -2
- package/evals/integration/test_bundle_install.sh +17 -12
- package/evals/integration/test_console_learning_projection.sh +1 -1
- package/evals/integration/test_flow_kit_install_git.sh +7 -7
- package/evals/integration/test_flow_kit_repository.sh +4 -4
- package/evals/integration/test_kit_conformance_levels.sh +1 -1
- package/evals/integration/test_local_flow_kit_install.sh +7 -7
- package/evals/integration/test_publish_change_helper.sh +1 -1
- package/evals/integration/test_pull_work_provider.sh +1 -1
- package/evals/integration/test_runtime_adapter_activation.sh +140 -19
- package/evals/lib/node.sh +2 -2
- package/evals/run.sh +2 -0
- package/evals/static/test_console_presets.sh +49 -0
- package/evals/static/test_workflow_skills.sh +15 -15
- package/integrations/strands/flow_agents_strands/steering.py +1 -1
- package/integrations/strands-ts/src/hooks.ts +1 -1
- package/kits/builder/kit.json +17 -0
- package/{skills → kits/builder/skills}/builder-shape/SKILL.md +4 -4
- package/{skills → kits/builder/skills}/idea-to-backlog/SKILL.md +1 -1
- package/kits/knowledge/kit.json +16 -9
- package/package.json +8 -5
- package/packaging/packs.json +1 -21
- package/scripts/README.md +1 -1
- package/scripts/kit.js +2 -0
- package/scripts/telemetry/console-presets.sh +1 -1
- package/skills/README.md +23 -0
- 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/init.ts +7 -1
- package/src/cli/{flow-kit.ts → kit.ts} +124 -109
- 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 +4 -4
- package/src/cli/veritas-governance.ts +7 -1
- package/src/cli/workflow-artifact-cleanup-audit.ts +7 -1
- package/src/cli.ts +3 -3
- package/src/flow-kit/validate.ts +63 -57
- package/src/runtime-adapters.ts +54 -26
- package/src/tools/build-universal-bundles.ts +67 -14
- package/src/tools/generate-context-map.ts +43 -7
- package/src/tools/validate-package.ts +7 -1
- package/src/tools/validate-source-tree.ts +34 -2
- package/scripts/flow-kit.js +0 -2
- package/skills/context-budget/SKILL.md +0 -40
- package/skills/explore/SKILL.md +0 -137
- package/skills/feedback-loop/SKILL.md +0 -87
- package/skills/frontend-design/SKILL.md +0 -80
- /package/{skills → kits/builder/skills}/deliver/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/design-probe/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/evidence-gate/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/execute-plan/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/fix-bug/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/learning-review/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/pickup-probe/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/plan-work/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/pull-work/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/release-readiness/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/review-work/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/tdd-workflow/SKILL.md +0 -0
- /package/{skills → kits/builder/skills}/verify-work/SKILL.md +0 -0
- /package/{skills → kits/knowledge/skills}/knowledge-capture/SKILL.md +0 -0
package/packaging/packs.json
CHANGED
|
@@ -7,13 +7,6 @@
|
|
|
7
7
|
"description": "Small default surface for reliable coding and workflow execution.",
|
|
8
8
|
"skills": [
|
|
9
9
|
"search-first",
|
|
10
|
-
"plan-work",
|
|
11
|
-
"execute-plan",
|
|
12
|
-
"review-work",
|
|
13
|
-
"verify-work",
|
|
14
|
-
"evidence-gate",
|
|
15
|
-
"feedback-loop",
|
|
16
|
-
"knowledge-capture",
|
|
17
10
|
"browser-test"
|
|
18
11
|
],
|
|
19
12
|
"agents": [
|
|
@@ -32,23 +25,10 @@
|
|
|
32
25
|
"default": false,
|
|
33
26
|
"description": "Development workflow depth for backlog, release, dependency, GitHub, TDD, and frontend work.",
|
|
34
27
|
"skills": [
|
|
35
|
-
"builder-shape",
|
|
36
|
-
"idea-to-backlog",
|
|
37
|
-
"pull-work",
|
|
38
|
-
"design-probe",
|
|
39
|
-
"pickup-probe",
|
|
40
|
-
"deliver",
|
|
41
|
-
"fix-bug",
|
|
42
|
-
"tdd-workflow",
|
|
43
|
-
"release-readiness",
|
|
44
|
-
"learning-review",
|
|
45
28
|
"dependency-update",
|
|
46
29
|
"eval-rebuild",
|
|
47
|
-
"explore",
|
|
48
30
|
"github-cli",
|
|
49
|
-
"
|
|
50
|
-
"agentic-engineering",
|
|
51
|
-
"context-budget"
|
|
31
|
+
"agentic-engineering"
|
|
52
32
|
],
|
|
53
33
|
"agents": [
|
|
54
34
|
"dev",
|
package/scripts/README.md
CHANGED
|
@@ -11,7 +11,7 @@ These files are stable launchers for TypeScript code compiled under `build/src/`
|
|
|
11
11
|
| `build-universal-bundles.js` | `build/src/tools/build-universal-bundles.js` |
|
|
12
12
|
| `filter-installed-packs.js` | `build/src/tools/filter-installed-packs.js` |
|
|
13
13
|
| `generate-context-map.js` | `build/src/tools/generate-context-map.js` |
|
|
14
|
-
| `
|
|
14
|
+
| `kit.js` | `build/src/cli/kit.js` |
|
|
15
15
|
| `pull-work-provider.js` | `build/src/cli/pull-work-provider.js` |
|
|
16
16
|
| `effective-backlog-settings.js` | `build/src/cli/effective-backlog-settings.js` |
|
|
17
17
|
| `publish-change-helper.js` | `build/src/cli/publish-change-helper.js` |
|
package/scripts/kit.js
ADDED
|
@@ -6,7 +6,7 @@ flow_agents_local_kontour_console_url() {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
flow_agents_kontour_cloud_console_url() {
|
|
9
|
-
printf '%s\n' "${FLOW_AGENTS_KONTOUR_CLOUD_CONSOLE_URL:-https://console.kontourai.
|
|
9
|
+
printf '%s\n' "${FLOW_AGENTS_KONTOUR_CLOUD_CONSOLE_URL:-https://console.kontourai.io}"
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
flow_agents_kontour_hosted_console_url() {
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# skills/
|
|
2
|
+
|
|
3
|
+
This directory contains standalone skills that are **not yet claimed by a specific kit**.
|
|
4
|
+
|
|
5
|
+
## Status: Pending Tool Reclassification
|
|
6
|
+
|
|
7
|
+
The 6 items here are currently classified as TOOLS pending formal reclassification in a follow-up ADR pass (tracking: #62 follow-up, ADR 0007).
|
|
8
|
+
|
|
9
|
+
| Skill | Notes |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| agentic-engineering | Pending reclassification as a tool or context doc |
|
|
12
|
+
| browser-test | Pending reclassification — closely tied to Playwright power |
|
|
13
|
+
| dependency-update | Pending reclassification — tooling-oriented |
|
|
14
|
+
| eval-rebuild | Pending reclassification — project-specific build hook |
|
|
15
|
+
| github-cli | Pending reclassification — CLI tool wrapper |
|
|
16
|
+
| search-first | Pending reclassification — research heuristic |
|
|
17
|
+
|
|
18
|
+
## Kit-owned skills
|
|
19
|
+
|
|
20
|
+
Builder Kit skills now live in `kits/builder/skills/`.
|
|
21
|
+
Knowledge Kit skills now live in `kits/knowledge/skills/`.
|
|
22
|
+
|
|
23
|
+
See `docs/adr/0007-skill-audit.md` for the full audit table and disposition rationale.
|
|
@@ -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
|
import { buildWorkflowLearningProjection, readWorkflowLearningSources } from "../lib/workflow-learning-projection.js";
|
|
@@ -137,4 +138,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
142
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
143
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
144
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
145
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
146
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -96,4 +96,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
100
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
101
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
102
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
103
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
104
|
+
if (_selfRealPath === _argv1RealPath) { process.exitCode = main(); }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
|
|
5
6
|
type FixtureAuditItem = {
|
|
@@ -151,4 +152,9 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
156
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
157
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
158
|
+
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
159
|
+
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
160
|
+
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(); }
|
|
@@ -34,7 +34,7 @@ function contentHash(root: string): string {
|
|
|
34
34
|
return `sha256:${hash.digest("hex")}`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Content hash that excludes .git and other VCS/cache directories (for install
|
|
37
|
+
/** Content hash that excludes .git and other VCS/cache directories (for install git clones). */
|
|
38
38
|
function kitContentHash(root: string): string {
|
|
39
39
|
const EXCLUDE_DIRS = new Set([".git", "__pycache__", ".pytest_cache"]);
|
|
40
40
|
const hash = crypto.createHash("sha256");
|
|
@@ -50,13 +50,40 @@ function kitContentHash(root: string): string {
|
|
|
50
50
|
return `sha256:${hash.digest("hex")}`;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* install <source> [--dest <path>] [--force] [--update] [--ref <branch|tag|sha>]
|
|
55
|
+
*
|
|
56
|
+
* Installs a Flow Kit from a local path or a git URL.
|
|
57
|
+
*
|
|
58
|
+
* - Local path: validates then copies the kit into the destination registry.
|
|
59
|
+
* - Git URL (http://, https://, git+, ssh://, file://): shallow-clones the repository,
|
|
60
|
+
* validates the kit container with @kontourai/flow, then delegates to the install path.
|
|
61
|
+
* Supports an optional #ref fragment in the URL or a separate --ref flag.
|
|
62
|
+
*/
|
|
63
|
+
async function install(argv: string[]): Promise<number> {
|
|
64
|
+
const args = parseArgs(argv);
|
|
65
|
+
const source = args.positionals[0] ?? "";
|
|
66
|
+
if (!source) {
|
|
67
|
+
console.error("install: missing <source> argument");
|
|
68
|
+
console.error("usage: flow-agents kit install <path-or-git-url> [--dest <path>] [--ref <ref>] [--force] [--update]");
|
|
69
|
+
return 2;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Detect git URL: starts with http(s)://, git+, ssh://, file://, or ends with .git
|
|
73
|
+
const isGitUrl = /^(https?:\/\/|git\+|ssh:\/\/|file:\/\/)/.test(source) || source.endsWith(".git");
|
|
74
|
+
|
|
75
|
+
if (isGitUrl) {
|
|
76
|
+
return await installGitSource(source, argv);
|
|
77
|
+
}
|
|
78
|
+
return await installLocalSource(path.resolve(source), argv);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function installLocalSource(source: string, argv: string[]): Promise<number> {
|
|
54
82
|
const args = parseArgs(argv);
|
|
55
|
-
const source = path.resolve(args.positionals[0] ?? "");
|
|
56
83
|
const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
|
|
57
84
|
let manifest: Record<string, unknown>;
|
|
58
85
|
try {
|
|
59
|
-
manifest = assertKitRepository(source);
|
|
86
|
+
manifest = await assertKitRepository(source);
|
|
60
87
|
} catch (error) {
|
|
61
88
|
console.log("Flow Kit repository validation failed:");
|
|
62
89
|
for (const diagnostic of ((error as Error & { diagnostics?: string[] }).diagnostics ?? [(error as Error).message])) console.log(` - ${diagnostic}`);
|
|
@@ -93,6 +120,86 @@ function installLocal(argv: string[]): number {
|
|
|
93
120
|
return 0;
|
|
94
121
|
}
|
|
95
122
|
|
|
123
|
+
async function installGitSource(rawUrl: string, argv: string[]): Promise<number> {
|
|
124
|
+
const args = parseArgs(argv);
|
|
125
|
+
|
|
126
|
+
// Parse ref: #fragment in URL takes precedence over --ref flag.
|
|
127
|
+
let repoUrl = rawUrl;
|
|
128
|
+
let ref: string | null = null;
|
|
129
|
+
const hashIdx = rawUrl.indexOf("#");
|
|
130
|
+
if (hashIdx !== -1) {
|
|
131
|
+
repoUrl = rawUrl.slice(0, hashIdx);
|
|
132
|
+
ref = rawUrl.slice(hashIdx + 1) || null;
|
|
133
|
+
}
|
|
134
|
+
if (!ref) ref = flagString(args.flags, "ref") ?? null;
|
|
135
|
+
|
|
136
|
+
const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
|
|
137
|
+
const force = flagBool(args.flags, "force") ?? false;
|
|
138
|
+
const update = flagBool(args.flags, "update") ?? false;
|
|
139
|
+
|
|
140
|
+
// Shallow-clone into a temporary directory.
|
|
141
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "flow-kit-git-"));
|
|
142
|
+
try {
|
|
143
|
+
const cloneArgs = ["clone", "--depth", "1"];
|
|
144
|
+
if (ref) cloneArgs.push("--branch", ref);
|
|
145
|
+
cloneArgs.push("--", repoUrl, tmpBase);
|
|
146
|
+
try {
|
|
147
|
+
child_process.execFileSync("git", cloneArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
148
|
+
} catch (err) {
|
|
149
|
+
const msg = err instanceof Error && (err as NodeJS.ErrnoException & { stderr?: Buffer }).stderr
|
|
150
|
+
? ((err as NodeJS.ErrnoException & { stderr?: Buffer }).stderr as Buffer).toString().trim()
|
|
151
|
+
: String(err);
|
|
152
|
+
console.error(`install: git clone failed: ${msg}`);
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate the cloned kit using the same logic as install local.
|
|
157
|
+
let manifest: Record<string, unknown>;
|
|
158
|
+
try {
|
|
159
|
+
manifest = await assertKitRepository(tmpBase);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.log("Flow Kit repository validation failed:");
|
|
162
|
+
for (const diagnostic of ((error as Error & { diagnostics?: string[] }).diagnostics ?? [(error as Error).message])) {
|
|
163
|
+
console.log(` - ${diagnostic}`);
|
|
164
|
+
}
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Delegate to the shared install logic (copy + registry update).
|
|
169
|
+
const kitId = String(manifest.id);
|
|
170
|
+
const hash = kitContentHash(tmpBase);
|
|
171
|
+
const registry = loadRegistry(dest);
|
|
172
|
+
const existing = registry.kits.find((entry) => entry.id === kitId);
|
|
173
|
+
const target = installedPath(dest, kitId);
|
|
174
|
+
assertPathContained(dest, target);
|
|
175
|
+
const sourceText = repoUrl + (ref ? `#${ref}` : "");
|
|
176
|
+
if (existing && existing.source !== sourceText && !update) {
|
|
177
|
+
console.log(`conflict: kit '${kitId}' is already installed from ${existing.source}; rerun with --update to replace it`);
|
|
178
|
+
return 2;
|
|
179
|
+
}
|
|
180
|
+
if (existing && existing.source === sourceText && existing.hash === hash && fs.existsSync(target) && !force) {
|
|
181
|
+
console.log(`kit '${kitId}' is already installed from ${sourceText}`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
copyDir(tmpBase, target);
|
|
185
|
+
const entry: Record<string, unknown> = {
|
|
186
|
+
id: kitId,
|
|
187
|
+
source: sourceText,
|
|
188
|
+
hash,
|
|
189
|
+
installed_at: existing && existing.source === sourceText && !update ? existing.installed_at : isoNow(),
|
|
190
|
+
installed_path: target,
|
|
191
|
+
state: "installed",
|
|
192
|
+
};
|
|
193
|
+
if (typeof manifest.version === "string" && manifest.version) entry.version = manifest.version;
|
|
194
|
+
registry.kits = existing ? registry.kits.map((item) => item.id === kitId ? entry : item) : [...registry.kits, entry];
|
|
195
|
+
writeJson(registryPath(dest), registry);
|
|
196
|
+
console.log(`${existing ? "updated" : "installed"} git kit '${kitId}' from ${sourceText} at ${target}`);
|
|
197
|
+
return 0;
|
|
198
|
+
} finally {
|
|
199
|
+
fs.rmSync(tmpBase, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
96
203
|
function list(argv: string[]): number {
|
|
97
204
|
const args = parseArgs(argv);
|
|
98
205
|
const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
|
|
@@ -153,7 +260,8 @@ function activate(argv: string[]): number {
|
|
|
153
260
|
* inspect <kit-dir> [--json]
|
|
154
261
|
*
|
|
155
262
|
* Derives conformance level (K0/K1/K2) and consumer targets from a kit's
|
|
156
|
-
* observable asset classes.
|
|
263
|
+
* observable asset classes. Delegates core container validation to @kontourai/flow.
|
|
264
|
+
* Exits 1 if the kit fails core container validation.
|
|
157
265
|
* Outputs stable JSON suitable for use by catalog tooling and CI.
|
|
158
266
|
*
|
|
159
267
|
* K-levels (issue #52):
|
|
@@ -166,7 +274,7 @@ function activate(argv: string[]): number {
|
|
|
166
274
|
* flow-agents present at K1+ (Flow Agents extension activated)
|
|
167
275
|
* <namespace> unknown top-level keys list verbatim as third-party consumer targets
|
|
168
276
|
*/
|
|
169
|
-
function inspect(argv: string[]): number {
|
|
277
|
+
async function inspect(argv: string[]): Promise<number> {
|
|
170
278
|
const args = parseArgs(argv);
|
|
171
279
|
const kitDir = path.resolve(args.positionals[0] ?? ".");
|
|
172
280
|
const manifestPath = path.join(kitDir, "kit.json");
|
|
@@ -181,116 +289,23 @@ function inspect(argv: string[]): number {
|
|
|
181
289
|
console.error(`inspect: invalid JSON in ${manifestPath}: ${(err as Error).message}`);
|
|
182
290
|
return 1;
|
|
183
291
|
}
|
|
184
|
-
|
|
292
|
+
// Pass the real kitDir so @kontourai/flow can validate flow file existence for K0.
|
|
293
|
+
const result = await deriveKitTargets(manifest, kitDir);
|
|
185
294
|
console.log(JSON.stringify(result, null, 2));
|
|
186
295
|
return result.conformance.k0 ? 0 : 1;
|
|
187
296
|
}
|
|
188
297
|
|
|
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
|
-
|
|
285
|
-
export function main(argv = process.argv.slice(2)): number {
|
|
298
|
+
export async function main(argv = process.argv.slice(2)): Promise<number> {
|
|
286
299
|
const [command, ...rest] = argv;
|
|
287
|
-
if (command === "install
|
|
288
|
-
|
|
300
|
+
if (command === "install") return await install(rest);
|
|
301
|
+
// Legacy sub-subcommands forwarded for backward compatibility within the kit subcommand.
|
|
302
|
+
if (command === "install-local") return await installLocalSource(path.resolve(rest[0] ?? ""), rest);
|
|
303
|
+
if (command === "install-git") return await installGitSource(rest[0] ?? "", rest);
|
|
289
304
|
if (command === "list") return list(rest);
|
|
290
305
|
if (command === "status") return status(rest);
|
|
291
306
|
if (command === "activate") return activate(rest);
|
|
292
|
-
if (command === "inspect") return inspect(rest);
|
|
293
|
-
console.error("usage: flow-kit <install
|
|
307
|
+
if (command === "inspect") return await inspect(rest);
|
|
308
|
+
console.error("usage: flow-agents kit <install|activate|inspect|list|status> ...");
|
|
294
309
|
return 2;
|
|
295
310
|
}
|
|
296
311
|
|
|
@@ -299,4 +314,4 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
299
314
|
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
300
315
|
const _selfRealPath = (() => { try { return fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return fileURLToPath(import.meta.url); } })();
|
|
301
316
|
const _argv1RealPath = (() => { try { return fs.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
302
|
-
if (_selfRealPath === _argv1RealPath) { process.exitCode =
|
|
317
|
+
if (_selfRealPath === _argv1RealPath) { main().then((code) => { process.exitCode = code; }).catch((err) => { console.error(err); process.exitCode = 1; }); }
|
|
@@ -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(); }
|
|
@@ -2,11 +2,11 @@ import * as path from "node:path";
|
|
|
2
2
|
import { parseArgs } from "../lib/args.js";
|
|
3
3
|
import { validateKitRepository } from "../flow-kit/validate.js";
|
|
4
4
|
|
|
5
|
-
export function main(argv = process.argv.slice(2)): number {
|
|
5
|
+
export async function main(argv = process.argv.slice(2)): Promise<number> {
|
|
6
6
|
const args = parseArgs(argv);
|
|
7
7
|
const kit = args.flags.kit;
|
|
8
8
|
if (typeof kit === "string") {
|
|
9
|
-
const errors = validateKitRepository(path.resolve(kit));
|
|
9
|
+
const errors = await validateKitRepository(path.resolve(kit));
|
|
10
10
|
if (errors.length) {
|
|
11
11
|
console.log("Flow Kit repository validation failed:");
|
|
12
12
|
for (const error of errors) console.log(` - ${error}`);
|
|
@@ -17,7 +17,7 @@ export function main(argv = process.argv.slice(2)): number {
|
|
|
17
17
|
}
|
|
18
18
|
const root = path.resolve(".");
|
|
19
19
|
const builder = path.join(root, "kits", "builder");
|
|
20
|
-
const errors = validateKitRepository(builder);
|
|
20
|
+
const errors = await validateKitRepository(builder);
|
|
21
21
|
if (errors.length) {
|
|
22
22
|
console.log("Source tree validation failed:");
|
|
23
23
|
for (const error of errors) console.log(` - ${error}`);
|
|
@@ -33,4 +33,4 @@ import * as _fsVST from "node:fs";
|
|
|
33
33
|
import { fileURLToPath as _ftpVST } from "node:url";
|
|
34
34
|
const _selfVST = (() => { try { return _fsVST.realpathSync(_ftpVST(import.meta.url)); } catch { return _ftpVST(import.meta.url); } })();
|
|
35
35
|
const _argv1VST = (() => { try { return _fsVST.realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
|
|
36
|
-
if (_selfVST === _argv1VST) { process.exitCode =
|
|
36
|
+
if (_selfVST === _argv1VST) { main().then((code) => { process.exitCode = code; }).catch((err) => { console.error(err); process.exitCode = 1; }); }
|
|
@@ -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(); }
|