@kal-elsam/kairo-runtime 0.14.0 → 0.16.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 (56) hide show
  1. package/CHANGELOG.md +110 -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 +182 -8
  6. package/src/global/check-resolutions.js +31 -0
  7. package/src/global/cli-help.js +21 -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/control-plane/attention.js +141 -0
  14. package/src/global/control-plane/build-report.js +146 -0
  15. package/src/global/control-plane/cli.js +36 -0
  16. package/src/global/control-plane/constants.js +38 -0
  17. package/src/global/control-plane/gentle-adapters.js +183 -0
  18. package/src/global/control-plane/provider.js +69 -0
  19. package/src/global/control-plane/review-status.js +115 -0
  20. package/src/global/control-plane/sdd-status.js +49 -0
  21. package/src/global/control-plane/team.js +63 -0
  22. package/src/global/fleet-configure-plan.js +123 -0
  23. package/src/global/fleet-configure.js +303 -0
  24. package/src/global/fleet-models.js +188 -0
  25. package/src/global/fleet-set.js +219 -0
  26. package/src/global/fleet-shared.js +38 -0
  27. package/src/global/ink/cockpit-controller.js +1 -1
  28. package/src/global/ink/cockpit-models.js +4 -1
  29. package/src/global/ink/orchestrator-app.js +21 -2
  30. package/src/global/ink/ux/live-overview.js +5 -11
  31. package/src/global/ink/ux/overview-needs.js +1 -1
  32. package/src/global/integrations/engram-evidence.js +7 -2
  33. package/src/global/integrations/sdd-apply.js +17 -7
  34. package/src/global/integrations/sdd-evidence.js +22 -3
  35. package/src/global/integrations/sdd-plan.js +21 -3
  36. package/src/global/integrations/sdd-resolutions.js +73 -0
  37. package/src/global/integrations/sdd-state.js +69 -0
  38. package/src/global/integrations/sdd-verify.js +9 -4
  39. package/src/global/mcp/kairo-mcp.js +56 -5
  40. package/src/global/mcp/resolve-mcp-workspace.js +51 -0
  41. package/src/global/mcp/work-snapshot-rule.js +89 -0
  42. package/src/global/mcp/work-snapshot-tool.js +49 -0
  43. package/src/global/mcp-install.js +239 -0
  44. package/src/global/next/next-cli.js +35 -0
  45. package/src/global/next/next-report.js +145 -0
  46. package/src/global/next/project-key.js +36 -0
  47. package/src/global/next/publish-work-snapshot.js +116 -0
  48. package/src/global/next/work-enroll.js +91 -0
  49. package/src/global/next/work-snapshot.js +216 -0
  50. package/src/global/observability/fleet-activity.js +197 -0
  51. package/src/global/observability/fleet-models-catalog.js +137 -0
  52. package/src/global/observability/fleet-platforms.js +166 -0
  53. package/src/global/observability/fleet-probe.js +229 -0
  54. package/src/global/observability/gentle-probe.js +30 -2
  55. package/src/global/observability/index.js +2 -1
  56. package/src/global/paths.js +2 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,116 @@ Historical entries below may reference the legacy `@kal-elsam/harness` package n
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.16.0 — 2026-08-13 (Kairo Runtime)
9
+
10
+ Minor release. Public `kairo control-plane` command and
11
+ `kairo.control-plane/v1` Gentle companion report. Publish tag:
12
+ `kairo-runtime-v0.16.0`. Extension VSIX stays out of this unit.
13
+
14
+ ### Added
15
+
16
+ - `kairo control-plane [--json] [--client cursor]`: atomic panel report
17
+ (work + Gentle workflow + team + attention) as `kairo.control-plane/v1`.
18
+ - Negotiate `gentle-ai.review-integration/v2` (protocol 2.0 and 2.1) before
19
+ any workflow fetch. Provider states: `connected`, `upgrade_required`,
20
+ `unavailable`, `incompatible`.
21
+ - Official `review status` from Gentle's announced bootstrap argv; pass
22
+ `next_transition` through unaltered. Receipt/gate only when Gentle publishes
23
+ them.
24
+ - `sdd-status --json` projection copies `changeName` / `nextRecommended` only.
25
+
26
+ ### Docs
27
+
28
+ - Gentle companion boundary: Kairo observes `gentle-ai.review-integration/v2`
29
+ and projects official `next_transition` / `sdd-status --json`. Freeze
30
+ `kairo review`, Cockpit receipts, orchestrator, and intelligence routing so
31
+ they do not feed the panel Workflow. Propose upstream `gentle-ai observe --json`
32
+ in Kairo docs only (`docs/gentle-companion.md`).
33
+
34
+ ## 0.15.0 — 2026-08-12 (Kairo Runtime)
35
+
36
+ Minor release. Declared Fleet board + model configure, and the Cursor
37
+ observable companion (work snapshots, MCP publish, `kairo next`, managed
38
+ rule, honest panel). Publish tag: `kairo-runtime-v0.15.0`. Extension
39
+ marketplace delivery stays out of this unit.
40
+
41
+ ### Added
42
+
43
+ - Contract `kairo.work-snapshot/v1` and MCP tool `kairo_publish_work_snapshot`
44
+ to enroll the current Cursor conversation with Goal / Progress / Now /
45
+ Constraints / Next.
46
+ - `kairo next` / `kairo.next/v1`: selected work snapshot plus integration
47
+ state (`active` / `enrolled` / `showRepair`) for the IDE panel.
48
+ - `kairo mcp install [--yes]`: consent-gated registration of Kairo MCP in
49
+ `~/.cursor/mcp.json` (plan first; atomic write + backup) and managed
50
+ Cursor rule `~/.cursor/rules/kairo-work-snapshot.mdc` (`alwaysApply`).
51
+ - Panel **0.7.0**: honest companion surface — Repair only when integration
52
+ is broken; otherwise active/enrolled without false prompts.
53
+ - MCP workspace binding: prefer Cursor env (`VSCODE_CWD` /
54
+ `WORKSPACE_FOLDER_PATHS`) over process cwd; canonicalize macOS `/tmp` vs
55
+ `/private/tmp` before hashing `projectKey`.
56
+ - Panel **0.6.5**: One **Configure all** for multi-agent (Claude + OpenCode +
57
+ Cursor agents). **Codex aparte** (`--codex-model`). `kairo fleet models`
58
+ shows available vs enabled per tool. Cursor desk: open agents/skills/rules.
59
+ Profile at `~/.harness/fleet-models.json` (seeded from disk; preserves
60
+ tuned OpenCode). `--from gentle` remaps OpenCode tiers when you want that.
61
+ - Panel **0.6.4**: Fleet Details puts configure buttons first (Edit per
62
+ minion on Claude/OpenCode; Cursor offers Claude/OpenCode configure +
63
+ optional Pixel Agents). `fleet configure` defaults to Claude only (no
64
+ surprise OpenCode overwrite).
65
+ - `kairo fleet configure`: Gentle-style model assignments across OpenCode +
66
+ Claude (+ Codex when `codex_default` set). Plan / `--yes` + backups. Panel
67
+ **Models** button. Cursor Auto remains IDE-managed.
68
+ - Panel **0.6.2**: Fleet floor (compact platform desks). Working floor only
69
+ shows live agents — no idle OpenCode wall. CLI `kairo fleet` compact by
70
+ default (`--verbose` for full minion list).
71
+ - Panel **0.6.3**: Configure models toolbar + desk actions for sync/set.
72
+ - `kairo fleet [--json]`: declared orchestrator→minion fleet topology from
73
+ OpenCode config (Gentle + SDD models); Cursor Auto marked opaque.
74
+ MCP tool `kairo_fleet`; `kairo connections --json` includes `fleets`.
75
+ - Panel **0.5.0**: Fleet tree under connections (platform · orchestrator ·
76
+ model, indented minions). Details note: declared config, not live tokens.
77
+ - `kairo connections [--json]`: Gentle / Hermes / Engram / Graphify / Agent
78
+ connection chips for the IDE panel (companion probes + MCP registration).
79
+ - VS Code / Cursor panel **0.3.0**: connection chips, Connect Agent button,
80
+ Entries + Details webview. Docs: `docs/mcp.md`.
81
+ - Panel **0.3.1**: soften "not installed / unconfigured" entries to `note`;
82
+ fix false Engram conflict when Claude uses `~/.claude/mcp/engram.json`
83
+ without `mcpServers.engram` in settings.json.
84
+ - Panel **0.3.2**: **Setup** button + per-connection actions (Configure /
85
+ How to install / Update graph). Optional tools labeled; Kairo never
86
+ auto-installs Gentle/Hermes/Graphify.
87
+ - Panel **0.3.3**: every Entry shows resolve buttons in Details (Fix SDD,
88
+ Repair, Update graph, Why optional?). Hermes chip offers **Start Hermes
89
+ gateway**. Conflicts sort first so buttons are visible immediately.
90
+ - Panel **0.3.4**: Hermes tip documents `API_SERVER_ENABLED=true` — gateway
91
+ alone without API Server does not expose :8642.
92
+ - Check **resolutions[]** contract + panel **0.4.0**: status checks can declare
93
+ clickable fix buttons. For `sdd-core:skills` conflicts:
94
+ - `kairo components diff sdd-core` (read-only)
95
+ - `kairo components adopt sdd-core` (keep disk; record adopted hashes)
96
+ - `kairo components configure sdd-core --overwrite-conflicts` (backup + replace)
97
+ Destructive overwrite asks for a modal confirm in the IDE panel.
98
+ - Panel **0.4.1**: adopt/overwrite buttons apply with `--yes` (click = consent).
99
+ Previous `--dry-run` left CONFLICT unchanged after clicking Conservar el mío.
100
+ - Panel **0.4.2**: status/connections/terminal use the open workspace folder as
101
+ `cwd`, so graphify-out/ is detected (was falsely "not found" from IDE cwd).
102
+
103
+ ### Fixed
104
+
105
+ - Cockpit Home opened with focus on the tab bar, so the two Home buttons never
106
+ showed a selection mark and the arrow keys moved between tabs instead. Home
107
+ now starts on the buttons.
108
+
109
+ ### Changed
110
+
111
+ - Home buttons render as `› [1] Label` with `← Press Enter` on the focused
112
+ option; press `1` / `2` to run a button without arrow navigation.
113
+ - Home footer names each key (`1·2 Select · Enter Run · ? Help · Esc Exit`)
114
+ instead of listing bare keys.
115
+ - Home shows at most two plain-language needs; the rest collapse into the
116
+ `More info (n)` disclosure, which absorbs the duplicated "n more" line.
117
+
8
118
  ## 0.14.0 — 2026-08-07 (Kairo Runtime)
9
119
 
10
120
  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.16.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,109 @@ 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 "control-plane": {
182
+ const { runControlPlaneCli } = await import("./global/control-plane/cli.js");
183
+ await runControlPlaneCli({
184
+ cwd: optionsWithPolicy.cwd,
185
+ json: optionsWithPolicy.json === true,
186
+ mcpClient: optionsWithPolicy.mcpClient ?? "cursor"
187
+ });
188
+ return;
189
+ }
190
+ case "fleet": {
191
+ const fleetAction = optionsWithPolicy.fleetAction ?? "show";
192
+ if (fleetAction === "set") {
193
+ const { runFleetSet } = await import("./global/fleet-set.js");
194
+ await runFleetSet({
195
+ platform: optionsWithPolicy.fleetPlatform,
196
+ agent: optionsWithPolicy.fleetAgent ?? optionsWithPolicy.agent,
197
+ model: optionsWithPolicy.model,
198
+ yes: optionsWithPolicy.yes === true,
199
+ dryRun: optionsWithPolicy.dryRun === true,
200
+ json: optionsWithPolicy.json === true
201
+ });
202
+ return;
203
+ }
204
+ if (fleetAction === "configure") {
205
+ const { runFleetConfigure } = await import("./global/fleet-configure.js");
206
+ await runFleetConfigure({
207
+ yes: optionsWithPolicy.yes === true,
208
+ json: optionsWithPolicy.json === true,
209
+ platforms: optionsWithPolicy.fleetPlatforms ?? null,
210
+ from: optionsWithPolicy.fleetFrom ?? "profile",
211
+ assignmentsRaw: optionsWithPolicy.fleetAssignments ?? null,
212
+ codexModel: optionsWithPolicy.fleetCodexModel ?? null
213
+ });
214
+ return;
215
+ }
216
+ if (fleetAction === "models") {
217
+ const { runFleetModels } = await import("./global/fleet-configure.js");
218
+ await runFleetModels({
219
+ json: optionsWithPolicy.json === true,
220
+ profile: optionsWithPolicy.fleetProfile === true
221
+ });
222
+ return;
223
+ }
224
+ const { buildFleetReport, formatFleetText } = await import("./global/observability/fleet-probe.js");
225
+ const { printJson } = await import("./global/json-output.js");
226
+ const report = await buildFleetReport({
227
+ includeVariants: optionsWithPolicy.includeVariants === true
228
+ });
229
+ if (optionsWithPolicy.json) {
230
+ printJson(report);
231
+ } else {
232
+ console.log(formatFleetText(report, {
233
+ verbose: optionsWithPolicy.verbose === true || optionsWithPolicy.includeVariants === true
234
+ }));
235
+ }
236
+ return;
237
+ }
142
238
  case "intelligence":
143
239
  await runIntelligenceCli(optionsWithPolicy, packageManifest);
144
240
  return;
@@ -285,6 +381,12 @@ async function dispatchComponentsCommand(options, invoke) {
285
381
  case "verify":
286
382
  await runComponentsVerify({ ...options, packageRoot });
287
383
  return;
384
+ case "adopt":
385
+ await runComponentsAdopt({ ...options, packageRoot });
386
+ return;
387
+ case "diff":
388
+ await runComponentsDiff({ ...options, packageRoot });
389
+ return;
288
390
  case "rollback":
289
391
  await runComponentsRollback(options);
290
392
  return;
@@ -363,6 +465,7 @@ export function parseArgs(argv) {
363
465
  const command = normalizeCommand(rawCommand);
364
466
  const options = {
365
467
  cwd: process.cwd(),
468
+ cwdExplicit: false,
366
469
  scope: null,
367
470
  mode: "standard",
368
471
  modeExplicit: false,
@@ -382,6 +485,7 @@ export function parseArgs(argv) {
382
485
  dryRun: false,
383
486
  yes: false,
384
487
  confirm: false,
488
+ overwriteConflicts: false,
385
489
  preflight: true,
386
490
  preflightExplicit: false,
387
491
  yesExplicit: false,
@@ -419,6 +523,18 @@ export function parseArgs(argv) {
419
523
  confirmDismiss: false,
420
524
  graphifyAction: null, graphifyArgs: [], graphifyBudget: null, graphPath: null,
421
525
  updatesAction: "check",
526
+ mcpAction: "serve",
527
+ mcpClient: "cursor",
528
+ fleetAction: "show",
529
+ fleetPlatform: null,
530
+ fleetAgent: null,
531
+ fleetPlatforms: null,
532
+ fleetFrom: "profile",
533
+ fleetAssignments: null,
534
+ fleetCodexModel: null,
535
+ fleetProfile: false,
536
+ includeVariants: false,
537
+ verbose: false,
422
538
  base: null,
423
539
  commit: null,
424
540
  staged: false,
@@ -473,6 +589,8 @@ export function parseArgs(argv) {
473
589
 
474
590
  if (command === "graphify") parseGraphifyAction(args, options);
475
591
  if (command === "updates") parseUpdatesAction(args, options);
592
+ if (command === "mcp") parseMcpAction(args, options);
593
+ if (command === "fleet") parseFleetAction(args, options);
476
594
 
477
595
  if (command === "help") {
478
596
  while (args[0] === "all" || args[0] === "--all") {
@@ -484,7 +602,10 @@ export function parseArgs(argv) {
484
602
  for (let index = 0; index < args.length; index += 1) {
485
603
  const arg = args[index];
486
604
 
487
- if (arg === "--cwd") options.cwd = resolve(args[++index]);
605
+ if (arg === "--cwd") {
606
+ options.cwd = resolve(args[++index]);
607
+ options.cwdExplicit = true;
608
+ }
488
609
  else if (arg === "--scope") options.scope = parseScope(args[++index]);
489
610
  else if (arg.startsWith("--scope=")) options.scope = parseScope(arg.slice("--scope=".length));
490
611
  else if (arg === "--mode") {
@@ -518,8 +639,26 @@ export function parseArgs(argv) {
518
639
  options.componentsExplicit = true;
519
640
  } else if (arg === "--force") options.force = true;
520
641
  else if (arg === "--dry-run") options.dryRun = true;
642
+ else if (arg === "--overwrite-conflicts") options.overwriteConflicts = true;
521
643
  else if (arg === "--json") options.json = true;
522
- else if (arg === "--yes" || arg === "-y") {
644
+ else if (arg === "--include-variants") options.includeVariants = true;
645
+ else if (arg === "--platforms") options.fleetPlatforms = args[++index];
646
+ else if (arg.startsWith("--platforms=")) options.fleetPlatforms = arg.slice("--platforms=".length);
647
+ else if (arg === "--from") options.fleetFrom = args[++index];
648
+ else if (arg.startsWith("--from=")) options.fleetFrom = arg.slice("--from=".length);
649
+ else if (arg === "--assignments") options.fleetAssignments = args[++index];
650
+ else if (arg.startsWith("--assignments=")) options.fleetAssignments = arg.slice("--assignments=".length);
651
+ else if (arg === "--codex-model") options.fleetCodexModel = args[++index];
652
+ else if (arg.startsWith("--codex-model=")) options.fleetCodexModel = arg.slice("--codex-model=".length);
653
+ else if (arg === "--profile") options.fleetProfile = true;
654
+ else if (arg === "--verbose") options.verbose = true;
655
+ else if (arg === "--platform") options.fleetPlatform = args[++index];
656
+ else if (arg.startsWith("--platform=")) options.fleetPlatform = arg.slice("--platform=".length);
657
+ else if (arg === "--client") {
658
+ options.mcpClient = args[++index];
659
+ } else if (arg.startsWith("--client=")) {
660
+ options.mcpClient = arg.slice("--client=".length);
661
+ } else if (arg === "--yes" || arg === "-y") {
523
662
  options.yes = true;
524
663
  options.yesExplicit = true;
525
664
  } else if (arg === "--confirm") {
@@ -651,7 +790,8 @@ function parseComponentsAction(args, options) {
651
790
 
652
791
  if (action === "validate") return;
653
792
 
654
- if (action === "configure" || action === "verify" || action === "rollback") {
793
+ if (action === "configure" || action === "verify" || action === "rollback"
794
+ || action === "adopt" || action === "diff") {
655
795
  const componentId = args[0];
656
796
  if (!componentId || componentId.startsWith("-")) {
657
797
  throw new Error(
@@ -662,7 +802,7 @@ function parseComponentsAction(args, options) {
662
802
  return;
663
803
  }
664
804
 
665
- throw new Error(`Unknown components action "${action}". Use validate, init, pack, import, configure, verify, or rollback.`);
805
+ throw new Error(`Unknown components action "${action}". Use validate, init, pack, import, configure, verify, adopt, diff, or rollback.`);
666
806
  }
667
807
 
668
808
  function parsePolicyAction(args, options) {
@@ -842,6 +982,36 @@ function parseUpdatesAction(args, options) {
842
982
  options.updatesAction = "check";
843
983
  }
844
984
 
985
+ function parseMcpAction(args, options) {
986
+ const action = args[0];
987
+ if (!action || action.startsWith("-")) {
988
+ options.mcpAction = "serve";
989
+ return;
990
+ }
991
+ if (!new Set(["serve", "install"]).has(action)) {
992
+ throw new Error(
993
+ `Unknown mcp action "${action}". Use: ${formatCliCommand("mcp")} or ${formatCliCommand("mcp install [--yes]")}`
994
+ );
995
+ }
996
+ args.shift();
997
+ options.mcpAction = action;
998
+ }
999
+
1000
+ function parseFleetAction(args, options) {
1001
+ const action = args[0];
1002
+ if (!action || action.startsWith("-")) {
1003
+ options.fleetAction = "show";
1004
+ return;
1005
+ }
1006
+ if (!new Set(["show", "set", "activity", "configure", "models"]).has(action)) {
1007
+ throw new Error(
1008
+ `Unknown fleet action "${action}". Use: ${formatCliCommand("fleet")}, ${formatCliCommand("fleet models")}, ${formatCliCommand("fleet configure")}, or ${formatCliCommand("fleet set --platform opencode --agent <id> --model <id>")}`
1009
+ );
1010
+ }
1011
+ args.shift();
1012
+ options.fleetAction = action === "activity" ? "show" : action;
1013
+ }
1014
+
845
1015
  function parsePathList(value) {
846
1016
  if (!value) return [];
847
1017
  return [...new Set(
@@ -889,6 +1059,10 @@ function normalizeCommand(command) {
889
1059
  if (command === "alerts") return "alerts";
890
1060
  if (command === "graphify") return "graphify";
891
1061
  if (command === "mcp") return "mcp";
1062
+ if (command === "connections") return "connections";
1063
+ if (command === "next") return "next";
1064
+ if (command === "control-plane") return "control-plane";
1065
+ if (command === "fleet") return "fleet";
892
1066
  if (command === "intelligence" || command === "intel") return "intelligence";
893
1067
  if (command === "setup") return "setup";
894
1068
  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,20 @@ 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} control-plane [--json] [--client cursor]
120
+ ${cli} fleet [--json] [--verbose] [--include-variants]
121
+ ${cli} fleet models [--profile] [--json]
122
+ ${cli} fleet configure [--platforms claude,opencode,cursor|codex] [--from profile|gentle] [--codex-model <id>] [--assignments a=b,...] [--yes] [--json]
123
+ ${cli} fleet set --platform opencode|claude|codex --agent <id> --model <id> [--yes] [--json]
115
124
  ${cli} mcp
125
+ ${cli} mcp install [--yes] [--json] [--client cursor]
116
126
  ${cli} install --scope=workspace [--mode minimal|standard|enterprise] (opt-in/legacy)
117
127
  ${cli} init [--mode minimal|standard|enterprise] (workspace alias)
118
128
 
@@ -157,12 +167,21 @@ Commands:
157
167
  policy View or edit local operation preferences under ~/.harness/policy.json.
158
168
  report Read-only diagnostics bundle: status, policy, adapters, diff, history.
159
169
  components List, validate, scaffold, pack, import, or configure integrations (Engram, SDD).
170
+ connections Companion chips + MCP registration (IDE panel).
171
+ next Selected work snapshot + integration state (panel contract).
172
+ control-plane Atomic panel report (work + Gentle workflow + team + attention).
173
+ fleet Declared fleet floor + working activity; configure/set models across CLIs.
174
+ fleet models [--profile] available vs enabled per tool
175
+ fleet configure one plan for Claude+OpenCode+Cursor (profile)
176
+ fleet configure --codex-model <id> Codex only (single-default)
177
+ fleet set --platform opencode|claude|codex --agent <id> --model <id> [--yes]
178
+ mcp Serve or install MCP for agents (mcp install writes entry + snapshot rule).
160
179
  uninstall Remove managed sections and global state. Backups are preserved.
161
180
  init Alias for install --scope=workspace (legacy).
162
181
 
163
182
  JSON output (--json on supported commands):
164
183
  status, sync, doctor, adapters, explain, diff, history, history last,
165
- policy (show/set/reset), report, monitor
184
+ policy (show/set/reset), report, monitor, connections, fleet
166
185
  Human text remains the default. See docs/cli-reference.md for examples and field notes.
167
186
 
168
187
  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) {