codiedev 0.9.59 → 0.9.61
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/captureSignals.d.ts +21 -0
- package/dist/captureSignals.js +52 -1
- package/dist/cli.js +3 -1
- package/dist/commands/doctor.js +11 -0
- package/dist/connect.js +9 -1
- package/dist/copilotHooks.d.ts +85 -0
- package/dist/copilotHooks.js +458 -0
- package/dist/copilotInstallation.d.ts +10 -0
- package/dist/copilotInstallation.js +65 -0
- package/dist/cursorHooks.d.ts +5 -1
- package/dist/cursorHooks.js +50 -7
- package/dist/cursorLifecycle.d.ts +2 -0
- package/dist/cursorLifecycle.js +115 -0
- package/dist/hook.js +154 -36
- package/dist/launchContext.d.ts +46 -0
- package/dist/launchContext.js +200 -0
- package/dist/limitEvent.d.ts +3 -0
- package/dist/limitEvent.js +42 -0
- package/dist/limitEventReporter.d.ts +1 -1
- package/dist/limitEventReporter.js +1 -1
- package/dist/nativeSteering.d.ts +5 -0
- package/dist/nativeSteering.js +27 -0
- package/dist/subagents.d.ts +36 -0
- package/dist/subagents.js +146 -0
- package/dist/teamSkillContext.d.ts +9 -0
- package/dist/teamSkillContext.js +11 -0
- package/dist/transcriptModels.d.ts +2 -0
- package/dist/transcriptModels.js +8 -2
- package/dist/utils.d.ts +22 -3
- package/dist/utils.js +164 -46
- package/package.json +8 -5
package/dist/utils.js
CHANGED
|
@@ -53,6 +53,7 @@ exports.parseAuthoredBranches = parseAuthoredBranches;
|
|
|
53
53
|
exports.parseAuthoredShas = parseAuthoredShas;
|
|
54
54
|
exports.getGitBranch = getGitBranch;
|
|
55
55
|
exports.getGitHeadSha = getGitHeadSha;
|
|
56
|
+
exports.stripRepoUrlCredentials = stripRepoUrlCredentials;
|
|
56
57
|
exports.buildCapturePayload = buildCapturePayload;
|
|
57
58
|
exports.hashToken = hashToken;
|
|
58
59
|
exports.extractHookNodePath = extractHookNodePath;
|
|
@@ -60,6 +61,7 @@ exports.resetStableNodeCacheForTests = resetStableNodeCacheForTests;
|
|
|
60
61
|
exports.claudeCodeInstalled = claudeCodeInstalled;
|
|
61
62
|
exports.codexInstalled = codexInstalled;
|
|
62
63
|
exports.cursorInstalled = cursorInstalled;
|
|
64
|
+
exports.copilotInstalled = copilotInstalled;
|
|
63
65
|
exports.openCodeInstalled = openCodeInstalled;
|
|
64
66
|
exports.installOpenCodeCapturePlugin = installOpenCodeCapturePlugin;
|
|
65
67
|
exports.vscodeCopilotInstalled = vscodeCopilotInstalled;
|
|
@@ -72,6 +74,8 @@ exports.ensureAllCodiedevHooksInstalled = ensureAllCodiedevHooksInstalled;
|
|
|
72
74
|
exports.ensureSessionStartHookInstalled = ensureSessionStartHookInstalled;
|
|
73
75
|
exports.installClaudeCodeInstructions = installClaudeCodeInstructions;
|
|
74
76
|
exports.installCodexInstructions = installCodexInstructions;
|
|
77
|
+
exports.copilotInstructionsPath = copilotInstructionsPath;
|
|
78
|
+
exports.installCopilotInstructions = installCopilotInstructions;
|
|
75
79
|
exports.installCursorInstructions = installCursorInstructions;
|
|
76
80
|
exports.installClaudeCodeMcp = installClaudeCodeMcp;
|
|
77
81
|
exports.installCursorMcp = installCursorMcp;
|
|
@@ -84,6 +88,9 @@ exports.installCodexMcp = installCodexMcp;
|
|
|
84
88
|
exports.installCodexHook = installCodexHook;
|
|
85
89
|
exports.ensureCodexLimitHookInstalled = ensureCodexLimitHookInstalled;
|
|
86
90
|
exports.installCursorHook = installCursorHook;
|
|
91
|
+
exports.copilotHooksPath = copilotHooksPath;
|
|
92
|
+
exports.installCopilotHook = installCopilotHook;
|
|
93
|
+
exports.ensureCopilotCodiedevHooksInstalled = ensureCopilotCodiedevHooksInstalled;
|
|
87
94
|
exports.ensureCursorCodiedevHooksInstalled = ensureCursorCodiedevHooksInstalled;
|
|
88
95
|
exports.parseClaudeSteering = parseClaudeSteering;
|
|
89
96
|
exports.parseCodexSteering = parseCodexSteering;
|
|
@@ -114,6 +121,7 @@ exports.runDeferredUpload = runDeferredUpload;
|
|
|
114
121
|
exports.transcriptReadSkipReason = transcriptReadSkipReason;
|
|
115
122
|
exports.shouldReportCaptureSkip = shouldReportCaptureSkip;
|
|
116
123
|
const fs = __importStar(require("fs"));
|
|
124
|
+
const transcriptModels_1 = require("./transcriptModels");
|
|
117
125
|
const path = __importStar(require("path"));
|
|
118
126
|
const os = __importStar(require("os"));
|
|
119
127
|
const child_process_1 = require("child_process");
|
|
@@ -122,6 +130,9 @@ const captureLog_1 = require("./captureLog");
|
|
|
122
130
|
const configDir_1 = require("./configDir");
|
|
123
131
|
Object.defineProperty(exports, "codiedevStateDir", { enumerable: true, get: function () { return configDir_1.codiedevStateDir; } });
|
|
124
132
|
const codexHookTrust_1 = require("./codexHookTrust");
|
|
133
|
+
const teamSkillContext_1 = require("./teamSkillContext");
|
|
134
|
+
const nativeSteering_1 = require("./nativeSteering");
|
|
135
|
+
const copilotHooks_1 = require("./copilotHooks");
|
|
125
136
|
const CONFIG_DIR = (0, configDir_1.codiedevStateDir)();
|
|
126
137
|
exports.CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
127
138
|
function readConfig() {
|
|
@@ -824,10 +835,13 @@ function getGitHeadSha(cwd) {
|
|
|
824
835
|
return null;
|
|
825
836
|
}
|
|
826
837
|
}
|
|
838
|
+
function stripRepoUrlCredentials(url) {
|
|
839
|
+
return url.replace(/^(\s*[a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/?#]*@/, "$1");
|
|
840
|
+
}
|
|
827
841
|
function buildCapturePayload(input) {
|
|
828
842
|
return {
|
|
829
843
|
sessionId: input.sessionId,
|
|
830
|
-
repoUrl: input.remoteUrl ?? "",
|
|
844
|
+
repoUrl: stripRepoUrlCredentials(input.remoteUrl ?? ""),
|
|
831
845
|
repoId: input.matchedRepoId,
|
|
832
846
|
companyId: input.companyId,
|
|
833
847
|
branch: input.branch ?? "",
|
|
@@ -836,7 +850,7 @@ function buildCapturePayload(input) {
|
|
|
836
850
|
// see no change; newer servers index the session under every key.
|
|
837
851
|
branches: input.branches && input.branches.length ? input.branches : undefined,
|
|
838
852
|
headShas: input.headShas && input.headShas.length ? input.headShas : undefined,
|
|
839
|
-
gitRefs: input.gitRefs && input.gitRefs.length ? input.gitRefs : undefined,
|
|
853
|
+
gitRefs: input.gitRefs && input.gitRefs.length ? input.gitRefs.map(ref => ({ ...ref, repoUrl: stripRepoUrlCredentials(ref.repoUrl) })) : undefined,
|
|
840
854
|
pullRequestUrls: input.pullRequestUrls && input.pullRequestUrls.length
|
|
841
855
|
? input.pullRequestUrls
|
|
842
856
|
: undefined,
|
|
@@ -1044,6 +1058,24 @@ function codexInstalled() {
|
|
|
1044
1058
|
function cursorInstalled() {
|
|
1045
1059
|
return fs.existsSync(CURSOR_DIR);
|
|
1046
1060
|
}
|
|
1061
|
+
/**
|
|
1062
|
+
* GitHub Copilot: `$COPILOT_HOME` / ~/.copilot (either surface has run once),
|
|
1063
|
+
* the `copilot` CLI on PATH, or VS Code's Copilot Chat extension. VS Code's
|
|
1064
|
+
* agent and the CLI read the same user hooks, so one install covers both.
|
|
1065
|
+
*/
|
|
1066
|
+
function copilotInstalled() {
|
|
1067
|
+
if (fs.existsSync((0, copilotHooks_1.copilotHome)()))
|
|
1068
|
+
return true;
|
|
1069
|
+
if (vscodeCopilotInstalled())
|
|
1070
|
+
return true;
|
|
1071
|
+
try {
|
|
1072
|
+
(0, child_process_1.execSync)(process.platform === "win32" ? "where copilot" : "command -v copilot", { stdio: ["pipe", "pipe", "pipe"] });
|
|
1073
|
+
return true;
|
|
1074
|
+
}
|
|
1075
|
+
catch {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1047
1079
|
function openCodeInstalled() {
|
|
1048
1080
|
if (fs.existsSync(OPENCODE_DIR))
|
|
1049
1081
|
return true;
|
|
@@ -1072,6 +1104,9 @@ function installOpenCodeCapturePlugin() {
|
|
|
1072
1104
|
`const CAPTURE = ${JSON.stringify(capture)};\n` +
|
|
1073
1105
|
`const FINISH = ${JSON.stringify(finish)};\n` +
|
|
1074
1106
|
`const PROMPT = ${JSON.stringify(prompt)};\n` +
|
|
1107
|
+
`// How this Codie process was started (run / serve / tui). OpenCode records\n` +
|
|
1108
|
+
`// nothing about it, so the plugin reads its own argv; see launchContext.ts.\n` +
|
|
1109
|
+
`const LAUNCH = (() => { const sub = process.argv.slice(2).find((a) => ["run", "serve", "acp", "web", "attach"].includes(a)); return sub ? "codie:" + sub : "codie:tui"; })();\n` +
|
|
1075
1110
|
`const fingerprints = new Map();\n\n` +
|
|
1076
1111
|
`const transcriptPaths = new Map();\n\n` +
|
|
1077
1112
|
`function sessionId(event) {\n` +
|
|
@@ -1098,7 +1133,7 @@ function installOpenCodeCapturePlugin() {
|
|
|
1098
1133
|
` const finalizeAll = () => {\n` +
|
|
1099
1134
|
` for (const [id, transcriptPath] of transcriptPaths) {\n` +
|
|
1100
1135
|
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1101
|
-
` dispatchSync(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1136
|
+
` dispatchSync(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd], launch: LAUNCH });\n` +
|
|
1102
1137
|
` }\n` +
|
|
1103
1138
|
` };\n` +
|
|
1104
1139
|
` process.once("exit", finalizeAll);\n` +
|
|
@@ -1116,12 +1151,12 @@ function installOpenCodeCapturePlugin() {
|
|
|
1116
1151
|
` if (!id) return;\n` +
|
|
1117
1152
|
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1118
1153
|
` if (event.type === "session.created") {\n` +
|
|
1119
|
-
` dispatch(START, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1154
|
+
` dispatch(START, { session_id: id, cwd, workspace_roots: [cwd], launch: LAUNCH });\n` +
|
|
1120
1155
|
` return;\n` +
|
|
1121
1156
|
` }\n` +
|
|
1122
1157
|
` if (event.type === "session.deleted") {\n` +
|
|
1123
1158
|
` const transcriptPath = transcriptPaths.get(id);\n` +
|
|
1124
|
-
` if (transcriptPath) dispatch(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1159
|
+
` if (transcriptPath) dispatch(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd], launch: LAUNCH });\n` +
|
|
1125
1160
|
` transcriptPaths.delete(id);\n` +
|
|
1126
1161
|
` fingerprints.delete(id);\n` +
|
|
1127
1162
|
` return;\n` +
|
|
@@ -1141,7 +1176,7 @@ function installOpenCodeCapturePlugin() {
|
|
|
1141
1176
|
` const transcriptPath = path.join(captureDir, safeId + ".json");\n` +
|
|
1142
1177
|
` await writeFile(transcriptPath, JSON.stringify(list), { mode: 0o600 });\n` +
|
|
1143
1178
|
` transcriptPaths.set(id, transcriptPath);\n` +
|
|
1144
|
-
` dispatch(CAPTURE, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1179
|
+
` dispatch(CAPTURE, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd], launch: LAUNCH });\n` +
|
|
1145
1180
|
` } catch {}\n` +
|
|
1146
1181
|
` },\n` +
|
|
1147
1182
|
` });\n` +
|
|
@@ -1472,13 +1507,13 @@ Tell the user the portal URL when the submit returns.
|
|
|
1472
1507
|
* (no per-repo CLAUDE.md needed). Idempotent — replaces the existing block
|
|
1473
1508
|
* between its begin/end markers on re-run.
|
|
1474
1509
|
*/
|
|
1475
|
-
function writeInstructionsBlock(targetPath) {
|
|
1510
|
+
function writeInstructionsBlock(targetPath, body = CODIEDEV_INSTRUCTIONS_BODY) {
|
|
1476
1511
|
const dir = path.dirname(targetPath);
|
|
1477
1512
|
if (!fs.existsSync(dir)) {
|
|
1478
1513
|
fs.mkdirSync(dir, { recursive: true });
|
|
1479
1514
|
}
|
|
1480
1515
|
const block = `${CODIEDEV_INSTRUCTIONS_BEGIN}\n` +
|
|
1481
|
-
|
|
1516
|
+
body +
|
|
1482
1517
|
`\n${CODIEDEV_INSTRUCTIONS_END}\n`;
|
|
1483
1518
|
let existing = "";
|
|
1484
1519
|
if (fs.existsSync(targetPath)) {
|
|
@@ -1509,6 +1544,12 @@ function installClaudeCodeInstructions() {
|
|
|
1509
1544
|
function installCodexInstructions() {
|
|
1510
1545
|
writeInstructionsBlock(CODEX_USER_INSTRUCTIONS_PATH);
|
|
1511
1546
|
}
|
|
1547
|
+
function copilotInstructionsPath() {
|
|
1548
|
+
return path.join((0, copilotHooks_1.copilotHome)(), "copilot-instructions.md");
|
|
1549
|
+
}
|
|
1550
|
+
function installCopilotInstructions() {
|
|
1551
|
+
writeInstructionsBlock(copilotInstructionsPath(), `${teamSkillContext_1.TEAM_SKILL_CONTEXT}\n\n${CODIEDEV_INSTRUCTIONS_BODY}`);
|
|
1552
|
+
}
|
|
1512
1553
|
// Cursor uses .mdc rule files with YAML frontmatter (description, alwaysApply,
|
|
1513
1554
|
// globs). `.cursorrules` is deprecated and ignored by Agent mode as of 2026.
|
|
1514
1555
|
const CURSOR_INSTRUCTIONS_FRONTMATTER = `---
|
|
@@ -1524,7 +1565,7 @@ function installCursorInstructions() {
|
|
|
1524
1565
|
const block = CURSOR_INSTRUCTIONS_FRONTMATTER +
|
|
1525
1566
|
CODIEDEV_INSTRUCTIONS_BEGIN +
|
|
1526
1567
|
"\n" +
|
|
1527
|
-
CODIEDEV_INSTRUCTIONS_BODY +
|
|
1568
|
+
`${teamSkillContext_1.TEAM_SKILL_CONTEXT}\n\n${CODIEDEV_INSTRUCTIONS_BODY}` +
|
|
1528
1569
|
"\n" +
|
|
1529
1570
|
CODIEDEV_INSTRUCTIONS_END +
|
|
1530
1571
|
"\n";
|
|
@@ -1542,7 +1583,7 @@ function installCursorInstructions() {
|
|
|
1542
1583
|
existing.slice(0, beginIdx) +
|
|
1543
1584
|
CODIEDEV_INSTRUCTIONS_BEGIN +
|
|
1544
1585
|
"\n" +
|
|
1545
|
-
CODIEDEV_INSTRUCTIONS_BODY +
|
|
1586
|
+
`${teamSkillContext_1.TEAM_SKILL_CONTEXT}\n\n${CODIEDEV_INSTRUCTIONS_BODY}` +
|
|
1546
1587
|
"\n" +
|
|
1547
1588
|
CODIEDEV_INSTRUCTIONS_END +
|
|
1548
1589
|
existing.slice(endIdx + CODIEDEV_INSTRUCTIONS_END.length);
|
|
@@ -1901,12 +1942,93 @@ function installCursorHook() {
|
|
|
1901
1942
|
replaceManaged("sessionStart", ["session-start-cursor"]);
|
|
1902
1943
|
replaceManaged("sessionEnd", ["capture-cursor"]);
|
|
1903
1944
|
replaceManaged("stop", ["backup-cursor"]);
|
|
1945
|
+
for (const event of ["postToolUse", "postToolUseFailure", "subagentStart", "subagentStop"])
|
|
1946
|
+
replaceManaged(event, ["observe-cursor"]);
|
|
1904
1947
|
// Cursor cannot inject dynamic context from beforeSubmitPrompt today.
|
|
1905
1948
|
// Remove older Codiedev discovery entries so prompts are not uploaded for
|
|
1906
1949
|
// a response the host will discard; preserve any third-party hooks.
|
|
1907
1950
|
replaceManaged("beforeSubmitPrompt", []);
|
|
1908
1951
|
fs.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1909
1952
|
}
|
|
1953
|
+
// GitHub Copilot reads every `$COPILOT_HOME/hooks/*.json`, so Codiedev owns
|
|
1954
|
+
// one file and never edits anyone else's. camelCase events only: VS Code also
|
|
1955
|
+
// accepts Claude-style PascalCase names from the same file, and configuring
|
|
1956
|
+
// both would fire every hook twice.
|
|
1957
|
+
function copilotHooksPath() {
|
|
1958
|
+
return path.join((0, copilotHooks_1.copilotHome)(), "hooks", "codiedev.json");
|
|
1959
|
+
}
|
|
1960
|
+
function copilotManagedHooks(agentSecurityEnabled) {
|
|
1961
|
+
return [
|
|
1962
|
+
["sessionStart", "session-start-copilot", 10],
|
|
1963
|
+
["userPromptSubmitted", "discover-copilot", 10],
|
|
1964
|
+
// agentStop = end of a turn: a non-final snapshot. VS Code sessions live
|
|
1965
|
+
// until the chat is closed, so this is what keeps them captured.
|
|
1966
|
+
["agentStop", "backup-copilot", 30],
|
|
1967
|
+
["sessionEnd", "capture-copilot", 30],
|
|
1968
|
+
["errorOccurred", "limit-copilot", 10],
|
|
1969
|
+
];
|
|
1970
|
+
}
|
|
1971
|
+
const COPILOT_EVENTS = ["sessionStart", "userPromptSubmitted", "agentStop", "sessionEnd", "preToolUse", "postToolUse", "errorOccurred"];
|
|
1972
|
+
function installCopilotHook(agentSecurityEnabled = false) {
|
|
1973
|
+
const hooksPath = copilotHooksPath();
|
|
1974
|
+
let hooksFile = {};
|
|
1975
|
+
try {
|
|
1976
|
+
if (fs.existsSync(hooksPath)) {
|
|
1977
|
+
const raw = fs.readFileSync(hooksPath, "utf8");
|
|
1978
|
+
hooksFile = raw.trim() ? JSON.parse(raw) : {};
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
catch {
|
|
1982
|
+
hooksFile = {};
|
|
1983
|
+
}
|
|
1984
|
+
hooksFile.version = 1;
|
|
1985
|
+
const hooks = (hooksFile.hooks && typeof hooksFile.hooks === "object" ? hooksFile.hooks : {});
|
|
1986
|
+
const managed = copilotManagedHooks(agentSecurityEnabled);
|
|
1987
|
+
for (const eventName of COPILOT_EVENTS) {
|
|
1988
|
+
const existing = Array.isArray(hooks[eventName]) ? hooks[eventName] : [];
|
|
1989
|
+
// Anything a user added to this file by hand stays.
|
|
1990
|
+
const preserved = existing.filter((hook) => !(0, detection_1.isCodiedevHookCommand)((hook.bash ?? hook.powershell ?? hook.command)));
|
|
1991
|
+
const ours = managed
|
|
1992
|
+
.filter(([event]) => event === eventName)
|
|
1993
|
+
.map(([, subcommand, timeoutSec]) => {
|
|
1994
|
+
const command = resolveHookCommand(subcommand);
|
|
1995
|
+
return { type: "command", bash: command, powershell: command, timeoutSec };
|
|
1996
|
+
});
|
|
1997
|
+
if (preserved.length + ours.length === 0)
|
|
1998
|
+
delete hooks[eventName];
|
|
1999
|
+
else
|
|
2000
|
+
hooks[eventName] = [...preserved, ...ours];
|
|
2001
|
+
}
|
|
2002
|
+
hooksFile.hooks = hooks;
|
|
2003
|
+
fs.mkdirSync(path.dirname(hooksPath), { recursive: true });
|
|
2004
|
+
fs.writeFileSync(hooksPath, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
2005
|
+
}
|
|
2006
|
+
/** Self-heal the Copilot hook file after upgrades or drift (like Cursor's). */
|
|
2007
|
+
function ensureCopilotCodiedevHooksInstalled() {
|
|
2008
|
+
const agentSecurityEnabled = false; // production line: no security hooks
|
|
2009
|
+
try {
|
|
2010
|
+
const hooksPath = copilotHooksPath();
|
|
2011
|
+
if (!fs.existsSync(hooksPath))
|
|
2012
|
+
return;
|
|
2013
|
+
const parsed = JSON.parse(fs.readFileSync(hooksPath, "utf8"));
|
|
2014
|
+
const hooks = parsed.hooks ?? {};
|
|
2015
|
+
const commandsFor = (eventName) => (Array.isArray(hooks[eventName]) ? hooks[eventName] : [])
|
|
2016
|
+
.map((entry) => (entry.bash ?? entry.powershell ?? entry.command))
|
|
2017
|
+
.filter((command) => typeof command === "string" && (0, detection_1.isCodiedevHookCommand)(command));
|
|
2018
|
+
const required = copilotManagedHooks(agentSecurityEnabled);
|
|
2019
|
+
const complete = required.every(([eventName, subcommand]) => commandsFor(eventName).filter((command) => command.trim().endsWith(subcommand)).length === 1);
|
|
2020
|
+
const stale = COPILOT_EVENTS.some((eventName) => commandsFor(eventName).length !== required.filter(([event]) => event === eventName).length);
|
|
2021
|
+
if (complete && !stale)
|
|
2022
|
+
return;
|
|
2023
|
+
installCopilotHook(agentSecurityEnabled);
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
// Best-effort — a malformed hook file must never block capture.
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
/** Repair Cursor's complete flat hook set after upgrades or configuration
|
|
2030
|
+
* drift. This is deliberately separate from Claude Code's nested-hook healer:
|
|
2031
|
+
* the schemas and event names differ, and mixing them risks corrupting both. */
|
|
1910
2032
|
function ensureCursorCodiedevHooksInstalled() {
|
|
1911
2033
|
try {
|
|
1912
2034
|
if (!fs.existsSync(CURSOR_HOOKS_PATH))
|
|
@@ -1917,6 +2039,10 @@ function ensureCursorCodiedevHooksInstalled() {
|
|
|
1917
2039
|
["sessionStart", "session-start-cursor"],
|
|
1918
2040
|
["sessionEnd", "capture-cursor"],
|
|
1919
2041
|
["stop", "backup-cursor"],
|
|
2042
|
+
["postToolUse", "observe-cursor"],
|
|
2043
|
+
["postToolUseFailure", "observe-cursor"],
|
|
2044
|
+
["subagentStart", "observe-cursor"],
|
|
2045
|
+
["subagentStop", "observe-cursor"],
|
|
1920
2046
|
];
|
|
1921
2047
|
const has = (eventName, subcommand) => {
|
|
1922
2048
|
const entries = hooks[eventName];
|
|
@@ -1978,46 +2104,27 @@ function parseCodexSteering(content) {
|
|
|
1978
2104
|
return { interrupts, rejections };
|
|
1979
2105
|
}
|
|
1980
2106
|
function parseSteering(content, mode) {
|
|
2107
|
+
if (mode === "cursor" || mode === "copilot")
|
|
2108
|
+
return (0, nativeSteering_1.parseNativeSteering)(content);
|
|
1981
2109
|
return mode === "codex"
|
|
1982
2110
|
? parseCodexSteering(content)
|
|
1983
2111
|
: parseClaudeSteering(content);
|
|
1984
2112
|
}
|
|
1985
2113
|
function parseClaudeUsage(content) {
|
|
1986
|
-
|
|
1987
|
-
// already fresh (cache read/creation are reported separately).
|
|
1988
|
-
let inputTokens = 0;
|
|
1989
|
-
let outputTokens = 0;
|
|
1990
|
-
let cacheReadTokens = 0;
|
|
1991
|
-
let cacheCreationTokens = 0;
|
|
2114
|
+
const models = (0, transcriptModels_1.parseTranscriptModels)(content, "claude-code");
|
|
1992
2115
|
let model = null;
|
|
1993
2116
|
for (const line of content.split("\n")) {
|
|
1994
|
-
if (!line.trim())
|
|
1995
|
-
continue;
|
|
1996
2117
|
try {
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
if (u) {
|
|
2001
|
-
inputTokens += u.input_tokens ?? 0;
|
|
2002
|
-
outputTokens += u.output_tokens ?? 0;
|
|
2003
|
-
cacheReadTokens += u.cache_read_input_tokens ?? 0;
|
|
2004
|
-
cacheCreationTokens += u.cache_creation_input_tokens ?? 0;
|
|
2005
|
-
}
|
|
2006
|
-
if (typeof m?.model === "string" && m.model)
|
|
2007
|
-
model = m.model;
|
|
2008
|
-
}
|
|
2009
|
-
catch {
|
|
2010
|
-
// skip malformed
|
|
2118
|
+
const value = JSON.parse(line)?.message?.model;
|
|
2119
|
+
if (typeof value === "string" && value.trim() && !value.startsWith("<"))
|
|
2120
|
+
model = value;
|
|
2011
2121
|
}
|
|
2122
|
+
catch { /* partial records */ }
|
|
2012
2123
|
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
cacheTokens: cacheReadTokens + cacheCreationTokens,
|
|
2017
|
-
cacheReadTokens,
|
|
2018
|
-
cacheCreationTokens,
|
|
2019
|
-
model,
|
|
2020
|
-
};
|
|
2124
|
+
const sum = (field) => models.reduce((total, usage) => total + usage[field], 0);
|
|
2125
|
+
const cacheReadTokens = sum("cacheReadTokens"), cacheCreationTokens = sum("cacheCreationTokens");
|
|
2126
|
+
return { inputTokens: sum("inputTokens"), outputTokens: sum("outputTokens"),
|
|
2127
|
+
cacheTokens: cacheReadTokens + cacheCreationTokens, cacheReadTokens, cacheCreationTokens, model };
|
|
2021
2128
|
}
|
|
2022
2129
|
function parseCodexUsage(content) {
|
|
2023
2130
|
// Codex reports a CUMULATIVE token_count per turn — the last one is the
|
|
@@ -2151,20 +2258,30 @@ function parseSessionStart(content) {
|
|
|
2151
2258
|
function parseClaudeCodeStats(content) {
|
|
2152
2259
|
let messageCount = 0;
|
|
2153
2260
|
let toolCallCount = 0;
|
|
2261
|
+
const messages = new Set();
|
|
2262
|
+
const tools = new Set();
|
|
2154
2263
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
2155
2264
|
for (const line of lines) {
|
|
2156
2265
|
try {
|
|
2157
2266
|
const entry = JSON.parse(line);
|
|
2158
2267
|
if (!entry || typeof entry !== "object")
|
|
2159
2268
|
continue;
|
|
2160
|
-
|
|
2269
|
+
const id = entry.message?.id ?? entry.uuid;
|
|
2270
|
+
const messageKey = `${entry.type}:${id ?? messages.size}`;
|
|
2271
|
+
if ((entry.type === "user" || entry.type === "assistant") && !messages.has(messageKey)) {
|
|
2272
|
+
messages.add(messageKey);
|
|
2161
2273
|
messageCount++;
|
|
2162
2274
|
}
|
|
2163
2275
|
const c = entry.message?.content;
|
|
2164
2276
|
if (Array.isArray(c)) {
|
|
2165
|
-
for (const block of c) {
|
|
2166
|
-
if (block
|
|
2277
|
+
for (const [index, block] of c.entries()) {
|
|
2278
|
+
if (!block || block.type !== "tool_use")
|
|
2279
|
+
continue;
|
|
2280
|
+
const toolKey = block.id ?? `${messageKey}:${index}`;
|
|
2281
|
+
if (!tools.has(toolKey)) {
|
|
2282
|
+
tools.add(toolKey);
|
|
2167
2283
|
toolCallCount++;
|
|
2284
|
+
}
|
|
2168
2285
|
}
|
|
2169
2286
|
}
|
|
2170
2287
|
}
|
|
@@ -2212,14 +2329,14 @@ function normalizeOpenCodeTranscript(content) {
|
|
|
2212
2329
|
else if (part.type === "tool") {
|
|
2213
2330
|
normalizedParts.push({
|
|
2214
2331
|
type: "tool_use",
|
|
2215
|
-
id: part.callID ?? part.id
|
|
2332
|
+
id: part.callID ?? part.id,
|
|
2216
2333
|
name: part.tool ?? "tool",
|
|
2217
2334
|
input: part.state?.input ?? {},
|
|
2218
2335
|
});
|
|
2219
2336
|
if (part.state?.output != null || part.state?.error != null) {
|
|
2220
2337
|
normalizedParts.push({
|
|
2221
2338
|
type: "tool_result",
|
|
2222
|
-
tool_use_id: part.callID ?? part.id
|
|
2339
|
+
tool_use_id: part.callID ?? part.id,
|
|
2223
2340
|
content: part.state?.output ?? part.state?.error,
|
|
2224
2341
|
is_error: part.state?.status === "error" || part.state?.error != null,
|
|
2225
2342
|
});
|
|
@@ -2235,6 +2352,7 @@ function normalizeOpenCodeTranscript(content) {
|
|
|
2235
2352
|
type: role,
|
|
2236
2353
|
timestamp,
|
|
2237
2354
|
message: {
|
|
2355
|
+
id: typeof info.id === "string" ? info.id : undefined,
|
|
2238
2356
|
role,
|
|
2239
2357
|
model: typeof info.modelID === "string" ? info.modelID : undefined,
|
|
2240
2358
|
usage: role === "assistant" ? {
|
|
@@ -2565,7 +2683,7 @@ function resolveCaptureFinal(argv2) {
|
|
|
2565
2683
|
return false;
|
|
2566
2684
|
if (argv2 === "capture-opencode-final" || argv2 === "capture-desktop-final")
|
|
2567
2685
|
return true;
|
|
2568
|
-
return argv2 === "capture" || argv2 === "capture-cursor" || argv2 === undefined
|
|
2686
|
+
return argv2 === "capture" || argv2 === "capture-cursor" || argv2 === "capture-copilot" || argv2 === undefined
|
|
2569
2687
|
? true
|
|
2570
2688
|
: undefined;
|
|
2571
2689
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration
|
|
3
|
+
"version": "0.9.61",
|
|
4
|
+
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration \u2014 and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|
|
7
7
|
"codiedev-hook": "dist/hook.js",
|
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
"dev": "tsc --watch",
|
|
13
13
|
"prepublishOnly": "tsc",
|
|
14
14
|
"test": "vitest run",
|
|
15
|
-
"test:functional": "vitest run src/__tests__/limitEvent.functional.test.ts src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts",
|
|
16
|
-
"test:harness-parity": "vitest run src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts",
|
|
15
|
+
"test:functional": "vitest run src/__tests__/limitEvent.functional.test.ts src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts src/__tests__/copilotCapture.functional.test.ts",
|
|
16
|
+
"test:harness-parity": "vitest run src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts src/__tests__/copilotCapture.functional.test.ts",
|
|
17
17
|
"test:harness-binaries": "node scripts/checkHarnessBinaries.mjs",
|
|
18
|
-
"test:watch": "vitest"
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"test:live-sources": "npm run build && node scripts/liveSourceParity.mjs",
|
|
20
|
+
"test:ide-sources": "npm run build && node scripts/liveSourceParity.mjs --ide",
|
|
21
|
+
"test:metadata-parity": "vitest run src/__tests__/sourceMetadataMatrix.test.ts src/__tests__/copilotCapture.functional.test.ts src/__tests__/copilotCapture.test.ts src/__tests__/cursorParity.functional.test.ts src/__tests__/nativeSourceParity.test.ts src/__tests__/harnessParity.functional.test.ts src/__tests__/subagents.test.ts src/__tests__/transcriptModels.test.ts src/__tests__/captureRetry.test.ts src/__tests__/limitEvent.functional.test.ts src/__tests__/captureExclusions.test.ts"
|
|
19
22
|
},
|
|
20
23
|
"files": [
|
|
21
24
|
"dist",
|