@kal-elsam/kairo-runtime 0.5.1 → 0.7.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.
- package/README.md +44 -6
- package/global-template/components/catalog.json +4 -2
- package/global-template/components/orchestrator/orchestrator.md +1 -1
- package/global-template/core/orchestrator.md +1 -1
- package/package.json +3 -2
- package/scripts/cockpit-smoke.mjs +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/installer-smoke-test.sh +6 -4
- package/src/cli.js +60 -1
- package/src/global/adapters/pi.js +53 -0
- package/src/global/agent-capabilities/index.js +11 -1
- package/src/global/brand/index.js +4 -2
- package/src/global/dashboard-guidance.js +1 -1
- package/src/global/global-cli.js +1 -1
- package/src/global/ink/cockpit-controller.js +10 -0
- package/src/global/ink/cockpit-focus.js +18 -3
- package/src/global/ink/cockpit-models.js +8 -1
- package/src/global/ink/cockpit-reviews.js +62 -0
- package/src/global/ink/cockpit-runs.js +11 -2
- package/src/global/ink/cockpit-views.js +19 -2
- package/src/global/ink/orchestrator-app.js +23 -3
- package/src/global/ink/orchestrator-state.js +2 -0
- package/src/global/ink/use-orchestrator-data.js +30 -0
- package/src/global/integrations/engram-evidence.js +41 -3
- package/src/global/integrations/sdd-destinations.js +2 -2
- package/src/global/paths.js +1 -0
- package/src/global/registry.js +2 -1
- package/src/global/runtime/execution-adapters/codex.js +2 -1
- package/src/global/runtime/execution-adapters/create-execution-adapter.js +1 -0
- package/src/global/runtime/execution-adapters/index.js +3 -1
- package/src/global/runtime/execution-adapters/pi.js +184 -0
- package/src/global/runtime/review/index.js +37 -0
- package/src/global/runtime/review/review-cli.js +150 -0
- package/src/global/runtime/review/review-codex.js +132 -0
- package/src/global/runtime/review/review-exec.js +136 -0
- package/src/global/runtime/review/review-fs.js +52 -0
- package/src/global/runtime/review/review-git.js +212 -0
- package/src/global/runtime/review/review-patch.js +122 -0
- package/src/global/runtime/review/review-pi.js +168 -0
- package/src/global/runtime/review/review-receipts.js +128 -0
- package/src/global/runtime/review/review-runner.js +119 -0
- package/src/global/runtime/review/review-types.js +108 -0
- package/src/global/runtime/review/review-validate.js +280 -0
- package/src/global/runtime/write-atomic-json.js +24 -20
package/README.md
CHANGED
|
@@ -261,7 +261,7 @@ node ./bin/kairo.js install --dry-run
|
|
|
261
261
|
|
|
262
262
|
## Supported adapters (agent-global)
|
|
263
263
|
|
|
264
|
-
Kairo Runtime does **not** install Cursor, Codex, OpenCode,
|
|
264
|
+
Kairo Runtime does **not** install Cursor, Codex, OpenCode, Claude Code, or Pi. It detects
|
|
265
265
|
their home-directory roots and writes managed sections into their config files.
|
|
266
266
|
|
|
267
267
|
| Adapter | Label | Root | Config file |
|
|
@@ -270,6 +270,7 @@ their home-directory roots and writes managed sections into their config files.
|
|
|
270
270
|
| `codex` | Codex | `~/.codex` | `~/.codex/AGENTS.md` |
|
|
271
271
|
| `opencode` | OpenCode | `~/.config/opencode` | `~/.config/opencode/AGENTS.md` |
|
|
272
272
|
| `claude` | Claude Code | `~/.claude` | `~/.claude/CLAUDE.md` |
|
|
273
|
+
| `pi` | Pi | `~/.pi/agent` | `~/.pi/agent/AGENTS.md` |
|
|
273
274
|
|
|
274
275
|
Inspect detection and managed state:
|
|
275
276
|
|
|
@@ -281,14 +282,49 @@ kairo adapters --json
|
|
|
281
282
|
Agent selection defaults:
|
|
282
283
|
|
|
283
284
|
- If agent roots are detected → configure detected agents only.
|
|
284
|
-
- If none are detected → safe fallback to all
|
|
285
|
-
- Force all
|
|
285
|
+
- If none are detected → safe fallback to all five supported adapters.
|
|
286
|
+
- Force all five explicitly:
|
|
286
287
|
|
|
287
288
|
```bash
|
|
288
289
|
kairo setup --agents all
|
|
289
290
|
kairo install --agents all
|
|
290
291
|
```
|
|
291
292
|
|
|
293
|
+
### Pi runtime (auditable)
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
kairo run --agent pi --task "Review this repository" --permissions read-only --follow
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Launches `pi --mode json --no-session` (optional `--model`). `read-only` maps to
|
|
300
|
+
`--tools read,grep,find,ls`; other permission aliases are rejected (never translated
|
|
301
|
+
to `--approve`). Custom `PI_CODING_AGENT_DIR` blocks config writes in 0.6.0 but does
|
|
302
|
+
not block runtime. Kairo does not install Pi or assert subscription/entitlement.
|
|
303
|
+
|
|
304
|
+
### Bounded review (Codex / Pi)
|
|
305
|
+
|
|
306
|
+
Read-only native review against a Git snapshot. Never mutates the repo, never
|
|
307
|
+
auto-fixes, and never persists prompts, diffs, or transcripts. Receipts land under
|
|
308
|
+
`~/.harness/reviews/<reviewId>/receipt.json` (secret-free). Only Codex and Pi are
|
|
309
|
+
`reviewCompatible` in 0.7.0; Cursor / Claude / OpenCode report `false` until audited.
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
kairo review --agent codex
|
|
313
|
+
kairo review --agent pi --base main
|
|
314
|
+
kairo review --agent codex --commit <sha>
|
|
315
|
+
kairo review --agent codex --fail-on high --json
|
|
316
|
+
kairo reviews list [--limit N] [--json]
|
|
317
|
+
kairo reviews show <reviewId> [--json]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`--agent` is always required. Scope is working-tree by default; use `--base` or
|
|
321
|
+
`--commit` (mutually exclusive). Private paths need `--include-private` plus TTY
|
|
322
|
+
confirm or `--yes`/`--confirm`. Exit codes: `0` ok, `1` severity threshold,
|
|
323
|
+
`2` operational/stale/invalid/cancel.
|
|
324
|
+
|
|
325
|
+
Cockpit: **Runs → Reviews** lists receipts and opens a read-only detail view
|
|
326
|
+
(no launch from Cockpit in v1).
|
|
327
|
+
|
|
292
328
|
Primary governance flow:
|
|
293
329
|
|
|
294
330
|
```txt
|
|
@@ -518,9 +554,11 @@ kairo components rollback engram-memory --receipt <id> --dry-run
|
|
|
518
554
|
|
|
519
555
|
Without `--agents`, Kairo uses the intersection of detected agents and
|
|
520
556
|
Kairo-managed Engram agents (`cursor`, `codex`, `opencode`, `claude` → setup slug
|
|
521
|
-
`claude-code`). After setup, status is `restart_required` — restart the agent to
|
|
557
|
+
`claude-code`, `pi` → `pi`). After setup, status is `restart_required` — restart the agent to
|
|
522
558
|
load MCP; configuration evidence is not runtime-active. Receipts live under
|
|
523
|
-
`~/.harness/integrations/engram/`.
|
|
559
|
+
`~/.harness/integrations/engram/`. For Pi, positive evidence requires
|
|
560
|
+
`~/.pi/agent/settings.json` packages (`npm:gentle-engram`, `npm:pi-mcp-adapter`) and
|
|
561
|
+
`~/.pi/agent/mcp.json` with `mcpServers.engram`.
|
|
524
562
|
|
|
525
563
|
### SDD Core skills
|
|
526
564
|
|
|
@@ -537,7 +575,7 @@ same `sdd-core` apply path. Lifecycle keeps persona frozen (`preservePersona`):
|
|
|
537
575
|
it never auto-activates teaching. Explicit persona changes stay on
|
|
538
576
|
`kairo components configure sdd-core`.
|
|
539
577
|
|
|
540
|
-
**Destinations.** Cursor, Codex, and
|
|
578
|
+
**Destinations.** Cursor, Codex, OpenCode, and Pi share `~/.agents/skills/<id>/`.
|
|
541
579
|
Claude uses `~/.claude/skills/<id>/`. One physical tree per root; consumers are
|
|
542
580
|
recorded per destination.
|
|
543
581
|
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"cursor": "- In repos with workspace harness: also read `.cursor/rules/` and `docs/specs/`.",
|
|
59
59
|
"codex": "- In repos with workspace harness: also read `.codex/skills/sdd/` when present.",
|
|
60
60
|
"claude": "- In repos with workspace harness: repo `AGENTS.md` wins over `CLAUDE.md` pointers.",
|
|
61
|
-
"opencode": "- In repos with workspace harness: also read `.opencode/` SDD assets when present."
|
|
61
|
+
"opencode": "- In repos with workspace harness: also read `.opencode/` SDD assets when present.",
|
|
62
|
+
"pi": "- Skills load from `~/.agents/skills` (shared Agent Skills root). Teaching persona activates only via the managed section of `~/.pi/agent/AGENTS.md`."
|
|
62
63
|
}
|
|
63
64
|
},
|
|
64
65
|
{
|
|
@@ -80,7 +81,8 @@
|
|
|
80
81
|
"cursor": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup cursor`).",
|
|
81
82
|
"codex": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup codex`).",
|
|
82
83
|
"claude": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup claude-code`).",
|
|
83
|
-
"opencode": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup opencode`)."
|
|
84
|
+
"opencode": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup opencode`).",
|
|
85
|
+
"pi": "- Configure Engram via `kairo components configure engram-memory` (official `engram setup pi`). Evidence: `~/.pi/agent/settings.json` packages + `mcp.json` engram server. Restart Pi after setup; Kairo never runs `engram doctor`/`serve` or touches `engram.db`."
|
|
84
86
|
}
|
|
85
87
|
},
|
|
86
88
|
{
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## Role
|
|
6
6
|
|
|
7
7
|
The Harness Orchestrator (conductor) coordinates work across local coding agents
|
|
8
|
-
(Cursor, Codex, OpenCode, Claude). It does not replace any agent. It defines how
|
|
8
|
+
(Cursor, Codex, OpenCode, Claude, Pi). It does not replace any agent. It defines how
|
|
9
9
|
agents hand off work and which source of truth governs.
|
|
10
10
|
|
|
11
11
|
## Authority order
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## Role
|
|
6
6
|
|
|
7
7
|
The Harness Orchestrator (conductor) coordinates work across local coding agents
|
|
8
|
-
(Cursor, Codex, OpenCode, Claude). It does not replace any agent. It defines how
|
|
8
|
+
(Cursor, Codex, OpenCode, Claude, Pi). It does not replace any agent. It defines how
|
|
9
9
|
agents hand off work and which source of truth governs.
|
|
10
10
|
|
|
11
11
|
## Authority order
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude,
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
7
7
|
"repository": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"agents",
|
|
46
46
|
"codex",
|
|
47
47
|
"cursor",
|
|
48
|
+
"pi",
|
|
48
49
|
"kairo",
|
|
49
50
|
"kairo-runtime",
|
|
50
51
|
"harness",
|
|
@@ -26,7 +26,7 @@ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
26
26
|
const require = createRequire(import.meta.url);
|
|
27
27
|
const pkg = require(join(root, "package.json"));
|
|
28
28
|
|
|
29
|
-
assert.equal(pkg.version, "0.
|
|
29
|
+
assert.equal(pkg.version, "0.7.0");
|
|
30
30
|
assert.ok(pkg.dependencies["ansi-escapes"]);
|
|
31
31
|
|
|
32
32
|
assert.equal(resolveLayoutMode({ columns: 120, rows: 40 }), LAYOUT_MODES.WIDE);
|
package/scripts/install.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Safe by design:
|
|
5
5
|
# - no sudo
|
|
6
6
|
# - no shell profile changes
|
|
7
|
-
# - does not install Cursor/Codex/OpenCode/Claude
|
|
7
|
+
# - does not install Cursor/Codex/OpenCode/Claude/Pi
|
|
8
8
|
# - default ends with setup --dry-run (no writes)
|
|
9
9
|
# - explicit apply: pass --yes to run setup --yes
|
|
10
10
|
#
|
|
@@ -88,9 +88,9 @@ if (!payload.ok || payload.overall !== 'ok') {
|
|
|
88
88
|
process.exit(1);
|
|
89
89
|
}
|
|
90
90
|
const managed = payload.agents.filter((agent) => agent.managed).map((agent) => agent.id).sort();
|
|
91
|
-
const expected = ['claude', 'codex', 'cursor', 'opencode'];
|
|
91
|
+
const expected = ['claude', 'codex', 'cursor', 'opencode', 'pi'];
|
|
92
92
|
if (JSON.stringify(managed) !== JSON.stringify(expected)) {
|
|
93
|
-
console.error('Expected all
|
|
93
|
+
console.error('Expected all five agents managed');
|
|
94
94
|
process.exit(1);
|
|
95
95
|
}
|
|
96
96
|
" "$status_json"
|
|
@@ -105,7 +105,8 @@ const targets = [
|
|
|
105
105
|
'.cursor/AGENTS.md',
|
|
106
106
|
'.codex/AGENTS.md',
|
|
107
107
|
'.config/opencode/AGENTS.md',
|
|
108
|
-
'.claude/CLAUDE.md'
|
|
108
|
+
'.claude/CLAUDE.md',
|
|
109
|
+
'.pi/agent/AGENTS.md'
|
|
109
110
|
];
|
|
110
111
|
|
|
111
112
|
for (const relativePath of targets) {
|
|
@@ -143,7 +144,8 @@ mkdir -p \
|
|
|
143
144
|
"$FAKE_HOME/.cursor" \
|
|
144
145
|
"$FAKE_HOME/.codex" \
|
|
145
146
|
"$FAKE_HOME/.config/opencode" \
|
|
146
|
-
"$FAKE_HOME/.claude"
|
|
147
|
+
"$FAKE_HOME/.claude" \
|
|
148
|
+
"$FAKE_HOME/.pi/agent"
|
|
147
149
|
|
|
148
150
|
echo
|
|
149
151
|
echo "== curl install.sh | sh --version ${VERSION} --yes --agents all =="
|
package/src/cli.js
CHANGED
|
@@ -38,6 +38,7 @@ import { runWorkspaceDetect, runWorkspaceDoctor, runWorkspaceInit, runWorkspaceU
|
|
|
38
38
|
import { runOrchestratorDiagnostics, runOrchestratorShell } from "./global/orchestrator.js";
|
|
39
39
|
import { runIntelligenceCli } from "./global/intelligence-cli.js";
|
|
40
40
|
import { runGlobalRun, runGlobalRuns } from "./global/runtime/run-cli.js";
|
|
41
|
+
import { runGlobalReview, runGlobalReviews } from "./global/runtime/review/review-cli.js";
|
|
41
42
|
import {
|
|
42
43
|
LEGACY_PACKAGE_NAME,
|
|
43
44
|
PACKAGE_NAME,
|
|
@@ -111,6 +112,12 @@ export async function runCli(argv) {
|
|
|
111
112
|
case "runs":
|
|
112
113
|
await runGlobalRuns(optionsWithPolicy, packageManifest);
|
|
113
114
|
return;
|
|
115
|
+
case "review":
|
|
116
|
+
await runGlobalReview(optionsWithPolicy, packageManifest);
|
|
117
|
+
return;
|
|
118
|
+
case "reviews":
|
|
119
|
+
await runGlobalReviews(optionsWithPolicy, packageManifest);
|
|
120
|
+
return;
|
|
114
121
|
case "intelligence":
|
|
115
122
|
await runIntelligenceCli(optionsWithPolicy, packageManifest);
|
|
116
123
|
return;
|
|
@@ -371,6 +378,11 @@ export function parseArgs(argv) {
|
|
|
371
378
|
intelligencePaths: [],
|
|
372
379
|
runsAction: null,
|
|
373
380
|
runId: null,
|
|
381
|
+
reviewId: null,
|
|
382
|
+
reviewsAction: null,
|
|
383
|
+
base: null,
|
|
384
|
+
commit: null,
|
|
385
|
+
failOn: null,
|
|
374
386
|
agent: null,
|
|
375
387
|
task: null,
|
|
376
388
|
model: null,
|
|
@@ -401,6 +413,10 @@ export function parseArgs(argv) {
|
|
|
401
413
|
parseRunsAction(args, options);
|
|
402
414
|
}
|
|
403
415
|
|
|
416
|
+
if (command === "reviews") {
|
|
417
|
+
parseReviewsAction(args, options);
|
|
418
|
+
}
|
|
419
|
+
|
|
404
420
|
if (command === "intelligence") {
|
|
405
421
|
parseIntelligenceAction(args, options);
|
|
406
422
|
}
|
|
@@ -495,6 +511,15 @@ export function parseArgs(argv) {
|
|
|
495
511
|
else if (arg.startsWith("--timeout=")) options.timeoutMs = parsePositiveInt(arg.slice("--timeout=".length), "timeout") * 1000;
|
|
496
512
|
else if (arg === "--include-private") options.includePrivate = true;
|
|
497
513
|
else if (arg === "--cloud-consent") options.cloudConsent = true;
|
|
514
|
+
else if (arg === "--base") options.base = requireFlagValue("--base", args[++index]);
|
|
515
|
+
else if (arg.startsWith("--base=")) options.base = requireFlagValue("--base", arg.slice("--base=".length));
|
|
516
|
+
else if (arg === "--commit") options.commit = requireFlagValue("--commit", args[++index]);
|
|
517
|
+
else if (arg.startsWith("--commit=")) {
|
|
518
|
+
options.commit = requireFlagValue("--commit", arg.slice("--commit=".length));
|
|
519
|
+
} else if (arg === "--fail-on") options.failOn = requireFlagValue("--fail-on", args[++index]);
|
|
520
|
+
else if (arg.startsWith("--fail-on=")) {
|
|
521
|
+
options.failOn = requireFlagValue("--fail-on", arg.slice("--fail-on=".length));
|
|
522
|
+
}
|
|
498
523
|
else if (arg === "--help" || arg === "-h") options.help = true;
|
|
499
524
|
else if (arg === "--version" || arg === "-v") options.version = true;
|
|
500
525
|
else throw new Error(`Unknown option "${arg}".`);
|
|
@@ -624,6 +649,26 @@ function parseRunsAction(args, options) {
|
|
|
624
649
|
}
|
|
625
650
|
}
|
|
626
651
|
|
|
652
|
+
function parseReviewsAction(args, options) {
|
|
653
|
+
const action = args[0];
|
|
654
|
+
if (!action || action.startsWith("-")) {
|
|
655
|
+
options.reviewsAction = "list";
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (!new Set(["list", "show"]).has(action)) {
|
|
659
|
+
throw new Error(`Unknown reviews action "${action}". Use list or show.`);
|
|
660
|
+
}
|
|
661
|
+
args.shift();
|
|
662
|
+
options.reviewsAction = action;
|
|
663
|
+
if (action === "show") {
|
|
664
|
+
const reviewId = args[0];
|
|
665
|
+
if (!reviewId || reviewId.startsWith("-")) {
|
|
666
|
+
throw new Error(`Missing review id. Use: ${formatCliCommand("reviews show <reviewId>")}`);
|
|
667
|
+
}
|
|
668
|
+
options.reviewId = args.shift();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
627
672
|
function parseIntelligenceAction(args, options) {
|
|
628
673
|
const action = args[0];
|
|
629
674
|
if (!action || action.startsWith("-")) {
|
|
@@ -657,6 +702,13 @@ function parsePositiveInt(value, label) {
|
|
|
657
702
|
return parsed;
|
|
658
703
|
}
|
|
659
704
|
|
|
705
|
+
function requireFlagValue(flag, value) {
|
|
706
|
+
if (value == null || value === "" || String(value).startsWith("-")) {
|
|
707
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
708
|
+
}
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
711
|
+
|
|
660
712
|
function parseScope(value) {
|
|
661
713
|
if (!SCOPES.has(value)) {
|
|
662
714
|
throw new Error(`Invalid scope "${value}". Use agent-global or workspace.`);
|
|
@@ -673,6 +725,8 @@ function normalizeCommand(command) {
|
|
|
673
725
|
if (command === "orchestrator") return "orchestrator";
|
|
674
726
|
if (command === "run") return "run";
|
|
675
727
|
if (command === "runs") return "runs";
|
|
728
|
+
if (command === "review") return "review";
|
|
729
|
+
if (command === "reviews") return "reviews";
|
|
676
730
|
if (command === "intelligence" || command === "intel") return "intelligence";
|
|
677
731
|
if (command === "setup") return "setup";
|
|
678
732
|
if (command === "status") return "status";
|
|
@@ -732,7 +786,7 @@ function printHelp() {
|
|
|
732
786
|
console.log(`${BRAND.displayName} (${PACKAGE_NAME})
|
|
733
787
|
|
|
734
788
|
${BRAND.tagline}. ${BRAND.displayName} does not install AI apps — it powers and
|
|
735
|
-
coordinates agents you already have (Cursor, Codex, OpenCode, Claude) with managed
|
|
789
|
+
coordinates agents you already have (Cursor, Codex, OpenCode, Claude, Pi) with managed
|
|
736
790
|
sections, components, backups, and drift repair under ~/.harness.
|
|
737
791
|
|
|
738
792
|
Bootstrap: see README.md (curl install.sh or npx ${PACKAGE_NAME}).
|
|
@@ -747,6 +801,9 @@ Usage:
|
|
|
747
801
|
${cli} runs list [--json] [--limit <n>] [--active-only]
|
|
748
802
|
${cli} runs show <runId> [--json] [--limit <n>] [--follow]
|
|
749
803
|
${cli} runs stop <runId> [--json]
|
|
804
|
+
${cli} review --agent codex|pi [--base <ref>|--commit <sha>] [--model <name>] [--include-private] [--yes|--confirm] [--fail-on high|medium|low] [--json]
|
|
805
|
+
${cli} reviews list [--limit <n>] [--json]
|
|
806
|
+
${cli} reviews show <reviewId> [--json]
|
|
750
807
|
${cli} orchestrator [--json] Read-only agent capability diagnostics
|
|
751
808
|
${cli} intelligence [status|models|context|route|ask] [--json]
|
|
752
809
|
${cli} intelligence models --backend opencode-go|opencode-zen|opencode
|
|
@@ -795,6 +852,8 @@ Commands:
|
|
|
795
852
|
Tab switches region only when content is interactive (runs/launch).
|
|
796
853
|
run Launch a managed agent run with local audit trail.
|
|
797
854
|
runs List, inspect, or cancel agent runs under ~/.harness/runs/.
|
|
855
|
+
review Bounded read-only review via Codex or Pi; receipts under ~/.harness/reviews/.
|
|
856
|
+
reviews List or show prior review receipts (secret-free).
|
|
798
857
|
orchestrator Read-only capability registry diagnostics (--json supported).
|
|
799
858
|
intelligence Harness Engineering layer: backends, context packs, routing, budgets.
|
|
800
859
|
Local-first (Ollama). Cloud only with --cloud-consent.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { createManagedConfigAdapter } from "../managed-config-adapter.js";
|
|
3
|
+
|
|
4
|
+
export const PI_DEFAULT_ROOT_DIR = ".pi/agent";
|
|
5
|
+
export const PI_DEFAULT_CONFIG_FILE = ".pi/agent/AGENTS.md";
|
|
6
|
+
export const PI_CODING_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
7
|
+
|
|
8
|
+
const CUSTOM_DIR_MESSAGE =
|
|
9
|
+
`Pi config writes are unsupported when ${PI_CODING_AGENT_DIR_ENV} points away from `
|
|
10
|
+
+ `~/${PI_DEFAULT_ROOT_DIR} (out of scope for Kairo 0.6.0). `
|
|
11
|
+
+ `Unset ${PI_CODING_AGENT_DIR_ENV} to manage the default directory; runtime launches remain available.`;
|
|
12
|
+
|
|
13
|
+
/** True when PI_CODING_AGENT_DIR is set to a non-default absolute/relative path. */
|
|
14
|
+
export function isCustomPiCodingAgentDir(env = process.env, homeDir) {
|
|
15
|
+
const raw = env?.[PI_CODING_AGENT_DIR_ENV];
|
|
16
|
+
if (raw == null || String(raw).trim() === "") return false;
|
|
17
|
+
if (homeDir == null) return true;
|
|
18
|
+
return resolve(String(raw).trim()) !== resolve(homeDir, PI_DEFAULT_ROOT_DIR);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function assertDefaultPiConfigDir(context = {}) {
|
|
22
|
+
const env = context.env ?? process.env;
|
|
23
|
+
if (isCustomPiCodingAgentDir(env, context.homeDir)) {
|
|
24
|
+
throw new Error(CUSTOM_DIR_MESSAGE);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const managed = createManagedConfigAdapter({
|
|
29
|
+
id: "pi",
|
|
30
|
+
label: "Pi",
|
|
31
|
+
rootDir: PI_DEFAULT_ROOT_DIR,
|
|
32
|
+
configFile: PI_DEFAULT_CONFIG_FILE
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Managed adapter for ~/.pi/agent/AGENTS.md; custom PI_CODING_AGENT_DIR fails before writes. */
|
|
36
|
+
export default {
|
|
37
|
+
...managed,
|
|
38
|
+
|
|
39
|
+
plan(context) {
|
|
40
|
+
assertDefaultPiConfigDir(context);
|
|
41
|
+
return managed.plan(context);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async apply(context, plan) {
|
|
45
|
+
assertDefaultPiConfigDir(context);
|
|
46
|
+
return managed.apply(context, plan);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async uninstall(context, stateEntry) {
|
|
50
|
+
assertDefaultPiConfigDir(context);
|
|
51
|
+
return managed.uninstall(context, stateEntry);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
@@ -2,6 +2,7 @@ import cursorManaged from "../adapters/cursor.js";
|
|
|
2
2
|
import codexManaged from "../adapters/codex.js";
|
|
3
3
|
import opencodeManaged from "../adapters/opencode.js";
|
|
4
4
|
import claudeManaged from "../adapters/claude.js";
|
|
5
|
+
import piManaged from "../adapters/pi.js";
|
|
5
6
|
import { createAgentCapabilityAdapter } from "./create-capability-adapter.js";
|
|
6
7
|
|
|
7
8
|
const cursor = createAgentCapabilityAdapter({
|
|
@@ -37,7 +38,16 @@ const claude = createAgentCapabilityAdapter({
|
|
|
37
38
|
runExecutable: "claude"
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
const
|
|
41
|
+
const pi = createAgentCapabilityAdapter({
|
|
42
|
+
id: "pi",
|
|
43
|
+
label: "Pi",
|
|
44
|
+
managedAdapter: piManaged,
|
|
45
|
+
executable: "pi",
|
|
46
|
+
opaqueAuth: true,
|
|
47
|
+
runExecutable: "pi"
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const CAPABILITY_ADAPTERS = [cursor, codex, opencode, claude, pi];
|
|
41
51
|
|
|
42
52
|
export const AGENT_CAPABILITY_IDS = CAPABILITY_ADAPTERS.map((adapter) => adapter.id);
|
|
43
53
|
|
|
@@ -30,14 +30,16 @@ export const AGENT_LABELS = {
|
|
|
30
30
|
cursor: "Cursor",
|
|
31
31
|
codex: "Codex",
|
|
32
32
|
opencode: "OpenCode",
|
|
33
|
-
claude: "Claude Code"
|
|
33
|
+
claude: "Claude Code",
|
|
34
|
+
pi: "Pi"
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export const AGENT_SYMBOLS = {
|
|
37
38
|
cursor: "◆",
|
|
38
39
|
codex: "◆",
|
|
39
40
|
opencode: "◆",
|
|
40
|
-
claude: "◆"
|
|
41
|
+
claude: "◆",
|
|
42
|
+
pi: "◆"
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
export const TOKENS = {
|
|
@@ -150,7 +150,7 @@ export function resolveDashboardRecommendation({
|
|
|
150
150
|
kind: NEXT_STEP_KINDS.LAUNCH,
|
|
151
151
|
title: "Create a new run",
|
|
152
152
|
message: "Create a new run",
|
|
153
|
-
detail: "Delegate a task to Cursor, Codex or
|
|
153
|
+
detail: "Delegate a task to Cursor, Codex, Claude, or Pi.",
|
|
154
154
|
targetView: RECOMMENDATION_TARGETS.LAUNCH,
|
|
155
155
|
targetAction: "launch"
|
|
156
156
|
};
|
package/src/global/global-cli.js
CHANGED
|
@@ -734,7 +734,7 @@ function printAdapterMatrixReport(report) {
|
|
|
734
734
|
console.log(commandHeader("adapters — supported agent integrations"));
|
|
735
735
|
console.log(`Home: ${report.homeDir}`);
|
|
736
736
|
console.log("");
|
|
737
|
-
console.log(`${BRAND.displayName} does not install Cursor, Codex, OpenCode,
|
|
737
|
+
console.log(`${BRAND.displayName} does not install Cursor, Codex, OpenCode, Claude Code, or Pi.`);
|
|
738
738
|
console.log("It configures managed sections in each agent's config files.");
|
|
739
739
|
console.log("");
|
|
740
740
|
console.log(`Supported: ${report.supportedCount} Detected: ${report.detectedCount} Managed: ${report.managedCount}`);
|
|
@@ -144,6 +144,16 @@ export function reduceCockpitUi(state, action) {
|
|
|
144
144
|
returnView: null
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
|
+
if (state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
148
|
+
const back = ORCHESTRATOR_VIEWS.REVIEWS;
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
view: back,
|
|
152
|
+
listIndex: 0,
|
|
153
|
+
region: defaultRegionForView(back, state.layoutMode),
|
|
154
|
+
returnView: null
|
|
155
|
+
};
|
|
156
|
+
}
|
|
147
157
|
if (isRunsChildView(state.view)) {
|
|
148
158
|
return goRunsHub(state);
|
|
149
159
|
}
|
|
@@ -18,6 +18,8 @@ const CONTENT_INTERACTIVE_VIEWS = new Set([
|
|
|
18
18
|
ORCHESTRATOR_VIEWS.ACTIVE_RUNS,
|
|
19
19
|
ORCHESTRATOR_VIEWS.RECENT_RUNS,
|
|
20
20
|
ORCHESTRATOR_VIEWS.RUN_DETAIL,
|
|
21
|
+
ORCHESTRATOR_VIEWS.REVIEWS,
|
|
22
|
+
ORCHESTRATOR_VIEWS.REVIEW_DETAIL,
|
|
21
23
|
ORCHESTRATOR_VIEWS.LAUNCH,
|
|
22
24
|
ORCHESTRATOR_VIEWS.ACTIVITY
|
|
23
25
|
]);
|
|
@@ -41,7 +43,11 @@ export function defaultRegionForView(view, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
|
41
43
|
|
|
42
44
|
export function interactiveRegionsFor(state) {
|
|
43
45
|
const regions = regionsForLayout(state.layoutMode);
|
|
44
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
!isContentInteractiveView(state.view)
|
|
48
|
+
|| state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
49
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
50
|
+
) {
|
|
45
51
|
return regions.filter((region) => region === COCKPIT_REGIONS.NAV);
|
|
46
52
|
}
|
|
47
53
|
return regions.filter(
|
|
@@ -51,7 +57,12 @@ export function interactiveRegionsFor(state) {
|
|
|
51
57
|
|
|
52
58
|
export function canTabBetweenRegions(state) {
|
|
53
59
|
if (!isContentInteractiveView(state.view)) return false;
|
|
54
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
62
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
63
|
+
) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
55
66
|
return interactiveRegionsFor(state).length >= 2;
|
|
56
67
|
}
|
|
57
68
|
|
|
@@ -70,7 +81,11 @@ export function routeCockpitKey(state, keyAction) {
|
|
|
70
81
|
return { type: "arrow", direction: keyAction.direction };
|
|
71
82
|
}
|
|
72
83
|
if (state.region === COCKPIT_REGIONS.CONTENT && isContentInteractiveView(state.view)) {
|
|
73
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
86
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
87
|
+
|| state.view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
88
|
+
) {
|
|
74
89
|
return null;
|
|
75
90
|
}
|
|
76
91
|
return {
|
|
@@ -250,6 +250,11 @@ export function buildFooterModel({
|
|
|
250
250
|
return { text: parts.join(` ${glyphs.bullet} `) };
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
if (view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
254
|
+
parts.push("Esc Back");
|
|
255
|
+
return { text: parts.join(` ${glyphs.bullet} `) };
|
|
256
|
+
}
|
|
257
|
+
|
|
253
258
|
if (view === ORCHESTRATOR_VIEWS.CHANGES) {
|
|
254
259
|
return { text: buildChangesFooterParts(changesPhase).join(` ${glyphs.bullet} `) };
|
|
255
260
|
}
|
|
@@ -263,6 +268,7 @@ export function buildFooterModel({
|
|
|
263
268
|
const showTab = view === ORCHESTRATOR_VIEWS.RUNS
|
|
264
269
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
265
270
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
271
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
266
272
|
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
267
273
|
|| view === ORCHESTRATOR_VIEWS.ACTIVITY;
|
|
268
274
|
if (showTab) {
|
|
@@ -280,7 +286,8 @@ export function buildFooterModel({
|
|
|
280
286
|
|| region === COCKPIT_REGIONS.NAV
|
|
281
287
|
|| view === ORCHESTRATOR_VIEWS.RUNS
|
|
282
288
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
283
|
-
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
289
|
+
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
290
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS) {
|
|
284
291
|
parts.push("Enter Open");
|
|
285
292
|
}
|
|
286
293
|
if (view !== ORCHESTRATOR_VIEWS.LAUNCH) {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Read-only Cockpit helpers for review receipts under ~/.harness/reviews/. */
|
|
2
|
+
|
|
3
|
+
export function countFindingsBySeverity(findings = []) {
|
|
4
|
+
const counts = { high: 0, medium: 0, low: 0 };
|
|
5
|
+
for (const finding of findings) {
|
|
6
|
+
const severity = String(finding?.severity ?? "").toLowerCase();
|
|
7
|
+
if (counts[severity] != null) counts[severity] += 1;
|
|
8
|
+
}
|
|
9
|
+
return counts;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function selectReviewFromList(receipts = [], listIndex = 0) {
|
|
13
|
+
if (!Array.isArray(receipts) || receipts.length === 0) return null;
|
|
14
|
+
const index = Math.min(Math.max(0, listIndex), receipts.length - 1);
|
|
15
|
+
return receipts[index] ?? null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function formatReviewListLines(receipts = []) {
|
|
19
|
+
if (!Array.isArray(receipts) || receipts.length === 0) {
|
|
20
|
+
return ["No review receipts yet. Run kairo review --agent codex|pi."];
|
|
21
|
+
}
|
|
22
|
+
return receipts.map((receipt) => {
|
|
23
|
+
const counts = countFindingsBySeverity(receipt.findings);
|
|
24
|
+
const findingTotal = (receipt.findings ?? []).length;
|
|
25
|
+
const created = String(receipt.createdAt ?? "").slice(0, 19).replace("T", " ");
|
|
26
|
+
return `${receipt.reviewId} ${String(receipt.state).padEnd(10)} ${String(receipt.agentId).padEnd(6)} ${findingTotal}f (h${counts.high}/m${counts.medium}/l${counts.low}) ${created}`;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function formatReviewDetailLines(receipt) {
|
|
31
|
+
if (!receipt) return ["Review receipt not found."];
|
|
32
|
+
const counts = countFindingsBySeverity(receipt.findings);
|
|
33
|
+
const snapshot = receipt.snapshot ?? {};
|
|
34
|
+
const totals = snapshot.totals ?? {};
|
|
35
|
+
const lines = [
|
|
36
|
+
`Id: ${receipt.reviewId}`,
|
|
37
|
+
`Agent: ${receipt.agentId}${receipt.model ? ` · model ${receipt.model}` : ""}`,
|
|
38
|
+
`State: ${receipt.state}`,
|
|
39
|
+
`Created: ${receipt.createdAt ?? "n/a"}`,
|
|
40
|
+
`Findings: ${(receipt.findings ?? []).length} (high ${counts.high}, medium ${counts.medium}, low ${counts.low})`,
|
|
41
|
+
`Snapshot: ${snapshot.mode ?? "n/a"} · files ${totals.fileCount ?? 0} · fingerprint ${String(snapshot.fingerprint ?? "").slice(0, 12) || "n/a"}`
|
|
42
|
+
];
|
|
43
|
+
if ((receipt.warnings ?? []).length > 0) {
|
|
44
|
+
lines.push(`Warnings: ${receipt.warnings.length}`);
|
|
45
|
+
}
|
|
46
|
+
const findings = receipt.findings ?? [];
|
|
47
|
+
if (findings.length === 0) {
|
|
48
|
+
lines.push("", "No findings recorded.");
|
|
49
|
+
return lines;
|
|
50
|
+
}
|
|
51
|
+
lines.push("", "Findings (read-only):");
|
|
52
|
+
for (const finding of findings.slice(0, 20)) {
|
|
53
|
+
const loc = finding.line != null ? `:${finding.line}` : "";
|
|
54
|
+
lines.push(
|
|
55
|
+
` [${String(finding.severity).toUpperCase()}] ${finding.path ?? "?"}${loc} — ${finding.title ?? "untitled"}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (findings.length > 20) {
|
|
59
|
+
lines.push(` … ${findings.length - 20} more (use kairo reviews show ${receipt.reviewId})`);
|
|
60
|
+
}
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
@@ -13,6 +13,12 @@ export const RUNS_HUB_ITEMS = [
|
|
|
13
13
|
view: ORCHESTRATOR_VIEWS.RECENT_RUNS,
|
|
14
14
|
description: "Review completed and failed run outcomes."
|
|
15
15
|
},
|
|
16
|
+
{
|
|
17
|
+
id: "reviews",
|
|
18
|
+
label: "Reviews",
|
|
19
|
+
view: ORCHESTRATOR_VIEWS.REVIEWS,
|
|
20
|
+
description: "Browse secret-free bounded-review receipts (read-only)."
|
|
21
|
+
},
|
|
16
22
|
{
|
|
17
23
|
id: "launch",
|
|
18
24
|
label: "New run",
|
|
@@ -27,13 +33,16 @@ export function isRunsBranchView(view) {
|
|
|
27
33
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
28
34
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
29
35
|
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
30
|
-
|| view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
36
|
+
|| view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
37
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
38
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export function isRunsChildView(view) {
|
|
34
42
|
return view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
35
43
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
36
|
-
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
44
|
+
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
45
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
export function resolveRunsHubItem(listIndex = 0, items = RUNS_HUB_ITEMS) {
|