@kontourai/flow-agents 1.0.1 → 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.
Files changed (46) hide show
  1. package/.github/workflows/ci.yml +110 -0
  2. package/CHANGELOG.md +16 -0
  3. package/build/src/cli/console-learning-projection.js +19 -2
  4. package/build/src/cli/effective-backlog-settings.js +18 -2
  5. package/build/src/cli/fixture-retirement-audit.js +19 -2
  6. package/build/src/cli/init.js +19 -2
  7. package/build/src/cli/promote-workflow-artifact.js +19 -2
  8. package/build/src/cli/publish-change-helper.js +19 -2
  9. package/build/src/cli/pull-work-provider.js +19 -2
  10. package/build/src/cli/runtime-adapter.js +20 -2
  11. package/build/src/cli/usage-feedback.js +19 -2
  12. package/build/src/cli/utterance-check.js +19 -2
  13. package/build/src/cli/validate-hook-influence.js +19 -2
  14. package/build/src/cli/veritas-governance.js +19 -2
  15. package/build/src/cli/workflow-artifact-cleanup-audit.js +19 -2
  16. package/build/src/runtime-adapters.js +55 -24
  17. package/build/src/tools/build-universal-bundles.js +19 -2
  18. package/build/src/tools/generate-context-map.js +19 -2
  19. package/build/src/tools/validate-package.js +19 -2
  20. package/build/src/tools/validate-source-tree.js +19 -2
  21. package/context/scripts/telemetry/console-presets.sh +1 -1
  22. package/docs/kit-authoring-guide.md +20 -3
  23. package/evals/ci/run-baseline.sh +55 -8
  24. package/evals/integration/test_runtime_adapter_activation.sh +138 -17
  25. package/evals/run.sh +2 -0
  26. package/evals/static/test_console_presets.sh +49 -0
  27. package/package.json +1 -1
  28. package/scripts/telemetry/console-presets.sh +1 -1
  29. package/src/cli/console-learning-projection.ts +7 -1
  30. package/src/cli/effective-backlog-settings.ts +6 -1
  31. package/src/cli/fixture-retirement-audit.ts +7 -1
  32. package/src/cli/init.ts +7 -1
  33. package/src/cli/promote-workflow-artifact.ts +7 -1
  34. package/src/cli/publish-change-helper.ts +7 -1
  35. package/src/cli/pull-work-provider.ts +7 -1
  36. package/src/cli/runtime-adapter.ts +8 -1
  37. package/src/cli/usage-feedback.ts +7 -1
  38. package/src/cli/utterance-check.ts +7 -1
  39. package/src/cli/validate-hook-influence.ts +7 -1
  40. package/src/cli/veritas-governance.ts +7 -1
  41. package/src/cli/workflow-artifact-cleanup-audit.ts +7 -1
  42. package/src/runtime-adapters.ts +54 -26
  43. package/src/tools/build-universal-bundles.ts +7 -1
  44. package/src/tools/generate-context-map.ts +7 -1
  45. package/src/tools/validate-package.ts +7 -1
  46. package/src/tools/validate-source-tree.ts +7 -1
@@ -196,6 +196,55 @@ jobs:
196
196
  continue-on-error: true
197
197
  run: bash evals/ci/run-baseline.sh --check bundle-install-integration
198
198
 
199
+ - name: Bundle lifecycle integration
200
+ continue-on-error: true
201
+ run: bash evals/ci/run-baseline.sh --check bundle-lifecycle-integration
202
+
203
+ - name: Activate npx context integration
204
+ continue-on-error: true
205
+ run: bash evals/ci/run-baseline.sh --check activate-npx-context-integration
206
+
207
+ - name: Kit conformance levels integration
208
+ continue-on-error: true
209
+ run: bash evals/ci/run-baseline.sh --check kit-conformance-levels-integration
210
+
211
+ - name: Local Flow Kit install integration
212
+ continue-on-error: true
213
+ run: bash evals/ci/run-baseline.sh --check local-flow-kit-install-integration
214
+
215
+ - name: Flow Kit install-git integration
216
+ continue-on-error: true
217
+ run: bash evals/ci/run-baseline.sh --check flow-kit-install-git-integration
218
+
219
+
220
+ - name: Context map integration
221
+ continue-on-error: true
222
+ run: bash evals/ci/run-baseline.sh --check context-map-integration
223
+
224
+ - name: Effective backlog settings integration
225
+ continue-on-error: true
226
+ run: bash evals/ci/run-baseline.sh --check effective-backlog-settings-integration
227
+
228
+ - name: Flow agents statusline integration
229
+ continue-on-error: true
230
+ run: bash evals/ci/run-baseline.sh --check flow-agents-statusline-integration
231
+
232
+ - name: Telemetry contract integration
233
+ continue-on-error: true
234
+ run: bash evals/ci/run-baseline.sh --check telemetry-contract-integration
235
+
236
+ - name: Telemetry doctor integration
237
+ continue-on-error: true
238
+ run: bash evals/ci/run-baseline.sh --check telemetry-doctor-integration
239
+
240
+ - name: Utterance check integration
241
+ continue-on-error: true
242
+ run: bash evals/ci/run-baseline.sh --check utterance-check-integration
243
+
244
+ - name: Pull work provider integration
245
+ continue-on-error: true
246
+ run: bash evals/ci/run-baseline.sh --check pull-work-provider-integration
247
+
199
248
  - name: Finalize CI evidence
200
249
  if: always()
201
250
  run: bash evals/ci/run-baseline.sh --finalize
@@ -208,3 +257,64 @@ jobs:
208
257
  path: evals/results/ci-baseline/runtime-and-kit/
209
258
  if-no-files-found: warn
210
259
  retention-days: 14
260
+
261
+ usage-feedback:
262
+ name: Usage Feedback
263
+ runs-on: ubuntu-latest
264
+ timeout-minutes: 15
265
+ env:
266
+ FLOW_AGENTS_CI_LANE: usage-feedback
267
+ FLOW_AGENTS_CI_RESULTS_DIR: evals/results/ci-baseline/usage-feedback
268
+
269
+ steps:
270
+ - name: Checkout
271
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
272
+
273
+ - name: Set up Node.js
274
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
275
+ with:
276
+ node-version: "22"
277
+
278
+ - name: Install Node dependencies
279
+ run: npm ci
280
+
281
+ - name: Install shell tools
282
+ run: |
283
+ sudo apt-get update
284
+ sudo apt-get install -y jq ripgrep
285
+
286
+ - name: Initialize CI evidence
287
+ run: bash evals/ci/run-baseline.sh --init
288
+
289
+ - name: Usage feedback import integration
290
+ continue-on-error: true
291
+ run: bash evals/ci/run-baseline.sh --check usage-feedback-import-integration
292
+
293
+ - name: Usage feedback outcomes integration
294
+ continue-on-error: true
295
+ run: bash evals/ci/run-baseline.sh --check usage-feedback-outcomes-integration
296
+
297
+ - name: Usage feedback report integration
298
+ continue-on-error: true
299
+ run: bash evals/ci/run-baseline.sh --check usage-feedback-report-integration
300
+
301
+ - name: Usage feedback dashboard integration
302
+ continue-on-error: true
303
+ run: bash evals/ci/run-baseline.sh --check usage-feedback-dashboard-integration
304
+
305
+ - name: Usage feedback global integration
306
+ continue-on-error: true
307
+ run: bash evals/ci/run-baseline.sh --check usage-feedback-global-integration
308
+
309
+ - name: Finalize CI evidence
310
+ if: always()
311
+ run: bash evals/ci/run-baseline.sh --finalize
312
+
313
+ - name: Upload CI evidence artifacts
314
+ if: always()
315
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
316
+ with:
317
+ name: flow-agents-ci-usage-feedback
318
+ path: evals/results/ci-baseline/usage-feedback/
319
+ if-no-files-found: warn
320
+ retention-days: 14
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0](https://github.com/kontourai/flow-agents/compare/v1.0.1...v1.1.0) (2026-06-15)
4
+
5
+
6
+ ### Features
7
+
8
+ * activate skills and docs in kit runtime adapters (fix [#58](https://github.com/kontourai/flow-agents/issues/58)) ([dc726fd](https://github.com/kontourai/flow-agents/commit/dc726fd1d56e79b5bda577985aa87befe3a1eb9d))
9
+ * activate skills and docs in kit runtime adapters (fix [#58](https://github.com/kontourai/flow-agents/issues/58)) ([dc774e0](https://github.com/kontourai/flow-agents/commit/dc774e001b54f7a09f6ab81a6aafcf2ad8552b6d))
10
+
11
+
12
+ ### Fixes
13
+
14
+ * apply realpathSync entry-guard to 7 remaining CLI/tool files ([c1b3272](https://github.com/kontourai/flow-agents/commit/c1b3272c623055662a63c5d11c6dbef5aabe1cf0))
15
+ * apply realpathSync entry-guard to all 16 affected CLI/tool files (closes [#71](https://github.com/kontourai/flow-agents/issues/71)) ([8a676b0](https://github.com/kontourai/flow-agents/commit/8a676b0c9c91464d64755719ff778a13e298beb9))
16
+ * apply realpathSync entry-guard to remaining 10 CLI/tool files ([159bb78](https://github.com/kontourai/flow-agents/commit/159bb785b6b28f99b0a3a5c95e0d4428512f4314))
17
+ * use hosted console io preset ([5b4bb7b](https://github.com/kontourai/flow-agents/commit/5b4bb7bc86d154bcf4d529a62e398a2196b702cc))
18
+
3
19
  ## [1.0.1](https://github.com/kontourai/flow-agents/compare/v1.0.0...v1.0.1) (2026-06-12)
4
20
 
5
21
 
@@ -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";
@@ -119,5 +120,21 @@ export function main(argv = process.argv.slice(2)) {
119
120
  return 1;
120
121
  }
121
122
  }
122
- if (import.meta.url === `file://${process.argv[1]}`)
123
- process.exit(main());
123
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
124
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
125
+ // entry-point guard fires correctly when the module is loaded directly as a script.
126
+ const _selfRealPath = (() => { try {
127
+ return fs.realpathSync(fileURLToPath(import.meta.url));
128
+ }
129
+ catch {
130
+ return fileURLToPath(import.meta.url);
131
+ } })();
132
+ const _argv1RealPath = (() => { try {
133
+ return fs.realpathSync(process.argv[1]);
134
+ }
135
+ catch {
136
+ return process.argv[1];
137
+ } })();
138
+ if (_selfRealPath === _argv1RealPath) {
139
+ process.exitCode = main();
140
+ }
@@ -98,5 +98,21 @@ export function main(argv = process.argv.slice(2)) {
98
98
  return 1;
99
99
  }
100
100
  }
101
- if (import.meta.url === `file://${process.argv[1]}`)
102
- process.exit(main());
101
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
102
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
103
+ // entry-point guard fires correctly when the module is loaded directly as a script.
104
+ const _selfRealPath = (() => { try {
105
+ return fs.realpathSync(fileURLToPath(import.meta.url));
106
+ }
107
+ catch {
108
+ return fileURLToPath(import.meta.url);
109
+ } })();
110
+ const _argv1RealPath = (() => { try {
111
+ return fs.realpathSync(process.argv[1]);
112
+ }
113
+ catch {
114
+ return process.argv[1];
115
+ } })();
116
+ if (_selfRealPath === _argv1RealPath) {
117
+ process.exitCode = main();
118
+ }
@@ -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
  const root = path.resolve(".");
5
6
  const fixtureRoot = "evals/fixtures";
@@ -136,5 +137,21 @@ export function main(argv = process.argv.slice(2)) {
136
137
  return 1;
137
138
  }
138
139
  }
139
- if (import.meta.url === `file://${process.argv[1]}`)
140
- process.exit(main());
140
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
141
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
142
+ // entry-point guard fires correctly when the module is loaded directly as a script.
143
+ const _selfRealPath = (() => { try {
144
+ return fs.realpathSync(fileURLToPath(import.meta.url));
145
+ }
146
+ catch {
147
+ return fileURLToPath(import.meta.url);
148
+ } })();
149
+ const _argv1RealPath = (() => { try {
150
+ return fs.realpathSync(process.argv[1]);
151
+ }
152
+ catch {
153
+ return process.argv[1];
154
+ } })();
155
+ if (_selfRealPath === _argv1RealPath) {
156
+ process.exitCode = main();
157
+ }
@@ -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";
@@ -445,5 +446,21 @@ export async function mainDogfood(argv = process.argv.slice(2)) {
445
446
  return 2;
446
447
  }
447
448
  }
448
- if (import.meta.url === `file://${process.argv[1]}`)
449
- process.exit(await main());
449
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
450
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
451
+ // entry-point guard fires correctly when the module is loaded directly as a script.
452
+ const _selfRealPath = (() => { try {
453
+ return fs.realpathSync(fileURLToPath(import.meta.url));
454
+ }
455
+ catch {
456
+ return fileURLToPath(import.meta.url);
457
+ } })();
458
+ const _argv1RealPath = (() => { try {
459
+ return fs.realpathSync(process.argv[1]);
460
+ }
461
+ catch {
462
+ return process.argv[1];
463
+ } })();
464
+ if (_selfRealPath === _argv1RealPath) {
465
+ process.exitCode = await main();
466
+ }
@@ -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";
@@ -59,5 +60,21 @@ export function main(argv = process.argv.slice(2)) {
59
60
  console.log(`archived_artifact=${archive}`);
60
61
  return 0;
61
62
  }
62
- if (import.meta.url === `file://${process.argv[1]}`)
63
- process.exit(main());
63
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
64
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
65
+ // entry-point guard fires correctly when the module is loaded directly as a script.
66
+ const _selfRealPath = (() => { try {
67
+ return fs.realpathSync(fileURLToPath(import.meta.url));
68
+ }
69
+ catch {
70
+ return fileURLToPath(import.meta.url);
71
+ } })();
72
+ const _argv1RealPath = (() => { try {
73
+ return fs.realpathSync(process.argv[1]);
74
+ }
75
+ catch {
76
+ return process.argv[1];
77
+ } })();
78
+ if (_selfRealPath === _argv1RealPath) {
79
+ process.exitCode = main();
80
+ }
@@ -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";
@@ -150,5 +151,21 @@ export function main(argv = process.argv.slice(2)) {
150
151
  return 1;
151
152
  }
152
153
  }
153
- if (import.meta.url === `file://${process.argv[1]}`)
154
- process.exit(main());
154
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
155
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
156
+ // entry-point guard fires correctly when the module is loaded directly as a script.
157
+ const _selfRealPath = (() => { try {
158
+ return fs.realpathSync(fileURLToPath(import.meta.url));
159
+ }
160
+ catch {
161
+ return fileURLToPath(import.meta.url);
162
+ } })();
163
+ const _argv1RealPath = (() => { try {
164
+ return fs.realpathSync(process.argv[1]);
165
+ }
166
+ catch {
167
+ return process.argv[1];
168
+ } })();
169
+ if (_selfRealPath === _argv1RealPath) {
170
+ process.exitCode = main();
171
+ }
@@ -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
  const FLOW_ARTIFACT_PATTERN = /(?<path>\.flow-agents\/[^\s`'")]+)/g;
4
5
  const GITHUB_ISSUE_PATTERN = /(?:(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+))?#(?<number>\d+)/g;
@@ -465,5 +466,21 @@ export function main(argv = process.argv.slice(2)) {
465
466
  console.log(JSON.stringify({ items }, null, 2));
466
467
  return 0;
467
468
  }
468
- if (import.meta.url === `file://${process.argv[1]}`)
469
- process.exit(main());
469
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
470
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
471
+ // entry-point guard fires correctly when the module is loaded directly as a script.
472
+ const _selfRealPath = (() => { try {
473
+ return fs.realpathSync(fileURLToPath(import.meta.url));
474
+ }
475
+ catch {
476
+ return fileURLToPath(import.meta.url);
477
+ } })();
478
+ const _argv1RealPath = (() => { try {
479
+ return fs.realpathSync(process.argv[1]);
480
+ }
481
+ catch {
482
+ return process.argv[1];
483
+ } })();
484
+ if (_selfRealPath === _argv1RealPath) {
485
+ process.exitCode = main();
486
+ }
@@ -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";
@@ -23,5 +25,21 @@ export function main(argv = process.argv.slice(2)) {
23
25
  console.log(JSON.stringify(result, null, 2));
24
26
  return Array.isArray(result.errors) && result.errors.length ? 1 : 0;
25
27
  }
26
- if (import.meta.url === `file://${process.argv[1]}`)
27
- process.exit(main());
28
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
29
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
30
+ // entry-point guard fires correctly when the module is loaded directly as a script.
31
+ const _selfRealPath = (() => { try {
32
+ return fs.realpathSync(fileURLToPath(import.meta.url));
33
+ }
34
+ catch {
35
+ return fileURLToPath(import.meta.url);
36
+ } })();
37
+ const _argv1RealPath = (() => { try {
38
+ return fs.realpathSync(process.argv[1]);
39
+ }
40
+ catch {
41
+ return process.argv[1];
42
+ } })();
43
+ if (_selfRealPath === _argv1RealPath) {
44
+ process.exitCode = main();
45
+ }
@@ -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";
@@ -439,5 +440,21 @@ export function main(argv = process.argv.slice(2)) {
439
440
  return 1;
440
441
  }
441
442
  }
442
- if (import.meta.url === `file://${process.argv[1]}`)
443
- process.exit(main());
443
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
444
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
445
+ // entry-point guard fires correctly when the module is loaded directly as a script.
446
+ const _selfRealPath = (() => { try {
447
+ return fs.realpathSync(fileURLToPath(import.meta.url));
448
+ }
449
+ catch {
450
+ return fileURLToPath(import.meta.url);
451
+ } })();
452
+ const _argv1RealPath = (() => { try {
453
+ return fs.realpathSync(process.argv[1]);
454
+ }
455
+ catch {
456
+ return process.argv[1];
457
+ } })();
458
+ if (_selfRealPath === _argv1RealPath) {
459
+ process.exitCode = main();
460
+ }
@@ -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
  // ---------------------------------------------------------------------------
@@ -232,5 +233,21 @@ export async function main(argv = process.argv.slice(2)) {
232
233
  }
233
234
  return runCheck(rest);
234
235
  }
235
- if (import.meta.url === `file://${process.argv[1]}`)
236
- process.exit(await main());
236
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
237
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
238
+ // entry-point guard fires correctly when the module is loaded directly as a script.
239
+ const _selfRealPath = (() => { try {
240
+ return fs.realpathSync(fileURLToPath(import.meta.url));
241
+ }
242
+ catch {
243
+ return fileURLToPath(import.meta.url);
244
+ } })();
245
+ const _argv1RealPath = (() => { try {
246
+ return fs.realpathSync(process.argv[1]);
247
+ }
248
+ catch {
249
+ return process.argv[1];
250
+ } })();
251
+ if (_selfRealPath === _argv1RealPath) {
252
+ process.exitCode = await main();
253
+ }
@@ -1,4 +1,5 @@
1
1
  import * as fs from "node:fs";
2
+ import { fileURLToPath } from "node:url";
2
3
  const validTiers = new Set(["adapter", "design-target", "installed-command", "live-acceptance", "documented-runtime-gap"]);
3
4
  const validRuntimes = new Set(["codex", "claude-code", "kiro-cli"]);
4
5
  const validHooks = new Set(["workflow-steering", "stop-goal-fit"]);
@@ -148,5 +149,21 @@ export function main(argv = process.argv.slice(2)) {
148
149
  return 1;
149
150
  }
150
151
  }
151
- if (import.meta.url === `file://${process.argv[1]}`)
152
- process.exit(main());
152
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
153
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
154
+ // entry-point guard fires correctly when the module is loaded directly as a script.
155
+ const _selfRealPath = (() => { try {
156
+ return fs.realpathSync(fileURLToPath(import.meta.url));
157
+ }
158
+ catch {
159
+ return fileURLToPath(import.meta.url);
160
+ } })();
161
+ const _argv1RealPath = (() => { try {
162
+ return fs.realpathSync(process.argv[1]);
163
+ }
164
+ catch {
165
+ return process.argv[1];
166
+ } })();
167
+ if (_selfRealPath === _argv1RealPath) {
168
+ process.exitCode = main();
169
+ }
@@ -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";
@@ -258,5 +259,21 @@ export function main(argv = process.argv.slice(2)) {
258
259
  const options = parseEvidenceOptions(argv);
259
260
  return options ? runEvidence(options) : 2;
260
261
  }
261
- if (import.meta.url === `file://${process.argv[1]}`)
262
- process.exit(main());
262
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
263
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
264
+ // entry-point guard fires correctly when the module is loaded directly as a script.
265
+ const _selfRealPath = (() => { try {
266
+ return fs.realpathSync(fileURLToPath(import.meta.url));
267
+ }
268
+ catch {
269
+ return fileURLToPath(import.meta.url);
270
+ } })();
271
+ const _argv1RealPath = (() => { try {
272
+ return fs.realpathSync(process.argv[1]);
273
+ }
274
+ catch {
275
+ return process.argv[1];
276
+ } })();
277
+ if (_selfRealPath === _argv1RealPath) {
278
+ process.exitCode = main();
279
+ }
@@ -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
  const ACTIVE_STATUSES = new Set([
@@ -268,5 +269,21 @@ export function main(argv = process.argv.slice(2)) {
268
269
  printText(result);
269
270
  return 0;
270
271
  }
271
- if (import.meta.url === `file://${process.argv[1]}`)
272
- process.exit(main());
272
+ // Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
273
+ // Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
274
+ // entry-point guard fires correctly when the module is loaded directly as a script.
275
+ const _selfRealPath = (() => { try {
276
+ return fs.realpathSync(fileURLToPath(import.meta.url));
277
+ }
278
+ catch {
279
+ return fileURLToPath(import.meta.url);
280
+ } })();
281
+ const _argv1RealPath = (() => { try {
282
+ return fs.realpathSync(process.argv[1]);
283
+ }
284
+ catch {
285
+ return process.argv[1];
286
+ } })();
287
+ if (_selfRealPath === _argv1RealPath) {
288
+ process.exitCode = main();
289
+ }
@@ -118,31 +118,48 @@ export function readKitInventory(sourceRoot, dest) {
118
118
  function safeSegment(value) {
119
119
  return value.replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "") || "asset";
120
120
  }
121
+ // Asset classes that are directly activated (copied to the runtime directory) by both adapters.
122
+ // flows: gate definitions read by the adapter's flow-routing layer.
123
+ // skills: agent guidance markdown copied to skills/<kit-id>/ for agent discovery.
124
+ // docs: documentation markdown copied to docs/<kit-id>/ for agent reference.
125
+ const ACTIVATED_ASSET_CLASSES = new Set(["flows", "skills", "docs"]);
121
126
  export function activateCodexLocal(sourceRoot, dest) {
122
127
  const inventory = readKitInventory(sourceRoot, dest);
123
128
  const runtimeDir = path.join(dest, ".flow-agents", "runtime", "codex");
124
129
  const generated = [];
125
130
  const skipped = [];
126
131
  for (const asset of inventory.assets) {
127
- if (asset.asset_class !== "flows") {
128
- 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 diagnostic-only for codex-local" });
129
- continue;
132
+ if (asset.asset_class === "flows") {
133
+ if (!asset.asset_id) {
134
+ 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" });
135
+ continue;
136
+ }
137
+ const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
138
+ fs.mkdirSync(path.dirname(output), { recursive: true });
139
+ fs.copyFileSync(asset.source_path, output);
140
+ 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("/") });
130
141
  }
131
- if (!asset.asset_id) {
132
- 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" });
133
- continue;
142
+ else if (asset.asset_class === "skills" || asset.asset_class === "docs") {
143
+ // Copy skills and docs to runtime/<adapter>/<class>/<kit-id>/<filename> so the
144
+ // agent's guidance index (AGENTS.md) can reference them and they are co-located
145
+ // with flow definitions for the same kit.
146
+ const filename = path.basename(asset.source_path);
147
+ const output = path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
148
+ fs.mkdirSync(path.dirname(output), { recursive: true });
149
+ fs.copyFileSync(asset.source_path, output);
150
+ 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("/") });
151
+ }
152
+ else {
153
+ 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" });
134
154
  }
135
- const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
136
- fs.mkdirSync(path.dirname(output), { recursive: true });
137
- fs.copyFileSync(asset.source_path, output);
138
- 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("/") });
139
155
  }
140
156
  fs.mkdirSync(runtimeDir, { recursive: true });
141
- const manifest = { schema_version: "1.0", adapter: "codex-local", supported_asset_classes: ["flows"], generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
157
+ const supportedClasses = Array.from(ACTIVATED_ASSET_CLASSES);
158
+ 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 };
142
159
  const manifestPath = path.join(runtimeDir, "activation.json");
143
160
  writeJson(manifestPath, manifest);
144
161
  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("/") });
145
- return { selected_adapter: "codex-local", supported_asset_classes: ["flows"], generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
162
+ return { selected_adapter: "codex-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
146
163
  }
147
164
  // Decision Q3 (Issue #32): Option (a) — new adapter id "strands-local" rather than
148
165
  // loading kit flows inside FlowAgentsHooks. Rationale: activation is a workspace-prep
@@ -154,27 +171,41 @@ export function activateStrandsLocal(sourceRoot, dest) {
154
171
  const inventory = readKitInventory(sourceRoot, dest);
155
172
  // Runtime flows land at .flow-agents/runtime/strands/flows/<kit-id>/<asset-id>.flow.json
156
173
  // so the Strands steering context can glob for *.flow.json under this path.
174
+ // Runtime skills land at .flow-agents/runtime/strands/skills/<kit-id>/<filename> and
175
+ // docs at .flow-agents/runtime/strands/docs/<kit-id>/<filename> for system-prompt injection.
157
176
  const runtimeDir = path.join(dest, ".flow-agents", "runtime", "strands");
158
177
  const generated = [];
159
178
  const skipped = [];
160
179
  for (const asset of inventory.assets) {
161
- if (asset.asset_class !== "flows") {
162
- 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 diagnostic-only for strands-local" });
163
- continue;
180
+ if (asset.asset_class === "flows") {
181
+ if (!asset.asset_id) {
182
+ 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" });
183
+ continue;
184
+ }
185
+ const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
186
+ fs.mkdirSync(path.dirname(output), { recursive: true });
187
+ fs.copyFileSync(asset.source_path, output);
188
+ 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("/") });
164
189
  }
165
- if (!asset.asset_id) {
166
- 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" });
167
- continue;
190
+ else if (asset.asset_class === "skills" || asset.asset_class === "docs") {
191
+ // Mirror the codex-local layout: strands/<class>/<kit-id>/<filename>.
192
+ // The Strands system-prompt injection layer can glob for all *.md files under
193
+ // .flow-agents/runtime/strands/skills/ to include agent guidance in the context.
194
+ const filename = path.basename(asset.source_path);
195
+ const output = path.join(runtimeDir, asset.asset_class, safeSegment(asset.kit_id), filename);
196
+ fs.mkdirSync(path.dirname(output), { recursive: true });
197
+ fs.copyFileSync(asset.source_path, output);
198
+ 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("/") });
199
+ }
200
+ else {
201
+ 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" });
168
202
  }
169
- const output = path.join(runtimeDir, "flows", safeSegment(asset.kit_id), `${safeSegment(asset.asset_id)}.flow.json`);
170
- fs.mkdirSync(path.dirname(output), { recursive: true });
171
- fs.copyFileSync(asset.source_path, output);
172
- 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("/") });
173
203
  }
174
204
  fs.mkdirSync(runtimeDir, { recursive: true });
175
- const manifest = { schema_version: "1.0", adapter: "strands-local", supported_asset_classes: ["flows"], generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
205
+ const supportedClasses = Array.from(ACTIVATED_ASSET_CLASSES);
206
+ 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 };
176
207
  const manifestPath = path.join(runtimeDir, "activation.json");
177
208
  writeJson(manifestPath, manifest);
178
209
  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("/") });
179
- return { selected_adapter: "strands-local", supported_asset_classes: ["flows"], generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
210
+ return { selected_adapter: "strands-local", supported_asset_classes: supportedClasses, generated_runtime_files: generated, skipped_assets: skipped, warnings: inventory.warnings, errors: inventory.errors };
180
211
  }