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.
- package/README.md +4 -4
- package/dist/captureSignals.js +2 -0
- package/dist/cli.js +38 -50
- package/dist/codexHookTrust.js +67 -4
- package/dist/codie.js +23 -74
- package/dist/codieLauncher.js +18 -22
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1068 -0
- package/dist/commands/agentsSdk.generated.js +7 -0
- package/dist/commands/doctor.js +12 -38
- package/dist/commands/evals.js +187 -0
- package/dist/commands/pack.js +1 -1
- package/dist/commands/traces.js +79 -15
- package/dist/commands/tracesLabel.js +348 -0
- package/dist/connect.js +11 -52
- package/dist/copilotHooks.js +0 -4
- package/dist/cursorHooks.js +0 -15
- package/dist/harnessDirs.js +4 -0
- package/dist/hook.js +33 -197
- package/dist/limitEventReporter.js +1 -1
- package/dist/managedPi.js +240 -0
- package/dist/mcp.js +0 -307
- package/dist/packs/harnesses.js +40 -3
- package/dist/packs/install.js +2 -1
- package/dist/piHooks.js +216 -0
- package/dist/piRuntime.js +120 -0
- package/dist/selfUpdate.js +47 -2
- package/dist/utils.js +232 -115
- package/package.json +5 -7
- package/dist/codieStartup.js +0 -27
- package/dist/commands/promote.js +0 -53
- package/dist/commands/react.js +0 -38
- package/dist/commands/reverseTicket.js +0 -444
- package/dist/commands/security.js +0 -156
- package/dist/prUrl.js +0 -28
- package/dist/security.js +0 -417
- package/dist/securityDeepScan.js +0 -235
- package/dist/securityDoctor.js +0 -61
- package/dist/securityFeature.js +0 -6
- package/dist/securityInstall.js +0 -232
- package/dist/securityInventory.js +0 -566
- package/dist/securityPilot.js +0 -59
- package/dist/securityPolicy.js +0 -181
- 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
|
}
|
package/dist/packs/harnesses.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/packs/install.js
CHANGED
|
@@ -88,7 +88,8 @@ function codexPackTrustEntries(registry, codexDir) {
|
|
|
88
88
|
if (!hooksPath)
|
|
89
89
|
continue;
|
|
90
90
|
try {
|
|
91
|
-
|
|
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
|
}
|
package/dist/piHooks.js
ADDED
|
@@ -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
|
+
}
|