codiedev 0.10.0-beta.5 → 0.10.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +4 -4
  2. package/dist/captureSignals.js +2 -0
  3. package/dist/cli.js +38 -50
  4. package/dist/codexHookTrust.js +67 -4
  5. package/dist/codie.js +23 -74
  6. package/dist/codieLauncher.js +18 -22
  7. package/dist/commands/agentTemplates.generated.js +136 -0
  8. package/dist/commands/agents.js +1068 -0
  9. package/dist/commands/agentsSdk.generated.js +7 -0
  10. package/dist/commands/doctor.js +12 -38
  11. package/dist/commands/evals.js +187 -0
  12. package/dist/commands/pack.js +1 -1
  13. package/dist/commands/traces.js +79 -15
  14. package/dist/commands/tracesLabel.js +348 -0
  15. package/dist/connect.js +11 -52
  16. package/dist/copilotHooks.js +0 -4
  17. package/dist/cursorHooks.js +0 -15
  18. package/dist/harnessDirs.js +4 -0
  19. package/dist/hook.js +33 -197
  20. package/dist/limitEventReporter.js +1 -1
  21. package/dist/managedPi.js +240 -0
  22. package/dist/mcp.js +0 -307
  23. package/dist/packs/harnesses.js +40 -3
  24. package/dist/packs/install.js +2 -1
  25. package/dist/piHooks.js +216 -0
  26. package/dist/piRuntime.js +120 -0
  27. package/dist/selfUpdate.js +47 -2
  28. package/dist/utils.js +232 -115
  29. package/package.json +5 -7
  30. package/dist/codieStartup.js +0 -27
  31. package/dist/commands/promote.js +0 -53
  32. package/dist/commands/react.js +0 -38
  33. package/dist/commands/reverseTicket.js +0 -444
  34. package/dist/commands/security.js +0 -156
  35. package/dist/prUrl.js +0 -28
  36. package/dist/security.js +0 -417
  37. package/dist/securityDeepScan.js +0 -235
  38. package/dist/securityDoctor.js +0 -61
  39. package/dist/securityFeature.js +0 -6
  40. package/dist/securityInstall.js +0 -232
  41. package/dist/securityInventory.js +0 -566
  42. package/dist/securityPilot.js +0 -59
  43. package/dist/securityPolicy.js +0 -181
  44. package/dist/securitySession.js +0 -72
package/dist/mcp.js CHANGED
@@ -222,29 +222,6 @@ const TOOLS = [
222
222
  required: ["body"],
223
223
  },
224
224
  },
225
- {
226
- name: "codiedev_promote",
227
- description: "Promote an auto-extracted Codiedev artifact (spec/bugfix/decision " +
228
- "produced by the session-capture pipeline) to an authored artifact " +
229
- "so teammates can collaborate on it. Use when the user says 'publish " +
230
- "the extracted spec', 'promote this', or 'make the auto-extracted X " +
231
- "shareable'.",
232
- inputSchema: {
233
- type: "object",
234
- properties: {
235
- artifactId: {
236
- type: "string",
237
- description: "The extracted artifact's id from Codiedev.",
238
- },
239
- keyOverride: {
240
- type: "string",
241
- description: "Optional filename key for the promoted artifact. If omitted, " +
242
- "one is generated from the artifact's type + session id.",
243
- },
244
- },
245
- required: ["artifactId"],
246
- },
247
- },
248
225
  {
249
226
  name: "codiedev_share_with",
250
227
  description: "Grant a teammate persistent read (or edit) access to an artifact " +
@@ -291,27 +268,6 @@ const TOOLS = [
291
268
  required: ["filename", "to"],
292
269
  },
293
270
  },
294
- {
295
- name: "codiedev_react",
296
- description: "React to a feed post with an emoji. Use when the user says 'react " +
297
- "to that post with X', 'mark as used', or 'upvote'. The 🛠 " +
298
- "reaction is the signal for 'I used this' and bumps the linked " +
299
- "artifact's reuse count. Pass the post id (get it from " +
300
- "codiedev_get_library or a previous feed response).",
301
- inputSchema: {
302
- type: "object",
303
- properties: {
304
- postId: { type: "string", description: "Feed post id." },
305
- emoji: {
306
- type: "string",
307
- enum: ["👍", "🔥", "💡", "🛠", "👑"],
308
- description: "Reaction emoji. 🛠 = 'I used this' (counts heaviest toward " +
309
- "the author's score and the artifact's reuse count).",
310
- },
311
- },
312
- required: ["postId", "emoji"],
313
- },
314
- },
315
271
  {
316
272
  name: "codiedev_search",
317
273
  description: "Search the team's artifact library by natural-language query. Use " +
@@ -398,41 +354,6 @@ const TOOLS = [
398
354
  },
399
355
  },
400
356
  },
401
- {
402
- name: "codiedev_reverse_ticket",
403
- description: "Generate a Jira-ready reverse-ticket draft from the work the user has " +
404
- "done in their CURRENT git branch — mid-session, before any PR exists. " +
405
- "Reads local git state (current branch, divergence from main, diff, " +
406
- "commits, files changed) and runs it through the codiedev reverse-" +
407
- "ticket writer. The draft lands in the user's portal at " +
408
- "/portal/agentic-ticketing where they can edit the title, description, " +
409
- "and scope, then push to Jira. Use when the user asks to 'create a " +
410
- "reverse ticket', 'draft a Jira ticket from this work', 'write up what " +
411
- "I've done in this branch', or runs `codiedev reverse-ticket` directly. " +
412
- "The webhook on PR-open later dedupes against this draft so the user " +
413
- "never gets a double ticket. The draft is intentionally not auto-pushed " +
414
- "to Jira — trust comes from the user reviewing in the portal first.",
415
- inputSchema: {
416
- type: "object",
417
- properties: {
418
- title: {
419
- type: "string",
420
- description: "Optional working title for the ticket. Defaults to the most " +
421
- "recent commit subject on the branch.",
422
- },
423
- base: {
424
- type: "string",
425
- description: "Optional base ref to diff against. Defaults to 'origin/main', " +
426
- "falls back to 'origin/master'.",
427
- },
428
- forcedArtifactKey: {
429
- type: "string",
430
- description: "Optional — force the writer to link to a specific artifact key " +
431
- "for context (e.g. 'spec-auth.md').",
432
- },
433
- },
434
- },
435
- },
436
357
  ];
437
358
  const server = new index_js_1.Server({ name: PKG_NAME, version: PKG_VERSION }, { capabilities: { tools: {} } });
438
359
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
@@ -515,22 +436,16 @@ async function dispatchTool(name, args, config) {
515
436
  return await handleInbox(args, config);
516
437
  case "codiedev_note":
517
438
  return await handleNote(args, config);
518
- case "codiedev_promote":
519
- return await handlePromote(args, config);
520
439
  case "codiedev_share_with":
521
440
  return await handleShareWith(args, config);
522
441
  case "codiedev_send_to":
523
442
  return await handleSendTo(args, config);
524
- case "codiedev_react":
525
- return await handleReact(args, config);
526
443
  case "codiedev_ask":
527
444
  return await handleAsk(args, config);
528
445
  case "codiedev_search":
529
446
  return await handleSearch(args, config);
530
447
  case "codiedev_get_library":
531
448
  return await handleGetLibrary(args, config);
532
- case "codiedev_reverse_ticket":
533
- return await handleReverseTicket(args, config);
534
449
  default:
535
450
  return {
536
451
  isError: true,
@@ -675,21 +590,6 @@ async function handleNote(args, config) {
675
590
  content: [{ type: "text", text: `✓ Noted (${res.key})` }],
676
591
  };
677
592
  }
678
- async function handlePromote(args, config) {
679
- const artifactId = asString(args.artifactId);
680
- const keyOverride = asStringOrUndefined(args.keyOverride);
681
- if (!artifactId)
682
- throw new Error("artifactId required");
683
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/promote", { config, body: { artifactId, keyOverride } });
684
- return {
685
- content: [
686
- {
687
- type: "text",
688
- text: `✓ Promoted to authored artifact ${res.key} (id=${res.artifactId}).`,
689
- },
690
- ],
691
- };
692
- }
693
593
  async function handleShareWith(args, config) {
694
594
  const filename = asString(args.filename);
695
595
  const to = asString(args.to);
@@ -778,26 +678,6 @@ async function handleSendTo(args, config) {
778
678
  return formatAmbiguousRecipient(err, to);
779
679
  }
780
680
  }
781
- async function handleReact(args, config) {
782
- const postId = asString(args.postId);
783
- const emoji = asString(args.emoji);
784
- if (!postId)
785
- throw new Error("postId required");
786
- if (!emoji)
787
- throw new Error("emoji required");
788
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/react", {
789
- config,
790
- body: { postId, emoji },
791
- });
792
- const verb = res.action === "added"
793
- ? "Reacted"
794
- : res.action === "removed"
795
- ? "Removed reaction"
796
- : "Swapped reaction to";
797
- return {
798
- content: [{ type: "text", text: `✓ ${verb} ${res.emoji}` }],
799
- };
800
- }
801
681
  async function handleSearch(args, config) {
802
682
  const query = asString(args.query);
803
683
  const limit = asIntOrUndefined(args.limit);
@@ -902,193 +782,6 @@ function formatAmbiguousRecipient(err, to) {
902
782
  }
903
783
  throw err;
904
784
  }
905
- async function handleReverseTicket(args, config) {
906
- const explicitTitle = asStringOrUndefined(args.title);
907
- const explicitBase = asStringOrUndefined(args.base);
908
- const forcedKey = asStringOrUndefined(args.forcedArtifactKey);
909
- const { execSync } = await Promise.resolve().then(() => __importStar(require("child_process")));
910
- const git = (cmd) => {
911
- try {
912
- return execSync(`git ${cmd}`, {
913
- stdio: ["pipe", "pipe", "pipe"],
914
- maxBuffer: 32 * 1024 * 1024,
915
- })
916
- .toString("utf8")
917
- .trim();
918
- }
919
- catch (err) {
920
- throw new Error(`git ${cmd} failed: ${err.message}`);
921
- }
922
- };
923
- try {
924
- git("rev-parse --is-inside-work-tree");
925
- }
926
- catch {
927
- throw new Error("Not inside a git repository. Run this from a checked-out repo.");
928
- }
929
- const candidates = explicitBase
930
- ? [explicitBase]
931
- : ["origin/main", "origin/master"];
932
- let baseRef = null;
933
- for (const c of candidates) {
934
- try {
935
- git(`rev-parse --verify ${c}`);
936
- baseRef = c;
937
- break;
938
- }
939
- catch {
940
- }
941
- }
942
- if (!baseRef) {
943
- throw new Error(`Couldn't find a base ref (tried ${candidates.join(", ")}). ` +
944
- `Pass an explicit base via the 'base' argument.`);
945
- }
946
- const branch = git("rev-parse --abbrev-ref HEAD");
947
- if (branch === "HEAD") {
948
- throw new Error("Detached HEAD — checkout a branch first.");
949
- }
950
- const baseSha = git(`merge-base HEAD ${baseRef}`);
951
- const headSha = git("rev-parse HEAD");
952
- const isNoise = (p) => /(^|\/)(node_modules|dist|build|\.next|\.turbo|coverage|out)\//.test(p) ||
953
- /(^|\/)package-lock\.json$/.test(p) ||
954
- /(^|\/)yarn\.lock$/.test(p) ||
955
- /(^|\/)pnpm-lock\.yaml$/.test(p) ||
956
- /(^|\/)bun\.lock(b)?$/.test(p) ||
957
- /(^|\/)Cargo\.lock$/.test(p) ||
958
- /(^|\/)Gemfile\.lock$/.test(p) ||
959
- /(^|\/)poetry\.lock$/.test(p) ||
960
- /(^|\/)go\.sum$/.test(p);
961
- const shellTolerant = (cmd) => {
962
- try {
963
- return execSync(`${cmd} || true`, {
964
- stdio: ["pipe", "pipe", "pipe"],
965
- maxBuffer: 32 * 1024 * 1024,
966
- })
967
- .toString("utf8")
968
- .trim();
969
- }
970
- catch {
971
- return "";
972
- }
973
- };
974
- const shQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
975
- const trackedRaw = git(`diff --name-only ${baseSha}`);
976
- const trackedFiles = trackedRaw ? trackedRaw.split("\n").filter(Boolean) : [];
977
- const untrackedRaw = safeGit(() => git("ls-files --others --exclude-standard"));
978
- const untrackedFiles = untrackedRaw
979
- ? untrackedRaw.split("\n").filter(Boolean)
980
- : [];
981
- const filesChanged = Array.from(new Set([...trackedFiles, ...untrackedFiles]));
982
- if (filesChanged.length === 0) {
983
- throw new Error(`No changes detected between ${baseRef} and your working tree on ${branch}.`);
984
- }
985
- let stat = "";
986
- try {
987
- const trackedStat = git(`diff --stat=200 ${baseSha}`);
988
- const untrackedStatLines = untrackedFiles.map((f) => {
989
- const wc = shellTolerant(`wc -l < ${shQuote(f)}`);
990
- const n = parseInt(wc.split(/\s+/)[0] || "0", 10) || 0;
991
- return ` ${f} | ${n} ++++++++++ (new file)`;
992
- });
993
- stat = [trackedStat, ...untrackedStatLines]
994
- .filter(Boolean)
995
- .join("\n")
996
- .slice(0, 8_000);
997
- }
998
- catch {
999
- }
1000
- let diff = "";
1001
- try {
1002
- const sourceTracked = trackedFiles.filter((f) => !isNoise(f));
1003
- const trackedDiff = sourceTracked.length > 0
1004
- ? git(`diff ${baseSha} -- ${sourceTracked.map(shQuote).join(" ")}`)
1005
- : git(`diff ${baseSha}`);
1006
- const sourceUntracked = untrackedFiles.filter((f) => !isNoise(f));
1007
- const untrackedDiffs = sourceUntracked.map((f) => shellTolerant(`git diff --no-index --no-color -- /dev/null ${shQuote(f)}`));
1008
- diff = [trackedDiff, ...untrackedDiffs]
1009
- .filter(Boolean)
1010
- .join("\n")
1011
- .slice(0, 40_000);
1012
- }
1013
- catch {
1014
- }
1015
- let repo;
1016
- try {
1017
- const remoteUrl = git("config --get remote.origin.url");
1018
- const m = remoteUrl.match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?$/);
1019
- if (!m) {
1020
- throw new Error(`Couldn't parse owner/repo from remote: ${remoteUrl}`);
1021
- }
1022
- repo = `${m[1]}/${m[2]}`;
1023
- }
1024
- catch (err) {
1025
- throw new Error(`Couldn't read git remote.origin.url: ${err.message}`);
1026
- }
1027
- const title = explicitTitle ?? safeGit(() => git("log -1 --format=%s")) ?? `WIP: ${branch}`;
1028
- const author = safeGit(() => git("config --get user.name"));
1029
- const authorEmail = safeGit(() => git("config --get user.email"));
1030
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/reverseTicketFromBranch", {
1031
- config,
1032
- body: {
1033
- branch: {
1034
- repo,
1035
- branch,
1036
- baseSha,
1037
- headSha,
1038
- title,
1039
- author,
1040
- authorEmail,
1041
- filesChanged,
1042
- diff: diff || undefined,
1043
- stat: stat || undefined,
1044
- },
1045
- forcedArtifactKey: forcedKey,
1046
- },
1047
- });
1048
- if (!res.ok || !res.artifactId) {
1049
- return {
1050
- isError: true,
1051
- content: [
1052
- {
1053
- type: "text",
1054
- text: "Reverse-ticket generation returned no result. The writer may have " +
1055
- "skipped due to missing context (no captured sessions, etc.). Try " +
1056
- "again after working on the branch a bit more, or check the " +
1057
- "server logs.",
1058
- },
1059
- ],
1060
- };
1061
- }
1062
- const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
1063
- const portalLink = res.portalUrl.startsWith("http")
1064
- ? res.portalUrl
1065
- : `${portalHost}${res.portalUrl.startsWith("/") ? "" : "/"}${res.portalUrl}`;
1066
- const verb = res.action === "overwritten" ? "Updated" : "Created";
1067
- return {
1068
- content: [
1069
- {
1070
- type: "text",
1071
- text: `✓ ${verb} reverse-ticket draft for branch \`${branch}\` (${repo}).\n` +
1072
- ` files changed: ${filesChanged.length}\n` +
1073
- ` base: ${baseSha.slice(0, 12)} → head: ${headSha.slice(0, 12)} (${baseRef})\n` +
1074
- ` diff size: ${diff.length} chars\n` +
1075
- ` artifact: ${res.artifactKey ?? res.artifactId}\n\n` +
1076
- `Open in your portal to review, edit, and push to Jira:\n` +
1077
- ` ${portalLink}\n\n` +
1078
- `Each call creates a fresh draft — if you've made several for the ` +
1079
- `same branch, clean them up from the portal.`,
1080
- },
1081
- ],
1082
- };
1083
- }
1084
- function safeGit(fn) {
1085
- try {
1086
- return fn();
1087
- }
1088
- catch {
1089
- return undefined;
1090
- }
1091
- }
1092
785
  function asString(v) {
1093
786
  return typeof v === "string" ? v : "";
1094
787
  }
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ADAPTERS = exports.codieAdapter = exports.cursorAdapter = exports.CURSOR_MARKER = exports.codexAdapter = exports.copilotAdapter = exports.claudeAdapter = exports.HARNESSES = void 0;
36
+ exports.ADAPTERS = exports.piAdapter = exports.codieAdapter = exports.cursorAdapter = exports.CURSOR_MARKER = exports.codexAdapter = exports.copilotAdapter = exports.claudeAdapter = exports.HARNESSES = void 0;
37
37
  exports.defaultRunner = defaultRunner;
38
38
  exports.onPath = onPath;
39
39
  exports.copyCursorSkills = copyCursorSkills;
@@ -48,7 +48,7 @@ const path = __importStar(require("path"));
48
48
  const child_process_1 = require("child_process");
49
49
  const harnessDirs_1 = require("../harnessDirs");
50
50
  const paths_1 = require("./paths");
51
- exports.HARNESSES = ["claude", "copilot", "codex", "cursor", "codie"];
51
+ exports.HARNESSES = ["claude", "copilot", "codex", "cursor", "codie", "pi"];
52
52
  function defaultRunner(env) {
53
53
  return (command, args, options = {}) => {
54
54
  const result = (0, child_process_1.spawnSync)(command, args, {
@@ -362,7 +362,44 @@ exports.codieAdapter = {
362
362
  plan: () => "inject skills/agents/hook shim at next codie launch",
363
363
  uninstall: () => "removed from next `codie` launch",
364
364
  };
365
- exports.ADAPTERS = [exports.claudeAdapter, exports.copilotAdapter, exports.codexAdapter, exports.cursorAdapter, exports.codieAdapter];
365
+ function piSettingsPath(env, home) {
366
+ return path.join((0, harnessDirs_1.piAgentDir)(env, home), "settings.json");
367
+ }
368
+ function updatePiSkillPaths(file, update) {
369
+ let settings = {};
370
+ if (fs.existsSync(file)) {
371
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
372
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
373
+ throw new Error(`${file} is not a JSON object; leaving it alone`);
374
+ settings = parsed;
375
+ }
376
+ const current = Array.isArray(settings.skills) ? settings.skills.filter((p) => typeof p === "string") : [];
377
+ const next = update(current);
378
+ if (next.length === current.length && next.every((p, i) => p === current[i]))
379
+ return false;
380
+ settings.skills = next;
381
+ fs.mkdirSync(path.dirname(file), { recursive: true });
382
+ const tmp = `${file}.${process.pid}.tmp`;
383
+ fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n");
384
+ fs.renameSync(tmp, file);
385
+ return true;
386
+ }
387
+ exports.piAdapter = {
388
+ name: "pi",
389
+ detect: (ctx) => onPath("pi", ctx.env) || fs.existsSync((0, harnessDirs_1.piAgentDir)(ctx.env, ctx.home)),
390
+ install: (ctx) => {
391
+ const skills = path.join(ctx.portablePluginDir, "skills");
392
+ const changed = updatePiSkillPaths(piSettingsPath(ctx.env, ctx.home), (paths) => paths.includes(skills) ? paths : [...paths, skills]);
393
+ return changed ? "installed" : "already installed";
394
+ },
395
+ plan: (ctx) => `add ${path.join(ctx.portablePluginDir, "skills")} to ${piSettingsPath(ctx.env, ctx.home)} skills`,
396
+ uninstall: (ctx) => {
397
+ const dir = (0, paths_1.pluginDir)((0, paths_1.portableRoot)(ctx.root), ctx.slug);
398
+ const changed = updatePiSkillPaths(piSettingsPath(ctx.env, ctx.home), (paths) => paths.filter((p) => p !== dir && !p.startsWith(dir + path.sep)));
399
+ return changed ? "uninstalled" : "already removed";
400
+ },
401
+ };
402
+ exports.ADAPTERS = [exports.claudeAdapter, exports.copilotAdapter, exports.codexAdapter, exports.cursorAdapter, exports.codieAdapter, exports.piAdapter];
366
403
  function parseHarnessList(value) {
367
404
  if (value === undefined)
368
405
  return undefined;
@@ -88,7 +88,8 @@ function codexPackTrustEntries(registry, codexDir) {
88
88
  if (!hooksPath)
89
89
  continue;
90
90
  try {
91
- bySlug.set(slug, (0, codexHookTrust_1.packCodexHookTrustEntries)(hooksPath, JSON.parse(fs.readFileSync(hooksPath, "utf8"))));
91
+ const plugin = { id: `${slug}@${paths_1.MARKETPLACE_NAME}`, root: path.dirname(path.dirname(hooksPath)) };
92
+ bySlug.set(slug, (0, codexHookTrust_1.packCodexHookTrustEntries)(hooksPath, JSON.parse(fs.readFileSync(hooksPath, "utf8")), plugin));
92
93
  }
93
94
  catch {
94
95
  }
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PI_CONTEXT_CUSTOM_TYPE = void 0;
37
+ exports.normalizePiTranscript = normalizePiTranscript;
38
+ exports.isPiTranscriptEmpty = isPiTranscriptEmpty;
39
+ exports.piLaunchFromArgv = piLaunchFromArgv;
40
+ exports.piExtensionPath = piExtensionPath;
41
+ exports.inspectPiExtension = inspectPiExtension;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const harnessDirs_1 = require("./harnessDirs");
45
+ exports.PI_CONTEXT_CUSTOM_TYPE = "codiedev";
46
+ const SKILL_EXPANSION = /^<skill name="([^"]{1,200})" location="[^"]*">/;
47
+ const num = (n) => (typeof n === "number" && Number.isFinite(n) ? Math.max(0, n) : 0);
48
+ function textOf(content) {
49
+ if (typeof content === "string")
50
+ return content;
51
+ if (!Array.isArray(content))
52
+ return "";
53
+ return content
54
+ .map((block) => (block?.type === "text" && typeof block.text === "string" ? block.text : ""))
55
+ .filter(Boolean)
56
+ .join("\n");
57
+ }
58
+ function userBlocks(content) {
59
+ if (typeof content === "string")
60
+ return [{ type: "text", text: content }];
61
+ if (!Array.isArray(content))
62
+ return [];
63
+ return content.flatMap((block) => block?.type === "text" && typeof block.text === "string"
64
+ ? [{ type: "text", text: block.text }]
65
+ : block?.type === "image" ? [{ type: "text", text: "[image]" }] : []);
66
+ }
67
+ function skillFromPath(value) {
68
+ if (typeof value !== "string")
69
+ return null;
70
+ const m = value.replace(/\\/g, "/").match(/\/([^/]+)\/SKILL\.md$/);
71
+ return m ? m[1] : null;
72
+ }
73
+ function errorCode(message) {
74
+ return message.match(/^\s*(\d{3})\b/)?.[1] ?? "";
75
+ }
76
+ function normalizePiTranscript(content) {
77
+ const out = [];
78
+ const skillReads = new Map();
79
+ let errorStreakAt;
80
+ const push = (record) => out.push(JSON.stringify(record));
81
+ for (const line of content.split("\n")) {
82
+ if (!line.trim())
83
+ continue;
84
+ let entry;
85
+ try {
86
+ entry = JSON.parse(line);
87
+ }
88
+ catch {
89
+ continue;
90
+ }
91
+ if (!entry || typeof entry !== "object")
92
+ continue;
93
+ const timestamp = typeof entry.timestamp === "string" ? entry.timestamp : undefined;
94
+ const uuid = typeof entry.id === "string" ? entry.id : undefined;
95
+ if (entry.type !== "message" || !entry.message || typeof entry.message !== "object")
96
+ continue;
97
+ const message = entry.message;
98
+ if (!(message.role === "assistant" && message.stopReason === "error"))
99
+ errorStreakAt = undefined;
100
+ if (message.role === "user") {
101
+ const text = textOf(message.content);
102
+ const skill = text.match(SKILL_EXPANSION)?.[1];
103
+ if (skill)
104
+ push({ type: "pi.skill_invoked", timestamp, skill });
105
+ push({ type: "user", uuid, timestamp, message: { role: "user", content: userBlocks(message.content) } });
106
+ continue;
107
+ }
108
+ if (message.role === "assistant") {
109
+ const blocks = [];
110
+ for (const block of Array.isArray(message.content) ? message.content : []) {
111
+ if (block?.type === "text" && typeof block.text === "string")
112
+ blocks.push({ type: "text", text: block.text });
113
+ else if (block?.type === "thinking" && typeof block.thinking === "string")
114
+ blocks.push({ type: "thinking", thinking: block.thinking });
115
+ else if (block?.type === "toolCall") {
116
+ const input = block.arguments && typeof block.arguments === "object" ? block.arguments : {};
117
+ blocks.push({ type: "tool_use", id: block.id, name: block.name ?? "tool", input });
118
+ const skill = block.name === "read" ? skillFromPath(input.path) : null;
119
+ if (skill && typeof block.id === "string")
120
+ skillReads.set(block.id, skill);
121
+ }
122
+ }
123
+ const usage = message.usage ?? {};
124
+ const tokens = num(usage.input) + num(usage.output) + num(usage.cacheRead) + num(usage.cacheWrite);
125
+ const measured = tokens > 0 || message.stopReason !== "error";
126
+ push({
127
+ type: "assistant",
128
+ uuid,
129
+ timestamp,
130
+ message: {
131
+ id: typeof message.responseId === "string" ? message.responseId : uuid,
132
+ role: "assistant",
133
+ model: typeof message.model === "string" ? message.model : undefined,
134
+ ...(measured ? {
135
+ usage: {
136
+ input_tokens: num(usage.input),
137
+ output_tokens: num(usage.output),
138
+ cache_read_input_tokens: num(usage.cacheRead),
139
+ cache_creation_input_tokens: num(usage.cacheWrite),
140
+ },
141
+ } : {}),
142
+ content: blocks,
143
+ },
144
+ });
145
+ if (message.stopReason === "aborted") {
146
+ push({ type: "user", timestamp, message: { role: "user", content: [{ type: "text", text: "[Request interrupted by user]" }] } });
147
+ }
148
+ if (message.stopReason === "error" && typeof message.errorMessage === "string") {
149
+ errorStreakAt ??= typeof message.timestamp === "number" ? new Date(message.timestamp).toISOString() : timestamp;
150
+ push({
151
+ type: "agent.error",
152
+ timestamp: errorStreakAt,
153
+ data: { errorContext: "model_call", error: { code: errorCode(message.errorMessage), message: message.errorMessage } },
154
+ });
155
+ }
156
+ continue;
157
+ }
158
+ if (message.role === "toolResult") {
159
+ const id = typeof message.toolCallId === "string" ? message.toolCallId : undefined;
160
+ const skill = id ? skillReads.get(id) : undefined;
161
+ if (skill && message.isError !== true)
162
+ push({ type: "pi.skill_invoked", timestamp, skill });
163
+ push({
164
+ type: "user",
165
+ uuid,
166
+ timestamp,
167
+ message: {
168
+ role: "user",
169
+ content: [{ type: "tool_result", tool_use_id: id, content: textOf(message.content), is_error: message.isError === true }],
170
+ },
171
+ });
172
+ continue;
173
+ }
174
+ if (message.role === "bashExecution" && typeof message.command === "string") {
175
+ push({ type: "user", uuid, timestamp, message: { role: "user", content: [{ type: "text", text: `<bash-input>${message.command}</bash-input>` }] } });
176
+ }
177
+ }
178
+ return out.join("\n") + (out.length ? "\n" : "");
179
+ }
180
+ function isPiTranscriptEmpty(content) {
181
+ return !content.split("\n").some((line) => line.includes('"type":"message"'));
182
+ }
183
+ function piLaunchFromArgv(argv) {
184
+ for (let i = 0; i < argv.length; i++) {
185
+ const a = argv[i];
186
+ if (a === "-p" || a === "--print")
187
+ return "pi:print";
188
+ if (a === "--mode") {
189
+ const mode = argv[i + 1];
190
+ if (mode === "json" || mode === "rpc")
191
+ return `pi:${mode}`;
192
+ }
193
+ if (a === "--mode=json" || a === "--mode=rpc")
194
+ return `pi:${a.slice(7)}`;
195
+ }
196
+ return "pi:tui";
197
+ }
198
+ function piExtensionPath(agentDir = (0, harnessDirs_1.piAgentDir)()) {
199
+ return path.join(agentDir, "extensions", "codiedev.ts");
200
+ }
201
+ function inspectPiExtension(file = piExtensionPath()) {
202
+ if (!fs.existsSync(file))
203
+ return { status: "not-installed", detail: `${file} not found` };
204
+ let source;
205
+ try {
206
+ source = fs.readFileSync(file, "utf8");
207
+ }
208
+ catch {
209
+ return { status: "fail", detail: `${file} unreadable` };
210
+ }
211
+ const expected = ["session-start-pi", "backup-pi", "capture-pi-final"];
212
+ const missing = expected.filter((subcommand) => !source.includes(JSON.stringify(subcommand)));
213
+ if (missing.length)
214
+ return { status: "fail", detail: `missing ${missing.join(", ")} — re-run \`codiedev connect\`` };
215
+ return { status: "pass", detail: `session start, capture and backup events (${(0, harnessDirs_1.displayPath)(file)})` };
216
+ }