@kal-elsam/kairo-runtime 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -9
- package/global-template/components/catalog.json +26 -0
- package/global-template/components/engram-memory/memory.md +58 -0
- package/global-template/components/graphify-context/context-graph.md +58 -0
- package/package.json +1 -1
- package/scripts/install.sh +77 -29
- package/scripts/installer-smoke-test.sh +41 -6
- package/scripts/lib/install-script-url.mjs +85 -0
- package/src/cli.js +4 -34
- package/src/global/brand/cli.js +41 -0
- package/src/global/brand/index.js +7 -1
- package/src/global/component-builders.js +5 -1
- package/src/global/component-ecosystem-checks.js +147 -0
- package/src/global/components/engram-memory.js +16 -0
- package/src/global/components/graphify-context.js +16 -0
- package/src/global/global-doctor.js +5 -0
- package/src/global/ink/setup-state.js +1 -1
package/README.md
CHANGED
|
@@ -51,14 +51,14 @@ curl -fsSL https://raw.githubusercontent.com/Kal-elSam/harness/main/scripts/inst
|
|
|
51
51
|
The bootstrap installer:
|
|
52
52
|
|
|
53
53
|
- requires Node.js 20.12+ and npm
|
|
54
|
-
-
|
|
55
|
-
-
|
|
54
|
+
- installs `@kal-elsam/kairo-runtime` globally (`kairo` CLI)
|
|
55
|
+
- runs `kairo setup --dry-run` by default (no agent configs, no `~/.harness` writes)
|
|
56
56
|
- never uses `sudo`, never modifies shell profiles, and never installs AI apps
|
|
57
57
|
|
|
58
58
|
Apply the plan when you are ready:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
|
|
61
|
+
kairo setup --yes
|
|
62
62
|
# or
|
|
63
63
|
curl -fsSL https://raw.githubusercontent.com/Kal-elSam/harness/main/scripts/install.sh | sh -s -- --yes
|
|
64
64
|
```
|
|
@@ -66,8 +66,8 @@ curl -fsSL https://raw.githubusercontent.com/Kal-elSam/harness/main/scripts/inst
|
|
|
66
66
|
CI, scripts, and advanced non-interactive configure:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
kairo install --agents cursor,codex --yes
|
|
70
|
+
kairo setup --yes --agents all
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
Passthrough examples:
|
|
@@ -83,11 +83,11 @@ Control plane:
|
|
|
83
83
|
kairo status
|
|
84
84
|
kairo sync
|
|
85
85
|
kairo upgrade --dry-run
|
|
86
|
-
|
|
86
|
+
kairo setup --yes
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
After `install.sh --yes`, verify health with `kairo status`, repair drift with `kairo sync`,
|
|
90
|
-
and
|
|
90
|
+
and preview upgrades with `kairo upgrade --dry-run`.
|
|
91
91
|
|
|
92
92
|
### Version and updates
|
|
93
93
|
|
|
@@ -737,7 +737,7 @@ git fetch --tags origin
|
|
|
737
737
|
git fetch origin main
|
|
738
738
|
npm run release:published -- --version 0.1.1 --tag kairo-runtime-v0.1.1
|
|
739
739
|
npm run smoke:registry -- --version 0.1.1
|
|
740
|
-
npm run smoke:installer -- --version 0.1.1
|
|
740
|
+
npm run smoke:installer -- --version 0.1.1 --tag kairo-runtime-v0.1.1
|
|
741
741
|
npm run smoke:bridge
|
|
742
742
|
```
|
|
743
743
|
|
|
@@ -760,7 +760,13 @@ Without `--tag`, provenance checks fall back to `v${version}` (legacy tags).
|
|
|
760
760
|
|
|
761
761
|
`smoke:registry` installs `@kal-elsam/kairo-runtime` from the npm registry (not the local tarball) into a throwaway workspace with a fake `HARNESS_HOME` and npm cache, then runs the recommended flow via `kairo`: `setup --dry-run`, `setup --yes`, `status`, drift simulation, `sync`, `status --json` (expects `overall=ok`), and `uninstall`. Use `latest` by default, pin with `--version x.y.z`, or override with `--package`.
|
|
762
762
|
|
|
763
|
-
`smoke:installer` validates the public one-liner path: `curl .../install.sh | sh` against GitHub `raw` and the npm registry with isolated `HARNESS_HOME`. Preview must not write `~/.harness`; `--yes --agents all` must reach `kairo status --json` with `overall=ok`, then `kairo uninstall` must remove managed sections. Pin with `--version x.y.z` after publish.
|
|
763
|
+
`smoke:installer` validates the public one-liner path: `curl .../install.sh | sh` against GitHub `raw` and the npm registry with isolated `HARNESS_HOME`. Preview must not write `~/.harness`; `--yes --agents all` must reach `kairo status --json` with `overall=ok`, then `kairo uninstall` must remove managed sections. Pin with `--version x.y.z` after publish. For package-aware Kairo Runtime tags, pass the git tag explicitly:
|
|
764
|
+
|
|
765
|
+
```bash
|
|
766
|
+
npm run smoke:installer -- --version 0.1.1 --tag kairo-runtime-v0.1.1
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
Without `--tag`, the install script resolves from legacy `v${version}` tags.
|
|
764
770
|
|
|
765
771
|
Suggested first Kairo Runtime tag after bootstrap: `kairo-runtime-v0.1.1`.
|
|
766
772
|
|
|
@@ -20,6 +20,32 @@
|
|
|
20
20
|
"claude": "- In repos with workspace harness: repo `AGENTS.md` wins over `CLAUDE.md` pointers.",
|
|
21
21
|
"opencode": "- In repos with workspace harness: also read `.opencode/` SDD assets when present."
|
|
22
22
|
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "engram-memory",
|
|
26
|
+
"label": "Engram Memory",
|
|
27
|
+
"version": "1.0.0",
|
|
28
|
+
"defaultEnabled": false,
|
|
29
|
+
"assetFiles": ["memory.md"],
|
|
30
|
+
"adapterHints": {
|
|
31
|
+
"cursor": "- Engram MCP (`mem_*` tools) is not auto-installed; configure in Cursor MCP settings when ready.",
|
|
32
|
+
"codex": "- Engram MCP is not auto-installed; add MCP server config when ready.",
|
|
33
|
+
"claude": "- Engram MCP is not auto-installed; add MCP server config when ready.",
|
|
34
|
+
"opencode": "- Engram MCP is not auto-installed; add MCP server config when ready."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "graphify-context",
|
|
39
|
+
"label": "Graphify Context",
|
|
40
|
+
"version": "1.0.0",
|
|
41
|
+
"defaultEnabled": false,
|
|
42
|
+
"assetFiles": ["context-graph.md"],
|
|
43
|
+
"adapterHints": {
|
|
44
|
+
"cursor": "- Graphify CLI is not auto-installed; run `graphify update .` after code changes when graphify is in PATH.",
|
|
45
|
+
"codex": "- Graphify CLI is not auto-installed; run `graphify update .` after code changes when graphify is in PATH.",
|
|
46
|
+
"claude": "- Graphify CLI is not auto-installed; run `graphify update .` after code changes when graphify is in PATH.",
|
|
47
|
+
"opencode": "- Graphify CLI is not auto-installed; run `graphify update .` after code changes when graphify is in PATH."
|
|
48
|
+
}
|
|
23
49
|
}
|
|
24
50
|
]
|
|
25
51
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Engram Memory Contract
|
|
2
|
+
|
|
3
|
+
> Managed by `@kal-elsam/kairo-runtime` component `engram-memory`.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Engram is an optional external memory layer. It indexes decisions, bugs, and
|
|
8
|
+
conventions across sessions. It does not replace the repository as source of
|
|
9
|
+
truth.
|
|
10
|
+
|
|
11
|
+
## Authority order
|
|
12
|
+
|
|
13
|
+
```txt
|
|
14
|
+
1. Current user instruction
|
|
15
|
+
2. Repository AGENTS.md (when working inside a repo)
|
|
16
|
+
3. Repository docs/ai/ and docs/specs/
|
|
17
|
+
4. Engram (persistent memory)
|
|
18
|
+
5. Graphify (context graph)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If Engram contradicts AGENTS.md or repo docs, the repo wins. Update or discard
|
|
22
|
+
stale memory instead of following it blindly.
|
|
23
|
+
|
|
24
|
+
## When to search memory
|
|
25
|
+
|
|
26
|
+
Search Engram before:
|
|
27
|
+
|
|
28
|
+
- Starting a non-trivial task or a standard/complex spec.
|
|
29
|
+
- Diagnosing a bug that feels familiar or recurring.
|
|
30
|
+
- Proposing a new architecture or convention.
|
|
31
|
+
- Resuming work after context compaction or a new session.
|
|
32
|
+
- Repeating a loop task (see loop-retrospective guidance when present).
|
|
33
|
+
|
|
34
|
+
Skip memory search for trivial changes (typos, formatting, comments) or tasks
|
|
35
|
+
fully specified in an active spec.
|
|
36
|
+
|
|
37
|
+
## When to save memory
|
|
38
|
+
|
|
39
|
+
Save proactively after:
|
|
40
|
+
|
|
41
|
+
- Architecture or policy decisions (with reasoning, not just the outcome).
|
|
42
|
+
- Bug root causes that may recur.
|
|
43
|
+
- Undocumented conventions discovered in code review.
|
|
44
|
+
- Loop retrospectives and discarded approaches (with why they were rejected).
|
|
45
|
+
|
|
46
|
+
Do not save secrets, credentials, PII, ephemeral debug output, or content
|
|
47
|
+
already documented in AGENTS.md or docs/ai/.
|
|
48
|
+
|
|
49
|
+
## MCP tools
|
|
50
|
+
|
|
51
|
+
Engram is not bundled with Kairo Runtime. Configure Engram MCP tools
|
|
52
|
+
(`mem_search`, `mem_save`, etc.) in your agent when you want persistent memory.
|
|
53
|
+
Kairo only ships the contract and health checks — not the MCP server itself.
|
|
54
|
+
|
|
55
|
+
## Decision traceability
|
|
56
|
+
|
|
57
|
+
Important decisions belong in `docs/ai/decision-log.md` or an ADR. Engram
|
|
58
|
+
indexes; the repo certifies.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Graphify Context Contract
|
|
2
|
+
|
|
3
|
+
> Managed by `@kal-elsam/kairo-runtime` component `graphify-context`.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Graphify is an optional context graph for architecture navigation. It maps
|
|
8
|
+
modules, dependencies, features, and risks. It does not replace reading code or
|
|
9
|
+
repo documentation.
|
|
10
|
+
|
|
11
|
+
## Authority order
|
|
12
|
+
|
|
13
|
+
```txt
|
|
14
|
+
1. Current user instruction
|
|
15
|
+
2. Repository AGENTS.md (when working inside a repo)
|
|
16
|
+
3. Repository docs/ai/ and docs/specs/
|
|
17
|
+
4. Engram (persistent memory)
|
|
18
|
+
5. Graphify (context graph)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If the graph contradicts code or repo docs, investigate the code first. Regenerate
|
|
22
|
+
the graph with `graphify update .` — never edit graph artifacts by hand.
|
|
23
|
+
|
|
24
|
+
## When to consult Graphify
|
|
25
|
+
|
|
26
|
+
Before answering architecture or cross-module questions:
|
|
27
|
+
|
|
28
|
+
1. Read `graphify-out/GRAPH_REPORT.md` for god nodes and community structure.
|
|
29
|
+
2. If `graphify-out/wiki/index.md` exists, navigate it instead of scanning raw files.
|
|
30
|
+
3. For "how does X relate to Y", prefer `graphify query`, `graphify path`, or
|
|
31
|
+
`graphify explain` over broad grep when the graph is fresh.
|
|
32
|
+
|
|
33
|
+
Before modifying code in an unfamiliar area:
|
|
34
|
+
|
|
35
|
+
- Check module boundaries and dependency fan-in in the graph.
|
|
36
|
+
- Note circular dependencies, god modules, and untested high-centrality nodes.
|
|
37
|
+
|
|
38
|
+
## When to update the graph
|
|
39
|
+
|
|
40
|
+
Run `graphify update .` after modifying code files in a session (AST-only, no API
|
|
41
|
+
cost). Compare `GRAPH_REPORT.md` commit hash with `git rev-parse HEAD` to detect
|
|
42
|
+
staleness.
|
|
43
|
+
|
|
44
|
+
## CLI prerequisite
|
|
45
|
+
|
|
46
|
+
Graphify is not bundled with Kairo Runtime. Install the `graphify` CLI separately
|
|
47
|
+
when you want local graph generation. Kairo only ships the contract and health
|
|
48
|
+
checks.
|
|
49
|
+
|
|
50
|
+
## Graph signals to watch
|
|
51
|
+
|
|
52
|
+
| Signal | Risk | Action |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Circular dependencies | Hidden coupling | Report and propose cycle break |
|
|
55
|
+
| High fan-in ("god module") | Risky change surface | Evaluate split or narrower API |
|
|
56
|
+
| Orphan module | Dead code or stale docs | Confirm before deleting |
|
|
57
|
+
| High centrality, no tests | Silent regression | Prioritize coverage before edits |
|
|
58
|
+
| Spec without code | Pending or abandoned work | Verify with human |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Gemini, Copilot, Engram, and Graphify.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Kal-elSam/harness#readme",
|
package/scripts/install.sh
CHANGED
|
@@ -43,8 +43,9 @@ usage() {
|
|
|
43
43
|
"" \
|
|
44
44
|
"What it does:" \
|
|
45
45
|
" 1. Checks for Node.js and npm." \
|
|
46
|
-
" 2.
|
|
47
|
-
" 3.
|
|
46
|
+
" 2. Installs ${PACKAGE} globally (adds kairo to npm global bin)." \
|
|
47
|
+
" 3. Runs kairo setup --dry-run (default) or kairo setup --yes (--yes)." \
|
|
48
|
+
" 4. Prints next steps." \
|
|
48
49
|
"" \
|
|
49
50
|
"Security:" \
|
|
50
51
|
" - Never uses sudo." \
|
|
@@ -62,6 +63,51 @@ append_setup_arg() {
|
|
|
62
63
|
SETUP_EXTRA="${SETUP_EXTRA}${SETUP_EXTRA:+ }$1"
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
resolve_global_spec() {
|
|
67
|
+
if [ "$VERSION" = "latest" ]; then
|
|
68
|
+
printf '%s@latest' "$PACKAGE"
|
|
69
|
+
else
|
|
70
|
+
printf '%s@%s' "$PACKAGE" "$VERSION"
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
resolve_npm_global_bin() {
|
|
75
|
+
npm_prefix="$(npm prefix -g 2>/dev/null || true)"
|
|
76
|
+
[ -n "$npm_prefix" ] || return 1
|
|
77
|
+
printf '%s/bin' "$(printf '%s' "$npm_prefix" | sed 's#/*$##')"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
resolve_kairo_bin() {
|
|
81
|
+
if command -v "$PREFERRED_CLI" >/dev/null 2>&1; then
|
|
82
|
+
command -v "$PREFERRED_CLI"
|
|
83
|
+
return 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
global_bin="$(resolve_npm_global_bin || true)"
|
|
87
|
+
if [ -n "$global_bin" ] && [ -x "${global_bin}/${PREFERRED_CLI}" ]; then
|
|
88
|
+
printf '%s\n' "${global_bin}/${PREFERRED_CLI}"
|
|
89
|
+
return 0
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
return 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
print_path_hint_if_needed() {
|
|
96
|
+
if command -v "$PREFERRED_CLI" >/dev/null 2>&1; then
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
global_bin="$(resolve_npm_global_bin || true)"
|
|
101
|
+
if [ -n "$global_bin" ]; then
|
|
102
|
+
printf '%s\n' \
|
|
103
|
+
"" \
|
|
104
|
+
"PATH note:" \
|
|
105
|
+
" ${PREFERRED_CLI} was installed to ${global_bin}." \
|
|
106
|
+
" Add it to PATH if the command is not found:" \
|
|
107
|
+
" export PATH=\"${global_bin}:\$PATH\""
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
65
111
|
while [ "$#" -gt 0 ]; do
|
|
66
112
|
case "$1" in
|
|
67
113
|
--dry-run)
|
|
@@ -137,14 +183,9 @@ require_cmd npm
|
|
|
137
183
|
|
|
138
184
|
NODE_VERSION="$(node --version 2>/dev/null || true)"
|
|
139
185
|
NPM_VERSION="$(npm --version 2>/dev/null || true)"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
RUN_CMD="npx --yes ${PACKAGE}@${VERSION} setup ${SETUP_MODE}${SETUP_EXTRA:+ ${SETUP_EXTRA}}"
|
|
144
|
-
else
|
|
145
|
-
RUNNER="npm-exec"
|
|
146
|
-
RUN_CMD="npm exec --yes --package=${PACKAGE}@${VERSION} -- ${PREFERRED_CLI} setup ${SETUP_MODE}${SETUP_EXTRA:+ ${SETUP_EXTRA}}"
|
|
147
|
-
fi
|
|
186
|
+
GLOBAL_SPEC="$(resolve_global_spec)"
|
|
187
|
+
GLOBAL_INSTALL_CMD="npm install -g ${GLOBAL_SPEC}"
|
|
188
|
+
SETUP_CMD="${PREFERRED_CLI} setup ${SETUP_MODE}${SETUP_EXTRA:+ ${SETUP_EXTRA}}"
|
|
148
189
|
|
|
149
190
|
printf '%s\n' \
|
|
150
191
|
"Kairo Runtime bootstrap installer" \
|
|
@@ -153,13 +194,13 @@ printf '%s\n' \
|
|
|
153
194
|
"Prerequisites:" \
|
|
154
195
|
" node ${NODE_VERSION:-unknown}" \
|
|
155
196
|
" npm ${NPM_VERSION:-unknown}" \
|
|
156
|
-
" runner ${RUNNER}" \
|
|
157
197
|
"" \
|
|
158
198
|
"Will run:" \
|
|
159
|
-
" ${
|
|
199
|
+
" ${GLOBAL_INSTALL_CMD}" \
|
|
200
|
+
" ${SETUP_CMD}" \
|
|
160
201
|
"" \
|
|
161
202
|
"Effects:" \
|
|
162
|
-
" -
|
|
203
|
+
" - Installs ${GLOBAL_SPEC} globally (kairo CLI in npm global bin)." \
|
|
163
204
|
" - Configures managed sections only (never installs AI apps)." \
|
|
164
205
|
" - Never uses sudo or modifies shell profiles." \
|
|
165
206
|
""
|
|
@@ -181,19 +222,25 @@ if [ "$INSTALLER_DRY_RUN" -eq 1 ]; then
|
|
|
181
222
|
exit 0
|
|
182
223
|
fi
|
|
183
224
|
|
|
225
|
+
printf '%s\n' "Installing global CLI..." ""
|
|
226
|
+
# shellcheck disable=SC2086
|
|
227
|
+
npm install -g ${GLOBAL_SPEC}
|
|
228
|
+
|
|
229
|
+
KAIRO_BIN="$(resolve_kairo_bin || true)"
|
|
230
|
+
if [ -z "$KAIRO_BIN" ]; then
|
|
231
|
+
die "Installed ${GLOBAL_SPEC}, but ${PREFERRED_CLI} is not available.
|
|
232
|
+
Check npm global bin: $(resolve_npm_global_bin || echo unknown)
|
|
233
|
+
Add it to PATH, then re-run: ${SETUP_CMD}"
|
|
234
|
+
fi
|
|
235
|
+
|
|
184
236
|
if [ "$APPLY" -eq 1 ]; then
|
|
185
237
|
printf '%s\n' "Applying setup..." ""
|
|
186
238
|
else
|
|
187
239
|
printf '%s\n' "Running preview..." ""
|
|
188
240
|
fi
|
|
189
241
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
npx --yes "${PACKAGE}@${VERSION}" setup ${SETUP_MODE} ${SETUP_EXTRA}
|
|
193
|
-
else
|
|
194
|
-
# shellcheck disable=SC2086
|
|
195
|
-
npm exec --yes --package="${PACKAGE}@${VERSION}" -- ${PREFERRED_CLI} setup ${SETUP_MODE} ${SETUP_EXTRA}
|
|
196
|
-
fi
|
|
242
|
+
# shellcheck disable=SC2086
|
|
243
|
+
"$KAIRO_BIN" setup ${SETUP_MODE} ${SETUP_EXTRA}
|
|
197
244
|
|
|
198
245
|
if [ "$APPLY" -eq 1 ]; then
|
|
199
246
|
printf '%s\n' \
|
|
@@ -202,24 +249,25 @@ if [ "$APPLY" -eq 1 ]; then
|
|
|
202
249
|
"Next steps:" \
|
|
203
250
|
" 1. Check health: ${PREFERRED_CLI} status" \
|
|
204
251
|
" 2. Repair drift: ${PREFERRED_CLI} sync" \
|
|
205
|
-
" 3. Upgrade latest:
|
|
252
|
+
" 3. Upgrade latest: ${PREFERRED_CLI} upgrade --dry-run" \
|
|
206
253
|
"" \
|
|
207
254
|
"Version:" \
|
|
208
|
-
" Installed CLI:
|
|
209
|
-
" Published package: npm view ${PACKAGE} version"
|
|
210
|
-
|
|
255
|
+
" Installed CLI: ${PREFERRED_CLI} --version" \
|
|
256
|
+
" Published package: npm view ${PACKAGE} version"
|
|
257
|
+
print_path_hint_if_needed
|
|
211
258
|
else
|
|
212
259
|
printf '%s\n' \
|
|
213
260
|
"" \
|
|
214
261
|
"Bootstrap complete." \
|
|
215
262
|
"Next steps:" \
|
|
216
|
-
" 1. Apply the plan:
|
|
263
|
+
" 1. Apply the plan: ${PREFERRED_CLI} setup --yes" \
|
|
217
264
|
" (or re-run installer: curl ... | sh -s -- --yes)" \
|
|
218
|
-
" 2. Check health:
|
|
219
|
-
" 3. Repair drift:
|
|
265
|
+
" 2. Check health: ${PREFERRED_CLI} status" \
|
|
266
|
+
" 3. Repair drift: ${PREFERRED_CLI} sync" \
|
|
220
267
|
"" \
|
|
221
268
|
"Version:" \
|
|
222
|
-
" Installed CLI:
|
|
269
|
+
" Installed CLI: ${PREFERRED_CLI} --version" \
|
|
223
270
|
" Published package: npm view ${PACKAGE} version" \
|
|
224
|
-
" Update / converge:
|
|
271
|
+
" Update / converge: ${PREFERRED_CLI} sync"
|
|
272
|
+
print_path_hint_if_needed
|
|
225
273
|
fi
|
|
@@ -4,7 +4,9 @@ set -euo pipefail
|
|
|
4
4
|
PACKAGE="@kal-elsam/kairo-runtime"
|
|
5
5
|
PREFERRED_CLI="kairo"
|
|
6
6
|
VERSION="latest"
|
|
7
|
+
GIT_TAG=""
|
|
7
8
|
REPO="Kal-elSam/harness"
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
10
|
|
|
9
11
|
while [ "$#" -gt 0 ]; do
|
|
10
12
|
case "$1" in
|
|
@@ -24,6 +26,14 @@ while [ "$#" -gt 0 ]; do
|
|
|
24
26
|
VERSION="${1#*=}"
|
|
25
27
|
shift
|
|
26
28
|
;;
|
|
29
|
+
--tag)
|
|
30
|
+
GIT_TAG="$2"
|
|
31
|
+
shift 2
|
|
32
|
+
;;
|
|
33
|
+
--tag=*)
|
|
34
|
+
GIT_TAG="${1#*=}"
|
|
35
|
+
shift
|
|
36
|
+
;;
|
|
27
37
|
*)
|
|
28
38
|
echo "Unknown option: $1" >&2
|
|
29
39
|
exit 1
|
|
@@ -53,12 +63,13 @@ assert_harness_home_isolated() {
|
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
resolve_install_script_url() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
local args=(--version "$VERSION" --repo "$REPO")
|
|
67
|
+
|
|
68
|
+
if [ -n "$GIT_TAG" ]; then
|
|
69
|
+
args+=(--tag "$GIT_TAG")
|
|
59
70
|
fi
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
node "${SCRIPT_DIR}/lib/install-script-url.mjs" "${args[@]}"
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
assert_no_harness_state() {
|
|
@@ -117,6 +128,9 @@ cd "$WORKDIR"
|
|
|
117
128
|
|
|
118
129
|
INSTALL_SCRIPT_URL="$(resolve_install_script_url)"
|
|
119
130
|
echo "Installer smoke for ${PACKAGE}@${VERSION}"
|
|
131
|
+
if [ -n "$GIT_TAG" ]; then
|
|
132
|
+
echo "Git tag: ${GIT_TAG}"
|
|
133
|
+
fi
|
|
120
134
|
echo "Install script: ${INSTALL_SCRIPT_URL}"
|
|
121
135
|
echo "Harness home: ${HARNESS_HOME}"
|
|
122
136
|
|
|
@@ -140,15 +154,36 @@ if [ ! -d "$FAKE_HOME/.harness" ]; then
|
|
|
140
154
|
exit 1
|
|
141
155
|
fi
|
|
142
156
|
|
|
157
|
+
NPM_GLOBAL_BIN="$(npm prefix -g 2>/dev/null)/bin"
|
|
158
|
+
export PATH="${NPM_GLOBAL_BIN}:${PATH}"
|
|
159
|
+
|
|
160
|
+
resolve_kairo_bin() {
|
|
161
|
+
if command -v "$PREFERRED_CLI" >/dev/null 2>&1; then
|
|
162
|
+
command -v "$PREFERRED_CLI"
|
|
163
|
+
return 0
|
|
164
|
+
fi
|
|
165
|
+
if [ -x "${NPM_GLOBAL_BIN}/${PREFERRED_CLI}" ]; then
|
|
166
|
+
printf '%s\n' "${NPM_GLOBAL_BIN}/${PREFERRED_CLI}"
|
|
167
|
+
return 0
|
|
168
|
+
fi
|
|
169
|
+
return 1
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
KAIRO_BIN="$(resolve_kairo_bin || true)"
|
|
173
|
+
if [ -z "$KAIRO_BIN" ]; then
|
|
174
|
+
echo "Expected ${PREFERRED_CLI} after install.sh global install" >&2
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
177
|
+
|
|
143
178
|
echo
|
|
144
179
|
echo "== ${PREFERRED_CLI} status --json =="
|
|
145
|
-
STATUS_JSON="$(
|
|
180
|
+
STATUS_JSON="$("$KAIRO_BIN" status --json)"
|
|
146
181
|
echo "$STATUS_JSON"
|
|
147
182
|
assert_status_ok "$STATUS_JSON"
|
|
148
183
|
|
|
149
184
|
echo
|
|
150
185
|
echo "== ${PREFERRED_CLI} uninstall =="
|
|
151
|
-
|
|
186
|
+
"$KAIRO_BIN" uninstall
|
|
152
187
|
assert_managed_configs_removed
|
|
153
188
|
|
|
154
189
|
echo
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_REPO = "Kal-elSam/harness";
|
|
5
|
+
|
|
6
|
+
export function resolveInstallScriptRef({ version, tag = null }) {
|
|
7
|
+
if (version === "latest") {
|
|
8
|
+
return "main";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (tag) {
|
|
12
|
+
return tag;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return `v${version}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveInstallScriptUrl({
|
|
19
|
+
repo = DEFAULT_REPO,
|
|
20
|
+
version,
|
|
21
|
+
tag = null
|
|
22
|
+
}) {
|
|
23
|
+
const ref = resolveInstallScriptRef({ version, tag });
|
|
24
|
+
return `https://raw.githubusercontent.com/${repo}/${ref}/scripts/install.sh`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parseInstallScriptUrlArgs(argv) {
|
|
28
|
+
const args = [...argv];
|
|
29
|
+
let version = "latest";
|
|
30
|
+
let tag = null;
|
|
31
|
+
let repo = DEFAULT_REPO;
|
|
32
|
+
|
|
33
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
34
|
+
const arg = args[index];
|
|
35
|
+
|
|
36
|
+
if (arg === "--version") {
|
|
37
|
+
version = args[++index];
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (arg.startsWith("--version=")) {
|
|
42
|
+
version = arg.slice("--version=".length);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (arg === "--tag") {
|
|
47
|
+
tag = args[++index];
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (arg.startsWith("--tag=")) {
|
|
52
|
+
tag = arg.slice("--tag=".length);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (arg === "--repo") {
|
|
57
|
+
repo = args[++index];
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (arg.startsWith("--repo=")) {
|
|
62
|
+
repo = arg.slice("--repo=".length);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
throw new Error(`Unknown option "${arg}".`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { version, tag, repo };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isMainModule() {
|
|
73
|
+
const entry = process.argv[1];
|
|
74
|
+
|
|
75
|
+
if (!entry) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return path.resolve(fileURLToPath(import.meta.url)) === path.resolve(entry);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (isMainModule()) {
|
|
83
|
+
const { version, tag, repo } = parseInstallScriptUrlArgs(process.argv);
|
|
84
|
+
console.log(resolveInstallScriptUrl({ repo, version, tag }));
|
|
85
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -31,51 +31,21 @@ import { applyPolicyToOptions, loadPolicyFile } from "./global/policy.js";
|
|
|
31
31
|
import { resolveHomeDir } from "./global/paths.js";
|
|
32
32
|
import { runWorkspaceDetect, runWorkspaceDoctor, runWorkspaceInit, runWorkspaceUpdate } from "./workspace-cli.js";
|
|
33
33
|
import {
|
|
34
|
-
ALL_CLI_NAMES,
|
|
35
34
|
LEGACY_PACKAGE_NAME,
|
|
36
35
|
PACKAGE_NAME,
|
|
37
36
|
PREFERRED_CLI,
|
|
38
37
|
formatCliCommand,
|
|
39
|
-
maybeWarnLegacyCli
|
|
38
|
+
maybeWarnLegacyCli,
|
|
39
|
+
resolveSuggestedInvocation
|
|
40
40
|
} from "./global/brand/cli.js";
|
|
41
41
|
import { BRAND } from "./global/brand/index.js";
|
|
42
42
|
|
|
43
|
+
export { resolveSuggestedInvocation };
|
|
44
|
+
|
|
43
45
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
44
46
|
const packageRoot = resolve(__dirname, "..");
|
|
45
47
|
const SCOPES = new Set(["agent-global", "workspace"]);
|
|
46
48
|
|
|
47
|
-
export function resolveSuggestedInvocation(packageName, argv = process.argv) {
|
|
48
|
-
const invokedPath = argv[1] ?? PREFERRED_CLI;
|
|
49
|
-
const invokedBase = basename(invokedPath);
|
|
50
|
-
|
|
51
|
-
if (!invokedBase.endsWith(".js") && ALL_CLI_NAMES.has(invokedBase)) {
|
|
52
|
-
return invokedBase;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const packageManager = detectInvocationPackageManager();
|
|
56
|
-
|
|
57
|
-
switch (packageManager) {
|
|
58
|
-
case "pnpm":
|
|
59
|
-
return `pnpm dlx ${packageName}`;
|
|
60
|
-
case "yarn":
|
|
61
|
-
return `yarn dlx ${packageName}`;
|
|
62
|
-
case "bun":
|
|
63
|
-
return `bunx ${packageName}`;
|
|
64
|
-
default:
|
|
65
|
-
return `npx ${packageName}`;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function detectInvocationPackageManager() {
|
|
70
|
-
const execPath = process.env.npm_execpath ?? "";
|
|
71
|
-
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
72
|
-
|
|
73
|
-
if (execPath.includes("pnpm") || userAgent.startsWith("pnpm/")) return "pnpm";
|
|
74
|
-
if (execPath.includes("yarn") || userAgent.startsWith("yarn/")) return "yarn";
|
|
75
|
-
if (execPath.includes("bun") || userAgent.startsWith("bun/")) return "bun";
|
|
76
|
-
return "npm";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
49
|
export async function runCli(argv) {
|
|
80
50
|
const { command, options } = parseArgs(argv);
|
|
81
51
|
maybeWarnLegacyCli(process.argv, { json: options.json });
|
package/src/global/brand/cli.js
CHANGED
|
@@ -50,3 +50,44 @@ export function formatCliCommand(subcommand, cliName = PREFERRED_CLI) {
|
|
|
50
50
|
const trimmed = subcommand.trim();
|
|
51
51
|
return trimmed ? `${cliName} ${trimmed}` : cliName;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
function detectInvocationPackageManager() {
|
|
55
|
+
const execPath = process.env.npm_execpath ?? "";
|
|
56
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
57
|
+
|
|
58
|
+
if (execPath.includes("pnpm") || userAgent.startsWith("pnpm/")) return "pnpm";
|
|
59
|
+
if (execPath.includes("yarn") || userAgent.startsWith("yarn/")) return "yarn";
|
|
60
|
+
if (execPath.includes("bun") || userAgent.startsWith("bun/")) return "bun";
|
|
61
|
+
return "npm";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function resolveSuggestedInvocation(packageName = PACKAGE_NAME, argv = process.argv) {
|
|
65
|
+
const invokedPath = argv[1] ?? PREFERRED_CLI;
|
|
66
|
+
const invokedBase = basename(invokedPath);
|
|
67
|
+
|
|
68
|
+
if (!invokedBase.endsWith(".js") && ALL_CLI_NAMES.has(invokedBase)) {
|
|
69
|
+
return invokedBase;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const packageManager = detectInvocationPackageManager();
|
|
73
|
+
|
|
74
|
+
switch (packageManager) {
|
|
75
|
+
case "pnpm":
|
|
76
|
+
return `pnpm dlx ${packageName}`;
|
|
77
|
+
case "yarn":
|
|
78
|
+
return `yarn dlx ${packageName}`;
|
|
79
|
+
case "bun":
|
|
80
|
+
return `bunx ${packageName}`;
|
|
81
|
+
default:
|
|
82
|
+
return `npx ${packageName}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function formatSuggestedCliCommand(
|
|
87
|
+
subcommand,
|
|
88
|
+
{ packageName = PACKAGE_NAME, argv = process.argv, suggestedInvocation } = {}
|
|
89
|
+
) {
|
|
90
|
+
const invoke = suggestedInvocation ?? resolveSuggestedInvocation(packageName, argv);
|
|
91
|
+
const trimmed = subcommand.trim();
|
|
92
|
+
return trimmed ? `${invoke} ${trimmed}` : invoke;
|
|
93
|
+
}
|
|
@@ -82,4 +82,10 @@ export function commandHeader(label) {
|
|
|
82
82
|
return `${BRAND.displayName} ${label}`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export {
|
|
85
|
+
export {
|
|
86
|
+
formatCliCommand,
|
|
87
|
+
formatSuggestedCliCommand,
|
|
88
|
+
PACKAGE_NAME,
|
|
89
|
+
PREFERRED_CLI,
|
|
90
|
+
resolveSuggestedInvocation
|
|
91
|
+
} from "./cli.js";
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { buildEngramMemoryManagedSection } from "./components/engram-memory.js";
|
|
3
|
+
import { buildGraphifyContextManagedSection } from "./components/graphify-context.js";
|
|
2
4
|
import { buildOrchestratorManagedSection } from "./components/orchestrator.js";
|
|
3
5
|
import { buildSddCoreManagedSection } from "./components/sdd-core.js";
|
|
4
6
|
|
|
5
7
|
export const COMPONENT_BUILDERS = {
|
|
6
8
|
orchestrator: buildOrchestratorManagedSection,
|
|
7
|
-
"sdd-core": buildSddCoreManagedSection
|
|
9
|
+
"sdd-core": buildSddCoreManagedSection,
|
|
10
|
+
"engram-memory": buildEngramMemoryManagedSection,
|
|
11
|
+
"graphify-context": buildGraphifyContextManagedSection
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
export function resolveComponentBuilder(componentId) {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { formatCliCommand } from "./brand/cli.js";
|
|
6
|
+
|
|
7
|
+
const GRAPH_REPORT_COMMIT_PATTERN = /Built from commit:\s*`([0-9a-f]+)`/i;
|
|
8
|
+
|
|
9
|
+
export async function runComponentEcosystemChecks({ installedComponents, workspaceRoot = null } = {}) {
|
|
10
|
+
const installedIds = new Set(installedComponents.map((component) => component.id));
|
|
11
|
+
const checks = [];
|
|
12
|
+
|
|
13
|
+
if (installedIds.has("engram-memory")) {
|
|
14
|
+
checks.push(buildEngramMcpCheck());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (installedIds.has("graphify-context")) {
|
|
18
|
+
checks.push(...await buildGraphifyChecks(workspaceRoot));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return checks;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildEngramMcpCheck() {
|
|
25
|
+
return {
|
|
26
|
+
name: "engram:mcp-tools",
|
|
27
|
+
status: "warning",
|
|
28
|
+
category: "integration",
|
|
29
|
+
componentId: "engram-memory",
|
|
30
|
+
detail: "Engram MCP is not bundled. Configure mem_* tools in your agent (user-engram or plugin-engram-engram) when ready."
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function buildGraphifyChecks(workspaceRoot) {
|
|
35
|
+
const checks = [];
|
|
36
|
+
|
|
37
|
+
if (!workspaceRoot) {
|
|
38
|
+
checks.push({
|
|
39
|
+
name: "graphify:workspace",
|
|
40
|
+
status: "warning",
|
|
41
|
+
category: "integration",
|
|
42
|
+
componentId: "graphify-context",
|
|
43
|
+
detail: "No workspace root; graph freshness checks skipped. Run doctor from a repo cwd."
|
|
44
|
+
});
|
|
45
|
+
return checks;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resolvedRoot = resolve(workspaceRoot);
|
|
49
|
+
const graphifyAvailable = isCommandAvailable("graphify");
|
|
50
|
+
|
|
51
|
+
checks.push({
|
|
52
|
+
name: "graphify:cli",
|
|
53
|
+
status: graphifyAvailable ? "ok" : "warning",
|
|
54
|
+
category: "integration",
|
|
55
|
+
componentId: "graphify-context",
|
|
56
|
+
detail: graphifyAvailable
|
|
57
|
+
? "graphify CLI found in PATH."
|
|
58
|
+
: "graphify CLI not found in PATH. Install separately when you want local graph generation."
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const graphPath = join(resolvedRoot, "graphify-out", "graph.json");
|
|
62
|
+
const reportPath = join(resolvedRoot, "graphify-out", "GRAPH_REPORT.md");
|
|
63
|
+
|
|
64
|
+
if (!existsSync(graphPath)) {
|
|
65
|
+
checks.push({
|
|
66
|
+
name: "graphify:graph.json",
|
|
67
|
+
status: "warning",
|
|
68
|
+
category: "integration",
|
|
69
|
+
componentId: "graphify-context",
|
|
70
|
+
detail: existsSync(join(resolvedRoot, "graphify-out"))
|
|
71
|
+
? "graphify-out/graph.json is absent. Run `graphify update .` when the workspace is ready."
|
|
72
|
+
: "graphify-out/ not found. Run `graphify update .` when the workspace is ready."
|
|
73
|
+
});
|
|
74
|
+
return checks;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const freshness = await readGraphFreshness(resolvedRoot, reportPath);
|
|
78
|
+
checks.push({
|
|
79
|
+
name: "graphify:graph.json",
|
|
80
|
+
status: freshness.status,
|
|
81
|
+
category: "integration",
|
|
82
|
+
componentId: "graphify-context",
|
|
83
|
+
detail: freshness.detail
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return checks;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function readGraphFreshness(workspaceRoot, reportPath) {
|
|
90
|
+
if (!existsSync(reportPath)) {
|
|
91
|
+
return {
|
|
92
|
+
status: "warning",
|
|
93
|
+
detail: "graphify-out/graph.json exists but GRAPH_REPORT.md is missing. Run `graphify update .` to refresh metadata."
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const report = await readFile(reportPath, "utf8");
|
|
98
|
+
const match = report.match(GRAPH_REPORT_COMMIT_PATTERN);
|
|
99
|
+
|
|
100
|
+
if (!match) {
|
|
101
|
+
return {
|
|
102
|
+
status: "ok",
|
|
103
|
+
detail: "graphify-out/graph.json present. Commit metadata unavailable in GRAPH_REPORT.md."
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const graphCommit = match[1];
|
|
108
|
+
const headCommit = resolveGitHead(workspaceRoot);
|
|
109
|
+
|
|
110
|
+
if (!headCommit) {
|
|
111
|
+
return {
|
|
112
|
+
status: "warning",
|
|
113
|
+
detail: `graphify-out/graph.json present (built from ${graphCommit}). Git HEAD unavailable for staleness check.`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const isFresh = headCommit.startsWith(graphCommit) || graphCommit.startsWith(headCommit);
|
|
118
|
+
|
|
119
|
+
if (isFresh) {
|
|
120
|
+
return {
|
|
121
|
+
status: "ok",
|
|
122
|
+
detail: `graphify-out/graph.json in sync with HEAD (${headCommit.slice(0, 8)}).`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
status: "warning",
|
|
128
|
+
detail: `graphify-out/graph.json may be stale (graph ${graphCommit}, HEAD ${headCommit.slice(0, 8)}). Run \`graphify update .\` or ${formatCliCommand("sync")} for managed assets only.`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolveGitHead(workspaceRoot) {
|
|
133
|
+
const result = spawnSync("git", ["rev-parse", "HEAD"], {
|
|
134
|
+
cwd: workspaceRoot,
|
|
135
|
+
encoding: "utf8"
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (result.status !== 0) return null;
|
|
139
|
+
|
|
140
|
+
const head = result.stdout.trim();
|
|
141
|
+
return head.length > 0 ? head : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function isCommandAvailable(command) {
|
|
145
|
+
const result = spawnSync("which", [command], { encoding: "utf8" });
|
|
146
|
+
return result.status === 0 && result.stdout.trim().length > 0;
|
|
147
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
export function buildEngramMemoryManagedSection(context, adapter, catalogEntry) {
|
|
4
|
+
const contractPath = join(context.componentsDir, "engram-memory", "memory.md");
|
|
5
|
+
const adapterNote = catalogEntry?.adapterHints?.[adapter.id] ?? null;
|
|
6
|
+
|
|
7
|
+
return [
|
|
8
|
+
"### Engram Memory",
|
|
9
|
+
"",
|
|
10
|
+
`- Contract: ${contractPath}`,
|
|
11
|
+
"- Optional persistent memory; repo AGENTS.md and docs/ai/ govern when present.",
|
|
12
|
+
"- Search memory before non-trivial tasks; save decisions, bugs, and conventions proactively.",
|
|
13
|
+
"- Authority: user > AGENTS.md > repo docs > Engram > Graphify.",
|
|
14
|
+
adapterNote
|
|
15
|
+
].filter(Boolean).join("\n");
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
export function buildGraphifyContextManagedSection(context, adapter, catalogEntry) {
|
|
4
|
+
const contractPath = join(context.componentsDir, "graphify-context", "context-graph.md");
|
|
5
|
+
const adapterNote = catalogEntry?.adapterHints?.[adapter.id] ?? null;
|
|
6
|
+
|
|
7
|
+
return [
|
|
8
|
+
"### Graphify Context",
|
|
9
|
+
"",
|
|
10
|
+
`- Contract: ${contractPath}`,
|
|
11
|
+
"- Optional architecture graph; read GRAPH_REPORT.md before cross-module questions.",
|
|
12
|
+
"- Run `graphify update .` after code changes when the CLI is available.",
|
|
13
|
+
"- Authority: user > AGENTS.md > repo docs > Engram > Graphify.",
|
|
14
|
+
adapterNote
|
|
15
|
+
].filter(Boolean).join("\n");
|
|
16
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { listBackupSnapshots } from "./backups.js";
|
|
2
2
|
import { buildAdapterContext } from "./adapter-context.js";
|
|
3
|
+
import { runComponentEcosystemChecks } from "./component-ecosystem-checks.js";
|
|
3
4
|
import { detectGlobalDrift, hasRepairableDrift } from "./drift.js";
|
|
4
5
|
import { harnessHomePaths } from "./paths.js";
|
|
5
6
|
import { resolveComponent } from "./component-registry.js";
|
|
@@ -23,6 +24,10 @@ export async function runGlobalDoctorChecks(homeDir, { packageRoot, workspaceRoo
|
|
|
23
24
|
|
|
24
25
|
if (packageRoot) {
|
|
25
26
|
checks.push(await backupsCheck(paths));
|
|
27
|
+
checks.push(...await runComponentEcosystemChecks({
|
|
28
|
+
installedComponents,
|
|
29
|
+
workspaceRoot
|
|
30
|
+
}));
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
const hasMissing = checks.some((check) => check.status === "missing");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGENT_HINTS, BRAND,
|
|
1
|
+
import { AGENT_HINTS, BRAND, formatCliCommand, getAgentLabel, PREFERRED_CLI, WIZARD_COPY } from "../brand/index.js";
|
|
2
2
|
import { formatAgentMultiselectHint } from "../clack/theme.js";
|
|
3
3
|
|
|
4
4
|
export const SETUP_STEPS = {
|