@kontourai/flow-agents 0.4.0 → 1.0.1
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/kit-gates-demo.yml +171 -0
- package/CHANGELOG.md +43 -0
- package/CONTEXT.md +1 -1
- package/README.md +13 -2
- package/build/src/cli/flow-kit.js +175 -6
- package/build/src/cli/validate-source-tree.js +19 -2
- package/build/src/flow-kit/validate.js +98 -0
- package/build/src/runtime-adapters.js +1 -1
- package/build/src/tools/validate-source-tree.js +3 -2
- package/context/scripts/hooks/config-protection.js +217 -15
- package/docs/fixture-ownership.md +2 -1
- package/docs/index.md +9 -1
- package/docs/kit-authoring-guide.md +126 -0
- package/docs/knowledge-kit.md +69 -0
- package/docs/vision.md +22 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/kit.json +13 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/flows/build.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/kit.json +20 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/eval-suites/contract-suite/suite.test.js +1 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/flows/synthesize.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/kit.json +27 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/kit.json +19 -0
- package/evals/integration/test_activate_npx_context.sh +134 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +163 -0
- package/evals/integration/test_hook_category_behaviors.sh +51 -0
- package/evals/integration/test_kit_conformance_levels.sh +209 -0
- package/evals/run.sh +2 -0
- package/kits/catalog.json +6 -0
- package/kits/knowledge/adapters/default-store/index.js +2 -2
- package/kits/knowledge/adapters/flow-runner/entity-extractor.js +194 -0
- package/kits/knowledge/adapters/flow-runner/index.js +349 -0
- package/kits/knowledge/adapters/obsidian-store/README.md +141 -0
- package/kits/knowledge/adapters/obsidian-store/demo.js +181 -0
- package/kits/knowledge/adapters/obsidian-store/index.js +868 -0
- package/kits/knowledge/adapters/shared/codec.js +325 -0
- package/kits/knowledge/docs/store-contract.md +72 -0
- package/kits/knowledge/evals/entities/demo-acme.js +125 -0
- package/kits/knowledge/evals/entities/suite.test.js +722 -0
- package/kits/knowledge/kit.json +10 -0
- package/kits/release-evidence/fixtures/claims/README.md +14 -0
- package/kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json +22 -0
- package/kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json +22 -0
- package/kits/release-evidence/flows/release-evidence.flow.json +38 -0
- package/kits/release-evidence/kit.json +13 -0
- package/package.json +1 -1
- package/packaging/conformance/fixtures/config-protection--allow-no-verify-in-string.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-git-no-verify.json +23 -0
- package/scripts/hooks/config-protection.js +217 -15
- package/src/cli/flow-kit.ts +162 -5
- package/src/cli/validate-source-tree.ts +7 -1
- package/src/flow-kit/validate.ts +127 -0
- package/src/runtime-adapters.ts +1 -1
- package/src/tools/validate-source-tree.ts +3 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
name: Kit Gates Demo (Agentless)
|
|
2
|
+
|
|
3
|
+
# Proves that Flow Kit gates can be evaluated over claim files in CI with no
|
|
4
|
+
# agent in the loop. The workflow runs:
|
|
5
|
+
# flow validate-definition — validates the Flow Definition in the kit
|
|
6
|
+
# flow validate-kit — validates kit.json container (requires
|
|
7
|
+
# @kontourai/flow >= 0.1.21; merged in source
|
|
8
|
+
# PR #67 but not yet in the published package
|
|
9
|
+
# as of 0.1.20 — step is continue-on-error)
|
|
10
|
+
# flow start — creates a Flow run
|
|
11
|
+
# flow attach-evidence --trust-artifact — attaches a claim file to the gate
|
|
12
|
+
# flow evaluate --exit-code — evaluates gate; exits 1 when gate does not pass
|
|
13
|
+
#
|
|
14
|
+
# Two jobs exercise one passing and one failing claim fixture:
|
|
15
|
+
# pass-case: trusted claim → evaluate exits 0 → job succeeds
|
|
16
|
+
# fail-case: rejected claim → evaluate exits 1 → workflow asserts the
|
|
17
|
+
# failure is detected (non-zero exit) rather than silently ignored
|
|
18
|
+
#
|
|
19
|
+
# Triggered on: workflow_dispatch or PR touching kits/release-evidence/** or
|
|
20
|
+
# this workflow file.
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
pull_request:
|
|
25
|
+
paths:
|
|
26
|
+
- "kits/release-evidence/**"
|
|
27
|
+
- ".github/workflows/kit-gates-demo.yml"
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
pass-case:
|
|
34
|
+
name: Gate pass — trusted release.evidence claim
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
timeout-minutes: 10
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
41
|
+
|
|
42
|
+
- name: Set up Node.js
|
|
43
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
44
|
+
with:
|
|
45
|
+
node-version: "22"
|
|
46
|
+
|
|
47
|
+
- name: Install Flow CLI
|
|
48
|
+
# Mirrors the FLOW_CLI_ROOT install pattern used in ci.yml.
|
|
49
|
+
run: |
|
|
50
|
+
mkdir -p .flow-cli
|
|
51
|
+
cd .flow-cli
|
|
52
|
+
printf '{"name":"flow-cli-host","private":true}\n' > package.json
|
|
53
|
+
npm install --no-save @kontourai/flow
|
|
54
|
+
env:
|
|
55
|
+
FLOW_CLI_ROOT: ${{ github.workspace }}/.flow-cli/node_modules/@kontourai/flow
|
|
56
|
+
|
|
57
|
+
- name: Validate Flow Definition (available in published CLI)
|
|
58
|
+
run: |
|
|
59
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
60
|
+
validate-definition \
|
|
61
|
+
kits/release-evidence/flows/release-evidence.flow.json \
|
|
62
|
+
--json
|
|
63
|
+
|
|
64
|
+
- name: Validate kit container (requires @kontourai/flow >= next release after 0.1.20)
|
|
65
|
+
# validate-kit is merged in kontourai/flow source (PR #67) but not yet
|
|
66
|
+
# validate-kit ships in @kontourai/flow >= 0.2.0 (published 2026-06-12).
|
|
67
|
+
run: |
|
|
68
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
69
|
+
validate-kit kits/release-evidence
|
|
70
|
+
|
|
71
|
+
- name: Init Flow workspace and start run
|
|
72
|
+
run: |
|
|
73
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js init
|
|
74
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
75
|
+
start kits/release-evidence/flows/release-evidence.flow.json \
|
|
76
|
+
--run-id gate-pass-demo
|
|
77
|
+
|
|
78
|
+
- name: Attach trusted release.evidence claim
|
|
79
|
+
run: |
|
|
80
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
81
|
+
attach-evidence gate-pass-demo \
|
|
82
|
+
--gate release-evidence-gate \
|
|
83
|
+
--file kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json \
|
|
84
|
+
--trust-artifact
|
|
85
|
+
|
|
86
|
+
- name: Evaluate gate — trusted claim must produce pass and exit 0
|
|
87
|
+
run: |
|
|
88
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
89
|
+
evaluate gate-pass-demo --exit-code
|
|
90
|
+
|
|
91
|
+
- name: Show run status
|
|
92
|
+
if: always()
|
|
93
|
+
run: |
|
|
94
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
95
|
+
status gate-pass-demo --format summary
|
|
96
|
+
|
|
97
|
+
fail-case:
|
|
98
|
+
name: Gate fail — rejected claim (failure MUST be detected)
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
timeout-minutes: 10
|
|
101
|
+
|
|
102
|
+
steps:
|
|
103
|
+
- name: Checkout
|
|
104
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
105
|
+
|
|
106
|
+
- name: Set up Node.js
|
|
107
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
108
|
+
with:
|
|
109
|
+
node-version: "22"
|
|
110
|
+
|
|
111
|
+
- name: Install Flow CLI
|
|
112
|
+
run: |
|
|
113
|
+
mkdir -p .flow-cli
|
|
114
|
+
cd .flow-cli
|
|
115
|
+
printf '{"name":"flow-cli-host","private":true}\n' > package.json
|
|
116
|
+
npm install --no-save @kontourai/flow
|
|
117
|
+
env:
|
|
118
|
+
FLOW_CLI_ROOT: ${{ github.workspace }}/.flow-cli/node_modules/@kontourai/flow
|
|
119
|
+
|
|
120
|
+
- name: Validate Flow Definition
|
|
121
|
+
run: |
|
|
122
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
123
|
+
validate-definition \
|
|
124
|
+
kits/release-evidence/flows/release-evidence.flow.json \
|
|
125
|
+
--json
|
|
126
|
+
|
|
127
|
+
- name: Validate kit container (requires @kontourai/flow >= next release after 0.1.20)
|
|
128
|
+
continue-on-error: true
|
|
129
|
+
run: |
|
|
130
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
131
|
+
validate-kit kits/release-evidence
|
|
132
|
+
|
|
133
|
+
- name: Init Flow workspace and start run
|
|
134
|
+
run: |
|
|
135
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js init
|
|
136
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
137
|
+
start kits/release-evidence/flows/release-evidence.flow.json \
|
|
138
|
+
--run-id gate-fail-demo
|
|
139
|
+
|
|
140
|
+
- name: Attach rejected release.evidence claim
|
|
141
|
+
run: |
|
|
142
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
143
|
+
attach-evidence gate-fail-demo \
|
|
144
|
+
--gate release-evidence-gate \
|
|
145
|
+
--file kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json \
|
|
146
|
+
--trust-artifact
|
|
147
|
+
|
|
148
|
+
- name: Evaluate gate — rejected claim must produce non-pass and exit 1
|
|
149
|
+
# --exit-code makes the CLI exit 1 when the gate does not pass.
|
|
150
|
+
# continue-on-error: true captures the outcome without failing the job here.
|
|
151
|
+
id: evaluate_fail
|
|
152
|
+
continue-on-error: true
|
|
153
|
+
run: |
|
|
154
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
155
|
+
evaluate gate-fail-demo --exit-code
|
|
156
|
+
|
|
157
|
+
- name: Assert failure was detected (gate must NOT pass a rejected claim)
|
|
158
|
+
# If evaluate exited 0 (outcome == success), the gate incorrectly
|
|
159
|
+
# accepted a rejected claim — false negative in gate evaluation.
|
|
160
|
+
run: |
|
|
161
|
+
if [ "${{ steps.evaluate_fail.outcome }}" = "success" ]; then
|
|
162
|
+
echo "ERROR: gate passed a rejected claim — false negative in gate evaluation"
|
|
163
|
+
exit 1
|
|
164
|
+
fi
|
|
165
|
+
echo "PASS: gate correctly did not pass the rejected claim (outcome=${{ steps.evaluate_fail.outcome }})"
|
|
166
|
+
|
|
167
|
+
- name: Show run status
|
|
168
|
+
if: always()
|
|
169
|
+
run: |
|
|
170
|
+
node .flow-cli/node_modules/@kontourai/flow/dist/cli.js \
|
|
171
|
+
status gate-fail-demo --format summary
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.1](https://github.com/kontourai/flow-agents/compare/v1.0.0...v1.0.1) (2026-06-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Fixes
|
|
7
|
+
|
|
8
|
+
* resolve three kit-distribution blockers ([#55](https://github.com/kontourai/flow-agents/issues/55) [#56](https://github.com/kontourai/flow-agents/issues/56) [#57](https://github.com/kontourai/flow-agents/issues/57)) ([3350cb1](https://github.com/kontourai/flow-agents/commit/3350cb15f44bff92d8d9c57f447761d0e1a1b20c))
|
|
9
|
+
* resolve three kit-distribution blockers ([#55](https://github.com/kontourai/flow-agents/issues/55), [#56](https://github.com/kontourai/flow-agents/issues/56), [#57](https://github.com/kontourai/flow-agents/issues/57)) ([13bf732](https://github.com/kontourai/flow-agents/commit/13bf732ff365efa84423e9ea46042e501d202db8))
|
|
10
|
+
|
|
11
|
+
## [1.0.0](https://github.com/kontourai/flow-agents/compare/v0.4.0...v1.0.0) (2026-06-12)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* agentless Flow Kit gate evaluation proof (issue [#52](https://github.com/kontourai/flow-agents/issues/52) item 3) ([f7857ec](https://github.com/kontourai/flow-agents/commit/f7857ec8ba69d614b0c3c70548d724f7a97c164a))
|
|
17
|
+
* agentless Flow Kit gate evaluation proof (issue [#52](https://github.com/kontourai/flow-agents/issues/52) item 3) ([86c881f](https://github.com/kontourai/flow-agents/commit/86c881f579a08ab75787cd32e401f83b77952c39))
|
|
18
|
+
* config-protection blocks git hook-skip flags ([#41](https://github.com/kontourai/flow-agents/issues/41)) ([6d9e981](https://github.com/kontourai/flow-agents/commit/6d9e9810b3d4e60fe172ade340e61dbe4053d0c9))
|
|
19
|
+
* K-level conformance, degradation invariant, and consumer-target derivation ([#52](https://github.com/kontourai/flow-agents/issues/52) items 1+2) ([d5c332a](https://github.com/kontourai/flow-agents/commit/d5c332a3f4400eb29e9f8fd4e845ec34cf30ae0b))
|
|
20
|
+
* K-level conformance, degradation invariant, and consumer-target derivation (issue [#52](https://github.com/kontourai/flow-agents/issues/52) items 1+2) ([6ac62eb](https://github.com/kontourai/flow-agents/commit/6ac62eb4cec195baca3d039b398f29e45e5d62de))
|
|
21
|
+
* **knowledge-kit:** obsidian layout — insights at node root, sources nested; dimension frontmatter ([5d6489b](https://github.com/kontourai/flow-agents/commit/5d6489b6dc30eacc3a4d0c51487c1a7d3a004f00))
|
|
22
|
+
* **knowledge-kit:** Obsidian store adapter — file-is-the-record spike ([#43](https://github.com/kontourai/flow-agents/issues/43)) ([83d9ff4](https://github.com/kontourai/flow-agents/commit/83d9ff43c2e1d59ac8d3235ae7250fc43be47725))
|
|
23
|
+
* **knowledge-kit:** Obsidian store adapter spike — file-is-the-record RATIFIED ([#43](https://github.com/kontourai/flow-agents/issues/43)) ([467c8dc](https://github.com/kontourai/flow-agents/commit/467c8dc60180a5f6bf15b30a4f0e29e486803fb8))
|
|
24
|
+
* **knowledge-kit:** person entity cards with backlinks + gated resolution ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([9456cef](https://github.com/kontourai/flow-agents/commit/9456cef3b55c639bd50a9aeaea675bef425ea0be))
|
|
25
|
+
* **knowledge-kit:** person entity cards with backlinks + gated resolution ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([ac5ccb0](https://github.com/kontourai/flow-agents/commit/ac5ccb08e40bb6adadd35ff165a617be29e8d23a))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Fixes
|
|
29
|
+
|
|
30
|
+
* **entity-extractor:** parse trailing-period last-entry correctly ([76abd87](https://github.com/kontourai/flow-agents/commit/76abd87ce2e6bfa3650c3d20de7309d498a446f8))
|
|
31
|
+
* **entity-extractor:** parse trailing-period last-entry correctly ([#48](https://github.com/kontourai/flow-agents/issues/48)) ([ac64fc1](https://github.com/kontourai/flow-agents/commit/ac64fc1cb3151af9032948ac463337da3eeaf907))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Documentation
|
|
35
|
+
|
|
36
|
+
* elevate Flow Kits as the authorable-ecosystem pillar ([#45](https://github.com/kontourai/flow-agents/issues/45)) ([fa81820](https://github.com/kontourai/flow-agents/commit/fa8182089c4e3c404e3020c6d516be93353a897b))
|
|
37
|
+
* elevate Flow Kits as the authorable-ecosystem pillar ([#45](https://github.com/kontourai/flow-agents/issues/45)) ([7ffa44e](https://github.com/kontourai/flow-agents/commit/7ffa44ea8be799e709b41d4ac4220948e3819fb8))
|
|
38
|
+
* kit container/extension layering — container contract is Flow-owned ([#50](https://github.com/kontourai/flow-agents/issues/50)) ([33d6ec0](https://github.com/kontourai/flow-agents/commit/33d6ec0bf0fefd9095c19dc76b995ee7dd8079fb))
|
|
39
|
+
* kit container/extension layering — container contract is Flow-owned ([#50](https://github.com/kontourai/flow-agents/issues/50)) ([fd87366](https://github.com/kontourai/flow-agents/commit/fd873663d64e205e1a8c2b898b913a56d410591f))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Maintenance
|
|
43
|
+
|
|
44
|
+
* cut v1.0.0 ([5f88ac5](https://github.com/kontourai/flow-agents/commit/5f88ac51598fe3f13b16360572ff851b822013a2))
|
|
45
|
+
|
|
3
46
|
## [0.4.0](https://github.com/kontourai/flow-agents/compare/v0.3.0...v0.4.0) (2026-06-12)
|
|
4
47
|
|
|
5
48
|
|
package/CONTEXT.md
CHANGED
|
@@ -75,7 +75,7 @@ A reusable procedure Flow Agents can invoke to carry out part of a work mode. Sk
|
|
|
75
75
|
|
|
76
76
|
### Flow Kit
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Flow's distribution unit for portable workflow bundles. A Flow Kit has two layers: (1) the **container** (owned by Kontour Flow) — a `kit.json` manifest with `schema_version`, `id`, `name`, and a non-empty `flows` list, plus optional `description` and `product_name`; and (2) the **agent extension** (owned by Flow Agents) — optional `skills`, `docs`, `adapters`, `evals`, and `assets` fields that make it a Flow Agents Kit. The container contract permits unknown top-level fields so consumers can extend it without breaking core validation. A kit with only core container fields is a valid Flow Kit; adding Flow Agents extension fields makes it a Flow Agents Kit.
|
|
79
79
|
_Avoid_: Pack, plugin, marketplace package
|
|
80
80
|
|
|
81
81
|
### Flow Kit Repository
|
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ L2 means all four policy classes with blocking; L1 means steering and stop-goal-
|
|
|
71
71
|
|
|
72
72
|
## Install
|
|
73
73
|
|
|
74
|
+
Requires Node.js 22 or newer.
|
|
75
|
+
|
|
74
76
|
```bash
|
|
75
77
|
# guided install into your workspace (auto-detects runtime)
|
|
76
78
|
npx @kontourai/flow-agents init --dest /path/to/workspace
|
|
@@ -111,9 +113,15 @@ The [Workflow Usage Guide](docs/workflow-usage-guide.md) has example prompts and
|
|
|
111
113
|
|
|
112
114
|
## Flow Kits
|
|
113
115
|
|
|
114
|
-
A Flow Kit
|
|
116
|
+
A Flow Kit bundles a workflow AND its opinionated output shape into a single validated unit: a `kit.json` manifest (schema version 1.0), one or more Flow Definitions, and optional skills, docs, adapters, evals, and assets. Authoring a kit means deciding not just _what_ an agent does but _how the result is rendered_ — the same pipeline produces different representations depending on which store adapter is active. Kits are the extension model for Flow Agents: validated by the `flow-kit` CLI, installed through a single command, and activatable into any workspace that runs Flow Agents.
|
|
117
|
+
|
|
118
|
+
**Builder Kit** — ships with `builder.shape` (shape a problem into slices and fileable work items) and `builder.build` (pull ready work through design probing, planning, execution, verification, PR readiness, merge readiness, and learning). Installed automatically by `npx @kontourai/flow-agents init`.
|
|
119
|
+
|
|
120
|
+
**Knowledge Kit** — a Flow Kit for durable, gated knowledge storage. It ships a store contract with four record types (`raw`, `compiled`, `concept`, `snapshot`), five pipeline flows (`ingest`, `compile`, `synthesize`, `consolidate`, `retire`), and a mutation policy of propose→evidence-gate→apply/reject with supersede-not-delete. All mutations require provenance; nothing is silently overwritten or deleted. Ships with 198 tests.
|
|
115
121
|
|
|
116
|
-
|
|
122
|
+
The output-shape story is the core reason kits matter. The Knowledge Kit store contract is representation-neutral: two adapters ship today. The **default adapter** stores records as flat markdown files with YAML frontmatter and a JSON graph index. The **Obsidian adapter** renders the same workflow into the shape a human already thinks in — one canonical note per record, category→folder hierarchy, configurable frontmatter dimensions (e.g. territory/customer/initiative as filterable fields), living overview notes with sources nested below, and superseded records moved to an `archive/` folder rather than deleted. Same flows, same mutation gates, different rendering layer. (The Obsidian adapter is shipped; layout/dimensions refinements and person/entity card support are in development.)
|
|
123
|
+
|
|
124
|
+
The Knowledge Kit is also LIVE-proven: the default adapter passes the parameterized contract suite; keyless operation is validated via a Strands agent + local ollama acceptance harness; vector similarity clustering uses ollama embeddings (`nomic-embed-text`) with a pluggable detector interface.
|
|
117
125
|
|
|
118
126
|
Install a local kit:
|
|
119
127
|
|
|
@@ -123,6 +131,9 @@ npx @kontourai/flow-agents flow-kit install-local path/to/my-kit --dest /path/to
|
|
|
123
131
|
|
|
124
132
|
- [Kit Authoring Guide](docs/kit-authoring-guide.md) — build your own kit from scratch: directory layout, `kit.json`, a flow file, validation, install, and activation.
|
|
125
133
|
- [Flow Kit Repository Contract](docs/flow-kit-repository-contract.md) — the full validation rules, registry schema, and activation diagnostics.
|
|
134
|
+
- [Knowledge Kit docs](kits/knowledge/docs/README.md) — store contract, record types, mutation ops, similarity detectors, and the Obsidian adapter.
|
|
135
|
+
|
|
136
|
+
**Direction** (not shipped): domain kits that compose this substrate — a Sales Kit (territory/customer/initiative schema with side-effect adapters for CRM logging), a Research Kit (transcript capture→compile→recall). Distribution follows sequencing: authoring tooling and covetable reference kits first, then a registry, then a marketplace. No marketplace claims are shipped.
|
|
126
137
|
|
|
127
138
|
## Framework adapters
|
|
128
139
|
|
|
@@ -1,9 +1,12 @@
|
|
|
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
|
-
import { assertKitRepository } from "../flow-kit/validate.js";
|
|
9
|
+
import { assertKitRepository, deriveKitTargets } from "../flow-kit/validate.js";
|
|
7
10
|
import { activateCodexLocal, activateStrandsLocal } from "../runtime-adapters.js";
|
|
8
11
|
const REGISTRY_REL = path.join("kits", "local", "installed-kits.json");
|
|
9
12
|
const REPOSITORIES_REL = path.join("kits", "local", "repositories");
|
|
@@ -27,6 +30,22 @@ function contentHash(root) {
|
|
|
27
30
|
}
|
|
28
31
|
return `sha256:${hash.digest("hex")}`;
|
|
29
32
|
}
|
|
33
|
+
/** Content hash that excludes .git and other VCS/cache directories (for install-git clones). */
|
|
34
|
+
function kitContentHash(root) {
|
|
35
|
+
const EXCLUDE_DIRS = new Set([".git", "__pycache__", ".pytest_cache"]);
|
|
36
|
+
const hash = crypto.createHash("sha256");
|
|
37
|
+
for (const file of walkFiles(root)) {
|
|
38
|
+
const parts = path.relative(root, file).split(path.sep);
|
|
39
|
+
if (parts.some((p) => EXCLUDE_DIRS.has(p)))
|
|
40
|
+
continue;
|
|
41
|
+
const rel = parts.join("/");
|
|
42
|
+
hash.update(rel);
|
|
43
|
+
hash.update("\0");
|
|
44
|
+
hash.update(fs.readFileSync(file));
|
|
45
|
+
hash.update("\0");
|
|
46
|
+
}
|
|
47
|
+
return `sha256:${hash.digest("hex")}`;
|
|
48
|
+
}
|
|
30
49
|
function installLocal(argv) {
|
|
31
50
|
const args = parseArgs(argv);
|
|
32
51
|
const source = path.resolve(args.positionals[0] ?? "");
|
|
@@ -36,13 +55,11 @@ function installLocal(argv) {
|
|
|
36
55
|
manifest = assertKitRepository(source);
|
|
37
56
|
}
|
|
38
57
|
catch (error) {
|
|
39
|
-
console.log("warning: Flow validation surface unavailable; local kit check uses the minimal Flow Definition fallback");
|
|
40
58
|
console.log("Flow Kit repository validation failed:");
|
|
41
59
|
for (const diagnostic of (error.diagnostics ?? [error.message]))
|
|
42
60
|
console.log(` - ${diagnostic}`);
|
|
43
61
|
return 1;
|
|
44
62
|
}
|
|
45
|
-
console.log("warning: Flow validation surface unavailable; local kit check uses the minimal Flow Definition fallback");
|
|
46
63
|
const kitId = String(manifest.id);
|
|
47
64
|
const hash = contentHash(source);
|
|
48
65
|
const registry = loadRegistry(dest);
|
|
@@ -126,18 +143,170 @@ function activate(argv) {
|
|
|
126
143
|
console.log(JSON.stringify(result, null, 2));
|
|
127
144
|
return Array.isArray(result.errors) && result.errors.length ? 1 : 0;
|
|
128
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* inspect <kit-dir> [--json]
|
|
148
|
+
*
|
|
149
|
+
* Derives conformance level (K0/K1/K2) and consumer targets from a kit's
|
|
150
|
+
* observable asset classes. Exits 1 if the kit fails core container validation.
|
|
151
|
+
* Outputs stable JSON suitable for use by catalog tooling and CI.
|
|
152
|
+
*
|
|
153
|
+
* K-levels (issue #52):
|
|
154
|
+
* K0 valid core Flow Kit container — gates evaluable agentlessly by any Flow consumer.
|
|
155
|
+
* K1 K0 + Flow Agents extension assets present (skills/docs/adapters/evals/assets).
|
|
156
|
+
* K2 K1 + evals present (live evidence layer).
|
|
157
|
+
*
|
|
158
|
+
* Consumer targets derived from observable asset classes:
|
|
159
|
+
* flow always present at K0 (any Flow consumer: gates/definition-of-done)
|
|
160
|
+
* flow-agents present at K1+ (Flow Agents extension activated)
|
|
161
|
+
* <namespace> unknown top-level keys list verbatim as third-party consumer targets
|
|
162
|
+
*/
|
|
163
|
+
function inspect(argv) {
|
|
164
|
+
const args = parseArgs(argv);
|
|
165
|
+
const kitDir = path.resolve(args.positionals[0] ?? ".");
|
|
166
|
+
const manifestPath = path.join(kitDir, "kit.json");
|
|
167
|
+
if (!fs.existsSync(manifestPath)) {
|
|
168
|
+
console.error(`inspect: kit.json not found at ${manifestPath}`);
|
|
169
|
+
return 1;
|
|
170
|
+
}
|
|
171
|
+
let manifest;
|
|
172
|
+
try {
|
|
173
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
console.error(`inspect: invalid JSON in ${manifestPath}: ${err.message}`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
const result = deriveKitTargets(manifest);
|
|
180
|
+
console.log(JSON.stringify(result, null, 2));
|
|
181
|
+
return result.conformance.k0 ? 0 : 1;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* install-git <repo-url>[#ref] [--ref <branch|tag|sha>] [--dest <path>] [--force] [--update]
|
|
185
|
+
*
|
|
186
|
+
* Shallow-clones a remote git repository to a temporary directory, validates the kit
|
|
187
|
+
* container with the same logic used by install-local, then delegates to the existing
|
|
188
|
+
* install path. Supports an optional #ref fragment in the URL or a separate --ref flag.
|
|
189
|
+
*
|
|
190
|
+
* Implements kontourai/flow-agents#56 (git-ref install surface).
|
|
191
|
+
*/
|
|
192
|
+
function installGit(argv) {
|
|
193
|
+
const args = parseArgs(argv);
|
|
194
|
+
const rawUrl = args.positionals[0] ?? "";
|
|
195
|
+
if (!rawUrl) {
|
|
196
|
+
console.error("install-git: missing <repo-url> argument");
|
|
197
|
+
console.error("usage: flow-kit install-git <repo-url>[#ref] [--ref <branch|tag|sha>] [--dest <path>]");
|
|
198
|
+
return 2;
|
|
199
|
+
}
|
|
200
|
+
// Parse ref: #fragment in URL takes precedence over --ref flag.
|
|
201
|
+
let repoUrl = rawUrl;
|
|
202
|
+
let ref = null;
|
|
203
|
+
const hashIdx = rawUrl.indexOf("#");
|
|
204
|
+
if (hashIdx !== -1) {
|
|
205
|
+
repoUrl = rawUrl.slice(0, hashIdx);
|
|
206
|
+
ref = rawUrl.slice(hashIdx + 1) || null;
|
|
207
|
+
}
|
|
208
|
+
if (!ref)
|
|
209
|
+
ref = flagString(args.flags, "ref") ?? null;
|
|
210
|
+
const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
|
|
211
|
+
const force = flagBool(args.flags, "force") ?? false;
|
|
212
|
+
const update = flagBool(args.flags, "update") ?? false;
|
|
213
|
+
// Shallow-clone into a temporary directory.
|
|
214
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "flow-kit-git-"));
|
|
215
|
+
try {
|
|
216
|
+
const cloneArgs = ["clone", "--depth", "1"];
|
|
217
|
+
if (ref)
|
|
218
|
+
cloneArgs.push("--branch", ref);
|
|
219
|
+
cloneArgs.push("--", repoUrl, tmpBase);
|
|
220
|
+
try {
|
|
221
|
+
child_process.execFileSync("git", cloneArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
const msg = err instanceof Error && err.stderr
|
|
225
|
+
? err.stderr.toString().trim()
|
|
226
|
+
: String(err);
|
|
227
|
+
console.error(`install-git: git clone failed: ${msg}`);
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
// Validate the cloned kit using the same logic as install-local.
|
|
231
|
+
let manifest;
|
|
232
|
+
try {
|
|
233
|
+
manifest = assertKitRepository(tmpBase);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.log("Flow Kit repository validation failed:");
|
|
237
|
+
for (const diagnostic of (error.diagnostics ?? [error.message])) {
|
|
238
|
+
console.log(` - ${diagnostic}`);
|
|
239
|
+
}
|
|
240
|
+
return 1;
|
|
241
|
+
}
|
|
242
|
+
// Delegate to the shared install logic (copy + registry update).
|
|
243
|
+
const kitId = String(manifest.id);
|
|
244
|
+
const hash = kitContentHash(tmpBase);
|
|
245
|
+
const registry = loadRegistry(dest);
|
|
246
|
+
const existing = registry.kits.find((entry) => entry.id === kitId);
|
|
247
|
+
const target = installedPath(dest, kitId);
|
|
248
|
+
assertPathContained(dest, target);
|
|
249
|
+
const sourceText = repoUrl + (ref ? `#${ref}` : "");
|
|
250
|
+
if (existing && existing.source !== sourceText && !update) {
|
|
251
|
+
console.log(`conflict: kit '${kitId}' is already installed from ${existing.source}; rerun with --update to replace it`);
|
|
252
|
+
return 2;
|
|
253
|
+
}
|
|
254
|
+
if (existing && existing.source === sourceText && existing.hash === hash && fs.existsSync(target) && !force) {
|
|
255
|
+
console.log(`kit '${kitId}' is already installed from ${sourceText}`);
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
copyDir(tmpBase, target);
|
|
259
|
+
const entry = {
|
|
260
|
+
id: kitId,
|
|
261
|
+
source: sourceText,
|
|
262
|
+
hash,
|
|
263
|
+
installed_at: existing && existing.source === sourceText && !update ? existing.installed_at : isoNow(),
|
|
264
|
+
installed_path: target,
|
|
265
|
+
state: "installed",
|
|
266
|
+
};
|
|
267
|
+
if (typeof manifest.version === "string" && manifest.version)
|
|
268
|
+
entry.version = manifest.version;
|
|
269
|
+
registry.kits = existing ? registry.kits.map((item) => item.id === kitId ? entry : item) : [...registry.kits, entry];
|
|
270
|
+
writeJson(registryPath(dest), registry);
|
|
271
|
+
console.log(`${existing ? "updated" : "installed"} git kit '${kitId}' from ${sourceText} at ${target}`);
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
fs.rmSync(tmpBase, { recursive: true, force: true });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
129
278
|
export function main(argv = process.argv.slice(2)) {
|
|
130
279
|
const [command, ...rest] = argv;
|
|
131
280
|
if (command === "install-local")
|
|
132
281
|
return installLocal(rest);
|
|
282
|
+
if (command === "install-git")
|
|
283
|
+
return installGit(rest);
|
|
133
284
|
if (command === "list")
|
|
134
285
|
return list(rest);
|
|
135
286
|
if (command === "status")
|
|
136
287
|
return status(rest);
|
|
137
288
|
if (command === "activate")
|
|
138
289
|
return activate(rest);
|
|
139
|
-
|
|
290
|
+
if (command === "inspect")
|
|
291
|
+
return inspect(rest);
|
|
292
|
+
console.error("usage: flow-kit <install-local|install-git|list|status|activate|inspect> ...");
|
|
140
293
|
return 2;
|
|
141
294
|
}
|
|
142
|
-
|
|
143
|
-
|
|
295
|
+
// Use process.exitCode (not process.exit) to allow stdout to be flushed before exit.
|
|
296
|
+
// Resolve real paths to handle symlinks (e.g. /tmp -> /private/tmp on macOS) so the
|
|
297
|
+
// entry-point guard fires correctly when the module is loaded directly as a script.
|
|
298
|
+
const _selfRealPath = (() => { try {
|
|
299
|
+
return fs.realpathSync(fileURLToPath(import.meta.url));
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return fileURLToPath(import.meta.url);
|
|
303
|
+
} })();
|
|
304
|
+
const _argv1RealPath = (() => { try {
|
|
305
|
+
return fs.realpathSync(process.argv[1]);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return process.argv[1];
|
|
309
|
+
} })();
|
|
310
|
+
if (_selfRealPath === _argv1RealPath) {
|
|
311
|
+
process.exitCode = main();
|
|
312
|
+
}
|
|
@@ -27,5 +27,22 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
27
27
|
console.log("Source tree validation passed");
|
|
28
28
|
return 0;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
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 {
|
|
35
|
+
return _fsVST.realpathSync(_ftpVST(import.meta.url));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return _ftpVST(import.meta.url);
|
|
39
|
+
} })();
|
|
40
|
+
const _argv1VST = (() => { try {
|
|
41
|
+
return _fsVST.realpathSync(process.argv[1]);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return process.argv[1];
|
|
45
|
+
} })();
|
|
46
|
+
if (_selfVST === _argv1VST) {
|
|
47
|
+
process.exitCode = main();
|
|
48
|
+
}
|
|
@@ -2,6 +2,91 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { readJson } from "../lib/fs.js";
|
|
4
4
|
const ASSET_CLASSES = ["flows", "skills", "docs", "adapters", "evals", "assets"];
|
|
5
|
+
// Core container fields owned by kontourai/flow (flow-kit-container.schema.json).
|
|
6
|
+
// agent-extension fields are skills, docs, adapters, evals, assets.
|
|
7
|
+
const CORE_CONTAINER_FIELDS = new Set(["schema_version", "id", "name", "description", "product_name", "flows"]);
|
|
8
|
+
const AGENT_EXTENSION_CLASSES = new Set(["skills", "docs", "adapters", "evals", "assets"]);
|
|
9
|
+
/**
|
|
10
|
+
* Validates that the manifest satisfies the core Flow Kit container contract
|
|
11
|
+
* (as specified by kontourai/flow PR #67) with all agent-extension fields stripped.
|
|
12
|
+
* Returns a list of violation messages (empty = valid).
|
|
13
|
+
*
|
|
14
|
+
* The degradation invariant: every Flow Agents Kit MUST remain a valid core
|
|
15
|
+
* Flow Kit container when agent-extension fields are ignored.
|
|
16
|
+
*/
|
|
17
|
+
export function validateCoreContainer(manifest, label) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
if (manifest.schema_version !== "1.0") {
|
|
20
|
+
errors.push(`${label}: .schema_version must be "1.0"`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof manifest.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(manifest.id)) {
|
|
23
|
+
errors.push(`${label}: .id must be a stable kebab-case string`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof manifest.name !== "string" || !manifest.name.trim()) {
|
|
26
|
+
errors.push(`${label}: .name must be a non-empty string`);
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(manifest.flows) || manifest.flows.length === 0) {
|
|
29
|
+
errors.push(`${label}: .flows must be a non-empty list`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
manifest.flows.forEach((entry, index) => {
|
|
33
|
+
if (typeof entry !== "object" || entry === null) {
|
|
34
|
+
errors.push(`${label}: flows[${index}] must be an object`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const flow = entry;
|
|
38
|
+
if (typeof flow.id !== "string" || !flow.id) {
|
|
39
|
+
errors.push(`${label}: flows[${index}].id must be a string`);
|
|
40
|
+
}
|
|
41
|
+
if (typeof flow.path !== "string" || !flow.path) {
|
|
42
|
+
errors.push(`${label}: flows[${index}].path must be a string`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return errors;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Derives the consumer-target level (K0/K1/K2) and target audience list from
|
|
50
|
+
* observable asset classes in the kit manifest. Does not require file I/O.
|
|
51
|
+
*
|
|
52
|
+
* Derivation rules (from kontourai/flow-agents#52 and Brian's layering review):
|
|
53
|
+
* - K0: valid core container (schema_version, id, name, flows non-empty).
|
|
54
|
+
* - K1: K0 + any Flow Agents extension field present (skills/docs/adapters/evals/assets).
|
|
55
|
+
* - K2: K1 + evals present.
|
|
56
|
+
* - targets.flow: always present when K0 (any Flow consumer can evaluate gates).
|
|
57
|
+
* - targets.flow-agents: present when K1 (agent extension assets activate in >=1 harness).
|
|
58
|
+
* - third-party: any top-level keys that are not core fields and not Flow Agents extension classes.
|
|
59
|
+
*/
|
|
60
|
+
export function deriveKitTargets(manifest) {
|
|
61
|
+
const kitId = typeof manifest.id === "string" ? manifest.id : "<unknown>";
|
|
62
|
+
const kitName = typeof manifest.name === "string" ? manifest.name : "<unknown>";
|
|
63
|
+
const coreErrors = validateCoreContainer(manifest, "kit.json");
|
|
64
|
+
const k0 = coreErrors.length === 0;
|
|
65
|
+
const hasAgentExtension = AGENT_EXTENSION_CLASSES.size > 0 &&
|
|
66
|
+
[...AGENT_EXTENSION_CLASSES].some((cls) => Array.isArray(manifest[cls]) && manifest[cls].length > 0);
|
|
67
|
+
const hasEvals = Array.isArray(manifest["evals"]) && manifest["evals"].length > 0;
|
|
68
|
+
const k1 = k0 && hasAgentExtension;
|
|
69
|
+
const k2 = k1 && hasEvals;
|
|
70
|
+
// Detect third-party extension namespaces: top-level keys that are neither
|
|
71
|
+
// core fields nor Flow Agents extension classes.
|
|
72
|
+
const thirdPartyExtensions = Object.keys(manifest)
|
|
73
|
+
.filter((key) => !CORE_CONTAINER_FIELDS.has(key) && !AGENT_EXTENSION_CLASSES.has(key))
|
|
74
|
+
.sort();
|
|
75
|
+
const targets = [];
|
|
76
|
+
if (k0)
|
|
77
|
+
targets.push("flow");
|
|
78
|
+
if (k1)
|
|
79
|
+
targets.push("flow-agents");
|
|
80
|
+
for (const ns of thirdPartyExtensions)
|
|
81
|
+
targets.push(ns);
|
|
82
|
+
return {
|
|
83
|
+
kit_id: kitId,
|
|
84
|
+
kit_name: kitName,
|
|
85
|
+
conformance: { k0, k1, k2 },
|
|
86
|
+
targets,
|
|
87
|
+
third_party_extensions: thirdPartyExtensions,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
5
90
|
export function validateKitRepository(kitDir) {
|
|
6
91
|
const errors = [];
|
|
7
92
|
const manifestPath = path.join(kitDir, "kit.json");
|
|
@@ -20,6 +105,19 @@ export function validateKitRepository(kitDir) {
|
|
|
20
105
|
}
|
|
21
106
|
if (typeof manifest.name !== "string" || !manifest.name.trim())
|
|
22
107
|
errors.push(`${manifestPath}: .name must be a non-empty string`);
|
|
108
|
+
// Degradation invariant: every Flow Agents Kit must remain a valid core Flow Kit container
|
|
109
|
+
// when agent-extension fields are stripped. Strip extensions and re-validate core contract.
|
|
110
|
+
const coreManifest = {};
|
|
111
|
+
for (const key of Object.keys(manifest)) {
|
|
112
|
+
if (CORE_CONTAINER_FIELDS.has(key))
|
|
113
|
+
coreManifest[key] = manifest[key];
|
|
114
|
+
}
|
|
115
|
+
const coreErrors = validateCoreContainer(coreManifest, manifestPath);
|
|
116
|
+
for (const err of coreErrors) {
|
|
117
|
+
// Deduplicate: only add if not already covered by top-level checks above.
|
|
118
|
+
if (!errors.some((existing) => existing === err))
|
|
119
|
+
errors.push(err);
|
|
120
|
+
}
|
|
23
121
|
for (const section of ASSET_CLASSES) {
|
|
24
122
|
const entries = manifest[section];
|
|
25
123
|
if (entries === undefined)
|
|
@@ -74,7 +74,7 @@ export function readKitInventory(sourceRoot, dest) {
|
|
|
74
74
|
const assets = [];
|
|
75
75
|
const catalogPath = path.join(sourceRoot, "kits", "catalog.json");
|
|
76
76
|
if (!fs.existsSync(catalogPath))
|
|
77
|
-
|
|
77
|
+
warnings.push(`${catalogPath}: built-in Kit Catalog not found; skipping built-in kits (this is normal when running outside a flow-agents checkout)`);
|
|
78
78
|
else {
|
|
79
79
|
const catalog = readJson(catalogPath);
|
|
80
80
|
const kits = catalog.kits;
|