@staff0rd/assist 0.393.0 → 0.394.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/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.393.0",
9
+ version: "0.394.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -18,6 +18,7 @@ var package_default = {
18
18
  "dist",
19
19
  "claude",
20
20
  "codex",
21
+ "pi",
21
22
  "allowed.cli-reads",
22
23
  "allowed.cli-writes"
23
24
  ],
@@ -141,10 +142,10 @@ import { stringify as stringifyYaml } from "yaml";
141
142
  // src/shared/loadRawYaml.ts
142
143
  import { existsSync, readFileSync } from "fs";
143
144
  import { parse as parseYaml } from "yaml";
144
- function loadRawYaml(path64) {
145
- if (!existsSync(path64)) return {};
145
+ function loadRawYaml(path67) {
146
+ if (!existsSync(path67)) return {};
146
147
  try {
147
- const content = readFileSync(path64, "utf8");
148
+ const content = readFileSync(path67, "utf8");
148
149
  return parseYaml(content) || {};
149
150
  } catch {
150
151
  return {};
@@ -344,8 +345,9 @@ var assistConfigSchema = z2.strictObject({
344
345
  autoConfirm: z2.boolean().default(false)
345
346
  }).default({ autoConfirm: false }),
346
347
  harness: z2.strictObject({
347
- engine: z2.enum(["claude", "codex"]).default("claude"),
348
- exposeCodexActions: z2.boolean().optional()
348
+ engine: z2.enum(["claude", "codex", "pi"]).default("claude"),
349
+ exposeCodexActions: z2.boolean().optional(),
350
+ exposePiActions: z2.boolean().optional()
349
351
  }).default({ engine: "claude" }),
350
352
  voice: z2.strictObject({
351
353
  wakeWords: z2.array(z2.string()).default(DEFAULT_WAKE_WORDS),
@@ -1361,8 +1363,8 @@ function findUnmergedPaths() {
1361
1363
  }
1362
1364
  const found = /* @__PURE__ */ new Set();
1363
1365
  for (const line of output.split("\n")) {
1364
- const path64 = line.split(" ")[1];
1365
- if (path64) found.add(path64.trim());
1366
+ const path67 = line.split(" ")[1];
1367
+ if (path67) found.add(path67.trim());
1366
1368
  }
1367
1369
  return [...found];
1368
1370
  }
@@ -1461,8 +1463,8 @@ function getPreferredRemoteRepo(cwd) {
1461
1463
  function gitRefUrl(kind, ref, cwd) {
1462
1464
  const repo = getPreferredRemoteRepo(cwd);
1463
1465
  if (!repo) return void 0;
1464
- const path64 = kind === "branch" ? "tree" : "commit";
1465
- return `https://github.com/${repo.org}/${repo.repo}/${path64}/${ref}`;
1466
+ const path67 = kind === "branch" ? "tree" : "commit";
1467
+ return `https://github.com/${repo.org}/${repo.repo}/${path67}/${ref}`;
1466
1468
  }
1467
1469
 
1468
1470
  // src/commands/commit/collectCommitRefs.ts
@@ -3233,9 +3235,9 @@ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
3233
3235
 
3234
3236
  // src/commands/verify/forbiddenStrings/findForbiddenStrings.ts
3235
3237
  import { minimatch as minimatch2 } from "minimatch";
3236
- function resolveStringsAtPath(data, path64) {
3238
+ function resolveStringsAtPath(data, path67) {
3237
3239
  let current = data;
3238
- for (const segment of path64.split(".")) {
3240
+ for (const segment of path67.split(".")) {
3239
3241
  if (current === null || typeof current !== "object") return [];
3240
3242
  current = current[segment];
3241
3243
  }
@@ -3248,10 +3250,10 @@ function resolveStringsAtPath(data, path64) {
3248
3250
  }
3249
3251
  function findRuleViolations(data, rule) {
3250
3252
  const violations = [];
3251
- for (const path64 of rule.paths) {
3252
- for (const value of resolveStringsAtPath(data, path64)) {
3253
+ for (const path67 of rule.paths) {
3254
+ for (const value of resolveStringsAtPath(data, path67)) {
3253
3255
  if (minimatch2(value, rule.disallowed))
3254
- violations.push({ file: rule.file, path: path64, value });
3256
+ violations.push({ file: rule.file, path: path67, value });
3255
3257
  }
3256
3258
  }
3257
3259
  return violations;
@@ -3290,8 +3292,8 @@ function forbiddenStrings() {
3290
3292
  process.exit(0);
3291
3293
  }
3292
3294
  console.log("Forbidden strings found:\n");
3293
- for (const { file, path: path64, value } of violations) {
3294
- console.log(` ${file} ${path64}: ${value}`);
3295
+ for (const { file, path: path67, value } of violations) {
3296
+ console.log(` ${file} ${path67}: ${value}`);
3295
3297
  }
3296
3298
  console.log(`
3297
3299
  Total: ${violations.length} forbidden string(s).`);
@@ -3583,15 +3585,15 @@ function readBaselineMigrations(repoRelativeDir, ref) {
3583
3585
  } catch {
3584
3586
  return baseline;
3585
3587
  }
3586
- const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path64) => MIGRATION_FILE3.test(basename(path64)));
3587
- for (const path64 of paths) {
3588
+ const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path67) => MIGRATION_FILE3.test(basename(path67)));
3589
+ for (const path67 of paths) {
3588
3590
  try {
3589
- const content = execSync12(`git show "${ref}:${path64}"`, {
3591
+ const content = execSync12(`git show "${ref}:${path67}"`, {
3590
3592
  encoding: "utf8",
3591
3593
  maxBuffer: 16 * 1024 * 1024,
3592
3594
  stdio: ["pipe", "pipe", "pipe"]
3593
3595
  });
3594
- baseline.set(basename(path64), content);
3596
+ baseline.set(basename(path67), content);
3595
3597
  } catch {
3596
3598
  }
3597
3599
  }
@@ -4956,15 +4958,15 @@ var hooksSettings = {
4956
4958
  }
4957
4959
  };
4958
4960
  function ensureHooksSettings() {
4959
- const path64 = daemonPaths.hooksSettings;
4960
- mkdirSync5(dirname12(path64), { recursive: true });
4961
- writeFileSync15(path64, JSON.stringify(hooksSettings, null, 2));
4962
- return path64;
4961
+ const path67 = daemonPaths.hooksSettings;
4962
+ mkdirSync5(dirname12(path67), { recursive: true });
4963
+ writeFileSync15(path67, JSON.stringify(hooksSettings, null, 2));
4964
+ return path67;
4963
4965
  }
4964
4966
 
4965
4967
  // src/shared/spawnInherit.ts
4966
4968
  import { spawn as spawn3 } from "child_process";
4967
- function spawnInherit(command, args) {
4969
+ function spawnInherit(command, args, options2 = {}) {
4968
4970
  const {
4969
4971
  ASSIST_ACTIVITY_ID: _activityId,
4970
4972
  CLAUDE_CODE_CHILD_SESSION: _childSession,
@@ -4972,7 +4974,8 @@ function spawnInherit(command, args) {
4972
4974
  } = process.env;
4973
4975
  const child = spawn3(command, args, {
4974
4976
  stdio: "inherit",
4975
- env
4977
+ env,
4978
+ cwd: options2.cwd
4976
4979
  });
4977
4980
  const done2 = new Promise((resolve18, reject) => {
4978
4981
  child.on("close", (code) => resolve18(code ?? 0));
@@ -5223,9 +5226,9 @@ var LOCAL_FILES = ["backlog.jsonl", "backlog.db"];
5223
5226
  function backupLocalBacklogFiles(dir) {
5224
5227
  const moved = [];
5225
5228
  for (const name of LOCAL_FILES) {
5226
- const path64 = join15(dir, ".assist", name);
5227
- if (existsSync18(path64)) {
5228
- renameSync(path64, `${path64}.bak`);
5229
+ const path67 = join15(dir, ".assist", name);
5230
+ if (existsSync18(path67)) {
5231
+ renameSync(path67, `${path67}.bak`);
5229
5232
  moved.push(`${name} \u2192 ${name}.bak`);
5230
5233
  }
5231
5234
  }
@@ -5664,8 +5667,8 @@ var backlogItemSchema = z3.strictObject({
5664
5667
  var backlogFileSchema = z3.array(backlogItemSchema);
5665
5668
 
5666
5669
  // src/commands/backlog/parseBacklogJsonl.ts
5667
- function parseBacklogJsonl(path64) {
5668
- const content = readFileSync14(path64, "utf8").trim();
5670
+ function parseBacklogJsonl(path67) {
5671
+ const content = readFileSync14(path67, "utf8").trim();
5669
5672
  if (content.length === 0) return [];
5670
5673
  return content.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => backlogItemSchema.parse(JSON.parse(line)));
5671
5674
  }
@@ -5747,8 +5750,8 @@ function findBacklogUp(startDir) {
5747
5750
 
5748
5751
  // src/commands/backlog/getCurrentOrigin.ts
5749
5752
  import { execFileSync } from "child_process";
5750
- function stripLeadingSlashes(path64) {
5751
- return path64.replace(/^\/+/, "");
5753
+ function stripLeadingSlashes(path67) {
5754
+ return path67.replace(/^\/+/, "");
5752
5755
  }
5753
5756
  function normalizeOrigin(raw) {
5754
5757
  const trimmed = raw.trim().replace(/\.git$/i, "").replace(/\/+$/, "");
@@ -6288,24 +6291,24 @@ function getOwnerPath(itemId2) {
6288
6291
  function recordSignalOwner(itemId2) {
6289
6292
  const sessionId = process.env.ASSIST_SESSION_ID;
6290
6293
  if (!sessionId) return;
6291
- const path64 = getOwnerPath(itemId2);
6292
- mkdirSync7(dirname14(path64), { recursive: true });
6293
- writeFileSync17(path64, JSON.stringify({ sessionId }));
6294
+ const path67 = getOwnerPath(itemId2);
6295
+ mkdirSync7(dirname14(path67), { recursive: true });
6296
+ writeFileSync17(path67, JSON.stringify({ sessionId }));
6294
6297
  }
6295
6298
  function readSignalOwner(itemId2) {
6296
- const path64 = getOwnerPath(itemId2);
6297
- if (!existsSync21(path64)) return void 0;
6299
+ const path67 = getOwnerPath(itemId2);
6300
+ if (!existsSync21(path67)) return void 0;
6298
6301
  try {
6299
- const parsed = JSON.parse(readFileSync15(path64, "utf8"));
6302
+ const parsed = JSON.parse(readFileSync15(path67, "utf8"));
6300
6303
  return parsed.sessionId;
6301
6304
  } catch {
6302
6305
  return void 0;
6303
6306
  }
6304
6307
  }
6305
6308
  function clearSignalOwner(itemId2) {
6306
- const path64 = getOwnerPath(itemId2);
6309
+ const path67 = getOwnerPath(itemId2);
6307
6310
  try {
6308
- rmSync(path64);
6311
+ rmSync(path67);
6309
6312
  } catch {
6310
6313
  }
6311
6314
  }
@@ -6342,19 +6345,19 @@ function resolveSignalTarget(event, data) {
6342
6345
  function writeSignal(event, data) {
6343
6346
  const target = resolveSignalTarget(event, data);
6344
6347
  if (!target) return;
6345
- const path64 = getSignalPath(target);
6346
- if (!path64) return;
6348
+ const path67 = getSignalPath(target);
6349
+ if (!path67) return;
6347
6350
  const signal = { event, sessionId: target, ...data };
6348
- mkdirSync8(dirname15(path64), { recursive: true });
6349
- writeFileSync18(path64, JSON.stringify(signal));
6351
+ mkdirSync8(dirname15(path67), { recursive: true });
6352
+ writeFileSync18(path67, JSON.stringify(signal));
6350
6353
  }
6351
6354
 
6352
6355
  // src/commands/backlog/readSignal.ts
6353
6356
  function readSignal() {
6354
- const path64 = getSignalPath();
6355
- if (!path64 || !existsSync22(path64)) return void 0;
6357
+ const path67 = getSignalPath();
6358
+ if (!path67 || !existsSync22(path67)) return void 0;
6356
6359
  try {
6357
- return JSON.parse(readFileSync16(path64, "utf8"));
6360
+ return JSON.parse(readFileSync16(path67, "utf8"));
6358
6361
  } catch {
6359
6362
  return void 0;
6360
6363
  }
@@ -6449,13 +6452,13 @@ function activityPath(sessionId) {
6449
6452
  function emitActivity(activity2) {
6450
6453
  const sessionId = process.env.ASSIST_ACTIVITY_ID;
6451
6454
  if (!sessionId) return;
6452
- const path64 = activityPath(sessionId);
6453
- mkdirSync9(dirname16(path64), { recursive: true });
6454
- writeFileSync19(path64, JSON.stringify({ ...activity2, startedAt: Date.now() }));
6455
+ const path67 = activityPath(sessionId);
6456
+ mkdirSync9(dirname16(path67), { recursive: true });
6457
+ writeFileSync19(path67, JSON.stringify({ ...activity2, startedAt: Date.now() }));
6455
6458
  }
6456
- function readActivity(path64) {
6459
+ function readActivity(path67) {
6457
6460
  try {
6458
- return JSON.parse(readFileSync17(path64, "utf8"));
6461
+ return JSON.parse(readFileSync17(path67, "utf8"));
6459
6462
  } catch {
6460
6463
  return void 0;
6461
6464
  }
@@ -6465,9 +6468,9 @@ function reconcileActivity(sessionId, activity2) {
6465
6468
  removeActivity(sessionId);
6466
6469
  return;
6467
6470
  }
6468
- const path64 = activityPath(sessionId);
6469
- mkdirSync9(dirname16(path64), { recursive: true });
6470
- writeFileSync19(path64, JSON.stringify(activity2));
6471
+ const path67 = activityPath(sessionId);
6472
+ mkdirSync9(dirname16(path67), { recursive: true });
6473
+ writeFileSync19(path67, JSON.stringify(activity2));
6471
6474
  }
6472
6475
  function removeActivity(sessionId) {
6473
6476
  try {
@@ -7698,8 +7701,8 @@ async function getBacklogExists(req, res) {
7698
7701
  // src/commands/backlog/originDisplayName.ts
7699
7702
  function originDisplayName(origin) {
7700
7703
  if (origin.startsWith("local:")) {
7701
- const path64 = origin.slice("local:".length).replace(/\/+$/, "");
7702
- const segments = path64.split("/").filter(Boolean);
7704
+ const path67 = origin.slice("local:".length).replace(/\/+$/, "");
7705
+ const segments = path67.split("/").filter(Boolean);
7703
7706
  return segments[segments.length - 1] ?? origin;
7704
7707
  }
7705
7708
  const firstSlash = origin.indexOf("/");
@@ -8102,7 +8105,7 @@ function findSynthesisForBranch(repoReviewsDir, branch2) {
8102
8105
  const parent = dirname20(branchKeyPath);
8103
8106
  const branchPrefix = basename5(branchKeyPath);
8104
8107
  if (!existsSync25(parent)) return null;
8105
- const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join23(parent, name, "synthesis.md")).filter((path64) => existsSync25(path64)).map((path64) => ({ path: path64, mtime: statSync2(path64).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
8108
+ const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join23(parent, name, "synthesis.md")).filter((path67) => existsSync25(path67)).map((path67) => ({ path: path67, mtime: statSync2(path67).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
8106
8109
  return synthesisFiles[0]?.path ?? null;
8107
8110
  }
8108
8111
 
@@ -8156,12 +8159,12 @@ async function getReviewSynthesis(req, res) {
8156
8159
  const cwd = getCwdParam(req, res);
8157
8160
  if (!cwd) return;
8158
8161
  try {
8159
- const path64 = await resolveSynthesisPath(cwd);
8160
- if (!path64) {
8162
+ const path67 = await resolveSynthesisPath(cwd);
8163
+ if (!path67) {
8161
8164
  respondJson(res, 404, { error: "No synthesis found" });
8162
8165
  return;
8163
8166
  }
8164
- respondJson(res, 200, { synthesis: readFileSync19(path64, "utf8") });
8167
+ respondJson(res, 200, { synthesis: readFileSync19(path67, "utf8") });
8165
8168
  } catch {
8166
8169
  respondJson(res, 404, { error: "No synthesis found" });
8167
8170
  }
@@ -8272,6 +8275,15 @@ var harnesses = {
8272
8275
  agentsFile: "AGENTS.md",
8273
8276
  commandDest: (name) => path25.join("skills", name, "SKILL.md")
8274
8277
  }
8278
+ },
8279
+ pi: {
8280
+ kind: "pi",
8281
+ command: "pi",
8282
+ homeDir: path25.join(os2.homedir(), ".pi", "agent"),
8283
+ sync: {
8284
+ agentsFile: "AGENTS.md",
8285
+ commandDest: (name) => path25.join("prompts", `${name}.md`)
8286
+ }
8275
8287
  }
8276
8288
  };
8277
8289
  function isHarnessAvailable(kind) {
@@ -8280,9 +8292,12 @@ function isHarnessAvailable(kind) {
8280
8292
 
8281
8293
  // src/commands/sessions/web/harnessCapabilities.ts
8282
8294
  function harnessCapabilities(_req, res) {
8283
- const forcedOff = loadConfig().harness.exposeCodexActions === false;
8284
- const exposeCodexActions = !forcedOff && isHarnessAvailable("codex");
8285
- respondJson(res, 200, { exposeCodexActions });
8295
+ const config = loadConfig().harness;
8296
+ const codexForcedOff = config.exposeCodexActions === false;
8297
+ const exposeCodexActions = !codexForcedOff && isHarnessAvailable("codex");
8298
+ const piForcedOff = config.exposePiActions === false;
8299
+ const exposePiActions = !piForcedOff && isHarnessAvailable("pi");
8300
+ respondJson(res, 200, { exposeCodexActions, exposePiActions });
8286
8301
  }
8287
8302
 
8288
8303
  // src/shared/loadJson.ts
@@ -8296,10 +8311,10 @@ function getStorePath(filename) {
8296
8311
  return join26(getStoreDir(), filename);
8297
8312
  }
8298
8313
  function loadJson(filename) {
8299
- const path64 = getStorePath(filename);
8300
- if (existsSync26(path64)) {
8314
+ const path67 = getStorePath(filename);
8315
+ if (existsSync26(path67)) {
8301
8316
  try {
8302
- return JSON.parse(readFileSync20(path64, "utf8"));
8317
+ return JSON.parse(readFileSync20(path67, "utf8"));
8303
8318
  } catch {
8304
8319
  return {};
8305
8320
  }
@@ -10092,11 +10107,20 @@ function spawnCodex(prompt, options2 = {}) {
10092
10107
  return spawnInherit(harnesses.codex.command, ["-C", cwd, prompt]);
10093
10108
  }
10094
10109
 
10110
+ // src/shared/spawnPi.ts
10111
+ function spawnPi(prompt, options2 = {}) {
10112
+ const cwd = options2.cwd ?? process.cwd();
10113
+ return spawnInherit(harnesses.pi.command, [prompt], { cwd });
10114
+ }
10115
+
10095
10116
  // src/shared/spawnHarness.ts
10096
10117
  function spawnHarness(harness, prompt, options2 = {}) {
10097
10118
  if (harness === "codex") {
10098
10119
  return spawnCodex(prompt, { cwd: options2.cwd });
10099
10120
  }
10121
+ if (harness === "pi") {
10122
+ return spawnPi(prompt, { cwd: options2.cwd });
10123
+ }
10100
10124
  return spawnClaude(prompt, {
10101
10125
  allowEdits: true,
10102
10126
  sessionId: options2.sessionId,
@@ -10261,7 +10285,7 @@ async function refine(id, options2) {
10261
10285
 
10262
10286
  // src/commands/backlog/resolveHarness.ts
10263
10287
  function resolveHarness(value) {
10264
- if (value === "claude" || value === "codex") return value;
10288
+ if (value === "claude" || value === "codex" || value === "pi") return value;
10265
10289
  return loadConfig().harness.engine;
10266
10290
  }
10267
10291
 
@@ -10269,7 +10293,7 @@ function resolveHarness(value) {
10269
10293
  function registerRefineCommand(cmd) {
10270
10294
  cmd.command("refine").argument("[id]", "Backlog item ID").description("Alias for refine").option("--once", "Exit when the initial task completes").option(
10271
10295
  "--harness <kind>",
10272
- "Coding harness to launch (claude|codex); defaults to the configured harness.engine"
10296
+ "Coding harness to launch (claude|codex|pi); defaults to the configured harness.engine"
10273
10297
  ).action(
10274
10298
  (id, opts) => refine(id, {
10275
10299
  once: opts.once,
@@ -11543,9 +11567,9 @@ var __dirname4 = dirname21(__filename3);
11543
11567
  function packageRoot() {
11544
11568
  return __dirname4;
11545
11569
  }
11546
- function readLines(path64) {
11547
- if (!existsSync27(path64)) return [];
11548
- return readFileSync23(path64, "utf8").split("\n").filter((line) => line.trim() !== "");
11570
+ function readLines(path67) {
11571
+ if (!existsSync27(path67)) return [];
11572
+ return readFileSync23(path67, "utf8").split("\n").filter((line) => line.trim() !== "");
11549
11573
  }
11550
11574
  var cachedReads;
11551
11575
  var cachedWrites;
@@ -12037,14 +12061,14 @@ function showProgress(p, label2) {
12037
12061
  const pct = Math.round(p.done / p.total * 100);
12038
12062
  process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
12039
12063
  }
12040
- async function resolveCommand(cli, path64, description, depth, p) {
12041
- showProgress(p, path64.join(" "));
12042
- const subHelp = await runHelp([cli, ...path64]);
12064
+ async function resolveCommand(cli, path67, description, depth, p) {
12065
+ showProgress(p, path67.join(" "));
12066
+ const subHelp = await runHelp([cli, ...path67]);
12043
12067
  if (!subHelp || !hasSubcommands(subHelp)) {
12044
- return [{ path: path64, description }];
12068
+ return [{ path: path67, description }];
12045
12069
  }
12046
- const children = await discoverAt(cli, path64, depth + 1, p);
12047
- return children.length > 0 ? children : [{ path: path64, description }];
12070
+ const children = await discoverAt(cli, path67, depth + 1, p);
12071
+ return children.length > 0 ? children : [{ path: path67, description }];
12048
12072
  }
12049
12073
  async function discoverAt(cli, parentPath, depth, p) {
12050
12074
  if (depth > SAFETY_DEPTH) return [];
@@ -12192,9 +12216,9 @@ function logPath(cli) {
12192
12216
  return join30(homedir16(), ".assist", `cli-discover-${safeName}.log`);
12193
12217
  }
12194
12218
  function readCache(cli) {
12195
- const path64 = logPath(cli);
12196
- if (!existsSync29(path64)) return void 0;
12197
- return readFileSync25(path64, "utf8");
12219
+ const path67 = logPath(cli);
12220
+ if (!existsSync29(path67)) return void 0;
12221
+ return readFileSync25(path67, "utf8");
12198
12222
  }
12199
12223
  function writeCache(cli, output) {
12200
12224
  const dir = join30(homedir16(), ".assist");
@@ -12366,9 +12390,9 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
12366
12390
  }
12367
12391
  const cutoff = Date.now() - STALE_AFTER_MS;
12368
12392
  for (const entry of entries) {
12369
- const path64 = join32(dir, entry);
12393
+ const path67 = join32(dir, entry);
12370
12394
  try {
12371
- if (statSync3(path64).mtimeMs < cutoff) unlinkSync7(path64);
12395
+ if (statSync3(path67).mtimeMs < cutoff) unlinkSync7(path67);
12372
12396
  } catch {
12373
12397
  continue;
12374
12398
  }
@@ -12378,10 +12402,10 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
12378
12402
  // src/commands/codeComment/readPinState.ts
12379
12403
  import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
12380
12404
  function readPinState(pin) {
12381
- const path64 = getPinStatePath(pin);
12382
- if (!existsSync30(path64)) return void 0;
12405
+ const path67 = getPinStatePath(pin);
12406
+ if (!existsSync30(path67)) return void 0;
12383
12407
  try {
12384
- const state = JSON.parse(readFileSync26(path64, "utf8"));
12408
+ const state = JSON.parse(readFileSync26(path67, "utf8"));
12385
12409
  if (state.pin !== pin) return void 0;
12386
12410
  return state;
12387
12411
  } catch {
@@ -13206,8 +13230,8 @@ function stepIntoNested(container, key, nextKey) {
13206
13230
  }
13207
13231
  return ensureObject(container, resolved);
13208
13232
  }
13209
- function setNestedValue(obj, path64, value) {
13210
- const keys = path64.split(".");
13233
+ function setNestedValue(obj, path67, value) {
13234
+ const keys = path67.split(".");
13211
13235
  const result = { ...obj };
13212
13236
  let current = result;
13213
13237
  for (let i = 0; i < keys.length - 1; i++) {
@@ -13287,9 +13311,9 @@ function isTraversable(value) {
13287
13311
  function stepInto(current, key) {
13288
13312
  return isTraversable(current) ? current[key] : void 0;
13289
13313
  }
13290
- function getNestedValue(obj, path64) {
13314
+ function getNestedValue(obj, path67) {
13291
13315
  let current = obj;
13292
- for (const key of path64.split(".")) current = stepInto(current, key);
13316
+ for (const key of path67.split(".")) current = stepInto(current, key);
13293
13317
  return current;
13294
13318
  }
13295
13319
 
@@ -15428,17 +15452,22 @@ function registerJira(program2) {
15428
15452
  }
15429
15453
 
15430
15454
  // src/commands/backlog/registerRefineLaunch.ts
15431
- var HARNESS_FLAG = "Coding harness to launch (claude|codex); defaults to the configured harness.engine";
15455
+ var HARNESS_FLAG = "Coding harness to launch (claude|codex|pi); defaults to the configured harness.engine";
15432
15456
  var HARNESS_CONFIG_HELP = [
15433
15457
  {
15434
15458
  key: "harness.engine",
15435
- setter: "assist config set harness.engine <claude|codex>",
15459
+ setter: "assist config set harness.engine <claude|codex|pi>",
15436
15460
  note: "default coding harness launched by refine and other flows"
15437
15461
  },
15438
15462
  {
15439
15463
  key: "harness.exposeCodexActions",
15440
15464
  setter: "assist config set harness.exposeCodexActions <true|false>",
15441
15465
  note: "force the web UI 'with Codex' actions off even when codex is on PATH"
15466
+ },
15467
+ {
15468
+ key: "harness.exposePiActions",
15469
+ setter: "assist config set harness.exposePiActions <true|false>",
15470
+ note: "force the web UI 'with pi' actions off even when pi is on PATH"
15442
15471
  }
15443
15472
  ];
15444
15473
  function runRefine(id, opts) {
@@ -15875,9 +15904,9 @@ function parseRscRows(flight) {
15875
15904
  }
15876
15905
  function makeRscResolver(rows) {
15877
15906
  return (ref) => {
15878
- const [id, ...path64] = ref.slice(1).replace(/^[L@]/, "").split(":");
15907
+ const [id, ...path67] = ref.slice(1).replace(/^[L@]/, "").split(":");
15879
15908
  let v = rows[id];
15880
- for (const key of path64) {
15909
+ for (const key of path67) {
15881
15910
  if (v == null || typeof v !== "object") return void 0;
15882
15911
  v = v[key];
15883
15912
  }
@@ -16354,6 +16383,43 @@ function registerNews(program2) {
16354
16383
  newsCommand.command("add").description("Add an RSS feed URL").argument("<url>", "RSS feed URL").action(add2);
16355
16384
  }
16356
16385
 
16386
+ // src/commands/piHook/index.ts
16387
+ var TOOL_MAP = {
16388
+ bash: "Bash",
16389
+ powershell: "PowerShell"
16390
+ };
16391
+ function parseInput2(raw) {
16392
+ try {
16393
+ const data = JSON.parse(raw);
16394
+ const command = data.command;
16395
+ if (typeof command !== "string" || !command.trim()) return void 0;
16396
+ const toolName = TOOL_MAP[(data.toolName ?? "bash").toLowerCase()];
16397
+ if (!toolName) return void 0;
16398
+ return { toolName, command: command.trim() };
16399
+ } catch {
16400
+ return void 0;
16401
+ }
16402
+ }
16403
+ async function piHook() {
16404
+ const input = parseInput2(await readStdin());
16405
+ if (!input) return;
16406
+ const decision = decideCommand(input.toolName, input.command);
16407
+ const output = decision ? {
16408
+ decision: decision.permissionDecision,
16409
+ reason: decision.permissionDecisionReason
16410
+ } : { decision: "gate" };
16411
+ console.log(JSON.stringify(output));
16412
+ }
16413
+
16414
+ // src/commands/registerPiHook.ts
16415
+ function registerPiHook(program2) {
16416
+ program2.command("pi-hook").description(
16417
+ "pi tool-call permission gate: reads pi's tool-call JSON from stdin and emits an allow/deny/gate decision, auto-approving read-only CLI commands via the shared cli-hook allowlist"
16418
+ ).action(() => {
16419
+ piHook();
16420
+ });
16421
+ }
16422
+
16357
16423
  // src/commands/prompts/printPromptsTable.ts
16358
16424
  import chalk153 from "chalk";
16359
16425
  function truncate(str, max) {
@@ -16524,15 +16590,15 @@ function postComment(vars) {
16524
16590
  const stdout = startLine === void 0 ? runGhGraphql(MUTATION_SINGLE, base) : runGhGraphql(MUTATION_MULTI, { ...base, startLine });
16525
16591
  assertThreadCreated(stdout);
16526
16592
  }
16527
- function comment2(path64, line, body, startLine) {
16593
+ function comment2(path67, line, body, startLine) {
16528
16594
  validateBody(body);
16529
16595
  validateLine(line);
16530
16596
  if (startLine !== void 0) validateLine(startLine);
16531
16597
  try {
16532
16598
  const prId = getCurrentPrNodeId();
16533
- postComment({ prId, body, path: path64, line, startLine });
16599
+ postComment({ prId, body, path: path67, line, startLine });
16534
16600
  const range = startLine !== void 0 ? `${startLine}-${line}` : `${line}`;
16535
- console.log(`Added review comment on ${path64}:${range}`);
16601
+ console.log(`Added review comment on ${path67}:${range}`);
16536
16602
  } catch (error) {
16537
16603
  if (isGhNotInstalled(error)) {
16538
16604
  console.error("Error: GitHub CLI (gh) is not installed.");
@@ -17393,8 +17459,8 @@ function registerPrs(program2) {
17393
17459
  prsCommand.command("reply <comment-id> <body>").description("Reply to a comment thread without resolving it").action((commentId, body) => {
17394
17460
  reply(Number.parseInt(commentId, 10), body);
17395
17461
  });
17396
- prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path64, line, body) => {
17397
- comment2(path64, Number.parseInt(line, 10), body);
17462
+ prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path67, line, body) => {
17463
+ comment2(path67, Number.parseInt(line, 10), body);
17398
17464
  });
17399
17465
  configHelp(prsCommand, prsConfigHelp);
17400
17466
  }
@@ -17647,10 +17713,10 @@ function resolveOpSecret(reference) {
17647
17713
  }
17648
17714
 
17649
17715
  // src/commands/ravendb/ravenFetch.ts
17650
- async function ravenFetch(connection, path64) {
17716
+ async function ravenFetch(connection, path67) {
17651
17717
  const apiKey = resolveOpSecret(connection.apiKeyRef);
17652
17718
  let accessToken = await getAccessToken(apiKey);
17653
- const url = `${connection.url}${path64}`;
17719
+ const url = `${connection.url}${path67}`;
17654
17720
  const headers = {
17655
17721
  Authorization: `Bearer ${accessToken}`,
17656
17722
  "Content-Type": "application/json"
@@ -17754,16 +17820,16 @@ import chalk166 from "chalk";
17754
17820
  // src/commands/ravendb/buildQueryPath.ts
17755
17821
  function buildQueryPath(opts) {
17756
17822
  const db = encodeURIComponent(opts.db);
17757
- let path64;
17823
+ let path67;
17758
17824
  if (opts.collection) {
17759
- path64 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
17825
+ path67 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
17760
17826
  } else {
17761
- path64 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
17827
+ path67 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
17762
17828
  }
17763
17829
  if (opts.query) {
17764
- path64 += `&query=${encodeURIComponent(opts.query)}`;
17830
+ path67 += `&query=${encodeURIComponent(opts.query)}`;
17765
17831
  }
17766
- return path64;
17832
+ return path67;
17767
17833
  }
17768
17834
 
17769
17835
  // src/commands/ravendb/fetchAllPages.ts
@@ -17772,7 +17838,7 @@ async function fetchAllPages(connection, opts) {
17772
17838
  let start3 = 0;
17773
17839
  while (true) {
17774
17840
  const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
17775
- const path64 = buildQueryPath({
17841
+ const path67 = buildQueryPath({
17776
17842
  db: connection.database,
17777
17843
  collection: opts.collection,
17778
17844
  start: start3,
@@ -17780,7 +17846,7 @@ async function fetchAllPages(connection, opts) {
17780
17846
  sort: opts.sort,
17781
17847
  query: opts.query
17782
17848
  });
17783
- const data = await ravenFetch(connection, path64);
17849
+ const data = await ravenFetch(connection, path67);
17784
17850
  const results = data.Results ?? [];
17785
17851
  const totalResults = data.TotalResults ?? 0;
17786
17852
  if (results.length === 0) break;
@@ -20168,8 +20234,8 @@ async function handlePostSynthesis(synthesisPath, options2) {
20168
20234
  // src/commands/review/prepareReviewDir.ts
20169
20235
  import { existsSync as existsSync41, mkdirSync as mkdirSync17, unlinkSync as unlinkSync14, writeFileSync as writeFileSync34 } from "fs";
20170
20236
  function clearReviewFiles(paths) {
20171
- for (const path64 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
20172
- if (existsSync41(path64)) unlinkSync14(path64);
20237
+ for (const path67 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
20238
+ if (existsSync41(path67)) unlinkSync14(path67);
20173
20239
  }
20174
20240
  }
20175
20241
  function prepareReviewDir(paths, requestBody, force) {
@@ -21317,8 +21383,8 @@ import chalk187 from "chalk";
21317
21383
 
21318
21384
  // src/commands/seq/fetchSeq.ts
21319
21385
  import chalk184 from "chalk";
21320
- async function fetchSeq(conn, path64, params) {
21321
- const url = `${conn.url}${path64}?${params}`;
21386
+ async function fetchSeq(conn, path67, params) {
21387
+ const url = `${conn.url}${path67}?${params}`;
21322
21388
  const response = await fetch(url, {
21323
21389
  headers: {
21324
21390
  Accept: "application/json",
@@ -22805,9 +22871,9 @@ function findPortFile(roamDir) {
22805
22871
  return void 0;
22806
22872
  }
22807
22873
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
22808
- const path64 = join60(roamDir, name);
22874
+ const path67 = join60(roamDir, name);
22809
22875
  try {
22810
- return { path: path64, mtimeMs: statSync8(path64).mtimeMs };
22876
+ return { path: path67, mtimeMs: statSync8(path67).mtimeMs };
22811
22877
  } catch {
22812
22878
  return void 0;
22813
22879
  }
@@ -23226,11 +23292,11 @@ function findLinkIndex() {
23226
23292
  function parseLinkArgs() {
23227
23293
  const idx = findLinkIndex();
23228
23294
  if (idx === -1) return null;
23229
- const path64 = process.argv[idx + 1];
23295
+ const path67 = process.argv[idx + 1];
23230
23296
  const rest = process.argv.slice(idx + 2);
23231
23297
  const { value: prefix2 } = extractOption(rest, "--prefix");
23232
23298
  if (!prefix2) return null;
23233
- return { path: path64, prefix: prefix2 };
23299
+ return { path: path67, prefix: prefix2 };
23234
23300
  }
23235
23301
  function hasDuplicateLink(runList, linkPath) {
23236
23302
  return runList.some(
@@ -24510,8 +24576,8 @@ async function seedRunningMsFromUsage(session, notify2) {
24510
24576
  var DEBOUNCE_MS = 50;
24511
24577
  function watchActivity(session, notify2) {
24512
24578
  if (session.commandType !== "assist" || !session.cwd) return;
24513
- const path64 = activityPath(session.id);
24514
- const dir = dirname29(path64);
24579
+ const path67 = activityPath(session.id);
24580
+ const dir = dirname29(path67);
24515
24581
  try {
24516
24582
  mkdirSync24(dir, { recursive: true });
24517
24583
  } catch {
@@ -24521,7 +24587,7 @@ function watchActivity(session, notify2) {
24521
24587
  let timer = null;
24522
24588
  const read = () => {
24523
24589
  timer = null;
24524
- const activity2 = readActivity(path64);
24590
+ const activity2 = readActivity(path67);
24525
24591
  if (!activity2) return;
24526
24592
  session.activity = activity2;
24527
24593
  if (activity2.claudeSessionId)
@@ -24531,11 +24597,11 @@ function watchActivity(session, notify2) {
24531
24597
  notify2();
24532
24598
  };
24533
24599
  session.activityWatcher = watch(dir, (_event, filename) => {
24534
- if (filename && !path64.endsWith(filename)) return;
24600
+ if (filename && !path67.endsWith(filename)) return;
24535
24601
  if (timer) clearTimeout(timer);
24536
24602
  timer = setTimeout(read, DEBOUNCE_MS);
24537
24603
  });
24538
- if (existsSync54(path64)) read();
24604
+ if (existsSync54(path67)) read();
24539
24605
  }
24540
24606
  function refreshActivity(session) {
24541
24607
  if (session.commandType !== "assist" || !session.cwd) return;
@@ -26624,9 +26690,9 @@ async function statusLine() {
26624
26690
  }
26625
26691
 
26626
26692
  // src/commands/sync.ts
26627
- import * as fs44 from "fs";
26693
+ import * as fs45 from "fs";
26628
26694
  import * as os3 from "os";
26629
- import * as path62 from "path";
26695
+ import * as path65 from "path";
26630
26696
  import { fileURLToPath as fileURLToPath9 } from "url";
26631
26697
 
26632
26698
  // src/commands/sync/syncClaudeMd.ts
@@ -26660,12 +26726,33 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
26660
26726
  }
26661
26727
 
26662
26728
  // src/commands/sync/syncCodex.ts
26663
- import * as fs41 from "fs";
26664
- import * as path59 from "path";
26729
+ import * as path60 from "path";
26665
26730
 
26666
- // src/commands/sync/syncCodexHooks.ts
26731
+ // src/commands/sync/installHarnessCommands.ts
26667
26732
  import * as fs40 from "fs";
26668
26733
  import * as path58 from "path";
26734
+ function installHarnessCommands(claudeDir, harness, transform) {
26735
+ const commandsSource = path58.join(claudeDir, "commands");
26736
+ const files = fs40.readdirSync(commandsSource);
26737
+ let synced = 0;
26738
+ for (const file of files) {
26739
+ if (!file.endsWith(".md")) continue;
26740
+ const name = file.replace(/\.md$/, "");
26741
+ const content = fs40.readFileSync(path58.join(commandsSource, file), "utf8");
26742
+ const target = path58.join(harness.homeDir, harness.sync.commandDest(name));
26743
+ fs40.mkdirSync(path58.dirname(target), { recursive: true });
26744
+ fs40.writeFileSync(target, transform(name, content));
26745
+ synced++;
26746
+ }
26747
+ const agentsTarget = path58.join(harness.homeDir, harness.sync.agentsFile);
26748
+ fs40.mkdirSync(path58.dirname(agentsTarget), { recursive: true });
26749
+ fs40.copyFileSync(path58.join(claudeDir, "CLAUDE.md"), agentsTarget);
26750
+ return { total: files.length, synced };
26751
+ }
26752
+
26753
+ // src/commands/sync/syncCodexHooks.ts
26754
+ import * as fs41 from "fs";
26755
+ import * as path59 from "path";
26669
26756
  var BEGIN = "# >>> assist codex hooks (managed) \u2014 do not edit >>>";
26670
26757
  var END = "# <<< assist codex hooks (managed) <<<";
26671
26758
  function upsertManagedBlock(existing, body) {
@@ -26695,11 +26782,11 @@ ${rest}
26695
26782
  `;
26696
26783
  }
26697
26784
  function syncCodexHooks(sourcePath) {
26698
- const body = fs40.readFileSync(sourcePath, "utf8");
26699
- const configPath = path58.join(harnesses.codex.homeDir, "config.toml");
26700
- const existing = fs40.existsSync(configPath) ? fs40.readFileSync(configPath, "utf8") : "";
26701
- fs40.mkdirSync(path58.dirname(configPath), { recursive: true });
26702
- fs40.writeFileSync(configPath, upsertManagedBlock(existing, body));
26785
+ const body = fs41.readFileSync(sourcePath, "utf8");
26786
+ const configPath = path59.join(harnesses.codex.homeDir, "config.toml");
26787
+ const existing = fs41.existsSync(configPath) ? fs41.readFileSync(configPath, "utf8") : "";
26788
+ fs41.mkdirSync(path59.dirname(configPath), { recursive: true });
26789
+ fs41.writeFileSync(configPath, upsertManagedBlock(existing, body));
26703
26790
  console.log(
26704
26791
  "Registered assist codex-hook in ~/.codex/config.toml (PreToolUse, PermissionRequest)"
26705
26792
  );
@@ -26725,58 +26812,103 @@ description: ${quoteYaml(description)}
26725
26812
  }
26726
26813
  function syncCodex(claudeDir) {
26727
26814
  if (!isHarnessAvailable("codex")) return;
26728
- const codex = harnesses.codex;
26729
- const commandsSource = path59.join(claudeDir, "commands");
26730
- const files = fs41.readdirSync(commandsSource);
26731
- for (const file of files) {
26732
- if (!file.endsWith(".md")) continue;
26733
- const name = file.replace(/\.md$/, "");
26734
- const content = fs41.readFileSync(path59.join(commandsSource, file), "utf8");
26735
- const target = path59.join(codex.homeDir, codex.sync.commandDest(name));
26736
- fs41.mkdirSync(path59.dirname(target), { recursive: true });
26737
- fs41.writeFileSync(target, commandToSkill(name, content));
26738
- }
26739
- const agentsTarget = path59.join(codex.homeDir, codex.sync.agentsFile);
26740
- fs41.mkdirSync(path59.dirname(agentsTarget), { recursive: true });
26741
- fs41.copyFileSync(path59.join(claudeDir, "CLAUDE.md"), agentsTarget);
26742
- syncCodexHooks(path59.join(claudeDir, "..", "codex", "config.toml"));
26815
+ const { total } = installHarnessCommands(
26816
+ claudeDir,
26817
+ harnesses.codex,
26818
+ commandToSkill
26819
+ );
26820
+ syncCodexHooks(path60.join(claudeDir, "..", "codex", "config.toml"));
26743
26821
  console.log(
26744
- `Synced ${files.length} skill(s) to ~/.codex/skills and CLAUDE.md to ~/.codex/AGENTS.md`
26822
+ `Synced ${total} skill(s) to ~/.codex/skills and CLAUDE.md to ~/.codex/AGENTS.md`
26745
26823
  );
26746
26824
  }
26747
26825
 
26748
26826
  // src/commands/sync/syncDesign.ts
26749
26827
  import * as fs42 from "fs";
26750
- import * as path60 from "path";
26828
+ import * as path61 from "path";
26751
26829
  function syncDesign(claudeDir, targetBase) {
26752
- const systemPromptSource = path60.join(claudeDir, "system-prompt.md");
26753
- const systemPromptTarget = path60.join(targetBase, "system-prompt.md");
26830
+ const systemPromptSource = path61.join(claudeDir, "system-prompt.md");
26831
+ const systemPromptTarget = path61.join(targetBase, "system-prompt.md");
26754
26832
  fs42.copyFileSync(systemPromptSource, systemPromptTarget);
26755
26833
  console.log("Copied system-prompt.md to ~/.claude/system-prompt.md");
26756
- const skillsSource = path60.join(claudeDir, "skills");
26757
- const skillsTarget = path60.join(targetBase, "skills");
26834
+ const skillsSource = path61.join(claudeDir, "skills");
26835
+ const skillsTarget = path61.join(targetBase, "skills");
26758
26836
  fs42.mkdirSync(skillsTarget, { recursive: true });
26759
26837
  const files = fs42.readdirSync(skillsSource);
26760
26838
  for (const file of files) {
26761
26839
  fs42.copyFileSync(
26762
- path60.join(skillsSource, file),
26763
- path60.join(skillsTarget, file)
26840
+ path61.join(skillsSource, file),
26841
+ path61.join(skillsTarget, file)
26764
26842
  );
26765
26843
  }
26766
26844
  console.log(`Synced ${files.length} design skill(s) to ~/.claude/skills`);
26767
26845
  }
26768
26846
 
26769
- // src/commands/sync/syncSettings.ts
26847
+ // src/commands/sync/syncPi.ts
26848
+ import * as path63 from "path";
26849
+
26850
+ // src/commands/sync/syncPiHooks.ts
26770
26851
  import * as fs43 from "fs";
26771
- import * as path61 from "path";
26852
+ import * as path62 from "path";
26853
+ var PI_PERMISSION_GATE_FILE = "assist-permission-gate.ts";
26854
+ function piPermissionGateTarget() {
26855
+ return path62.join(harnesses.pi.homeDir, "extensions", PI_PERMISSION_GATE_FILE);
26856
+ }
26857
+ function syncPiHooks(sourcePath) {
26858
+ const target = piPermissionGateTarget();
26859
+ fs43.mkdirSync(path62.dirname(target), { recursive: true });
26860
+ fs43.copyFileSync(sourcePath, target);
26861
+ console.log(
26862
+ "Registered assist permission-gate extension in ~/.pi/agent/extensions"
26863
+ );
26864
+ }
26865
+
26866
+ // src/commands/sync/syncPi.ts
26867
+ function unquote(value) {
26868
+ return value.trim().replace(/^["']|["']$/g, "");
26869
+ }
26870
+ function commandToPrompt(name, content) {
26871
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
26872
+ const frontmatter = match ? match[1] : "";
26873
+ const body = match ? content.slice(match[0].length) : content;
26874
+ const descriptionMatch = frontmatter.match(/^description:\s*(.*)$/m);
26875
+ const description = descriptionMatch ? unquote(descriptionMatch[1]) : name;
26876
+ const argsMatch = frontmatter.match(/^allowed_args:\s*(.*)$/m);
26877
+ const argumentHint = argsMatch ? unquote(argsMatch[1]) : void 0;
26878
+ const header = [
26879
+ "---",
26880
+ `description: ${description}`,
26881
+ ...argumentHint ? [`argument-hint: ${argumentHint}`] : [],
26882
+ "---",
26883
+ "",
26884
+ ""
26885
+ ].join("\n");
26886
+ return header + body.replace(/^\r?\n+/, "");
26887
+ }
26888
+ function syncPi(claudeDir) {
26889
+ if (!isHarnessAvailable("pi")) return;
26890
+ const { synced } = installHarnessCommands(
26891
+ claudeDir,
26892
+ harnesses.pi,
26893
+ commandToPrompt
26894
+ );
26895
+ syncPiHooks(path63.join(claudeDir, "..", "pi", "permission-gate.ts"));
26896
+ console.log(
26897
+ `Synced ${synced} prompt(s) to ~/.pi/agent/prompts and CLAUDE.md to ~/.pi/agent/AGENTS.md`
26898
+ );
26899
+ }
26900
+
26901
+ // src/commands/sync/syncSettings.ts
26902
+ import * as fs44 from "fs";
26903
+ import * as path64 from "path";
26772
26904
  import chalk200 from "chalk";
26773
26905
  async function syncSettings(claudeDir, targetBase, options2) {
26774
- const source = path61.join(claudeDir, "settings.json");
26775
- const target = path61.join(targetBase, "settings.json");
26776
- const sourceContent = fs43.readFileSync(source, "utf8");
26906
+ const source = path64.join(claudeDir, "settings.json");
26907
+ const target = path64.join(targetBase, "settings.json");
26908
+ const sourceContent = fs44.readFileSync(source, "utf8");
26777
26909
  const sourceSettings = JSON.parse(sourceContent);
26778
- const targetExists = fs43.existsSync(target);
26779
- const targetContent = targetExists ? fs43.readFileSync(target, "utf8") : "";
26910
+ const targetExists = fs44.existsSync(target);
26911
+ const targetContent = targetExists ? fs44.readFileSync(target, "utf8") : "";
26780
26912
  const preservedUserSettings = targetExists ? JSON.parse(targetContent) : {};
26781
26913
  const mergedContent = JSON.stringify(
26782
26914
  { ...preservedUserSettings, ...sourceSettings },
@@ -26805,31 +26937,32 @@ async function syncSettings(claudeDir, targetBase, options2) {
26805
26937
  }
26806
26938
  }
26807
26939
  }
26808
- fs43.writeFileSync(target, mergedContent);
26940
+ fs44.writeFileSync(target, mergedContent);
26809
26941
  console.log("Copied settings.json to ~/.claude/settings.json");
26810
26942
  }
26811
26943
 
26812
26944
  // src/commands/sync.ts
26813
26945
  var __filename5 = fileURLToPath9(import.meta.url);
26814
- var __dirname7 = path62.dirname(__filename5);
26946
+ var __dirname7 = path65.dirname(__filename5);
26815
26947
  async function sync(options2) {
26816
26948
  const config = loadConfig();
26817
26949
  const yes = options2?.yes ?? config.sync.autoConfirm;
26818
- const claudeDir = path62.join(__dirname7, "..", "claude");
26819
- const targetBase = path62.join(os3.homedir(), ".claude");
26950
+ const claudeDir = path65.join(__dirname7, "..", "claude");
26951
+ const targetBase = path65.join(os3.homedir(), ".claude");
26820
26952
  syncCommands(claudeDir, targetBase);
26821
26953
  syncDesign(claudeDir, targetBase);
26822
26954
  await syncSettings(claudeDir, targetBase, { yes });
26823
26955
  await syncClaudeMd(claudeDir, targetBase, { yes });
26824
26956
  syncCodex(claudeDir);
26957
+ syncPi(claudeDir);
26825
26958
  }
26826
26959
  function syncCommands(claudeDir, targetBase) {
26827
- const sourceDir = path62.join(claudeDir, "commands");
26828
- const targetDir = path62.join(targetBase, "commands");
26829
- fs44.mkdirSync(targetDir, { recursive: true });
26830
- const files = fs44.readdirSync(sourceDir);
26960
+ const sourceDir = path65.join(claudeDir, "commands");
26961
+ const targetDir = path65.join(targetBase, "commands");
26962
+ fs45.mkdirSync(targetDir, { recursive: true });
26963
+ const files = fs45.readdirSync(sourceDir);
26831
26964
  for (const file of files) {
26832
- fs44.copyFileSync(path62.join(sourceDir, file), path62.join(targetDir, file));
26965
+ fs45.copyFileSync(path65.join(sourceDir, file), path65.join(targetDir, file));
26833
26966
  console.log(`Copied ${file} to ${targetDir}`);
26834
26967
  }
26835
26968
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -26837,7 +26970,7 @@ function syncCommands(claudeDir, targetBase) {
26837
26970
 
26838
26971
  // src/commands/update.ts
26839
26972
  import { execSync as execSync57 } from "child_process";
26840
- import * as path63 from "path";
26973
+ import * as path66 from "path";
26841
26974
 
26842
26975
  // src/commands/restartDaemonAfterUpdate.ts
26843
26976
  async function restartDaemonAfterUpdate() {
@@ -26856,12 +26989,12 @@ async function restartDaemonAfterUpdate() {
26856
26989
  // src/commands/update.ts
26857
26990
  function isGlobalNpmInstall(dir) {
26858
26991
  try {
26859
- const resolved = path63.resolve(dir);
26860
- if (resolved.split(path63.sep).includes("node_modules")) {
26992
+ const resolved = path66.resolve(dir);
26993
+ if (resolved.split(path66.sep).includes("node_modules")) {
26861
26994
  return true;
26862
26995
  }
26863
26996
  const globalPrefix = execSync57("npm prefix -g", { stdio: "pipe" }).toString().trim();
26864
- return resolved.toLowerCase().startsWith(path63.resolve(globalPrefix).toLowerCase());
26997
+ return resolved.toLowerCase().startsWith(path66.resolve(globalPrefix).toLowerCase());
26865
26998
  } catch {
26866
26999
  return false;
26867
27000
  }
@@ -26907,7 +27040,7 @@ function reportCliError(error) {
26907
27040
  var program = new Command();
26908
27041
  program.name("assist").description("CLI application").version(package_default.version).option("--no-open", "Do not open a browser on startup").action((options2) => web({ port: "3100", open: options2.open }));
26909
27042
  var syncCommand = program.command("sync").description(
26910
- "Copy command files to ~/.claude/commands; when codex is detected, also install commands as ~/.codex/skills/<name>/SKILL.md, CLAUDE.md as ~/.codex/AGENTS.md, and register the assist codex-hook auto-approval hook in ~/.codex/config.toml"
27043
+ "Copy command files to ~/.claude/commands; when codex is detected, also install commands as ~/.codex/skills/<name>/SKILL.md, CLAUDE.md as ~/.codex/AGENTS.md, and register the assist codex-hook auto-approval hook in ~/.codex/config.toml; when pi is detected, also install commands as ~/.pi/agent/prompts/<name>.md, CLAUDE.md as ~/.pi/agent/AGENTS.md, and register the assist pi-hook permission-gate extension in ~/.pi/agent/extensions"
26911
27044
  ).option("-y, --yes", "Overwrite settings.json without prompting").action((options2) => sync(options2));
26912
27045
  configHelp(syncCommand, rootConfigHelp.sync);
26913
27046
  program.command("init").description("Initialize VS Code and verify configurations").action(init5);
@@ -26935,6 +27068,7 @@ registerDb(program);
26935
27068
  registerCliHook(program);
26936
27069
  registerCodeComment(program);
26937
27070
  registerCodexHook(program);
27071
+ registerPiHook(program);
26938
27072
  registerEditHook(program);
26939
27073
  registerGithub(program);
26940
27074
  registerHandover(program);