@kal-elsam/kairo-runtime 0.14.0 → 0.15.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 (45) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/bin/kairo-runtime.js +0 -0
  3. package/bin/kairo.js +0 -0
  4. package/package.json +1 -1
  5. package/src/cli.js +172 -8
  6. package/src/global/check-resolutions.js +31 -0
  7. package/src/global/cli-help.js +19 -2
  8. package/src/global/component-ecosystem-checks.js +2 -0
  9. package/src/global/component-integration-cli.js +29 -10
  10. package/src/global/components-resolve-cli.js +246 -0
  11. package/src/global/connection-actions.js +147 -0
  12. package/src/global/connections.js +269 -0
  13. package/src/global/fleet-configure-plan.js +123 -0
  14. package/src/global/fleet-configure.js +303 -0
  15. package/src/global/fleet-models.js +188 -0
  16. package/src/global/fleet-set.js +219 -0
  17. package/src/global/fleet-shared.js +38 -0
  18. package/src/global/ink/cockpit-controller.js +1 -1
  19. package/src/global/ink/cockpit-models.js +4 -1
  20. package/src/global/ink/orchestrator-app.js +21 -2
  21. package/src/global/ink/ux/live-overview.js +5 -11
  22. package/src/global/ink/ux/overview-needs.js +1 -1
  23. package/src/global/integrations/engram-evidence.js +7 -2
  24. package/src/global/integrations/sdd-apply.js +17 -7
  25. package/src/global/integrations/sdd-evidence.js +22 -3
  26. package/src/global/integrations/sdd-plan.js +21 -3
  27. package/src/global/integrations/sdd-resolutions.js +73 -0
  28. package/src/global/integrations/sdd-state.js +69 -0
  29. package/src/global/integrations/sdd-verify.js +9 -4
  30. package/src/global/mcp/kairo-mcp.js +56 -5
  31. package/src/global/mcp/resolve-mcp-workspace.js +51 -0
  32. package/src/global/mcp/work-snapshot-rule.js +89 -0
  33. package/src/global/mcp/work-snapshot-tool.js +49 -0
  34. package/src/global/mcp-install.js +239 -0
  35. package/src/global/next/next-cli.js +35 -0
  36. package/src/global/next/next-report.js +145 -0
  37. package/src/global/next/project-key.js +36 -0
  38. package/src/global/next/publish-work-snapshot.js +116 -0
  39. package/src/global/next/work-enroll.js +91 -0
  40. package/src/global/next/work-snapshot.js +216 -0
  41. package/src/global/observability/fleet-activity.js +197 -0
  42. package/src/global/observability/fleet-models-catalog.js +137 -0
  43. package/src/global/observability/fleet-platforms.js +166 -0
  44. package/src/global/observability/fleet-probe.js +229 -0
  45. package/src/global/paths.js +2 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,90 @@ Historical entries below may reference the legacy `@kal-elsam/harness` package n
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.15.0 — 2026-08-12 (Kairo Runtime)
9
+
10
+ Minor release. Declared Fleet board + model configure, and the Cursor
11
+ observable companion (work snapshots, MCP publish, `kairo next`, managed
12
+ rule, honest panel). Publish tag: `kairo-runtime-v0.15.0`. Extension
13
+ marketplace delivery stays out of this unit.
14
+
15
+ ### Added
16
+
17
+ - Contract `kairo.work-snapshot/v1` and MCP tool `kairo_publish_work_snapshot`
18
+ to enroll the current Cursor conversation with Goal / Progress / Now /
19
+ Constraints / Next.
20
+ - `kairo next` / `kairo.next/v1`: selected work snapshot plus integration
21
+ state (`active` / `enrolled` / `showRepair`) for the IDE panel.
22
+ - `kairo mcp install [--yes]`: consent-gated registration of Kairo MCP in
23
+ `~/.cursor/mcp.json` (plan first; atomic write + backup) and managed
24
+ Cursor rule `~/.cursor/rules/kairo-work-snapshot.mdc` (`alwaysApply`).
25
+ - Panel **0.7.0**: honest companion surface — Repair only when integration
26
+ is broken; otherwise active/enrolled without false prompts.
27
+ - MCP workspace binding: prefer Cursor env (`VSCODE_CWD` /
28
+ `WORKSPACE_FOLDER_PATHS`) over process cwd; canonicalize macOS `/tmp` vs
29
+ `/private/tmp` before hashing `projectKey`.
30
+ - Panel **0.6.5**: One **Configure all** for multi-agent (Claude + OpenCode +
31
+ Cursor agents). **Codex aparte** (`--codex-model`). `kairo fleet models`
32
+ shows available vs enabled per tool. Cursor desk: open agents/skills/rules.
33
+ Profile at `~/.harness/fleet-models.json` (seeded from disk; preserves
34
+ tuned OpenCode). `--from gentle` remaps OpenCode tiers when you want that.
35
+ - Panel **0.6.4**: Fleet Details puts configure buttons first (Edit per
36
+ minion on Claude/OpenCode; Cursor offers Claude/OpenCode configure +
37
+ optional Pixel Agents). `fleet configure` defaults to Claude only (no
38
+ surprise OpenCode overwrite).
39
+ - `kairo fleet configure`: Gentle-style model assignments across OpenCode +
40
+ Claude (+ Codex when `codex_default` set). Plan / `--yes` + backups. Panel
41
+ **Models** button. Cursor Auto remains IDE-managed.
42
+ - Panel **0.6.2**: Fleet floor (compact platform desks). Working floor only
43
+ shows live agents — no idle OpenCode wall. CLI `kairo fleet` compact by
44
+ default (`--verbose` for full minion list).
45
+ - Panel **0.6.3**: Configure models toolbar + desk actions for sync/set.
46
+ - `kairo fleet [--json]`: declared orchestrator→minion fleet topology from
47
+ OpenCode config (Gentle + SDD models); Cursor Auto marked opaque.
48
+ MCP tool `kairo_fleet`; `kairo connections --json` includes `fleets`.
49
+ - Panel **0.5.0**: Fleet tree under connections (platform · orchestrator ·
50
+ model, indented minions). Details note: declared config, not live tokens.
51
+ - `kairo connections [--json]`: Gentle / Hermes / Engram / Graphify / Agent
52
+ connection chips for the IDE panel (companion probes + MCP registration).
53
+ - VS Code / Cursor panel **0.3.0**: connection chips, Connect Agent button,
54
+ Entries + Details webview. Docs: `docs/mcp.md`.
55
+ - Panel **0.3.1**: soften "not installed / unconfigured" entries to `note`;
56
+ fix false Engram conflict when Claude uses `~/.claude/mcp/engram.json`
57
+ without `mcpServers.engram` in settings.json.
58
+ - Panel **0.3.2**: **Setup** button + per-connection actions (Configure /
59
+ How to install / Update graph). Optional tools labeled; Kairo never
60
+ auto-installs Gentle/Hermes/Graphify.
61
+ - Panel **0.3.3**: every Entry shows resolve buttons in Details (Fix SDD,
62
+ Repair, Update graph, Why optional?). Hermes chip offers **Start Hermes
63
+ gateway**. Conflicts sort first so buttons are visible immediately.
64
+ - Panel **0.3.4**: Hermes tip documents `API_SERVER_ENABLED=true` — gateway
65
+ alone without API Server does not expose :8642.
66
+ - Check **resolutions[]** contract + panel **0.4.0**: status checks can declare
67
+ clickable fix buttons. For `sdd-core:skills` conflicts:
68
+ - `kairo components diff sdd-core` (read-only)
69
+ - `kairo components adopt sdd-core` (keep disk; record adopted hashes)
70
+ - `kairo components configure sdd-core --overwrite-conflicts` (backup + replace)
71
+ Destructive overwrite asks for a modal confirm in the IDE panel.
72
+ - Panel **0.4.1**: adopt/overwrite buttons apply with `--yes` (click = consent).
73
+ Previous `--dry-run` left CONFLICT unchanged after clicking Conservar el mío.
74
+ - Panel **0.4.2**: status/connections/terminal use the open workspace folder as
75
+ `cwd`, so graphify-out/ is detected (was falsely "not found" from IDE cwd).
76
+
77
+ ### Fixed
78
+
79
+ - Cockpit Home opened with focus on the tab bar, so the two Home buttons never
80
+ showed a selection mark and the arrow keys moved between tabs instead. Home
81
+ now starts on the buttons.
82
+
83
+ ### Changed
84
+
85
+ - Home buttons render as `› [1] Label` with `← Press Enter` on the focused
86
+ option; press `1` / `2` to run a button without arrow navigation.
87
+ - Home footer names each key (`1·2 Select · Enter Run · ? Help · Esc Exit`)
88
+ instead of listing bare keys.
89
+ - Home shows at most two plain-language needs; the rest collapse into the
90
+ `More info (n)` disclosure, which absorbs the duplicated "n more" line.
91
+
8
92
  ## 0.14.0 — 2026-08-07 (Kairo Runtime)
9
93
 
10
94
  Minor release. Simpler Cockpit, one-command CLI self-update, pnpm for
File without changes
package/bin/kairo.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kal-elsam/kairo-runtime",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Pi, Engram, and Graphify.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Kal-elSam/harness#readme",
package/src/cli.js CHANGED
@@ -6,6 +6,10 @@ import {
6
6
  runComponentsRollback,
7
7
  runComponentsVerify
8
8
  } from "./global/component-integration-cli.js";
9
+ import {
10
+ runComponentsAdopt,
11
+ runComponentsDiff
12
+ } from "./global/components-resolve-cli.js";
9
13
  import {
10
14
  printGlobalComponents,
11
15
  printGlobalDetect,
@@ -128,17 +132,100 @@ export async function runCli(argv) {
128
132
  await runGraphifyCli(optionsWithPolicy, packageManifest);
129
133
  return;
130
134
  case "mcp": {
131
- // Lazy-load: MCP SDK is heavy and optional for day-to-day CLI (help/status/sync).
132
- const { runKairoMcp } = await import("./global/mcp/kairo-mcp.js");
133
- // stdout reserved for MCP protocol — no banners/logs here
134
- await runKairoMcp({
135
+ const { runMcpCli } = await import("./global/mcp-install.js");
136
+ await runMcpCli({
137
+ mcpAction: optionsWithPolicy.mcpAction ?? "serve",
138
+ mcpClient: optionsWithPolicy.mcpClient ?? "cursor",
139
+ yes: optionsWithPolicy.yes === true,
140
+ json: optionsWithPolicy.json === true,
135
141
  cwd: optionsWithPolicy.cwd,
142
+ cwdExplicit: optionsWithPolicy.cwdExplicit,
136
143
  packageRoot,
137
144
  packageName: packageManifest.name,
138
145
  version: packageManifest.version
139
146
  });
140
147
  return;
141
148
  }
149
+ case "connections": {
150
+ const { buildConnectionsReport } = await import("./global/connections.js");
151
+ const { printJson } = await import("./global/json-output.js");
152
+ const { commandHeader } = await import("./global/brand/index.js");
153
+ const report = await buildConnectionsReport({
154
+ workspaceRoot: optionsWithPolicy.cwd,
155
+ packageRoot,
156
+ packageName: packageManifest.name,
157
+ cliVersion: packageManifest.version,
158
+ client: optionsWithPolicy.mcpClient ?? "cursor"
159
+ });
160
+ if (optionsWithPolicy.json) {
161
+ printJson(report);
162
+ } else {
163
+ console.log(commandHeader("Connections"));
164
+ for (const c of report.connections) {
165
+ console.log(`${c.label} · ${c.state}`);
166
+ console.log(` ${c.access}`);
167
+ if (c.detail) console.log(` ${c.detail}`);
168
+ }
169
+ }
170
+ return;
171
+ }
172
+ case "next": {
173
+ const { runNextCli } = await import("./global/next/next-cli.js");
174
+ await runNextCli({
175
+ cwd: optionsWithPolicy.cwd,
176
+ json: optionsWithPolicy.json === true,
177
+ mcpClient: optionsWithPolicy.mcpClient ?? "cursor"
178
+ });
179
+ return;
180
+ }
181
+ case "fleet": {
182
+ const fleetAction = optionsWithPolicy.fleetAction ?? "show";
183
+ if (fleetAction === "set") {
184
+ const { runFleetSet } = await import("./global/fleet-set.js");
185
+ await runFleetSet({
186
+ platform: optionsWithPolicy.fleetPlatform,
187
+ agent: optionsWithPolicy.fleetAgent ?? optionsWithPolicy.agent,
188
+ model: optionsWithPolicy.model,
189
+ yes: optionsWithPolicy.yes === true,
190
+ dryRun: optionsWithPolicy.dryRun === true,
191
+ json: optionsWithPolicy.json === true
192
+ });
193
+ return;
194
+ }
195
+ if (fleetAction === "configure") {
196
+ const { runFleetConfigure } = await import("./global/fleet-configure.js");
197
+ await runFleetConfigure({
198
+ yes: optionsWithPolicy.yes === true,
199
+ json: optionsWithPolicy.json === true,
200
+ platforms: optionsWithPolicy.fleetPlatforms ?? null,
201
+ from: optionsWithPolicy.fleetFrom ?? "profile",
202
+ assignmentsRaw: optionsWithPolicy.fleetAssignments ?? null,
203
+ codexModel: optionsWithPolicy.fleetCodexModel ?? null
204
+ });
205
+ return;
206
+ }
207
+ if (fleetAction === "models") {
208
+ const { runFleetModels } = await import("./global/fleet-configure.js");
209
+ await runFleetModels({
210
+ json: optionsWithPolicy.json === true,
211
+ profile: optionsWithPolicy.fleetProfile === true
212
+ });
213
+ return;
214
+ }
215
+ const { buildFleetReport, formatFleetText } = await import("./global/observability/fleet-probe.js");
216
+ const { printJson } = await import("./global/json-output.js");
217
+ const report = await buildFleetReport({
218
+ includeVariants: optionsWithPolicy.includeVariants === true
219
+ });
220
+ if (optionsWithPolicy.json) {
221
+ printJson(report);
222
+ } else {
223
+ console.log(formatFleetText(report, {
224
+ verbose: optionsWithPolicy.verbose === true || optionsWithPolicy.includeVariants === true
225
+ }));
226
+ }
227
+ return;
228
+ }
142
229
  case "intelligence":
143
230
  await runIntelligenceCli(optionsWithPolicy, packageManifest);
144
231
  return;
@@ -285,6 +372,12 @@ async function dispatchComponentsCommand(options, invoke) {
285
372
  case "verify":
286
373
  await runComponentsVerify({ ...options, packageRoot });
287
374
  return;
375
+ case "adopt":
376
+ await runComponentsAdopt({ ...options, packageRoot });
377
+ return;
378
+ case "diff":
379
+ await runComponentsDiff({ ...options, packageRoot });
380
+ return;
288
381
  case "rollback":
289
382
  await runComponentsRollback(options);
290
383
  return;
@@ -363,6 +456,7 @@ export function parseArgs(argv) {
363
456
  const command = normalizeCommand(rawCommand);
364
457
  const options = {
365
458
  cwd: process.cwd(),
459
+ cwdExplicit: false,
366
460
  scope: null,
367
461
  mode: "standard",
368
462
  modeExplicit: false,
@@ -382,6 +476,7 @@ export function parseArgs(argv) {
382
476
  dryRun: false,
383
477
  yes: false,
384
478
  confirm: false,
479
+ overwriteConflicts: false,
385
480
  preflight: true,
386
481
  preflightExplicit: false,
387
482
  yesExplicit: false,
@@ -419,6 +514,18 @@ export function parseArgs(argv) {
419
514
  confirmDismiss: false,
420
515
  graphifyAction: null, graphifyArgs: [], graphifyBudget: null, graphPath: null,
421
516
  updatesAction: "check",
517
+ mcpAction: "serve",
518
+ mcpClient: "cursor",
519
+ fleetAction: "show",
520
+ fleetPlatform: null,
521
+ fleetAgent: null,
522
+ fleetPlatforms: null,
523
+ fleetFrom: "profile",
524
+ fleetAssignments: null,
525
+ fleetCodexModel: null,
526
+ fleetProfile: false,
527
+ includeVariants: false,
528
+ verbose: false,
422
529
  base: null,
423
530
  commit: null,
424
531
  staged: false,
@@ -473,6 +580,8 @@ export function parseArgs(argv) {
473
580
 
474
581
  if (command === "graphify") parseGraphifyAction(args, options);
475
582
  if (command === "updates") parseUpdatesAction(args, options);
583
+ if (command === "mcp") parseMcpAction(args, options);
584
+ if (command === "fleet") parseFleetAction(args, options);
476
585
 
477
586
  if (command === "help") {
478
587
  while (args[0] === "all" || args[0] === "--all") {
@@ -484,7 +593,10 @@ export function parseArgs(argv) {
484
593
  for (let index = 0; index < args.length; index += 1) {
485
594
  const arg = args[index];
486
595
 
487
- if (arg === "--cwd") options.cwd = resolve(args[++index]);
596
+ if (arg === "--cwd") {
597
+ options.cwd = resolve(args[++index]);
598
+ options.cwdExplicit = true;
599
+ }
488
600
  else if (arg === "--scope") options.scope = parseScope(args[++index]);
489
601
  else if (arg.startsWith("--scope=")) options.scope = parseScope(arg.slice("--scope=".length));
490
602
  else if (arg === "--mode") {
@@ -518,8 +630,26 @@ export function parseArgs(argv) {
518
630
  options.componentsExplicit = true;
519
631
  } else if (arg === "--force") options.force = true;
520
632
  else if (arg === "--dry-run") options.dryRun = true;
633
+ else if (arg === "--overwrite-conflicts") options.overwriteConflicts = true;
521
634
  else if (arg === "--json") options.json = true;
522
- else if (arg === "--yes" || arg === "-y") {
635
+ else if (arg === "--include-variants") options.includeVariants = true;
636
+ else if (arg === "--platforms") options.fleetPlatforms = args[++index];
637
+ else if (arg.startsWith("--platforms=")) options.fleetPlatforms = arg.slice("--platforms=".length);
638
+ else if (arg === "--from") options.fleetFrom = args[++index];
639
+ else if (arg.startsWith("--from=")) options.fleetFrom = arg.slice("--from=".length);
640
+ else if (arg === "--assignments") options.fleetAssignments = args[++index];
641
+ else if (arg.startsWith("--assignments=")) options.fleetAssignments = arg.slice("--assignments=".length);
642
+ else if (arg === "--codex-model") options.fleetCodexModel = args[++index];
643
+ else if (arg.startsWith("--codex-model=")) options.fleetCodexModel = arg.slice("--codex-model=".length);
644
+ else if (arg === "--profile") options.fleetProfile = true;
645
+ else if (arg === "--verbose") options.verbose = true;
646
+ else if (arg === "--platform") options.fleetPlatform = args[++index];
647
+ else if (arg.startsWith("--platform=")) options.fleetPlatform = arg.slice("--platform=".length);
648
+ else if (arg === "--client") {
649
+ options.mcpClient = args[++index];
650
+ } else if (arg.startsWith("--client=")) {
651
+ options.mcpClient = arg.slice("--client=".length);
652
+ } else if (arg === "--yes" || arg === "-y") {
523
653
  options.yes = true;
524
654
  options.yesExplicit = true;
525
655
  } else if (arg === "--confirm") {
@@ -651,7 +781,8 @@ function parseComponentsAction(args, options) {
651
781
 
652
782
  if (action === "validate") return;
653
783
 
654
- if (action === "configure" || action === "verify" || action === "rollback") {
784
+ if (action === "configure" || action === "verify" || action === "rollback"
785
+ || action === "adopt" || action === "diff") {
655
786
  const componentId = args[0];
656
787
  if (!componentId || componentId.startsWith("-")) {
657
788
  throw new Error(
@@ -662,7 +793,7 @@ function parseComponentsAction(args, options) {
662
793
  return;
663
794
  }
664
795
 
665
- throw new Error(`Unknown components action "${action}". Use validate, init, pack, import, configure, verify, or rollback.`);
796
+ throw new Error(`Unknown components action "${action}". Use validate, init, pack, import, configure, verify, adopt, diff, or rollback.`);
666
797
  }
667
798
 
668
799
  function parsePolicyAction(args, options) {
@@ -842,6 +973,36 @@ function parseUpdatesAction(args, options) {
842
973
  options.updatesAction = "check";
843
974
  }
844
975
 
976
+ function parseMcpAction(args, options) {
977
+ const action = args[0];
978
+ if (!action || action.startsWith("-")) {
979
+ options.mcpAction = "serve";
980
+ return;
981
+ }
982
+ if (!new Set(["serve", "install"]).has(action)) {
983
+ throw new Error(
984
+ `Unknown mcp action "${action}". Use: ${formatCliCommand("mcp")} or ${formatCliCommand("mcp install [--yes]")}`
985
+ );
986
+ }
987
+ args.shift();
988
+ options.mcpAction = action;
989
+ }
990
+
991
+ function parseFleetAction(args, options) {
992
+ const action = args[0];
993
+ if (!action || action.startsWith("-")) {
994
+ options.fleetAction = "show";
995
+ return;
996
+ }
997
+ if (!new Set(["show", "set", "activity", "configure", "models"]).has(action)) {
998
+ throw new Error(
999
+ `Unknown fleet action "${action}". Use: ${formatCliCommand("fleet")}, ${formatCliCommand("fleet models")}, ${formatCliCommand("fleet configure")}, or ${formatCliCommand("fleet set --platform opencode --agent <id> --model <id>")}`
1000
+ );
1001
+ }
1002
+ args.shift();
1003
+ options.fleetAction = action === "activity" ? "show" : action;
1004
+ }
1005
+
845
1006
  function parsePathList(value) {
846
1007
  if (!value) return [];
847
1008
  return [...new Set(
@@ -889,6 +1050,9 @@ function normalizeCommand(command) {
889
1050
  if (command === "alerts") return "alerts";
890
1051
  if (command === "graphify") return "graphify";
891
1052
  if (command === "mcp") return "mcp";
1053
+ if (command === "connections") return "connections";
1054
+ if (command === "next") return "next";
1055
+ if (command === "fleet") return "fleet";
892
1056
  if (command === "intelligence" || command === "intel") return "intelligence";
893
1057
  if (command === "setup") return "setup";
894
1058
  if (command === "status") return "status";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Stable resolution contract for doctor/status checks.
3
+ * Panels render these as buttons; commands stay consent-gated in the CLI.
4
+ */
5
+ export function resolution(id, label, command, {
6
+ kind = "run",
7
+ safety = "consent",
8
+ detail = ""
9
+ } = {}) {
10
+ return {
11
+ id: String(id),
12
+ label: String(label),
13
+ command: command == null ? null : String(command),
14
+ kind: String(kind),
15
+ safety: String(safety),
16
+ detail: detail == null ? "" : String(detail)
17
+ };
18
+ }
19
+
20
+ export const RESOLUTION_SAFETY = Object.freeze({
21
+ READ_ONLY: "read-only",
22
+ CONSENT: "consent",
23
+ DESTRUCTIVE: "destructive"
24
+ });
25
+
26
+ export const RESOLUTION_KIND = Object.freeze({
27
+ RUN: "run",
28
+ CONFIGURE: "configure",
29
+ GUIDE: "guide",
30
+ REFRESH: "refresh"
31
+ });
@@ -109,10 +109,19 @@ Bootstrap: see README.md and docs/install.md (curl install.sh or npx ${PACKAGE_N
109
109
  ${cli} components
110
110
  ${cli} components validate|init|pack|import ...
111
111
  ${cli} components configure engram-memory [--agents <list>] [--dry-run|--yes] [--json]
112
- ${cli} components configure sdd-core [--agents <list>] [--persona off|teaching] [--dry-run|--yes] [--json]
112
+ ${cli} components configure sdd-core [--agents <list>] [--persona off|teaching] [--overwrite-conflicts] [--dry-run|--yes] [--json]
113
113
  ${cli} components verify sdd-core [--agents <list>] [--json]
114
+ ${cli} components adopt sdd-core [--agents <list>] [--dry-run|--yes] [--json]
115
+ ${cli} components diff sdd-core [--agents <list>] [--json]
114
116
  ${cli} components rollback engram-memory|sdd-core --receipt <id> [--dry-run|--yes] [--json]
117
+ ${cli} connections [--json] [--client cursor]
118
+ ${cli} next [--json] [--client cursor]
119
+ ${cli} fleet [--json] [--verbose] [--include-variants]
120
+ ${cli} fleet models [--profile] [--json]
121
+ ${cli} fleet configure [--platforms claude,opencode,cursor|codex] [--from profile|gentle] [--codex-model <id>] [--assignments a=b,...] [--yes] [--json]
122
+ ${cli} fleet set --platform opencode|claude|codex --agent <id> --model <id> [--yes] [--json]
115
123
  ${cli} mcp
124
+ ${cli} mcp install [--yes] [--json] [--client cursor]
116
125
  ${cli} install --scope=workspace [--mode minimal|standard|enterprise] (opt-in/legacy)
117
126
  ${cli} init [--mode minimal|standard|enterprise] (workspace alias)
118
127
 
@@ -157,12 +166,20 @@ Commands:
157
166
  policy View or edit local operation preferences under ~/.harness/policy.json.
158
167
  report Read-only diagnostics bundle: status, policy, adapters, diff, history.
159
168
  components List, validate, scaffold, pack, import, or configure integrations (Engram, SDD).
169
+ connections Companion chips + MCP registration (IDE panel).
170
+ next Selected work snapshot + integration state (panel contract).
171
+ fleet Declared fleet floor + working activity; configure/set models across CLIs.
172
+ fleet models [--profile] available vs enabled per tool
173
+ fleet configure one plan for Claude+OpenCode+Cursor (profile)
174
+ fleet configure --codex-model <id> Codex only (single-default)
175
+ fleet set --platform opencode|claude|codex --agent <id> --model <id> [--yes]
176
+ mcp Serve or install MCP for agents (mcp install writes entry + snapshot rule).
160
177
  uninstall Remove managed sections and global state. Backups are preserved.
161
178
  init Alias for install --scope=workspace (legacy).
162
179
 
163
180
  JSON output (--json on supported commands):
164
181
  status, sync, doctor, adapters, explain, diff, history, history last,
165
- policy (show/set/reset), report, monitor
182
+ policy (show/set/reset), report, monitor, connections, fleet
166
183
  Human text remains the default. See docs/cli-reference.md for examples and field notes.
167
184
 
168
185
  Version:
@@ -9,6 +9,7 @@ import { requireIntegrationProvider } from "./integrations/provider-registry.js"
9
9
  import { buildEngramIntegrationChecks, buildSddIntegrationChecks } from "./component-integration-cli.js";
10
10
  import { KAIRO_ENGRAM_AGENT_IDS } from "./integrations/engram-evidence.js";
11
11
  import { SDD_MANAGED_AGENT_IDS } from "./integrations/sdd-destinations.js";
12
+ import { adoptedHashesFromState } from "./integrations/sdd-state.js";
12
13
  import { harnessHomePaths } from "./paths.js";
13
14
  import { readGlobalState } from "./state.js";
14
15
 
@@ -78,6 +79,7 @@ async function buildSddChecks({ homeDir }) {
78
79
  requestedAgentIds: installedAgents.length ? installedAgents : [],
79
80
  detectedAgentIds: installedAgents,
80
81
  trackedFiles,
82
+ adoptedFiles: adoptedHashesFromState(state?.sdd),
81
83
  personaAgentIds: state?.sdd?.personaAgentIds ?? [],
82
84
  packageRoot: PACKAGE_ROOT
83
85
  });
@@ -9,11 +9,13 @@ import { formatCliCommand } from "./brand/cli.js";
9
9
  import { requireIntegrationProvider } from "./integrations/provider-registry.js";
10
10
  import { ensureIntegrationProvidersRegistered } from "./integrations/index.js";
11
11
  import { ENGRAM_INTEGRATION_STATUS } from "./integrations/engram-evidence.js";
12
- import { SDD_HEALTH } from "./integrations/sdd-evidence.js";
12
+ import { SDD_HEALTH, SDD_FILE_OUTCOMES } from "./integrations/sdd-evidence.js";
13
13
  import {
14
- hasSuccessfulSddRollbackMutations, recordSddMaterialization, reconcileSddStateAfterRollback
14
+ hasSuccessfulSddRollbackMutations, recordSddMaterialization, reconcileSddStateAfterRollback,
15
+ adoptedHashesFromState, clearSddAdoptionsForPaths
15
16
  } from "./integrations/sdd-state.js";
16
17
  import { loadSddReceipt } from "./integrations/sdd-receipts.js";
18
+ import { buildSddSkillResolutions } from "./integrations/sdd-resolutions.js";
17
19
 
18
20
  const DEFAULT_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
19
21
 
@@ -169,14 +171,24 @@ async function buildProviderContext(options, { homeDir, componentId }) {
169
171
  packageRoot: options.packageRoot ?? DEFAULT_PACKAGE_ROOT,
170
172
  persona: options.persona ?? state?.sdd?.persona ?? "off",
171
173
  personaAgentIds: state?.sdd?.personaAgentIds ?? [],
172
- trackedFiles
174
+ trackedFiles,
175
+ adoptedFiles: adoptedHashesFromState(state?.sdd),
176
+ overwriteConflicts: Boolean(options.overwriteConflicts)
173
177
  };
174
178
  }
175
179
 
176
180
  async function persistSddReceiptState(homeDir, receipt) {
177
181
  const paths = harnessHomePaths(homeDir);
178
182
  const state = (await readGlobalState(paths.statePath)) ?? {};
179
- await writeGlobalState(paths.statePath, recordSddMaterialization(state, { receipt }));
183
+ let next = recordSddMaterialization(state, { receipt });
184
+ const appliedPaths = (receipt.files ?? [])
185
+ .filter((file) => file.outcome === SDD_FILE_OUTCOMES.APPLIED)
186
+ .map((file) => file.destinationPath)
187
+ .filter(Boolean);
188
+ if (appliedPaths.length) {
189
+ next = clearSddAdoptionsForPaths(next, appliedPaths);
190
+ }
191
+ await writeGlobalState(paths.statePath, next);
180
192
  }
181
193
 
182
194
  async function reconcileSddRollbackState(homeDir, result) {
@@ -230,13 +242,19 @@ export function buildEngramIntegrationChecks(inspection) {
230
242
  export function buildSddIntegrationChecks(verification) {
231
243
  const summary = verification.summary ?? {};
232
244
  const persona = verification.persona ?? {};
245
+ const skillsHealthy = verification.status === SDD_HEALTH.CONFIGURED
246
+ || verification.status === SDD_HEALTH.ADOPTED;
247
+ const skillsCheck = {
248
+ name: "sdd-core:skills",
249
+ status: skillsHealthy ? "ok" : "warning",
250
+ category: "integration",
251
+ componentId: "sdd-core",
252
+ detail: `SDD skills ${verification.status}: configured=${summary.configured ?? 0}, adopted=${summary.adopted ?? 0}, missing=${summary.missing ?? 0}, drifted=${summary.drifted ?? 0}, conflict=${summary.conflict ?? 0} (disk presence ≠ runtime active).`
253
+ };
254
+ const resolutions = buildSddSkillResolutions(verification);
255
+ if (resolutions.length) skillsCheck.resolutions = resolutions;
233
256
  return [
234
- {
235
- name: "sdd-core:skills",
236
- status: verification.status === SDD_HEALTH.CONFIGURED ? "ok" : "warning",
237
- category: "integration", componentId: "sdd-core",
238
- detail: `SDD skills ${verification.status}: configured=${summary.configured ?? 0}, missing=${summary.missing ?? 0}, drifted=${summary.drifted ?? 0}, conflict=${summary.conflict ?? 0} (disk presence ≠ runtime active).`
239
- },
257
+ skillsCheck,
240
258
  {
241
259
  name: "sdd-core:persona",
242
260
  status: persona.status === "configured" || persona.status === "off" ? "ok" : "warning",
@@ -283,6 +301,7 @@ function printSddConfigureHuman(result) {
283
301
  if (result.dryRun) return void console.log("Dry-run only — no skills materialized.");
284
302
  if (result.cancelled) return void console.log("Cancelled.");
285
303
  if (result.blocked) return void console.log(`Blocked: ${result.reason ?? "conflicts present"}`);
304
+ if (result.overwriteConflicts) console.log("Mode: overwrite-conflicts (backups before replace).");
286
305
  if (result.receipt) {
287
306
  console.log(`Receipt: ${result.receipt.id}${result.receipt.partial ? " (partial)" : ""}`);
288
307
  if (result.sessionRefreshRequired) {