@join-pantheon/cli 0.26.5 → 0.28.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/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 copilot, or open a project that already uses one");
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";
@@ -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 = {
@@ -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 Codex; Copilot asks once per tool per folder)"
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,
@@ -5,6 +5,7 @@ import { CONFIG_DIR } from "./config.js";
5
5
  import { repoRoot, PKG_NAME } from "./upgrade.js";
6
6
  import { VERSION } from "./version.js";
7
7
  import { npmSpawn } from "./which.js";
8
+ import { proxyEnvFor } from "./network.js";
8
9
  export function semverGt(x, y) {
9
10
  const p = (s) => s.split(".").map((n) => Number(n) || 0);
10
11
  const [a, b] = [p(x), p(y)];
@@ -81,7 +82,7 @@ export function defaultDeps(home) {
81
82
  },
82
83
  npmLatest: () => {
83
84
  try {
84
- const s = npmSpawn(["view", PKG_NAME, "version"]);
85
+ const s = npmSpawn(["view", PKG_NAME, "version"], undefined, undefined, proxyEnvFor());
85
86
  return execFileSync(s.command, s.args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 4000, ...s.options }).trim() || null;
86
87
  }
87
88
  catch {
package/dist/upgrade.js CHANGED
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { installForUser } from "./install.js";
6
6
  import { npmSpawn } from "./which.js";
7
+ import { proxyEnvFor } from "./network.js";
7
8
  import { relayProc, workerProc, notifierProc, menubarProc } from "./machine.js";
8
9
  export function repoRoot() {
9
10
  return join(dirname(fileURLToPath(import.meta.url)), "..");
@@ -30,7 +31,7 @@ export function rewireVia(root, fallback, run = (file, args) => execFileSync(pro
30
31
  export function defaultDeps() {
31
32
  const git = (root, ...args) => execFileSync("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
32
33
  const npm = (args, opts) => {
33
- const s = npmSpawn(args);
34
+ const s = npmSpawn(args, undefined, undefined, proxyEnvFor());
34
35
  return execFileSync(s.command, s.args, { encoding: "utf8", ...opts, ...s.options });
35
36
  };
36
37
  return {
package/dist/which.js CHANGED
@@ -76,10 +76,12 @@ export function spawnShape(binary, os = platform()) {
76
76
  return { command: binary, shell: false };
77
77
  return { command: binary, shell: /\.(cmd|bat)$/i.test(binary) };
78
78
  }
79
- export function npmSpawn(args, os = platform(), resolveNpm = () => which("npm", os)) {
79
+ export function npmSpawn(args, os = platform(), resolveNpm = () => which("npm", os), proxyEnv = {}) {
80
+ const env = Object.keys(proxyEnv).length ? { env: { ...process.env, ...proxyEnv } } : {};
80
81
  if (!isWindows(os))
81
- return { command: "npm", args: [...args], options: {} };
82
- return agentSpawn(resolveNpm() ?? "npm.cmd", args, os);
82
+ return { command: "npm", args: [...args], options: { ...env } };
83
+ const s = agentSpawn(resolveNpm() ?? "npm.cmd", args, os);
84
+ return { ...s, options: { ...s.options, ...env } };
83
85
  }
84
86
  const cmdQuote = (a) => (/[\s"&|<>^()]/.test(a) || a === "" ? `"${a.replace(/"/g, '\\"')}"` : a);
85
87
  export function agentSpawn(binary, args, os = platform()) {
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";
@@ -19,7 +19,11 @@ import { diagnoseRun, gitProbe } from "./run-failure.js";
19
19
  import { installedTools, preferredTool } from "./notifier.js";
20
20
  import { loadPersonas, matchPersona, personaByName, personaMatches, effectivePolicy, personaBrief, describePersona, } from "./persona.js";
21
21
  import { isContinuationFor, LIMITS, shouldDeferToHuman } from "./protocol.js";
22
- const git = (cwd, ...args) => execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
22
+ import { proxyEnvFor } from "./network.js";
23
+ const git = (cwd, ...args) => {
24
+ const px = proxyEnvFor();
25
+ return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], ...(Object.keys(px).length ? { env: { ...process.env, ...px } } : {}) }).trim();
26
+ };
23
27
  const CLOCK_SKEW_MS = 5_000;
24
28
  const isRepo = (cwd) => {
25
29
  try {
@@ -583,7 +587,7 @@ export class Worker {
583
587
  let command;
584
588
  let agentId;
585
589
  let args;
586
- let env = { ...process.env };
590
+ let env = { ...process.env, ...proxyEnvFor() };
587
591
  try {
588
592
  const runHome = this.runHome();
589
593
  agentId = this.opts.agentCmd ? "claude-code" : undefined;
@@ -604,6 +608,9 @@ export class Worker {
604
608
  this.opts.log(` agent ${said}`);
605
609
  this.lastAgentSaid = said;
606
610
  }
611
+ const refused = unattendedRefusal(choice);
612
+ if (refused)
613
+ throw new Error(refused);
607
614
  const plan = headlessSpawn({ backend: choice.backend, binary: choice.binary, prompt: brief, worktree, cfg: this.cfg, branch, allowBash, runHome });
608
615
  materializeSpawnFiles(worktree, plan);
609
616
  ({ 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.26.5",
3
+ "version": "0.28.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": {
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.0.0",
25
+ "undici": "^7.29.1",
25
26
  "ws": "^8.18.0",
26
27
  "zod": "^3.23.8"
27
28
  },