@join-pantheon/cli 0.26.5 → 0.27.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 +54 -1
- package/dist/agents.js +83 -5
- package/dist/cli.js +30 -12
- package/dist/doctor.js +21 -6
- package/dist/hooks-install.js +46 -1
- package/dist/install.js +29 -4
- package/dist/kiro-files.js +56 -0
- package/dist/machine.js +5 -1
- package/dist/measure.js +31 -3
- package/dist/notice.js +3 -1
- package/dist/notify.js +15 -3
- package/dist/persona.js +1 -1
- package/dist/profile.js +4 -1
- package/dist/run-failure.js +1 -0
- package/dist/setup-guide.js +1 -1
- package/dist/tray.js +1 -1
- package/dist/worker.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -404,7 +404,7 @@ with no human; the cap and lock governors under two workers per member.
|
|
|
404
404
|
|
|
405
405
|
### Which agent answers
|
|
406
406
|
|
|
407
|
-
The worker runs whichever agent CLI this machine has — `--agent claude-code|codex|cursor|copilot|auto`.
|
|
407
|
+
The worker runs whichever agent CLI this machine has — `--agent claude-code|codex|cursor|copilot|kiro|auto`.
|
|
408
408
|
With `auto` (the default) the order is: the agent a persona pins, then the one you chose at setup,
|
|
409
409
|
then PATH order — and a candidate that is not signed in is skipped and named in the run log, so a
|
|
410
410
|
run never fails on an expired session when another agent could have answered. If nothing is
|
|
@@ -418,6 +418,7 @@ actually works, with the same "no shell unless you said so" rule spelled in its
|
|
|
418
418
|
| codex | `--sandbox read-only` (codex has no edits-without-shell mode) | `--sandbox workspace-write` |
|
|
419
419
|
| cursor | proposes edits, never applies (`-p` without `--force`) | `--force` |
|
|
420
420
|
| copilot | `--allow-tool=pantheon-worker --allow-tool=write` (edits without shell, Copilot's `write` kind) | `--allow-all-tools` |
|
|
421
|
+
| kiro | an agent whose tools are pantheon's plus `read`, `write`, `grep`, `glob` — all pre-approved | the same, plus `shell` |
|
|
421
422
|
|
|
422
423
|
Claude Code, Cursor and Codex are verified with **real live woken runs** — an idle machine answering a
|
|
423
424
|
peer's ASK end to end. Codex quirks the tooling handles for
|
|
@@ -458,6 +459,58 @@ named as that, with `copilot login` as the fix.
|
|
|
458
459
|
also restrict MCP servers to an approved registry; the server is called `pantheon` and its only job
|
|
459
460
|
is moving messages between your teammates' agents — nothing here reads code or runs commands.
|
|
460
461
|
|
|
462
|
+
### Kiro
|
|
463
|
+
|
|
464
|
+
Kiro is one backend covering the **Kiro CLI** (`kiro-cli`) and the **Kiro IDE**, both of which read
|
|
465
|
+
`~/.kiro/settings/mcp.json`; `pantheon install --user` writes pantheon there. Everything below about
|
|
466
|
+
the CLI was checked live against kiro-cli 2.23.1.
|
|
467
|
+
|
|
468
|
+
**Sessions report presence on their own.** The entry names its agent (`PANTHEON_AGENT=kiro`), so a Kiro
|
|
469
|
+
session is labelled Kiro to your teammates and reports busy and idle through its own MCP server. Without
|
|
470
|
+
that label it would take the machine's main agent as its own, and on a Claude Code machine report nothing.
|
|
471
|
+
There is deliberately no repo-level Kiro file: Kiro uses a workspace-level server of the same name whole,
|
|
472
|
+
so a committed copy would replace each teammate's own entry.
|
|
473
|
+
|
|
474
|
+
**Approval.** The entry's `autoApprove` list carries your approval choice, and the Kiro IDE honours it.
|
|
475
|
+
The Kiro CLI ignores it — its default agent asks before each Pantheon call, and has no setting that
|
|
476
|
+
changes that.
|
|
477
|
+
|
|
478
|
+
**The `pantheon` agent, for the CLI.** In kiro-cli the only hooks that fire are the ones inside an agent
|
|
479
|
+
file; neither of its engines reads the standalone `.kiro/hooks/` files, and the built-in default agent
|
|
480
|
+
cannot carry any. So `pantheon hook install` adds an agent called `pantheon`: Kiro's default — the same
|
|
481
|
+
tools (`tools: ["*"]`) and your own MCP servers (`includeMcpJson: true`) — plus Pantheon's tools
|
|
482
|
+
pre-approved per your choice, a `userPromptSubmit` hook that reports a turn as started and prints
|
|
483
|
+
nothing, and a `stop` hook that holds the turn open while a teammate waits. It is opt-in and nothing
|
|
484
|
+
makes it your default:
|
|
485
|
+
|
|
486
|
+
```
|
|
487
|
+
kiro-cli chat --agent pantheon # this session
|
|
488
|
+
kiro-cli agent set-default pantheon # every session
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Checked live: with a teammate's question waiting and a prompt that said only "reply HELLO", the agent
|
|
492
|
+
replied, the stop hook held the turn, and it read, claimed and answered the question through Pantheon
|
|
493
|
+
without one approval prompt. An agent called `pantheon` that you wrote yourself is never overwritten.
|
|
494
|
+
|
|
495
|
+
**Woken runs.** A woken Kiro run is its own agent, `pantheon-worker`, in a `KIRO_HOME` of its own outside
|
|
496
|
+
the checkout. That matters: Kiro starts every MCP server in its config whatever the agent says, so your own
|
|
497
|
+
pantheon entry would otherwise join every run as a second server reporting you present. The run's home
|
|
498
|
+
holds only its agent and your Kiro settings; your sign-in, which lives in Kiro's data store, still applies.
|
|
499
|
+
The agent's `tools` and `allowedTools` are one list — Pantheon's server and `read`, `write`, `grep`,
|
|
500
|
+
`glob`, with `shell` only under `--allow-bash` — so nothing it can reach needs approval, and no trust flag
|
|
501
|
+
is passed. A repo's own committed Kiro MCP servers still start, as the repo's tooling; their tools are not
|
|
502
|
+
in the run's list, and one that fails does not stop the run.
|
|
503
|
+
|
|
504
|
+
**Sign in once with `kiro-cli login`, or set `KIRO_API_KEY`.** Signed out, Kiro does not fail: it opens a
|
|
505
|
+
browser to log in and waits. So a woken run is never started with Kiro signed out, even when a persona pins
|
|
506
|
+
Kiro or it is the only agent on the machine; the run is refused with the reason, and `pantheon doctor` says
|
|
507
|
+
so first. Pantheon reads the sign-in with `kiro-cli whoami --format json`, and only once Kiro has run on the
|
|
508
|
+
machine, because on a fresh install that one call creates `~/.kiro` and Kiro's data store.
|
|
509
|
+
|
|
510
|
+
Not supported: Kiro Crew, which is itself an always-on agent runner and would be a second answerer beside
|
|
511
|
+
Pantheon's worker, and Kiro Mobile, which has nowhere to load an MCP server. The Kiro IDE's own hooks are
|
|
512
|
+
not wired, because they could not be checked here; IDE sessions get the MCP entry and presence.
|
|
513
|
+
|
|
461
514
|
### Personas — telling the worker who to be
|
|
462
515
|
|
|
463
516
|
A worker with no instructions escalates too much and trusts too much. A **persona** is one
|
package/dist/agents.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
2
3
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { homedir, platform } from "node:os";
|
|
4
5
|
import { join, dirname, isAbsolute } from "node:path";
|
|
5
|
-
import { launcher } from "./install.js";
|
|
6
|
+
import { launcher, kiroDir } from "./install.js";
|
|
6
7
|
import { agentSpawn, which } from "./which.js";
|
|
7
8
|
import { PANTHEON_TOOL_NAMES, PANTHEON_TOOLS } from "./tools.js";
|
|
8
9
|
export function launchArgs(agent, prompt, name) {
|
|
@@ -10,6 +11,8 @@ export function launchArgs(agent, prompt, name) {
|
|
|
10
11
|
return ["--name", name, prompt];
|
|
11
12
|
if (agent === "copilot")
|
|
12
13
|
return ["-n", name, "-i", prompt];
|
|
14
|
+
if (agent === "kiro")
|
|
15
|
+
return ["chat", prompt];
|
|
13
16
|
return [prompt];
|
|
14
17
|
}
|
|
15
18
|
export const AGENTS = [
|
|
@@ -17,7 +20,11 @@ export const AGENTS = [
|
|
|
17
20
|
{ id: "codex", label: "Codex", binaries: ["codex"] },
|
|
18
21
|
{ id: "cursor", label: "Cursor", binaries: ["cursor-agent", "agent"] },
|
|
19
22
|
{ id: "copilot", label: "Copilot", binaries: ["copilot"] },
|
|
23
|
+
{ id: "kiro", label: "Kiro", binaries: ["kiro-cli"] },
|
|
20
24
|
];
|
|
25
|
+
export function kiroHome(home = homedir(), env = process.env) {
|
|
26
|
+
return kiroDir(home, env);
|
|
27
|
+
}
|
|
21
28
|
export function copilotHome(home = homedir(), env = process.env) {
|
|
22
29
|
return env.COPILOT_HOME || join(home, ".copilot");
|
|
23
30
|
}
|
|
@@ -57,6 +64,11 @@ export function detectForHooks(projectDir = process.cwd()) {
|
|
|
57
64
|
out.push(backend);
|
|
58
65
|
continue;
|
|
59
66
|
}
|
|
67
|
+
if (backend.id === "kiro") {
|
|
68
|
+
if (existsSync(kiroHome()) || existsSync(join(projectDir, ".kiro")))
|
|
69
|
+
out.push(backend);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
60
72
|
const userDir = backend.id === "claude-code" ? join(homedir(), ".claude")
|
|
61
73
|
: backend.id === "codex" ? join(homedir(), ".codex")
|
|
62
74
|
: join(homedir(), ".cursor");
|
|
@@ -107,7 +119,28 @@ export const SIGN_IN_CHECK = {
|
|
|
107
119
|
codex: { args: ["login", "status"], fix: "run `codex login`" },
|
|
108
120
|
cursor: { args: ["status"], fix: "run `cursor-agent login`" },
|
|
109
121
|
copilot: { args: [], fix: "run `copilot login` (or `gh auth login` — copilot uses gh's sign-in too)" },
|
|
122
|
+
kiro: { args: ["whoami", "--format", "json"], fix: "run `kiro-cli login` (or set KIRO_API_KEY)" },
|
|
110
123
|
};
|
|
124
|
+
export const NEEDS_SIGN_IN_BEFORE_SPAWN = new Set(["kiro"]);
|
|
125
|
+
export function kiroSignedIn(binary, run = runStatus, env = process.env, home = homedir(), os = platform()) {
|
|
126
|
+
if (env.KIRO_API_KEY)
|
|
127
|
+
return { ok: true, detail: "signed in (KIRO_API_KEY in the environment)" };
|
|
128
|
+
if (!kiroHasRunHere(home, env, os))
|
|
129
|
+
return { ok: false, detail: "never signed in on this machine" };
|
|
130
|
+
const r = run(binary, SIGN_IN_CHECK.kiro.args);
|
|
131
|
+
if (r.status === null)
|
|
132
|
+
return { ok: false, detail: `could not tell (${(r.out.split("\n")[0] ?? "").slice(0, 60) || "no answer"}), so it is not run unattended` };
|
|
133
|
+
const line = r.out.split("\n").map((l) => l.trim()).reverse().find((l) => l.startsWith("{"));
|
|
134
|
+
try {
|
|
135
|
+
const j = JSON.parse(line ?? "");
|
|
136
|
+
if (typeof j.accountType === "string" && j.accountType)
|
|
137
|
+
return { ok: true, detail: "signed in" };
|
|
138
|
+
if ("account" in j)
|
|
139
|
+
return j.account ? { ok: true, detail: "signed in" } : { ok: false, detail: "not signed in" };
|
|
140
|
+
}
|
|
141
|
+
catch { }
|
|
142
|
+
return { ok: false, detail: "could not tell (kiro-cli whoami answered in a shape this build does not know), so it is not run unattended" };
|
|
143
|
+
}
|
|
111
144
|
export const COPILOT_TOKEN_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"];
|
|
112
145
|
export function copilotSignedIn(run = runStatus, env = process.env, gh = () => !!cliOnPath("gh")) {
|
|
113
146
|
if (COPILOT_TOKEN_VARS.some((k) => env[k]))
|
|
@@ -132,10 +165,12 @@ const runStatus = (binary, args) => {
|
|
|
132
165
|
return { status: err.status ?? null, out: `${err.stdout ?? ""}${err.stderr ?? ""}`.trim() || (err.message ?? "") };
|
|
133
166
|
}
|
|
134
167
|
};
|
|
135
|
-
export const AGENT_HOME_DIR = { "claude-code": ".claude", codex: ".codex", cursor: ".cursor", copilot: ".copilot" };
|
|
168
|
+
export const AGENT_HOME_DIR = { "claude-code": ".claude", codex: ".codex", cursor: ".cursor", copilot: ".copilot", kiro: ".kiro" };
|
|
136
169
|
export function signedIn(backend, binary, run = runStatus, home = homedir()) {
|
|
137
170
|
if (backend.id === "copilot")
|
|
138
171
|
return copilotSignedIn(run);
|
|
172
|
+
if (backend.id === "kiro")
|
|
173
|
+
return kiroSignedIn(binary, run, process.env, home);
|
|
139
174
|
if (!existsSync(join(home, AGENT_HOME_DIR[backend.id])))
|
|
140
175
|
return { ok: false, detail: "never signed in on this machine" };
|
|
141
176
|
const check = SIGN_IN_CHECK[backend.id];
|
|
@@ -159,14 +194,14 @@ export function chooseAgent(preferred, opts = {}) {
|
|
|
159
194
|
if (preferred && preferred !== "auto") {
|
|
160
195
|
const backend = agentById(preferred);
|
|
161
196
|
if (!backend)
|
|
162
|
-
throw new Error(`unknown agent ${preferred} — use claude-code, codex, cursor, copilot, or auto`);
|
|
197
|
+
throw new Error(`unknown agent ${preferred} — use claude-code, codex, cursor, copilot, kiro, or auto`);
|
|
163
198
|
const hit = available.find((a) => a.backend.id === backend.id);
|
|
164
199
|
if (!hit)
|
|
165
200
|
throw new Error(`${backend.label} is not on PATH (looked for ${backend.binaries.join(", ")})`);
|
|
166
201
|
return { backend, binary: hit.binary, signIn: state(backend, hit.binary), because: "pinned", skipped: [] };
|
|
167
202
|
}
|
|
168
203
|
if (!available.length)
|
|
169
|
-
throw new Error("no agent CLI on PATH — install claude, codex, cursor-agent, or
|
|
204
|
+
throw new Error("no agent CLI on PATH — install claude, codex, cursor-agent, copilot, or kiro-cli");
|
|
170
205
|
const order = [];
|
|
171
206
|
const wanted = opts.prefer ? agentById(opts.prefer) : undefined;
|
|
172
207
|
const chosen = wanted ? available.find((a) => a.backend.id === wanted.id) : undefined;
|
|
@@ -182,9 +217,14 @@ export function chooseAgent(preferred, opts = {}) {
|
|
|
182
217
|
return { backend: c.backend, binary: c.binary, ...(s ? { signIn: s } : {}), because: c.because, skipped };
|
|
183
218
|
skipped.push({ backend: c.backend, why: `${s.detail} — ${SIGN_IN_CHECK[c.backend.id].fix}` });
|
|
184
219
|
}
|
|
185
|
-
const first = order[0];
|
|
220
|
+
const first = order.find((o) => !NEEDS_SIGN_IN_BEFORE_SPAWN.has(o.backend.id)) ?? order[0];
|
|
186
221
|
return { backend: first.backend, binary: first.binary, signIn: state(first.backend, first.binary), because: `${first.because} (none signed in)`, skipped };
|
|
187
222
|
}
|
|
223
|
+
export function unattendedRefusal(c) {
|
|
224
|
+
if (!NEEDS_SIGN_IN_BEFORE_SPAWN.has(c.backend.id) || c.signIn?.ok)
|
|
225
|
+
return null;
|
|
226
|
+
return `${c.backend.label} is not signed in (${c.signIn?.detail ?? "not checked"}), and signed out it opens a browser to log in, so it is not started while you are away — ${SIGN_IN_CHECK[c.backend.id].fix}`;
|
|
227
|
+
}
|
|
188
228
|
export function describeChoice(c) {
|
|
189
229
|
const head = `${c.backend.label}${c.signIn ? ` — ${c.signIn.detail}` : ""} (${c.because})`;
|
|
190
230
|
return c.skipped.length ? `${head}; skipped ${c.skipped.map((s) => `${s.backend.label} — ${s.why}`).join("; ")}` : head;
|
|
@@ -283,6 +323,30 @@ export function headlessSpawn(opts) {
|
|
|
283
323
|
...(opts.runHome && existsSync(settings) ? { copyFiles: [{ from: settings, to: join(home, "settings.json") }] } : {}),
|
|
284
324
|
};
|
|
285
325
|
}
|
|
326
|
+
if (opts.backend.id === "kiro") {
|
|
327
|
+
const home = opts.runHome ? join(opts.runHome, "kiro") : join(opts.worktree, ".pantheon", "kiro-home");
|
|
328
|
+
const tools = [...KIRO_WORKER_TOOLS, ...(opts.allowBash ? ["shell"] : []), `@${KIRO_WORKER_SERVER}`];
|
|
329
|
+
const agentName = `${KIRO_WORKER_AGENT}-${randomBytes(8).toString("hex")}`;
|
|
330
|
+
const settings = join(kiroHome(), "settings", "cli.json");
|
|
331
|
+
return {
|
|
332
|
+
command: opts.binary,
|
|
333
|
+
args: ["chat", "--no-interactive", "--agent", agentName, "--require-mcp-startup", "--wrap", "never", opts.prompt],
|
|
334
|
+
env: { ...procEnv, KIRO_HOME: home, KIRO_NO_AUTO_UPDATE: "1" },
|
|
335
|
+
writeFiles: [{
|
|
336
|
+
path: join(home, "agents", `${KIRO_WORKER_AGENT}.json`),
|
|
337
|
+
contents: JSON.stringify({
|
|
338
|
+
name: agentName,
|
|
339
|
+
description: "A Pantheon run: answers a teammate's request on an isolated branch, while its human is away",
|
|
340
|
+
tools,
|
|
341
|
+
allowedTools: tools,
|
|
342
|
+
includeMcpJson: false,
|
|
343
|
+
mcpServers: { [KIRO_WORKER_SERVER]: { command: l.command, args: l.args, env: fileEnv } },
|
|
344
|
+
resources: ["file://.kiro/steering/**/*.md"],
|
|
345
|
+
}, null, 2),
|
|
346
|
+
}],
|
|
347
|
+
...(opts.runHome && existsSync(settings) ? { copyFiles: [{ from: settings, to: join(home, "settings", "cli.json") }] } : {}),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
286
350
|
return {
|
|
287
351
|
command: opts.binary,
|
|
288
352
|
args: [
|
|
@@ -310,6 +374,20 @@ export function headlessSpawn(opts) {
|
|
|
310
374
|
};
|
|
311
375
|
}
|
|
312
376
|
export const COPILOT_WORKER_SERVER = "pantheon-worker";
|
|
377
|
+
export function kiroDataDir(home = homedir(), env = process.env, os = platform()) {
|
|
378
|
+
if (env.KIRO_DATA_DIR)
|
|
379
|
+
return env.KIRO_DATA_DIR;
|
|
380
|
+
if (os === "darwin")
|
|
381
|
+
return join(home, "Library", "Application Support", "kiro-cli");
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
export function kiroHasRunHere(home = homedir(), env = process.env, os = platform()) {
|
|
385
|
+
const data = kiroDataDir(home, env, os);
|
|
386
|
+
return data ? existsSync(join(data, "data.sqlite3")) : existsSync(kiroDir(home, env));
|
|
387
|
+
}
|
|
388
|
+
export const KIRO_WORKER_AGENT = "pantheon-worker";
|
|
389
|
+
export const KIRO_WORKER_SERVER = "pantheon";
|
|
390
|
+
export const KIRO_WORKER_TOOLS = ["read", "write", "grep", "glob"];
|
|
313
391
|
function readCopilotConfig() {
|
|
314
392
|
try {
|
|
315
393
|
const doc = JSON.parse(readFileSync(join(copilotHome(), "config.json"), "utf8"));
|
package/dist/cli.js
CHANGED
|
@@ -442,6 +442,8 @@ function printReady(tools) {
|
|
|
442
442
|
oneTime.set("cursor", `cursor enable the "pantheon" MCP server if it asks`);
|
|
443
443
|
if (/codex/i.test(t.tool))
|
|
444
444
|
oneTime.set("codex", `codex run /hooks inside codex once to approve the wake-up hook`);
|
|
445
|
+
if (/kiro/i.test(t.tool))
|
|
446
|
+
oneTime.set("kiro", `kiro run kiro-cli chat --agent pantheon (or kiro-cli agent set-default pantheon) to keep working while a teammate waits; stay signed in — a woken run never starts Kiro signed out, since Kiro would open a browser`);
|
|
445
447
|
if (/copilot/i.test(t.tool))
|
|
446
448
|
oneTime.set("copilot", `copilot say yes once when it asks to trust this folder; on a Copilot Business or Enterprise plan an org owner must have the "MCP servers in Copilot" policy on`);
|
|
447
449
|
}
|
|
@@ -501,7 +503,7 @@ ${B("control")}
|
|
|
501
503
|
pantheon admit <who> let someone in (--may-wake to allow unattended work here)
|
|
502
504
|
pantheon admit <who> --revoke remove them
|
|
503
505
|
pantheon worker stay answerable while you are away
|
|
504
|
-
(--agent claude-code|codex|cursor|copilot|auto, --persona <name>)
|
|
506
|
+
(--agent claude-code|codex|cursor|copilot|kiro|auto, --persona <name>)
|
|
505
507
|
pantheon policy what this machine's worker may do, in one screen (grant/revoke self-upgrade)
|
|
506
508
|
pantheon persona how a woken worker should behave: list / init <name> / show <name>
|
|
507
509
|
pantheon notifier alert you when a task requires your decision
|
|
@@ -514,7 +516,7 @@ ${B("control")}
|
|
|
514
516
|
pantheon approvals decisions waiting on you; show or answer one
|
|
515
517
|
pantheon stop [relay|worker|notifier|menubar|all] stop it
|
|
516
518
|
pantheon hook install your agent keeps working when a peer is waiting on it
|
|
517
|
-
(auto-detects claude / codex / cursor / copilot on this machine)
|
|
519
|
+
(auto-detects claude / codex / cursor / copilot / kiro on this machine)
|
|
518
520
|
pantheon spend woken runs on this machine, and what pantheon cannot see
|
|
519
521
|
pantheon leave remove what pantheon put on this machine
|
|
520
522
|
pantheon panic cut off all unattended work on this machine, now
|
|
@@ -1332,13 +1334,13 @@ async function main() {
|
|
|
1332
1334
|
const sub = argv[1] ?? "status";
|
|
1333
1335
|
const hookCfg = resolveForHook();
|
|
1334
1336
|
const sessionOf = (raw) => {
|
|
1337
|
+
let fromJson;
|
|
1335
1338
|
try {
|
|
1336
1339
|
const j = JSON.parse(raw);
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1339
|
-
catch {
|
|
1340
|
-
return "unknown";
|
|
1340
|
+
fromJson = j.session_id ?? j.sessionId ?? j.conversation_id;
|
|
1341
1341
|
}
|
|
1342
|
+
catch { }
|
|
1343
|
+
return fromJson ?? process.env.KIRO_SESSION_ID ?? "unknown";
|
|
1342
1344
|
};
|
|
1343
1345
|
const reportIdle = async (session, seen) => {
|
|
1344
1346
|
if (!hookCfg)
|
|
@@ -1353,6 +1355,22 @@ async function main() {
|
|
|
1353
1355
|
}
|
|
1354
1356
|
return false;
|
|
1355
1357
|
};
|
|
1358
|
+
if (sub === "kiro-prompt") {
|
|
1359
|
+
let raw = "";
|
|
1360
|
+
try {
|
|
1361
|
+
for await (const chunk of process.stdin)
|
|
1362
|
+
raw += chunk;
|
|
1363
|
+
}
|
|
1364
|
+
catch { }
|
|
1365
|
+
try {
|
|
1366
|
+
if (hookCfg) {
|
|
1367
|
+
const { reportActivity } = await import("./pending.js");
|
|
1368
|
+
await reportActivity({ cfg: hookCfg, session: `hook:${sessionOf(raw)}`, state: "busy" });
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
catch { }
|
|
1372
|
+
process.exit(0);
|
|
1373
|
+
}
|
|
1356
1374
|
if (sub === "prompt") {
|
|
1357
1375
|
let raw = "";
|
|
1358
1376
|
try {
|
|
@@ -1462,7 +1480,7 @@ async function main() {
|
|
|
1462
1480
|
process.exit(0);
|
|
1463
1481
|
}
|
|
1464
1482
|
}
|
|
1465
|
-
if (sub === "codex-stop" || sub === "copilot-stop") {
|
|
1483
|
+
if (sub === "codex-stop" || sub === "copilot-stop" || sub === "kiro-stop") {
|
|
1466
1484
|
let raw = "";
|
|
1467
1485
|
try {
|
|
1468
1486
|
for await (const chunk of process.stdin)
|
|
@@ -1571,7 +1589,7 @@ async function main() {
|
|
|
1571
1589
|
}
|
|
1572
1590
|
const states = agentHookStates();
|
|
1573
1591
|
if (!states.length) {
|
|
1574
|
-
console.log(`${D("not installed")}\n Install with: ${C("pantheon hook install")} (auto-detects claude / codex / cursor / copilot on PATH)`);
|
|
1592
|
+
console.log(`${D("not installed")}\n Install with: ${C("pantheon hook install")} (auto-detects claude / codex / cursor / copilot / kiro on PATH)`);
|
|
1575
1593
|
return;
|
|
1576
1594
|
}
|
|
1577
1595
|
for (const s of states) {
|
|
@@ -1654,7 +1672,7 @@ async function main() {
|
|
|
1654
1672
|
else {
|
|
1655
1673
|
console.error(` config: none to remove`);
|
|
1656
1674
|
}
|
|
1657
|
-
console.error(`\n ${D("Not touched: the MCP entries in Claude Code and
|
|
1675
|
+
console.error(`\n ${D("Not touched: the MCP entries in Claude Code, Cursor, Copilot, VS Code and Kiro (pantheon install wrote them; remove them there),")}`);
|
|
1658
1676
|
console.error(` ${D("the relay's ledger on disk, and anything you pushed to git. Codex's entry and approval line were removed above.")}`);
|
|
1659
1677
|
if (cfg) {
|
|
1660
1678
|
console.error(`\n ${D(`Your teammates still list you in "${cfg.space}". Ask one of them to run:`)}`);
|
|
@@ -2021,7 +2039,7 @@ async function main() {
|
|
|
2021
2039
|
const publicChange = name !== undefined || title !== undefined || responsibilities !== undefined ||
|
|
2022
2040
|
humanRequired !== undefined || agentCanDecide !== undefined || capabilities !== undefined || keywords !== undefined;
|
|
2023
2041
|
if (!surface && !publicChange) {
|
|
2024
|
-
die("usage: pantheon profile set [--surface codex|claude-code|cursor|copilot] [--name name] [--title title] [--responsibilities a,b] [--human-required a,b] [--agent-can-decide a,b] [--keywords 'domain=word|word;domain=word']");
|
|
2042
|
+
die("usage: pantheon profile set [--surface codex|claude-code|cursor|copilot|kiro] [--name name] [--title title] [--responsibilities a,b] [--human-required a,b] [--agent-can-decide a,b] [--keywords 'domain=word|word;domain=word']");
|
|
2025
2043
|
}
|
|
2026
2044
|
if (flag("space")) {
|
|
2027
2045
|
const pf = loadAll() ?? die("not configured");
|
|
@@ -2462,7 +2480,7 @@ async function main() {
|
|
|
2462
2480
|
if (hud.prompt) {
|
|
2463
2481
|
const agent = pickExplainAgent(hud.tool ?? p.agent.primary_surface, detectAvailable());
|
|
2464
2482
|
if (!agent)
|
|
2465
|
-
die("no agent CLI found on this machine (claude, cursor-agent, codex, or
|
|
2483
|
+
die("no agent CLI found on this machine (claude, cursor-agent, codex, copilot, or kiro-cli)");
|
|
2466
2484
|
console.error(D(`opening ${agent.id}…`));
|
|
2467
2485
|
const { spawnSync } = await import("node:child_process");
|
|
2468
2486
|
{
|
|
@@ -2494,7 +2512,7 @@ async function main() {
|
|
|
2494
2512
|
}
|
|
2495
2513
|
const agent = pickExplainAgent(loadProfile(cfg).agent.primary_surface, detectAvailable());
|
|
2496
2514
|
if (!agent)
|
|
2497
|
-
die("no agent CLI found on this machine (claude, cursor-agent, codex, or
|
|
2515
|
+
die("no agent CLI found on this machine (claude, cursor-agent, codex, copilot, or kiro-cli)");
|
|
2498
2516
|
console.error(D(`opening ${agent.id} with the question…`));
|
|
2499
2517
|
const { spawnSync } = await import("node:child_process");
|
|
2500
2518
|
{
|
package/dist/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { kiroDefaultIsPantheon, readKiroAgent, kiroAgentFile } from "./kiro-files.js";
|
|
1
2
|
import { execFileSync } from "node:child_process";
|
|
2
3
|
import { instructionBudget } from "./mcp-instructions.js";
|
|
3
4
|
import { describeWindows, scanMcpWindows } from "./mcp-windows.js";
|
|
@@ -14,7 +15,7 @@ import { approvalSummary, chosenApproval, onPath } from "./install.js";
|
|
|
14
15
|
import { hookState, readIntent, agentHookStates } from "./notify.js";
|
|
15
16
|
import { loadPersonas } from "./persona.js";
|
|
16
17
|
import { workerProc, menubarProc, notifierProc } from "./machine.js";
|
|
17
|
-
import { chooseAgent, describeChoice } from "./agents.js";
|
|
18
|
+
import { chooseAgent, describeChoice, unattendedRefusal } from "./agents.js";
|
|
18
19
|
import { onPathNamed } from "./which.js";
|
|
19
20
|
import { VERSION } from "./version.js";
|
|
20
21
|
import { serviceInstalled, servicesUnavailable } from "./service.js";
|
|
@@ -156,10 +157,10 @@ export async function doctor(projectDir) {
|
|
|
156
157
|
if (branch)
|
|
157
158
|
add("pass", "git branch", branch);
|
|
158
159
|
}
|
|
159
|
-
const agents = ["claude", "codex", "cursor-agent", "copilot"].filter((a) => onPathNamed(a));
|
|
160
|
+
const agents = ["claude", "codex", "cursor-agent", "copilot", "kiro-cli"].filter((a) => onPathNamed(a));
|
|
160
161
|
agents.length
|
|
161
162
|
? add("pass", "agent cli", agents.join(", "), undefined, agents.join(", "))
|
|
162
|
-
: add("warn", "agent cli", "found none of claude / codex / cursor-agent / copilot on PATH", "fine if you only drive agents through a GUI (Conductor, Cursor, VS Code), but `pantheon worker` needs a CLI to spawn", "none of claude / codex / cursor-agent / copilot on PATH");
|
|
163
|
+
: add("warn", "agent cli", "found none of claude / codex / cursor-agent / copilot / kiro-cli on PATH", "fine if you only drive agents through a GUI (Conductor, Cursor, VS Code, the Kiro IDE), but `pantheon worker` needs a CLI to spawn", "none of claude / codex / cursor-agent / copilot / kiro-cli on PATH");
|
|
163
164
|
if (agents.length) {
|
|
164
165
|
try {
|
|
165
166
|
const choice = chooseAgent("auto", { prefer: prof.agent.primary_surface });
|
|
@@ -167,7 +168,10 @@ export async function doctor(projectDir) {
|
|
|
167
168
|
const bad = choice.signIn && !choice.signIn.ok && !choice.signIn.unknown;
|
|
168
169
|
const fellBack = !!prof.agent.primary_surface && choice.backend.id !== prof.agent.primary_surface && agents.length > 0;
|
|
169
170
|
const shareLine = `${choice.backend.label} — ${choice.signIn?.detail ?? "not checked"}${choice.skipped.length ? `; ${choice.skipped.length} skipped (not signed in)` : ""}`;
|
|
170
|
-
|
|
171
|
+
const refused = unattendedRefusal(choice);
|
|
172
|
+
if (refused)
|
|
173
|
+
add("warn", "worker agent", line, `woken runs are refused here, not tried: ${refused}`, shareLine);
|
|
174
|
+
else if (bad)
|
|
171
175
|
add("warn", "worker agent", line, `nothing is signed in, so every woken run here fails until you sign in: ${choice.skipped.map((x) => x.why).join("; ") || "sign in to the agent CLI"}`, shareLine);
|
|
172
176
|
else if (fellBack)
|
|
173
177
|
add("warn", "worker agent", line, `you chose ${prof.agent.primary_surface} at setup; runs use ${choice.backend.label} until it is signed in again`, shareLine);
|
|
@@ -175,7 +179,13 @@ export async function doctor(projectDir) {
|
|
|
175
179
|
add("pass", "worker agent", line, undefined, shareLine);
|
|
176
180
|
}
|
|
177
181
|
catch (e) {
|
|
178
|
-
add("warn", "worker agent", e.message, "pantheon setup --agent <claude-code|codex|cursor|copilot> names one that is installed", "could not be chosen");
|
|
182
|
+
add("warn", "worker agent", e.message, "pantheon setup --agent <claude-code|codex|cursor|copilot|kiro> names one that is installed", "could not be chosen");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
{
|
|
186
|
+
const here = readKiroAgent(kiroAgentFile(join(projectDir, ".kiro")));
|
|
187
|
+
if (here && !here.ours) {
|
|
188
|
+
add("warn", "kiro agent", "this repo defines its own agent called pantheon — Kiro uses it instead of yours in this repo", "workspace agents win over your own in Kiro; read .kiro/agents/pantheon.json before running kiro-cli chat --agent pantheon here", "this repo defines its own pantheon agent");
|
|
179
189
|
}
|
|
180
190
|
}
|
|
181
191
|
const budget = instructionBudget(cfg);
|
|
@@ -302,7 +312,7 @@ export async function doctor(projectDir) {
|
|
|
302
312
|
}
|
|
303
313
|
}
|
|
304
314
|
if (!hookStates.length && !hook.installed) {
|
|
305
|
-
add("warn", "wake-up hook", "not installed — your agent will not notice a peer's message until you ask it to", "pantheon hook install --user (auto-detects claude / codex / cursor / copilot on PATH)", "not installed");
|
|
315
|
+
add("warn", "wake-up hook", "not installed — your agent will not notice a peer's message until you ask it to", "pantheon hook install --user (auto-detects claude / codex / cursor / copilot / kiro on PATH)", "not installed");
|
|
306
316
|
}
|
|
307
317
|
else if (!reportsActivity) {
|
|
308
318
|
add("warn", "wake-up hook", `installed (${hook.events?.join(", ")}) but predates the activity hook, so a worker cannot tell your idle editor from one you are using`, "re-run `pantheon hook install --user` to add the UserPromptSubmit half", `installed (${hook.events?.join(", ")}), predates the activity hook`);
|
|
@@ -316,6 +326,11 @@ export async function doctor(projectDir) {
|
|
|
316
326
|
if (hookStates.some((s) => s.agent === "codex")) {
|
|
317
327
|
add("warn", "codex hooks", "installed — but codex skips new or changed hooks until they are approved", "run /hooks inside codex once to approve them (needs codex ≥ 0.115; sessions still report presence over MCP either way)", "installed, not yet approved inside codex");
|
|
318
328
|
}
|
|
329
|
+
if (hookStates.some((s) => s.agent === "kiro")) {
|
|
330
|
+
kiroDefaultIsPantheon()
|
|
331
|
+
? add("pass", "kiro hooks", "in the pantheon agent, which is your Kiro default — every kiro-cli chat keeps working while a teammate waits", undefined, "in the pantheon agent, the default")
|
|
332
|
+
: add("warn", "kiro hooks", "in the pantheon agent — used only when you run kiro-cli chat --agent pantheon", "kiro-cli agent set-default pantheon makes it every session; without it, Kiro sessions still report presence but end their turn with a teammate waiting", "in the pantheon agent, not the default");
|
|
333
|
+
}
|
|
319
334
|
const personas = loadPersonas(projectDir);
|
|
320
335
|
if (personas.length) {
|
|
321
336
|
const broken = personas.filter((p) => p.warnings.length);
|
package/dist/hooks-install.js
CHANGED
|
@@ -2,7 +2,8 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync } from "node
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { MAX_CONSECUTIVE_BLOCKS, isOurHookCommand } from "./notify.js";
|
|
5
|
-
import { copilotHome } from "./agents.js";
|
|
5
|
+
import { copilotHome, kiroHome } from "./agents.js";
|
|
6
|
+
import { KIRO_AGENT_NAME, kiroAgentFile, kiroAgentDoc, readKiroAgent, writeKiroAgent, kiroDefaultIsPantheon } from "./kiro-files.js";
|
|
6
7
|
function ours(cmd) {
|
|
7
8
|
return isOurHookCommand(cmd);
|
|
8
9
|
}
|
|
@@ -247,4 +248,48 @@ export function removeCopilotHooks(projectDir = process.cwd()) {
|
|
|
247
248
|
}
|
|
248
249
|
return out;
|
|
249
250
|
}
|
|
251
|
+
export { KIRO_AGENT_NAME };
|
|
252
|
+
export function kiroAgentPath(scope, projectDir = process.cwd()) {
|
|
253
|
+
return kiroAgentFile(scope === "user" ? kiroHome() : join(projectDir, ".kiro"));
|
|
254
|
+
}
|
|
255
|
+
export function kiroHookState(scope, projectDir = process.cwd()) {
|
|
256
|
+
const file = kiroAgentPath(scope, projectDir);
|
|
257
|
+
const a = readKiroAgent(file);
|
|
258
|
+
return a?.ours
|
|
259
|
+
? { agent: "kiro", label: "Kiro", installed: true, scope, file, events: a.events }
|
|
260
|
+
: { agent: "kiro", label: "Kiro", installed: false };
|
|
261
|
+
}
|
|
262
|
+
export function installKiroHooks(bin, scope, projectDir = process.cwd(), allowed = []) {
|
|
263
|
+
const file = kiroAgentPath(scope, projectDir);
|
|
264
|
+
const a = readKiroAgent(file);
|
|
265
|
+
if (a && !a.ours)
|
|
266
|
+
return [` skipped ${file} is an agent called "${KIRO_AGENT_NAME}" that pantheon did not write — left as it is`];
|
|
267
|
+
writeKiroAgent(file, kiroAgentDoc(bin, allowed));
|
|
268
|
+
return [
|
|
269
|
+
` ${a ? "updated" : "installed"} ${file} (an agent: Kiro's default, plus pantheon's hooks)`,
|
|
270
|
+
` runs ${bin} hook kiro-stop`,
|
|
271
|
+
` ${bin} hook kiro-prompt`,
|
|
272
|
+
kiroDefaultIsPantheon()
|
|
273
|
+
? ` ${""}(it is your default agent, so every kiro-cli chat uses it)`
|
|
274
|
+
: ` \x1b[33mKiro uses these only in the pantheon agent:\x1b[0m kiro-cli chat --agent pantheon`,
|
|
275
|
+
...(kiroDefaultIsPantheon() ? [] : [` ${""}(or always: kiro-cli agent set-default pantheon — pantheon never changes your default)`]),
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
export function removeKiroHooks(projectDir = process.cwd()) {
|
|
279
|
+
const out = [];
|
|
280
|
+
for (const scope of ["project", "user"]) {
|
|
281
|
+
const file = kiroAgentPath(scope, projectDir);
|
|
282
|
+
if (!kiroHookState(scope, projectDir).installed)
|
|
283
|
+
continue;
|
|
284
|
+
try {
|
|
285
|
+
rmSync(file, { force: true });
|
|
286
|
+
out.push(` removed ${file}`);
|
|
287
|
+
}
|
|
288
|
+
catch { }
|
|
289
|
+
}
|
|
290
|
+
if (out.length && kiroDefaultIsPantheon()) {
|
|
291
|
+
out.push(" Kiro's default was the pantheon agent; it now falls back to kiro_default and warns each session — kiro-cli agent set-default kiro_default");
|
|
292
|
+
}
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
250
295
|
//# sourceMappingURL=hooks-install.js.map
|
package/dist/install.js
CHANGED
|
@@ -4,11 +4,12 @@ import { homedir, platform } from "node:os";
|
|
|
4
4
|
import { join, dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { grantToolPermissions, claudeSettingsFile, cursorCliConfigFile } from "./grants.js";
|
|
7
|
-
import { PANTHEON_TOOL_NAMES, WRITE_TOOL_NAMES } from "./tools.js";
|
|
7
|
+
import { PANTHEON_TOOL_NAMES, WRITE_TOOL_NAMES, toolsAllowed } from "./tools.js";
|
|
8
8
|
import { load } from "./config.js";
|
|
9
9
|
import { loadProfile } from "./profile.js";
|
|
10
10
|
import { agentSpawn, onPathNamed, which } from "./which.js";
|
|
11
11
|
import { FEEDBACK_ADDRESS } from "./feedback.js";
|
|
12
|
+
import { kiroDir, kiroAgentFile, refreshKiroAgentAllowed } from "./kiro-files.js";
|
|
12
13
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
export function onPath() {
|
|
14
15
|
return onPathNamed("pantheon");
|
|
@@ -40,7 +41,7 @@ function mergeMcpJson(file, entry, shape = {}) {
|
|
|
40
41
|
const existed = existsSync(file);
|
|
41
42
|
doc.mcpServers ??= {};
|
|
42
43
|
const before = JSON.stringify(doc.mcpServers["pantheon"] ?? null);
|
|
43
|
-
doc.mcpServers["pantheon"] = { ...shape, ...entry, env: {} };
|
|
44
|
+
doc.mcpServers["pantheon"] = { ...shape, ...entry, env: shape.env ?? {} };
|
|
44
45
|
if (before === JSON.stringify(doc.mcpServers["pantheon"]))
|
|
45
46
|
return "unchanged";
|
|
46
47
|
mkdirSync(dirname(file), { recursive: true });
|
|
@@ -61,6 +62,12 @@ export function mergeVsCodeMcpJson(file, entry) {
|
|
|
61
62
|
return existed ? "updated" : "created";
|
|
62
63
|
}
|
|
63
64
|
export const copilotMcpConfigFile = (home = homedir(), env = process.env) => join(env.COPILOT_HOME || join(home, ".copilot"), "mcp-config.json");
|
|
65
|
+
export { kiroDir };
|
|
66
|
+
export const kiroMcpConfigFile = (home = homedir(), env = process.env) => join(kiroDir(home, env), "settings", "mcp.json");
|
|
67
|
+
export const kiroEntryShape = (approval) => ({
|
|
68
|
+
autoApprove: toolsAllowed(approval),
|
|
69
|
+
env: { PANTHEON_AGENT: "kiro" },
|
|
70
|
+
});
|
|
64
71
|
export function vsCodeUserDir(home = homedir(), os = platform(), env = process.env) {
|
|
65
72
|
const dir = os === "darwin" ? join(home, "Library", "Application Support", "Code", "User")
|
|
66
73
|
: os === "win32" ? join(env.APPDATA || join(home, "AppData", "Roaming"), "Code", "User")
|
|
@@ -131,6 +138,9 @@ export function install(projectDir, cfg, force, os = platform(), claudeLocal = c
|
|
|
131
138
|
if (existsSync(p) || f === "AGENTS.md")
|
|
132
139
|
out.push(` ${f.padEnd(25)} ${upsertBrief(p, cfg)} (the collaboration brief)`);
|
|
133
140
|
}
|
|
141
|
+
if (existsSync(kiroDir()) || onPathNamed("kiro-cli")) {
|
|
142
|
+
out.push(` .kiro/settings/mcp.json ${"not used".padEnd(9)} Kiro reads ~/.kiro/settings/mcp.json, which pantheon join wrote — a repo copy would override each teammate's own (kiro)`);
|
|
143
|
+
}
|
|
134
144
|
out.push("");
|
|
135
145
|
out.push(" Copilot asks once to trust this folder before it reads the repo's MCP servers and hooks.");
|
|
136
146
|
out.push(" Codex reads ~/.codex/config.toml, which `pantheon join` already wrote at user scope.");
|
|
@@ -251,6 +261,21 @@ function copilotUserScope(entry) {
|
|
|
251
261
|
return { tool, where: file, status: `failed: ${e.message}` };
|
|
252
262
|
}
|
|
253
263
|
}
|
|
264
|
+
function kiroUserScope(entry, approval) {
|
|
265
|
+
const tool = "kiro";
|
|
266
|
+
const file = kiroMcpConfigFile();
|
|
267
|
+
if (!existsSync(kiroDir()) && !onPathNamed("kiro-cli"))
|
|
268
|
+
return { tool, where: "-", status: "skipped (kiro-cli not on PATH)" };
|
|
269
|
+
try {
|
|
270
|
+
const status = mergeMcpJson(file, entry, kiroEntryShape(approval));
|
|
271
|
+
const agent = refreshKiroAgentAllowed(kiroAgentFile(kiroDir()), toolsAllowed(approval));
|
|
272
|
+
const which = approval === "ask-before-sending" ? "reads run without asking; send, open_goal and claim prompt" : `the ${PANTHEON_TOOL_NAMES.length} pantheon tools run without asking`;
|
|
273
|
+
return { tool, where: file, status: `${status} — in the Kiro IDE, ${which}${agent === "absent" ? "; the CLI asks per call unless you use the pantheon agent (pantheon hook install)" : `; in the CLI's pantheon agent too (${agent})`}` };
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
return { tool, where: file, status: `failed: ${e.message}` };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
254
279
|
function vsCodeUserScope(entry) {
|
|
255
280
|
const tool = "vs code";
|
|
256
281
|
const dir = vsCodeUserDir();
|
|
@@ -268,7 +293,7 @@ export const CODEX_APPROVAL_LINE = `default_tools_approval_mode = "approve"`;
|
|
|
268
293
|
export function approvalSummary(approval) {
|
|
269
294
|
return approval === "ask-before-sending"
|
|
270
295
|
? `your agent asks you before each send, goal or claim in your name; reading and waiting run without a prompt. Unattended runs keep their own per-run approvals — this governs the agent you sit in front of`
|
|
271
|
-
: `pantheon's ${PANTHEON_TOOL_NAMES.length} tools run without a per-call prompt in Claude Code, Cursor and
|
|
296
|
+
: `pantheon's ${PANTHEON_TOOL_NAMES.length} tools run without a per-call prompt in Claude Code, Cursor, Codex, the Kiro IDE and Kiro's pantheon agent — your agent coordinates for you without stopping to ask. Copilot has no allow-list file: it asks once per tool per folder and remembers your answer; Kiro's default CLI agent asks per call`;
|
|
272
297
|
}
|
|
273
298
|
export function codexTomlBlock(entry, env = { PANTHEON_AGENT: "codex" }, approval = "approve") {
|
|
274
299
|
const envToml = Object.entries(env)
|
|
@@ -466,7 +491,7 @@ export function installForUser(approval = chosenApproval()) {
|
|
|
466
491
|
const entry = userEntry();
|
|
467
492
|
return [
|
|
468
493
|
claudeUserScope(entry), cursorUserScope(entry), codexUserScope(entry, approval),
|
|
469
|
-
copilotUserScope(entry), vsCodeUserScope(entry),
|
|
494
|
+
copilotUserScope(entry), vsCodeUserScope(entry), kiroUserScope(entry, approval),
|
|
470
495
|
...grantToolPermissions(approval).map((g) => ({
|
|
471
496
|
tool: g.tool,
|
|
472
497
|
where: g.tool.startsWith("claude") ? claudeSettingsFile() : cursorCliConfigFile(),
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export const kiroDir = (home = homedir(), env = process.env) => env.KIRO_HOME || join(home, ".kiro");
|
|
5
|
+
export const KIRO_AGENT_NAME = "pantheon";
|
|
6
|
+
export const kiroAgentFile = (kiroBase) => join(kiroBase, "agents", `${KIRO_AGENT_NAME}.json`);
|
|
7
|
+
export const isOurKiroHook = (cmd) => !!cmd && /\bhook\s+kiro-(?:prompt|stop)\b/.test(cmd);
|
|
8
|
+
export const kiroAllowed = (tools) => tools.map((t) => `@pantheon/${t}`);
|
|
9
|
+
export function kiroAgentDoc(bin, allowed) {
|
|
10
|
+
return {
|
|
11
|
+
name: KIRO_AGENT_NAME,
|
|
12
|
+
description: "Kiro's default agent, plus Pantheon: its tools run without asking, and a turn is held open while a teammate is waiting on you",
|
|
13
|
+
tools: ["*"],
|
|
14
|
+
allowedTools: kiroAllowed(allowed),
|
|
15
|
+
includeMcpJson: true,
|
|
16
|
+
hooks: {
|
|
17
|
+
userPromptSubmit: [{ command: `${bin} hook kiro-prompt >/dev/null 2>&1` }],
|
|
18
|
+
stop: [{ command: `${bin} hook kiro-stop` }],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function readKiroAgent(file) {
|
|
23
|
+
if (!existsSync(file))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const doc = JSON.parse(readFileSync(file, "utf8"));
|
|
27
|
+
const events = Object.entries(doc.hooks ?? {}).filter(([, es]) => (es ?? []).some((h) => isOurKiroHook(h.command))).map(([e]) => e);
|
|
28
|
+
return { doc, ours: events.length > 0, events };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { doc: {}, ours: false, events: [] };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function writeKiroAgent(file, doc) {
|
|
35
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
36
|
+
writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
37
|
+
}
|
|
38
|
+
export function refreshKiroAgentAllowed(file, allowed) {
|
|
39
|
+
const a = readKiroAgent(file);
|
|
40
|
+
if (!a?.ours)
|
|
41
|
+
return "absent";
|
|
42
|
+
const want = kiroAllowed(allowed);
|
|
43
|
+
if (JSON.stringify(a.doc.allowedTools) === JSON.stringify(want))
|
|
44
|
+
return "unchanged";
|
|
45
|
+
writeKiroAgent(file, { ...a.doc, allowedTools: want });
|
|
46
|
+
return "updated";
|
|
47
|
+
}
|
|
48
|
+
export function kiroDefaultIsPantheon(base = kiroDir()) {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(readFileSync(join(base, "settings", "cli.json"), "utf8"))["chat.defaultAgent"] === KIRO_AGENT_NAME;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=kiro-files.js.map
|
package/dist/machine.js
CHANGED
|
@@ -4,7 +4,7 @@ import { homedir, platform } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { CONFIG_DIR } from "./config.js";
|
|
6
6
|
import { plistPath } from "./service.js";
|
|
7
|
-
import { copilotMcpConfigFile, vsCodeUserDir } from "./install.js";
|
|
7
|
+
import { copilotMcpConfigFile, vsCodeUserDir, kiroMcpConfigFile, kiroDir } from "./install.js";
|
|
8
8
|
const sh = (cmd) => {
|
|
9
9
|
try {
|
|
10
10
|
return execFileSync("sh", ["-lc", cmd], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
@@ -120,6 +120,10 @@ export function wiring() {
|
|
|
120
120
|
const f = join(vscodeDir, "mcp.json");
|
|
121
121
|
out.push({ tool: "vs code", state: existsSync(f) && /"pantheon"/.test(readFileSync(f, "utf8")) ? "wired" : "not wired" });
|
|
122
122
|
}
|
|
123
|
+
if (existsSync(kiroDir())) {
|
|
124
|
+
const f = kiroMcpConfigFile();
|
|
125
|
+
out.push({ tool: "kiro", state: existsSync(f) && /"pantheon"/.test(readFileSync(f, "utf8")) ? "wired" : "not wired" });
|
|
126
|
+
}
|
|
123
127
|
return out;
|
|
124
128
|
}
|
|
125
129
|
export function logFileFor(kind, proc) {
|
package/dist/measure.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { resolve, dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { claudeGrantedEffective, cursorGrantedEffective, claudeAllow, cursorAllow, isServerWide } from "./grants.js";
|
|
6
|
-
import { codexApprovalState, copilotMcpConfigFile } from "./install.js";
|
|
6
|
+
import { codexApprovalState, copilotMcpConfigFile, kiroMcpConfigFile } from "./install.js";
|
|
7
7
|
import { existsSync as fileExists, readFileSync } from "node:fs";
|
|
8
8
|
import { dirname as dirOf } from "node:path";
|
|
9
9
|
import { PANTHEON_TOOL_NAMES, WRITE_TOOL_NAMES } from "./tools.js";
|
|
@@ -161,13 +161,41 @@ export function toolsReport(registered, seen, approval, opts = {}) {
|
|
|
161
161
|
}
|
|
162
162
|
const copilot = seen.copilot === undefined || seen.copilot === null ? null
|
|
163
163
|
: seen.copilot ? "Copilot wired (it asks once per tool per folder and remembers)" : "Copilot has no pantheon entry";
|
|
164
|
-
|
|
164
|
+
let kiro = null;
|
|
165
|
+
if (seen.kiro === "absent")
|
|
166
|
+
kiro = "Kiro has no pantheon entry";
|
|
167
|
+
else if (Array.isArray(seen.kiro)) {
|
|
168
|
+
const want = [...(approval === "ask-before-sending" ? PANTHEON_TOOL_NAMES.filter((t) => !WRITE_TOOL_NAMES.includes(t)) : PANTHEON_TOOL_NAMES)].sort();
|
|
169
|
+
const have = [...new Set(seen.kiro)].filter((t) => t.startsWith("pantheon_")).sort();
|
|
170
|
+
const extra = have.filter((t) => !want.includes(t)), missing = want.filter((t) => !have.includes(t));
|
|
171
|
+
if (!extra.length && !missing.length)
|
|
172
|
+
kiro = `Kiro IDE allows ${have.length}`;
|
|
173
|
+
else {
|
|
174
|
+
kiro = `Kiro IDE allows ${have.length}${missing.length ? `, missing ${missing.map((t) => t.replace(/^pantheon_/, "")).join(", ")}` : ""}${extra.length ? `, and allows ${extra.map((t) => t.replace(/^pantheon_/, "")).join(", ")} you chose to be asked about` : ""}`;
|
|
175
|
+
problems.push("Kiro's autoApprove does not match");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const detail = `${head}; ${claude.text}, ${cursor.text}, ${codex}${copilot ? `, ${copilot}` : ""}${kiro ? `, ${kiro}` : ""}`;
|
|
165
179
|
if (!problems.length)
|
|
166
180
|
return { level: "pass", detail };
|
|
167
181
|
return { level: "warn", detail, fix: `${problems.join("; ")} — pantheon install --user rewrites every grant in your own settings to match this build and your approval choice; a rule in this repo's .claude/settings*.json or .cursor/cli.json is yours to edit by hand` };
|
|
168
182
|
}
|
|
169
183
|
export function grantsSeen(projectDir = null) {
|
|
170
|
-
return { claude: claudeGrantedEffective(projectDir), cursor: cursorGrantedEffective(projectDir), codex: codexApprovalState(), copilot: copilotWired() };
|
|
184
|
+
return { claude: claudeGrantedEffective(projectDir), cursor: cursorGrantedEffective(projectDir), codex: codexApprovalState(), copilot: copilotWired(), kiro: kiroApproved() };
|
|
185
|
+
}
|
|
186
|
+
export function kiroApproved(file = kiroMcpConfigFile()) {
|
|
187
|
+
if (!fileExists(dirOf(dirOf(file))))
|
|
188
|
+
return null;
|
|
189
|
+
try {
|
|
190
|
+
const doc = JSON.parse(readFileSync(file, "utf8"));
|
|
191
|
+
const entry = doc.mcpServers?.["pantheon"];
|
|
192
|
+
if (!entry)
|
|
193
|
+
return "absent";
|
|
194
|
+
return Array.isArray(entry.autoApprove) ? entry.autoApprove.filter((t) => typeof t === "string") : [];
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return "absent";
|
|
198
|
+
}
|
|
171
199
|
}
|
|
172
200
|
export function copilotWired(file = copilotMcpConfigFile()) {
|
|
173
201
|
if (!fileExists(dirOf(file)))
|
package/dist/notice.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { decisionPrompt, decisionTitle } from "./decision-ui.js";
|
|
2
|
-
export const TOOL_LABELS = { "claude-code": "Claude Code", codex: "Codex", cursor: "Cursor", copilot: "Copilot", grok: "Grok" };
|
|
2
|
+
export const TOOL_LABELS = { "claude-code": "Claude Code", codex: "Codex", cursor: "Cursor", copilot: "Copilot", kiro: "Kiro", grok: "Grok" };
|
|
3
3
|
export const APP_TOOLS = [
|
|
4
4
|
{ id: "cursor-app", label: "Cursor app", kind: "app", bundle: "com.todesktop.230313mzl4w4u92", deeplink: "cursor://anysphere.cursor-deeplink/prompt?text={prompt}" },
|
|
5
5
|
{ id: "claude-app", label: "Claude app · question copied", kind: "app", bundle: "com.anthropic.claudefordesktop" },
|
|
@@ -67,6 +67,8 @@ const agentLabel = (agent) => {
|
|
|
67
67
|
return "Cursor";
|
|
68
68
|
if (a.includes("copilot"))
|
|
69
69
|
return "Copilot";
|
|
70
|
+
if (a.includes("kiro"))
|
|
71
|
+
return "Kiro";
|
|
70
72
|
return null;
|
|
71
73
|
};
|
|
72
74
|
function whoLine(from, agent, ts, now) {
|
package/dist/notify.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { chosenApproval } from "./install.js";
|
|
2
|
+
import { toolsAllowed } from "./tools.js";
|
|
1
3
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
4
|
import { homedir, platform } from "node:os";
|
|
3
5
|
import { dirname, join, resolve } from "node:path";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
import { whichPantheon } from "./service.js";
|
|
6
8
|
import { detectForHooks, agentById } from "./agents.js";
|
|
7
|
-
import { codexHookState, cursorHookState, copilotHookState, installCodexHooks, installCursorHooks, installCopilotHooks, removeCodexHooks, removeCursorHooks, removeCopilotHooks, } from "./hooks-install.js";
|
|
9
|
+
import { codexHookState, cursorHookState, copilotHookState, kiroHookState, installCodexHooks, installCursorHooks, installCopilotHooks, installKiroHooks, removeCodexHooks, removeCursorHooks, removeCopilotHooks, removeKiroHooks, } from "./hooks-install.js";
|
|
8
10
|
export const HOOK_MARKER = "pantheon hook";
|
|
9
|
-
export const HOOK_RE = /(?:pantheon(?:\.cmd|\.exe|\.bat)?"?\s+hook\s|cli\.js"?\s+hook\s+(?:stop|prompt|end|codex-stop|copilot-stop|cursor-stop)\b)/i;
|
|
11
|
+
export const HOOK_RE = /(?:pantheon(?:\.cmd|\.exe|\.bat)?"?\s+hook\s|cli\.js"?\s+hook\s+(?:stop|prompt|end|codex-stop|copilot-stop|cursor-stop|kiro-stop|kiro-prompt)\b)/i;
|
|
10
12
|
export const isOurHookCommand = (cmd) => !!cmd && (cmd.includes(HOOK_MARKER) || HOOK_RE.test(cmd));
|
|
11
13
|
export const hookBin = (bin) => (/\s/.test(bin) ? `"${bin}"` : bin);
|
|
12
14
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -108,6 +110,9 @@ export function agentHookStates(projectDir = process.cwd(), only) {
|
|
|
108
110
|
const copilot = copilotHookState(scope, projectDir);
|
|
109
111
|
if (copilot.installed)
|
|
110
112
|
out.push(copilot);
|
|
113
|
+
const kiro = kiroHookState(scope, projectDir);
|
|
114
|
+
if (kiro.installed)
|
|
115
|
+
out.push(kiro);
|
|
111
116
|
}
|
|
112
117
|
const byAgent = new Map();
|
|
113
118
|
for (const s of out) {
|
|
@@ -133,6 +138,8 @@ export function repairHook(home) {
|
|
|
133
138
|
missing.push(id);
|
|
134
139
|
if (id === "copilot" && !copilotHookState(intent.scope, projectDir).installed)
|
|
135
140
|
missing.push(id);
|
|
141
|
+
if (id === "kiro" && !kiroHookState(intent.scope, projectDir).installed)
|
|
142
|
+
missing.push(id);
|
|
136
143
|
}
|
|
137
144
|
if (!missing.length)
|
|
138
145
|
return null;
|
|
@@ -184,7 +191,7 @@ export function installHooks(scope, projectDir = process.cwd(), only) {
|
|
|
184
191
|
? only.map((id) => agentById(id)).filter((b) => !!b)
|
|
185
192
|
: available;
|
|
186
193
|
if (!pick.length) {
|
|
187
|
-
throw new Error("no agent found — install claude, codex, cursor-agent, or
|
|
194
|
+
throw new Error("no agent found — install claude, codex, cursor-agent, copilot, or kiro-cli, or open a project that already uses one");
|
|
188
195
|
}
|
|
189
196
|
const lines = [];
|
|
190
197
|
const agents = [];
|
|
@@ -206,6 +213,10 @@ export function installHooks(scope, projectDir = process.cwd(), only) {
|
|
|
206
213
|
lines.push(...installCopilotHooks(bin, scope, projectDir));
|
|
207
214
|
agents.push("copilot");
|
|
208
215
|
}
|
|
216
|
+
else if (backend.id === "kiro") {
|
|
217
|
+
lines.push(...installKiroHooks(bin, scope, projectDir, toolsAllowed(chosenApproval())));
|
|
218
|
+
agents.push("kiro");
|
|
219
|
+
}
|
|
209
220
|
}
|
|
210
221
|
lines.push(` effect when your agent tries to finish, it checks whether a peer is waiting`, ` on you — and keeps going instead of leaving them hanging.`, ` it also reports when a turn starts and ends, so a worker can tell`, ` an idle editor from one you are actually using.`);
|
|
211
222
|
return { lines, agents };
|
|
@@ -219,6 +230,7 @@ export function removeHook(projectDir = process.cwd()) {
|
|
|
219
230
|
out.push(...removeCodexHooks(projectDir));
|
|
220
231
|
out.push(...removeCursorHooks(projectDir));
|
|
221
232
|
out.push(...removeCopilotHooks(projectDir));
|
|
233
|
+
out.push(...removeKiroHooks(projectDir));
|
|
222
234
|
return out.length ? out : [" nothing installed"];
|
|
223
235
|
}
|
|
224
236
|
function removeClaudeHook(projectDir = process.cwd()) {
|
package/dist/persona.js
CHANGED
|
@@ -170,7 +170,7 @@ export const PERSONA_TEMPLATE = `---
|
|
|
170
170
|
# wake_by: alice, bob # only these peers' requests are handled by this persona
|
|
171
171
|
# kinds: ANSWER, ACCEPT # ANSWER (asks), ACCEPT (verdicts), REVISE (objections), CONTINUE (replies/deliveries)
|
|
172
172
|
# goals: launch-* # only goals whose id or title matches
|
|
173
|
-
# agent: auto # claude-code | codex | cursor | copilot | auto
|
|
173
|
+
# agent: auto # claude-code | codex | cursor | copilot | kiro | auto
|
|
174
174
|
---
|
|
175
175
|
Say how this worker should behave when it is woken while you are away — what it
|
|
176
176
|
should take on, what it should refuse, and where its judgment ends and yours
|
package/dist/profile.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { CONFIG_DIR } from "./config.js";
|
|
4
|
-
export const AGENT_SURFACES = ["codex", "claude-code", "cursor", "copilot"];
|
|
4
|
+
export const AGENT_SURFACES = ["codex", "claude-code", "cursor", "copilot", "kiro"];
|
|
5
5
|
export const SURFACE_BUNDLE_IDS = {
|
|
6
6
|
codex: "com.openai.codex",
|
|
7
7
|
"claude-code": "com.anthropic.claudefordesktop",
|
|
8
8
|
cursor: "com.todesktop.230313mzl4w4u92",
|
|
9
9
|
copilot: "com.microsoft.VSCode",
|
|
10
|
+
kiro: "com.amazon.codewhisperer",
|
|
10
11
|
};
|
|
11
12
|
export const profilePath = (home = CONFIG_DIR) => join(home, "profile.json");
|
|
12
13
|
export function isAgentSurface(value) {
|
|
@@ -17,6 +18,8 @@ export function surfaceFromAgent(agent) {
|
|
|
17
18
|
return "cursor";
|
|
18
19
|
if (agent && /copilot/i.test(agent))
|
|
19
20
|
return "copilot";
|
|
21
|
+
if (agent && /kiro/i.test(agent))
|
|
22
|
+
return "kiro";
|
|
20
23
|
if (agent && /claude/i.test(agent))
|
|
21
24
|
return "claude-code";
|
|
22
25
|
return "codex";
|
package/dist/run-failure.js
CHANGED
|
@@ -5,6 +5,7 @@ export const SIGN_IN_FIX = {
|
|
|
5
5
|
codex: "run `codex login`",
|
|
6
6
|
cursor: "run `cursor-agent login`",
|
|
7
7
|
copilot: "run `copilot login` (or `gh auth login` — copilot uses gh's sign-in too)",
|
|
8
|
+
kiro: "run `kiro-cli login` (or set KIRO_API_KEY)",
|
|
8
9
|
};
|
|
9
10
|
const sh = (dir, ...args) => execFileSync("git", ["-C", dir, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000 }).trim();
|
|
10
11
|
export const gitProbe = {
|
package/dist/setup-guide.js
CHANGED
|
@@ -195,7 +195,7 @@ export async function runGuide(io, ctx, deps = {}, preset = {}) {
|
|
|
195
195
|
: "only people you approve by hand (pantheon admit <name> --may-wake)"}`);
|
|
196
196
|
io.say(` • Alerts: ${shortAlert(a.notifications).toLowerCase()}`);
|
|
197
197
|
io.say(` • Approval: ${a.approval === "approve"
|
|
198
|
-
? "your agent sends to teammates in your name without asking each time (Claude Code, Cursor and
|
|
198
|
+
? "your agent sends to teammates in your name without asking each time (Claude Code, Cursor, Codex, and Kiro's IDE and pantheon agent; Copilot asks once per tool per folder)"
|
|
199
199
|
: "your agent asks you before each send, goal and claim; reading and waiting never prompt"}`);
|
|
200
200
|
io.say(` • Decisions: ${a.decides.length ? `you personally decide ${a.decides.join(", ")}` : "none reserved for you — your agent may decide; add an area when one comes up"}`);
|
|
201
201
|
if (a.wake !== "no" && ctx.project)
|
package/dist/tray.js
CHANGED
|
@@ -144,7 +144,7 @@ async function trayStatusOne(cfg) {
|
|
|
144
144
|
];
|
|
145
145
|
const profile = loadProfile(cfg);
|
|
146
146
|
const surface = profile.agent.primary_surface;
|
|
147
|
-
const surfaceName = surface === "claude-code" ? "Claude" : surface === "cursor" ? "Cursor" : surface === "copilot" ? "Copilot" : "Codex";
|
|
147
|
+
const surfaceName = surface === "claude-code" ? "Claude" : surface === "cursor" ? "Cursor" : surface === "copilot" ? "Copilot" : surface === "kiro" ? "Kiro" : "Codex";
|
|
148
148
|
const rawWorkerState = workerState();
|
|
149
149
|
return {
|
|
150
150
|
configured: true,
|
package/dist/worker.js
CHANGED
|
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
import { PantheonClient } from "./client.js";
|
|
7
|
-
import { headlessSpawn, materializeSpawnFiles, chooseAgent, describeChoice } from "./agents.js";
|
|
7
|
+
import { headlessSpawn, materializeSpawnFiles, chooseAgent, describeChoice, unattendedRefusal } from "./agents.js";
|
|
8
8
|
import { agentSpawn } from "./which.js";
|
|
9
9
|
import { VERSION } from "./version.js";
|
|
10
10
|
import { applyAnswer, loadPolicy, mayAsk, needsSelfUpgrade, recordAsk, savePolicy, useGrant, } from "./worker-policy.js";
|
|
@@ -604,6 +604,9 @@ export class Worker {
|
|
|
604
604
|
this.opts.log(` agent ${said}`);
|
|
605
605
|
this.lastAgentSaid = said;
|
|
606
606
|
}
|
|
607
|
+
const refused = unattendedRefusal(choice);
|
|
608
|
+
if (refused)
|
|
609
|
+
throw new Error(refused);
|
|
607
610
|
const plan = headlessSpawn({ backend: choice.backend, binary: choice.binary, prompt: brief, worktree, cfg: this.cfg, branch, allowBash, runHome });
|
|
608
611
|
materializeSpawnFiles(worktree, plan);
|
|
609
612
|
({ command, args, env } = { command: plan.command, args: plan.args, env: { ...env, ...plan.env } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@join-pantheon/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Let your coding agent collaborate directly with your teammates' agents — ask, agree an interface, and deliver, without a human relaying messages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|