codiedev 0.9.52 → 0.10.0-beta.1
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 +9 -1
- package/dist/captureLog.d.ts +1 -1
- package/dist/cli.js +17 -12
- package/dist/codie.js +146 -1
- package/dist/codieLauncher.d.ts +5 -0
- package/dist/codieLauncher.js +22 -1
- package/dist/codieRuntime.d.ts +11 -1
- package/dist/codieRuntime.js +20 -3
- package/dist/codieRuntimeInstall.d.ts +12 -11
- package/dist/codieRuntimeInstall.js +3 -1
- package/dist/codieStartup.d.ts +26 -0
- package/dist/codieStartup.js +30 -0
- package/dist/commands/doctor.js +8 -0
- package/dist/commands/security.d.ts +1 -0
- package/dist/commands/security.js +156 -0
- package/dist/connect.js +56 -4
- package/dist/cursorHooks.d.ts +23 -0
- package/dist/cursorHooks.js +81 -0
- package/dist/hook.js +272 -26
- package/dist/limitEvent.d.ts +5 -1
- package/dist/limitEvent.js +5 -1
- package/dist/mcp.js +1 -93
- package/dist/repoResolver.js +10 -4
- package/dist/security.d.ts +102 -0
- package/dist/security.js +437 -0
- package/dist/securityDeepScan.d.ts +41 -0
- package/dist/securityDeepScan.js +248 -0
- package/dist/securityDoctor.d.ts +32 -0
- package/dist/securityDoctor.js +63 -0
- package/dist/securityFeature.d.ts +5 -0
- package/dist/securityFeature.js +7 -0
- package/dist/securityInstall.d.ts +63 -0
- package/dist/securityInstall.js +239 -0
- package/dist/securityInventory.d.ts +54 -0
- package/dist/securityInventory.js +556 -0
- package/dist/securityPilot.d.ts +19 -0
- package/dist/securityPilot.js +61 -0
- package/dist/securityPolicy.d.ts +33 -0
- package/dist/securityPolicy.js +188 -0
- package/dist/securitySession.d.ts +6 -0
- package/dist/securitySession.js +72 -0
- package/dist/upgradeNudge.d.ts +1 -0
- package/dist/upgradeNudge.js +1 -0
- package/dist/utils.d.ts +16 -6
- package/dist/utils.js +311 -70
- package/package.json +14 -3
- package/dist/commands/post.d.ts +0 -2
- package/dist/commands/post.js +0 -96
package/dist/utils.js
CHANGED
|
@@ -64,6 +64,7 @@ exports.openCodeInstalled = openCodeInstalled;
|
|
|
64
64
|
exports.installOpenCodeCapturePlugin = installOpenCodeCapturePlugin;
|
|
65
65
|
exports.vscodeCopilotInstalled = vscodeCopilotInstalled;
|
|
66
66
|
exports.upsertCodiedevHook = upsertCodiedevHook;
|
|
67
|
+
exports.removeCodiedevHook = removeCodiedevHook;
|
|
67
68
|
exports.installHook = installHook;
|
|
68
69
|
exports.ensureDiscoveryHookInstalled = ensureDiscoveryHookInstalled;
|
|
69
70
|
exports.ensureBackupHookInstalled = ensureBackupHookInstalled;
|
|
@@ -84,6 +85,7 @@ exports.installCodexMcp = installCodexMcp;
|
|
|
84
85
|
exports.installCodexHook = installCodexHook;
|
|
85
86
|
exports.ensureCodexLimitHookInstalled = ensureCodexLimitHookInstalled;
|
|
86
87
|
exports.installCursorHook = installCursorHook;
|
|
88
|
+
exports.ensureCursorCodiedevHooksInstalled = ensureCursorCodiedevHooksInstalled;
|
|
87
89
|
exports.parseClaudeSteering = parseClaudeSteering;
|
|
88
90
|
exports.parseCodexSteering = parseCodexSteering;
|
|
89
91
|
exports.parseSteering = parseSteering;
|
|
@@ -99,6 +101,8 @@ exports.dispatchDeferredUpload = dispatchDeferredUpload;
|
|
|
99
101
|
exports.sweepPendingCaptures = sweepPendingCaptures;
|
|
100
102
|
exports.resolveCaptureFinal = resolveCaptureFinal;
|
|
101
103
|
exports.finishInputTmpFile = finishInputTmpFile;
|
|
104
|
+
exports.captureUploadTmpFile = captureUploadTmpFile;
|
|
105
|
+
exports.cancelDeferredCaptureUpload = cancelDeferredCaptureUpload;
|
|
102
106
|
exports.decideDispatch = decideDispatch;
|
|
103
107
|
exports.readSessionUploadState = readSessionUploadState;
|
|
104
108
|
exports.writeSessionUploadState = writeSessionUploadState;
|
|
@@ -109,6 +113,7 @@ exports.readLastCapture = readLastCapture;
|
|
|
109
113
|
exports.runCaptureUpload = runCaptureUpload;
|
|
110
114
|
exports.runDeferredUpload = runDeferredUpload;
|
|
111
115
|
exports.transcriptReadSkipReason = transcriptReadSkipReason;
|
|
116
|
+
exports.shouldReportCaptureSkip = shouldReportCaptureSkip;
|
|
112
117
|
const fs = __importStar(require("fs"));
|
|
113
118
|
const path = __importStar(require("path"));
|
|
114
119
|
const os = __importStar(require("os"));
|
|
@@ -852,7 +857,7 @@ function buildCapturePayload(input) {
|
|
|
852
857
|
sessionStartedAt: input.sessionStartedAt ?? undefined,
|
|
853
858
|
interruptCount: input.interruptCount,
|
|
854
859
|
rejectionCount: input.rejectionCount,
|
|
855
|
-
// final is only emitted when explicitly set (
|
|
860
|
+
// final is only emitted when explicitly set (omitted for Codex Stop).
|
|
856
861
|
final: input.final,
|
|
857
862
|
costUsd: input.costUsd,
|
|
858
863
|
};
|
|
@@ -1052,10 +1057,15 @@ function openCodeInstalled() {
|
|
|
1052
1057
|
/** Install a dormant global OpenCode adapter. Installing it unconditionally
|
|
1053
1058
|
* keeps `codiedev connect` as the only setup step even when Codie/OpenCode is
|
|
1054
1059
|
* installed later. OpenCode loads this file from its documented plugin dir. */
|
|
1055
|
-
function installOpenCodeCapturePlugin() {
|
|
1060
|
+
function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
1056
1061
|
const start = resolveHookInvocation("session-start-opencode");
|
|
1057
1062
|
const capture = resolveHookInvocation("capture-opencode");
|
|
1058
1063
|
const finish = resolveHookInvocation("capture-opencode-final");
|
|
1064
|
+
const backup = resolveHookInvocation("backup");
|
|
1065
|
+
const prompt = resolveHookInvocation("discover-codie");
|
|
1066
|
+
const securityPre = resolveHookInvocation("security-pre-codie");
|
|
1067
|
+
const securityPost = resolveHookInvocation("security-post-codie");
|
|
1068
|
+
const securityFailure = resolveHookInvocation("security-failure-codie");
|
|
1059
1069
|
const source = `// Managed by codiedev connect. Safe to remove and reinstall.\n` +
|
|
1060
1070
|
`import { spawn, spawnSync } from "node:child_process";\n` +
|
|
1061
1071
|
`import { mkdir, writeFile } from "node:fs/promises";\n` +
|
|
@@ -1064,8 +1074,15 @@ function installOpenCodeCapturePlugin() {
|
|
|
1064
1074
|
`const START = ${JSON.stringify(start)};\n` +
|
|
1065
1075
|
`const CAPTURE = ${JSON.stringify(capture)};\n` +
|
|
1066
1076
|
`const FINISH = ${JSON.stringify(finish)};\n` +
|
|
1077
|
+
`const BACKUP = ${JSON.stringify(backup)};\n` +
|
|
1078
|
+
`const PROMPT = ${JSON.stringify(prompt)};\n` +
|
|
1079
|
+
`const SECURITY_PRE = ${JSON.stringify(securityPre)};\n` +
|
|
1080
|
+
`const SECURITY_POST = ${JSON.stringify(securityPost)};\n` +
|
|
1081
|
+
`const SECURITY_FAILURE = ${JSON.stringify(securityFailure)};\n` +
|
|
1082
|
+
`const AGENT_SECURITY_ENABLED = ${JSON.stringify(agentSecurityEnabled)};\n` +
|
|
1067
1083
|
`const fingerprints = new Map();\n\n` +
|
|
1068
1084
|
`const transcriptPaths = new Map();\n\n` +
|
|
1085
|
+
`const sessionDirectories = new Map();\n\n` +
|
|
1069
1086
|
`function sessionId(event) {\n` +
|
|
1070
1087
|
` return event?.properties?.sessionID ?? event?.properties?.sessionId ?? event?.properties?.info?.id ?? null;\n` +
|
|
1071
1088
|
`}\n\n` +
|
|
@@ -1079,19 +1096,74 @@ function installOpenCodeCapturePlugin() {
|
|
|
1079
1096
|
`function dispatchSync(invocation, payload) {\n` +
|
|
1080
1097
|
` try { spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), stdio: ["pipe", "ignore", "ignore"], timeout: 1500 }); } catch {}\n` +
|
|
1081
1098
|
`}\n\n` +
|
|
1099
|
+
`function invokeSync(invocation, payload) {\n` +
|
|
1100
|
+
` try {\n` +
|
|
1101
|
+
` const result = spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 1500 });\n` +
|
|
1102
|
+
` if (result.error) return null;\n` +
|
|
1103
|
+
` return JSON.parse(result.stdout || "{}");\n` +
|
|
1104
|
+
` } catch { return null; }\n` +
|
|
1105
|
+
`}\n\n` +
|
|
1082
1106
|
`export const CodiedevCapture = async ({ client, directory, worktree }) => {\n` +
|
|
1107
|
+
` const enforce = async (invocation, payload) => {\n` +
|
|
1108
|
+
` const decision = invokeSync(invocation, payload);\n` +
|
|
1109
|
+
` const denied = decision?.decision === "block" || decision?.hookSpecificOutput?.permissionDecision === "deny";\n` +
|
|
1110
|
+
` const reason = denied ? (decision?.reason || decision?.hookSpecificOutput?.permissionDecisionReason || "Codie security policy blocked this action") : null;\n` +
|
|
1111
|
+
` if (!reason) return decision;\n` +
|
|
1112
|
+
` try { await client.tui.showToast({ title: "Codie Security", message: reason, variant: "error", duration: 10000 }); } catch {}\n` +
|
|
1113
|
+
` if (process.env.CODIEDEV_SECURITY_BLOCK_OUTPUT === "marker") {\n` +
|
|
1114
|
+
` process.stderr.write("__CODIEDEV_SECURITY_BLOCK__" + JSON.stringify({ reason }) + "\\n");\n` +
|
|
1115
|
+
` process.exit(1);\n` +
|
|
1116
|
+
` } else {\n` +
|
|
1117
|
+
` process.stderr.write("\\nCodie Security blocked this action:\\n" + reason + "\\n");\n` +
|
|
1118
|
+
` }\n` +
|
|
1119
|
+
` throw new Error(reason);\n` +
|
|
1120
|
+
` };\n` +
|
|
1121
|
+
` const resolveCwd = async (id, hint) => {\n` +
|
|
1122
|
+
` if (typeof hint === "string" && hint && hint !== "/") { sessionDirectories.set(id, hint); return hint; }\n` +
|
|
1123
|
+
` const remembered = sessionDirectories.get(id);\n` +
|
|
1124
|
+
` if (remembered) return remembered;\n` +
|
|
1125
|
+
` try {\n` +
|
|
1126
|
+
` const response = await client.session.get({ path: { id } });\n` +
|
|
1127
|
+
` const session = response?.data ?? response;\n` +
|
|
1128
|
+
` if (typeof session?.directory === "string" && session.directory) { sessionDirectories.set(id, session.directory); return session.directory; }\n` +
|
|
1129
|
+
` } catch {}\n` +
|
|
1130
|
+
` const fallback = worktree || directory || process.cwd();\n` +
|
|
1131
|
+
` sessionDirectories.set(id, fallback);\n` +
|
|
1132
|
+
` return fallback;\n` +
|
|
1133
|
+
` };\n` +
|
|
1083
1134
|
` const finalizeAll = () => {\n` +
|
|
1084
1135
|
` for (const [id, transcriptPath] of transcriptPaths) {\n` +
|
|
1085
|
-
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1136
|
+
` const cwd = sessionDirectories.get(id) || worktree || directory || process.cwd();\n` +
|
|
1086
1137
|
` dispatchSync(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1087
1138
|
` }\n` +
|
|
1088
1139
|
` };\n` +
|
|
1089
1140
|
` process.once("exit", finalizeAll);\n` +
|
|
1090
1141
|
` return ({\n` +
|
|
1142
|
+
` "chat.message": async (input, output) => {\n` +
|
|
1143
|
+
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1144
|
+
` const cwd = await resolveCwd(id);\n` +
|
|
1145
|
+
` const prompt = Array.isArray(output?.parts) ? output.parts.filter((part) => part?.type === "text").map((part) => part.text || "").join("\\n") : "";\n` +
|
|
1146
|
+
` const decision = await enforce(PROMPT, { session_id: id, cwd, prompt });\n` +
|
|
1147
|
+
` const context = decision?.hookSpecificOutput?.additionalContext;\n` +
|
|
1148
|
+
` if (context && Array.isArray(output?.parts)) output.parts.push({ type: "text", text: context });\n` +
|
|
1149
|
+
` },\n` +
|
|
1150
|
+
` "tool.execute.before": async (input, output) => {\n` +
|
|
1151
|
+
` if (!AGENT_SECURITY_ENABLED) return;\n` +
|
|
1152
|
+
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1153
|
+
` const cwd = await resolveCwd(id);\n` +
|
|
1154
|
+
` await enforce(SECURITY_PRE, { sessionId: id, cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: output?.args ?? output });\n` +
|
|
1155
|
+
` },\n` +
|
|
1156
|
+
` "tool.execute.after": async (input, output) => {\n` +
|
|
1157
|
+
` if (!AGENT_SECURITY_ENABLED) return;\n` +
|
|
1158
|
+
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1159
|
+
` const cwd = await resolveCwd(id);\n` +
|
|
1160
|
+
` const payload = { sessionId: id, cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: input?.args, toolResponse: output };\n` +
|
|
1161
|
+
` dispatch(output?.error ? SECURITY_FAILURE : SECURITY_POST, payload);\n` +
|
|
1162
|
+
` },\n` +
|
|
1091
1163
|
` event: async ({ event }) => {\n` +
|
|
1092
1164
|
` const id = sessionId(event);\n` +
|
|
1093
1165
|
` if (!id) return;\n` +
|
|
1094
|
-
` const cwd =
|
|
1166
|
+
` const cwd = await resolveCwd(id, event?.properties?.info?.directory);\n` +
|
|
1095
1167
|
` if (event.type === "session.created") {\n` +
|
|
1096
1168
|
` dispatch(START, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1097
1169
|
` return;\n` +
|
|
@@ -1100,6 +1172,7 @@ function installOpenCodeCapturePlugin() {
|
|
|
1100
1172
|
` const transcriptPath = transcriptPaths.get(id);\n` +
|
|
1101
1173
|
` if (transcriptPath) dispatch(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1102
1174
|
` transcriptPaths.delete(id);\n` +
|
|
1175
|
+
` sessionDirectories.delete(id);\n` +
|
|
1103
1176
|
` fingerprints.delete(id);\n` +
|
|
1104
1177
|
` return;\n` +
|
|
1105
1178
|
` }\n` +
|
|
@@ -1119,6 +1192,7 @@ function installOpenCodeCapturePlugin() {
|
|
|
1119
1192
|
` await writeFile(transcriptPath, JSON.stringify(list), { mode: 0o600 });\n` +
|
|
1120
1193
|
` transcriptPaths.set(id, transcriptPath);\n` +
|
|
1121
1194
|
` dispatch(CAPTURE, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1195
|
+
` dispatch(BACKUP, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1122
1196
|
` } catch {}\n` +
|
|
1123
1197
|
` },\n` +
|
|
1124
1198
|
` });\n` +
|
|
@@ -1144,7 +1218,7 @@ function vscodeCopilotInstalled() {
|
|
|
1144
1218
|
return false;
|
|
1145
1219
|
}
|
|
1146
1220
|
}
|
|
1147
|
-
// Idempotently put a single codiedev hook (matcher ".*", running our hook.js
|
|
1221
|
+
// Idempotently put a single codiedev hook (matcher ".*" by default, running our hook.js
|
|
1148
1222
|
// with `subcommand`) into the given hook event, stripping any prior codiedev
|
|
1149
1223
|
// entries first so connect re-runs / upgrades refresh the resolved binary path.
|
|
1150
1224
|
// Mutates `hooks` in place. Exported for unit testing.
|
|
@@ -1176,12 +1250,41 @@ function upsertCodiedevHook(hooks, eventName, subcommand, matcher = ".*") {
|
|
|
1176
1250
|
return !(0, detection_1.isCodiedevHookCommand)(hook.matcher);
|
|
1177
1251
|
});
|
|
1178
1252
|
filtered.push({
|
|
1179
|
-
matcher,
|
|
1253
|
+
...(matcher === null ? {} : { matcher }),
|
|
1180
1254
|
hooks: [{ type: "command", command: resolveHookCommand(subcommand, preservedNodePath) }],
|
|
1181
1255
|
});
|
|
1182
1256
|
hooks[eventName] = filtered;
|
|
1183
1257
|
}
|
|
1184
|
-
|
|
1258
|
+
/** Remove Codiedev's entries for one event while preserving foreign hooks. */
|
|
1259
|
+
function removeCodiedevHook(hooks, eventName) {
|
|
1260
|
+
const entries = hooks[eventName];
|
|
1261
|
+
if (!Array.isArray(entries))
|
|
1262
|
+
return false;
|
|
1263
|
+
let changed = false;
|
|
1264
|
+
const filtered = entries.flatMap((entry) => {
|
|
1265
|
+
const record = entry;
|
|
1266
|
+
const inner = record.hooks;
|
|
1267
|
+
if (!Array.isArray(inner))
|
|
1268
|
+
return [record];
|
|
1269
|
+
const remaining = inner.filter((hook) => {
|
|
1270
|
+
const remove = (0, detection_1.isCodiedevHookCommand)(hook.command);
|
|
1271
|
+
if (remove)
|
|
1272
|
+
changed = true;
|
|
1273
|
+
return !remove;
|
|
1274
|
+
});
|
|
1275
|
+
if (remaining.length === 0)
|
|
1276
|
+
return [];
|
|
1277
|
+
return [{ ...record, hooks: remaining }];
|
|
1278
|
+
});
|
|
1279
|
+
if (!changed)
|
|
1280
|
+
return false;
|
|
1281
|
+
if (filtered.length === 0)
|
|
1282
|
+
delete hooks[eventName];
|
|
1283
|
+
else
|
|
1284
|
+
hooks[eventName] = filtered;
|
|
1285
|
+
return true;
|
|
1286
|
+
}
|
|
1287
|
+
function installHook(agentSecurityEnabled = true) {
|
|
1185
1288
|
let settings = {};
|
|
1186
1289
|
try {
|
|
1187
1290
|
if (fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
@@ -1198,6 +1301,18 @@ function installHook() {
|
|
|
1198
1301
|
const hooks = settings.hooks;
|
|
1199
1302
|
// SessionEnd → capture (upload the finished session for the team library).
|
|
1200
1303
|
upsertCodiedevHook(hooks, "SessionEnd", "capture");
|
|
1304
|
+
// Tool boundaries → ADR shadow detection. PreToolUse is the eventual
|
|
1305
|
+
// enforcement point; alert mode always returns allow.
|
|
1306
|
+
if (agentSecurityEnabled) {
|
|
1307
|
+
upsertCodiedevHook(hooks, "PreToolUse", "security-pre");
|
|
1308
|
+
upsertCodiedevHook(hooks, "PostToolUse", "security-post");
|
|
1309
|
+
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure");
|
|
1310
|
+
}
|
|
1311
|
+
else {
|
|
1312
|
+
removeCodiedevHook(hooks, "PreToolUse");
|
|
1313
|
+
removeCodiedevHook(hooks, "PostToolUse");
|
|
1314
|
+
removeCodiedevHook(hooks, "PostToolUseFailure");
|
|
1315
|
+
}
|
|
1201
1316
|
// UserPromptSubmit → discover (surface a matching team skill for the task).
|
|
1202
1317
|
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover");
|
|
1203
1318
|
// Stop → backup (auto-back-up changed artifact files to your private library).
|
|
@@ -1299,7 +1414,7 @@ function ensureLimitHookInstalled() {
|
|
|
1299
1414
|
// shrinks that window and removes the intra-process version entirely.
|
|
1300
1415
|
// The per-hook ensure* functions below are kept for tests/back-compat but are
|
|
1301
1416
|
// no longer called from the hook entrypoint.
|
|
1302
|
-
function ensureAllCodiedevHooksInstalled() {
|
|
1417
|
+
function ensureAllCodiedevHooksInstalled(agentSecurityEnabled = readConfig()?.agentSecurityEnabled === true) {
|
|
1303
1418
|
try {
|
|
1304
1419
|
if (!fs.existsSync(CLAUDE_SETTINGS_PATH))
|
|
1305
1420
|
return; // not a Claude Code user
|
|
@@ -1312,8 +1427,17 @@ function ensureAllCodiedevHooksInstalled() {
|
|
|
1312
1427
|
["StopFailure", "limit", "rate_limit"],
|
|
1313
1428
|
["SessionStart", "session-start"],
|
|
1314
1429
|
];
|
|
1430
|
+
if (agentSecurityEnabled) {
|
|
1431
|
+
wanted.splice(1, 0, ["PreToolUse", "security-pre"], ["PostToolUse", "security-post"], ["PostToolUseFailure", "security-failure"]);
|
|
1432
|
+
}
|
|
1433
|
+
let changed = false;
|
|
1434
|
+
if (!agentSecurityEnabled) {
|
|
1435
|
+
for (const eventName of ["PreToolUse", "PostToolUse", "PostToolUseFailure"]) {
|
|
1436
|
+
changed = removeCodiedevHook(hooks, eventName) || changed;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1315
1439
|
const missing = wanted.filter(([eventName]) => !hasCodiedevHook(hooks, eventName));
|
|
1316
|
-
if (missing.length === 0)
|
|
1440
|
+
if (missing.length === 0 && !changed)
|
|
1317
1441
|
return; // steady state: no write, no churn
|
|
1318
1442
|
settings.hooks = hooks;
|
|
1319
1443
|
for (const [eventName, subcommand, matcher] of missing) {
|
|
@@ -1392,7 +1516,6 @@ thought, use the \`codiedev\` CLI via Bash:**
|
|
|
1392
1516
|
| "note that X is a follow-up" / "remember X" | \`codiedev note "<text>"\` |
|
|
1393
1517
|
| "promote the extracted spec" | \`codiedev promote <artifact-id>\` |
|
|
1394
1518
|
| "create a ticket" / "scope this into tickets" / "ticket this for me" | \`codiedev create-ticket [optional-inline-prompt]\` |
|
|
1395
|
-
| "post this to the team" / "publish to the feed" / "share with the team" | \`codiedev post --title "<t>" --body "<b>" [--intent <intent>] [--filename <key>] [--mention <name>]\` |
|
|
1396
1519
|
| "share with a teammate" / "give someone access" (no notification) | \`codiedev share <key> <name> [--role read\\|edit]\` |
|
|
1397
1520
|
| "send this to a teammate" / "ask someone to look at this" | \`codiedev send <key> <name> ["<msg>"]\` |
|
|
1398
1521
|
| "find something about X" / "has anyone solved Y?" | \`codiedev search "<query>" [--limit N]\` |
|
|
@@ -1426,31 +1549,6 @@ read it back to the user so they can confirm.
|
|
|
1426
1549
|
**Teammate names:** first name usually works (\`codiedev ping <name> ...\`).
|
|
1427
1550
|
If ambiguous, the CLI returns candidates — retry with the full email.
|
|
1428
1551
|
|
|
1429
|
-
**Feed posts (broad team reach):** use \`codiedev post\` when the user
|
|
1430
|
-
wants visibility beyond one teammate — e.g., announcing a new skill,
|
|
1431
|
-
asking the team who knows about X, proposing a change. Always pass
|
|
1432
|
-
\`--intent\` to match the request: \`share\` (here's a skill),
|
|
1433
|
-
\`request_review\`, \`request_expertise\` (stuck, who knows this),
|
|
1434
|
-
\`link_share\` (fyi), or \`rfc\` (proposing a change). Pass \`--filename\`
|
|
1435
|
-
to link a specific artifact and \`--mention <name>\` (repeatable) to
|
|
1436
|
-
tag teammates.
|
|
1437
|
-
|
|
1438
|
-
**Default feed-post shape.** When the user explicitly says "push/share/post
|
|
1439
|
-
to the feed", default to this shape unless they ask for something else:
|
|
1440
|
-
- **Title** ≤10 words: *what + why now*. Imperative or declarative — never
|
|
1441
|
-
"Check this out".
|
|
1442
|
-
- **Body** — three short lines, grounded in the current session and the
|
|
1443
|
-
artifact:
|
|
1444
|
-
- **Why this exists** — 1–2 sentences from session context (the problem
|
|
1445
|
-
or insight that produced the artifact).
|
|
1446
|
-
- **What's in it** — 1 sentence pulled from the artifact's frontmatter
|
|
1447
|
-
\`description:\` (skills) or first paragraph (specs). Don't restate
|
|
1448
|
-
the whole file — the card preview carries the detail.
|
|
1449
|
-
- **Ask** — 1 sentence. *Use it / review it / ping me with thoughts.*
|
|
1450
|
-
- **Filename**: always attach so the card preview renders.
|
|
1451
|
-
This is the default. If the user wants a one-liner or a different
|
|
1452
|
-
structure, follow their lead.
|
|
1453
|
-
|
|
1454
1552
|
**Search before pushing.** For any "save this as …" request, run
|
|
1455
1553
|
\`codiedev search "<query>"\` first. If prior art exists, pull it and
|
|
1456
1554
|
iterate rather than duplicating.
|
|
@@ -1754,7 +1852,7 @@ function installCodexMcp() {
|
|
|
1754
1852
|
// Codex doesn't have a SessionEnd event yet — Stop fires after every turn.
|
|
1755
1853
|
// The backend dedupes on (companyId, sessionId) and replaces the stored
|
|
1756
1854
|
// transcript on re-upload, so the latest turn's snapshot wins.
|
|
1757
|
-
function installCodexHook() {
|
|
1855
|
+
function installCodexHook(agentSecurityEnabled = true) {
|
|
1758
1856
|
let hooksFile = {};
|
|
1759
1857
|
try {
|
|
1760
1858
|
if (fs.existsSync(CODEX_HOOKS_PATH)) {
|
|
@@ -1769,9 +1867,8 @@ function installCodexHook() {
|
|
|
1769
1867
|
hooksFile.hooks = {};
|
|
1770
1868
|
}
|
|
1771
1869
|
const hooks = hooksFile.hooks;
|
|
1772
|
-
if (!hooks.Stop)
|
|
1870
|
+
if (!hooks.Stop)
|
|
1773
1871
|
hooks.Stop = [];
|
|
1774
|
-
}
|
|
1775
1872
|
const existing = hooks.Stop;
|
|
1776
1873
|
const filtered = existing.filter((hook) => {
|
|
1777
1874
|
const inner = hook.hooks;
|
|
@@ -1779,6 +1876,9 @@ function installCodexHook() {
|
|
|
1779
1876
|
return true;
|
|
1780
1877
|
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
1781
1878
|
});
|
|
1879
|
+
// Codex has no StopFailure matcher. Its Stop transcript includes a
|
|
1880
|
+
// structured rate-limit snapshot, so this tiny hook reports only when that
|
|
1881
|
+
// snapshot says a boundary was reached.
|
|
1782
1882
|
filtered.push({
|
|
1783
1883
|
hooks: [
|
|
1784
1884
|
{
|
|
@@ -1808,6 +1908,24 @@ function installCodexHook() {
|
|
|
1808
1908
|
],
|
|
1809
1909
|
});
|
|
1810
1910
|
hooks.Stop = filtered;
|
|
1911
|
+
// Codex uses the same Claude-style matcher-group shape. Keep these as
|
|
1912
|
+
// separate entries because Codex hook coverage is still evolving and a
|
|
1913
|
+
// malformed/unavailable post hook must not interfere with PreToolUse.
|
|
1914
|
+
if (agentSecurityEnabled) {
|
|
1915
|
+
upsertCodiedevHook(hooks, "PreToolUse", "security-pre-codex");
|
|
1916
|
+
upsertCodiedevHook(hooks, "PostToolUse", "security-post-codex");
|
|
1917
|
+
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure-codex");
|
|
1918
|
+
}
|
|
1919
|
+
else {
|
|
1920
|
+
removeCodiedevHook(hooks, "PreToolUse");
|
|
1921
|
+
removeCodiedevHook(hooks, "PostToolUse");
|
|
1922
|
+
removeCodiedevHook(hooks, "PostToolUseFailure");
|
|
1923
|
+
}
|
|
1924
|
+
// Prompt guard + skill discovery. The guard runs locally before any prompt
|
|
1925
|
+
// content can reach either the model or Codiedev's discovery endpoint.
|
|
1926
|
+
// Codex models UserPromptSubmit as a non-matcher event (the trust-key model
|
|
1927
|
+
// intentionally excludes it from MATCHER_EVENTS), so omit the matcher key.
|
|
1928
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex", null);
|
|
1811
1929
|
if (!fs.existsSync(CODEX_DIR)) {
|
|
1812
1930
|
fs.mkdirSync(CODEX_DIR, { recursive: true });
|
|
1813
1931
|
}
|
|
@@ -1815,7 +1933,7 @@ function installCodexHook() {
|
|
|
1815
1933
|
(0, codexHookTrust_1.writeCodexHookTrust)(CODEX_CONFIG_PATH, CODEX_HOOKS_PATH, hooksFile);
|
|
1816
1934
|
}
|
|
1817
1935
|
// Existing Codex users already have capture-codex on Stop. After a CLI
|
|
1818
|
-
// upgrade, that hook repairs
|
|
1936
|
+
// upgrade, that hook repairs newer lifecycle hooks without requiring connect.
|
|
1819
1937
|
function ensureCodexLimitHookInstalled() {
|
|
1820
1938
|
try {
|
|
1821
1939
|
if (!fs.existsSync(CODEX_HOOKS_PATH))
|
|
@@ -1823,16 +1941,31 @@ function ensureCodexLimitHookInstalled() {
|
|
|
1823
1941
|
const hooksFile = JSON.parse(fs.readFileSync(CODEX_HOOKS_PATH, "utf8"));
|
|
1824
1942
|
const hooks = (hooksFile.hooks ?? {});
|
|
1825
1943
|
const stop = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
1826
|
-
const
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1944
|
+
const hasCommand = (entries, subcommand) => entries.some((group) => {
|
|
1945
|
+
const record = group;
|
|
1946
|
+
const flat = record.command;
|
|
1947
|
+
if (typeof flat === "string" && flat.trim().endsWith(subcommand))
|
|
1948
|
+
return true;
|
|
1949
|
+
const inner = record.hooks;
|
|
1950
|
+
return Array.isArray(inner) && inner.some((hook) => typeof hook.command === "string" && hook.command.trim().endsWith(subcommand));
|
|
1829
1951
|
});
|
|
1830
|
-
|
|
1952
|
+
let changed = false;
|
|
1953
|
+
if (!hasCommand(stop, "limit-codex")) {
|
|
1954
|
+
stop.unshift({
|
|
1955
|
+
hooks: [{ type: "command", command: resolveHookCommand("limit-codex"), timeout: 10 }],
|
|
1956
|
+
});
|
|
1957
|
+
hooks.Stop = stop;
|
|
1958
|
+
changed = true;
|
|
1959
|
+
}
|
|
1960
|
+
const promptEntries = Array.isArray(hooks.UserPromptSubmit)
|
|
1961
|
+
? hooks.UserPromptSubmit
|
|
1962
|
+
: [];
|
|
1963
|
+
if (!hasCommand(promptEntries, "discover-codex")) {
|
|
1964
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex", null);
|
|
1965
|
+
changed = true;
|
|
1966
|
+
}
|
|
1967
|
+
if (!changed)
|
|
1831
1968
|
return;
|
|
1832
|
-
stop.unshift({
|
|
1833
|
-
hooks: [{ type: "command", command: resolveHookCommand("limit-codex"), timeout: 10 }],
|
|
1834
|
-
});
|
|
1835
|
-
hooks.Stop = stop;
|
|
1836
1969
|
hooksFile.hooks = hooks;
|
|
1837
1970
|
fs.writeFileSync(CODEX_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1838
1971
|
// Trust keys include each hook's positional index. Adding the new hook at
|
|
@@ -1847,7 +1980,7 @@ function ensureCodexLimitHookInstalled() {
|
|
|
1847
1980
|
// `sessionEnd` fires when a conversation closes (completed, aborted, error,
|
|
1848
1981
|
// window_close, user_close); the base stdin payload includes transcript_path
|
|
1849
1982
|
// and session_id which `codiedev-hook capture-cursor` reads to upload.
|
|
1850
|
-
function installCursorHook() {
|
|
1983
|
+
function installCursorHook(agentSecurityEnabled = true) {
|
|
1851
1984
|
if (!fs.existsSync(CURSOR_DIR)) {
|
|
1852
1985
|
fs.mkdirSync(CURSOR_DIR, { recursive: true });
|
|
1853
1986
|
}
|
|
@@ -1868,25 +2001,89 @@ function installCursorHook() {
|
|
|
1868
2001
|
hooksFile.hooks = {};
|
|
1869
2002
|
}
|
|
1870
2003
|
const hooks = hooksFile.hooks;
|
|
1871
|
-
|
|
1872
|
-
|
|
2004
|
+
const replaceManaged = (eventName, subcommands) => {
|
|
2005
|
+
const existing = Array.isArray(hooks[eventName])
|
|
2006
|
+
? hooks[eventName]
|
|
2007
|
+
: [];
|
|
2008
|
+
const preserved = existing.filter((hook) => !(0, detection_1.isCodiedevHookCommand)(hook.command));
|
|
2009
|
+
const managed = subcommands.map((subcommand) => ({
|
|
2010
|
+
command: resolveHookCommand(subcommand),
|
|
2011
|
+
type: "command",
|
|
2012
|
+
timeout: 30,
|
|
2013
|
+
}));
|
|
2014
|
+
if (preserved.length + managed.length === 0)
|
|
2015
|
+
delete hooks[eventName];
|
|
2016
|
+
else
|
|
2017
|
+
hooks[eventName] = [...preserved, ...managed];
|
|
2018
|
+
};
|
|
2019
|
+
// Cursor-native lifecycle names and flat hook entries. Capture only at the
|
|
2020
|
+
// real IDE session boundary; backup at every completed agent turn. The
|
|
2021
|
+
// Cursor-specific backup entrypoint keeps that non-final snapshot labeled
|
|
2022
|
+
// and normalized as Cursor rather than accidentally treating it as Claude.
|
|
2023
|
+
replaceManaged("sessionStart", ["session-start-cursor"]);
|
|
2024
|
+
replaceManaged("sessionEnd", ["capture-cursor"]);
|
|
2025
|
+
replaceManaged("stop", ["backup-cursor"]);
|
|
2026
|
+
if (agentSecurityEnabled) {
|
|
2027
|
+
// Cursor supports blocking here, but not dynamic context injection.
|
|
2028
|
+
replaceManaged("beforeSubmitPrompt", ["security-prompt-cursor"]);
|
|
2029
|
+
replaceManaged("preToolUse", ["security-pre-cursor"]);
|
|
2030
|
+
replaceManaged("postToolUse", ["security-post-cursor"]);
|
|
2031
|
+
replaceManaged("postToolUseFailure", ["security-failure-cursor"]);
|
|
2032
|
+
}
|
|
2033
|
+
else {
|
|
2034
|
+
replaceManaged("beforeSubmitPrompt", []);
|
|
2035
|
+
replaceManaged("preToolUse", []);
|
|
2036
|
+
replaceManaged("postToolUse", []);
|
|
2037
|
+
replaceManaged("postToolUseFailure", []);
|
|
1873
2038
|
}
|
|
1874
|
-
const existing = hooks.sessionEnd;
|
|
1875
|
-
const filtered = existing.filter((h) => !(0, detection_1.isCodiedevHookCommand)(h.command));
|
|
1876
|
-
filtered.push({
|
|
1877
|
-
command: resolveHookCommand("capture-cursor"),
|
|
1878
|
-
type: "command",
|
|
1879
|
-
timeout: 30,
|
|
1880
|
-
});
|
|
1881
|
-
// sessionEnd → backup: auto-back-up changed artifact files to the private library.
|
|
1882
|
-
filtered.push({
|
|
1883
|
-
command: resolveHookCommand("backup"),
|
|
1884
|
-
type: "command",
|
|
1885
|
-
timeout: 30,
|
|
1886
|
-
});
|
|
1887
|
-
hooks.sessionEnd = filtered;
|
|
1888
2039
|
fs.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1889
2040
|
}
|
|
2041
|
+
/** Repair Cursor's complete flat hook set after upgrades or configuration
|
|
2042
|
+
* drift. This is deliberately separate from Claude Code's nested-hook healer:
|
|
2043
|
+
* the schemas and event names differ, and mixing them risks corrupting both. */
|
|
2044
|
+
function ensureCursorCodiedevHooksInstalled(agentSecurityEnabled = readConfig()?.agentSecurityEnabled === true) {
|
|
2045
|
+
try {
|
|
2046
|
+
if (!fs.existsSync(CURSOR_HOOKS_PATH))
|
|
2047
|
+
return;
|
|
2048
|
+
const parsed = JSON.parse(fs.readFileSync(CURSOR_HOOKS_PATH, "utf8"));
|
|
2049
|
+
const hooks = parsed.hooks ?? {};
|
|
2050
|
+
const has = (eventName, subcommand) => {
|
|
2051
|
+
const entries = hooks[eventName];
|
|
2052
|
+
return Array.isArray(entries) && entries.some((entry) => {
|
|
2053
|
+
const command = entry.command;
|
|
2054
|
+
return typeof command === "string"
|
|
2055
|
+
&& (0, detection_1.isCodiedevHookCommand)(command)
|
|
2056
|
+
&& command.trim().endsWith(subcommand);
|
|
2057
|
+
});
|
|
2058
|
+
};
|
|
2059
|
+
const required = [
|
|
2060
|
+
["sessionStart", "session-start-cursor"],
|
|
2061
|
+
["sessionEnd", "capture-cursor"],
|
|
2062
|
+
["stop", "backup-cursor"],
|
|
2063
|
+
...(agentSecurityEnabled
|
|
2064
|
+
? [
|
|
2065
|
+
["beforeSubmitPrompt", "security-prompt-cursor"],
|
|
2066
|
+
["preToolUse", "security-pre-cursor"],
|
|
2067
|
+
["postToolUse", "security-post-cursor"],
|
|
2068
|
+
["postToolUseFailure", "security-failure-cursor"],
|
|
2069
|
+
]
|
|
2070
|
+
: []),
|
|
2071
|
+
];
|
|
2072
|
+
const hasStaleSecurity = !agentSecurityEnabled && [
|
|
2073
|
+
"beforeSubmitPrompt", "preToolUse", "postToolUse", "postToolUseFailure",
|
|
2074
|
+
].some((eventName) => {
|
|
2075
|
+
const entries = hooks[eventName];
|
|
2076
|
+
return Array.isArray(entries) && entries.some((entry) => (0, detection_1.isCodiedevHookCommand)(entry.command));
|
|
2077
|
+
});
|
|
2078
|
+
if (required.every(([eventName, subcommand]) => has(eventName, subcommand)) && !hasStaleSecurity) {
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
installCursorHook(agentSecurityEnabled);
|
|
2082
|
+
}
|
|
2083
|
+
catch {
|
|
2084
|
+
// Best-effort — a malformed third-party config must never block capture.
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1890
2087
|
function parseClaudeSteering(content) {
|
|
1891
2088
|
let interrupts = 0;
|
|
1892
2089
|
let rejections = 0;
|
|
@@ -2497,13 +2694,13 @@ exports.CAPTURE_THROTTLE_MS = 10 * 60 * 1000;
|
|
|
2497
2694
|
// retry storms.
|
|
2498
2695
|
exports.SWEEP_MIN_AGE_MS = exports.CAPTURE_THROTTLE_MS + 2 * 60 * 1000;
|
|
2499
2696
|
// Map the hook's capture argv to the payload's `final` flag. EXPLICIT
|
|
2500
|
-
// ALLOWLIST:
|
|
2501
|
-
//
|
|
2697
|
+
// ALLOWLIST: Claude Code and Cursor SessionEnd entrypoints are true final
|
|
2698
|
+
// signals. capture-opencode/
|
|
2502
2699
|
// capture-desktop (per-turn snapshot) and capture-opencode-final/
|
|
2503
2700
|
// capture-desktop-final (real session end) get an explicit false/true.
|
|
2504
2701
|
// Everything else — capture-codex (Codex's Stop fires per-turn; final:true
|
|
2505
|
-
// would close the server-side session lease every turn),
|
|
2506
|
-
//
|
|
2702
|
+
// would close the server-side session lease every turn), and any FUTURE mode
|
|
2703
|
+
// (e.g. a capture-windsurf added to resolveMode but not
|
|
2507
2704
|
// audited here) — gets undefined, i.e. legacy server behaviour. Defaulting
|
|
2508
2705
|
// unknowns to true would silently re-introduce the per-turn lease-close bug
|
|
2509
2706
|
// for every new agent.
|
|
@@ -2512,7 +2709,9 @@ function resolveCaptureFinal(argv2) {
|
|
|
2512
2709
|
return false;
|
|
2513
2710
|
if (argv2 === "capture-opencode-final" || argv2 === "capture-desktop-final")
|
|
2514
2711
|
return true;
|
|
2515
|
-
return argv2 === "capture" || argv2 ===
|
|
2712
|
+
return argv2 === "capture" || argv2 === "capture-cursor" || argv2 === undefined
|
|
2713
|
+
? true
|
|
2714
|
+
: undefined;
|
|
2516
2715
|
}
|
|
2517
2716
|
// Build a UNIQUE finish-input tmpFile name per dispatch. Both the Stop hook and
|
|
2518
2717
|
// SessionEnd dispatch capture-finish children, and the child unlinks its input
|
|
@@ -2525,7 +2724,35 @@ function resolveCaptureFinal(argv2) {
|
|
|
2525
2724
|
// negligible volume (one tiny JSON per crashed child), though note Windows
|
|
2526
2725
|
// %TEMP% is not auto-cleaned — acceptable.
|
|
2527
2726
|
function finishInputTmpFile(sessionId) {
|
|
2528
|
-
|
|
2727
|
+
const safeId = String(sessionId ?? process.pid).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
2728
|
+
return path.join(os.tmpdir(), `codiedev-finish-${safeId}-${process.pid}-${Date.now()}.json`);
|
|
2729
|
+
}
|
|
2730
|
+
// Immediate uploads must also own unique job files. A Codie/OpenCode runtime
|
|
2731
|
+
// emits idle + final captures almost simultaneously on process exit; when both
|
|
2732
|
+
// children shared codiedev-capture-<sid>.json, their uploader grandchildren
|
|
2733
|
+
// raced to read/unlink the same file and could lose the final handoff entirely.
|
|
2734
|
+
// Deferred uploads intentionally keep the stable per-session name because
|
|
2735
|
+
// overwriting it is how newer turns supersede older scheduled snapshots.
|
|
2736
|
+
function captureUploadTmpFile(sessionId, mode, pid = process.pid, now = Date.now()) {
|
|
2737
|
+
const id = String(sessionId ?? pid).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
2738
|
+
return path.join(os.tmpdir(), mode === "immediate"
|
|
2739
|
+
? `codiedev-capture-${id}-${pid}-${now}.json`
|
|
2740
|
+
: `codiedev-capture-${id}.json`);
|
|
2741
|
+
}
|
|
2742
|
+
// Best-effort cancellation for a scheduled per-turn upload when a real final
|
|
2743
|
+
// capture arrives. Deferred jobs deliberately share one canonical filename;
|
|
2744
|
+
// removing it makes every sleeping uploader lose its later claim-by-rename.
|
|
2745
|
+
// The backend's capturedAt guard covers the narrow race where a child claimed
|
|
2746
|
+
// the file immediately before this unlink.
|
|
2747
|
+
function cancelDeferredCaptureUpload(sessionId, deps = fs) {
|
|
2748
|
+
try {
|
|
2749
|
+
deps.unlinkSync(captureUploadTmpFile(sessionId, "deferred"));
|
|
2750
|
+
return true;
|
|
2751
|
+
}
|
|
2752
|
+
catch {
|
|
2753
|
+
// Missing means there was nothing pending (or another process won).
|
|
2754
|
+
return false;
|
|
2755
|
+
}
|
|
2529
2756
|
}
|
|
2530
2757
|
// Decide whether to upload immediately or defer until the throttle window expires.
|
|
2531
2758
|
// final=true → always immediate (SessionEnd: close the session now)
|
|
@@ -2690,6 +2917,14 @@ async function runCaptureUpload(jobFile, deps) {
|
|
|
2690
2917
|
const { storageId } = await deps.uploadBytes(uploadUrl, Buffer.from(transcript, "base64"), "application/json");
|
|
2691
2918
|
body = { ...body, transcriptStorageId: storageId };
|
|
2692
2919
|
delete body.transcript;
|
|
2920
|
+
// Persist the storage id before the metadata POST. Previously a failed
|
|
2921
|
+
// captureSession request retried the entire direct upload, creating one
|
|
2922
|
+
// unreferenced raw transcript blob per attempt. Once the PUT succeeds,
|
|
2923
|
+
// every retry must reuse that exact blob.
|
|
2924
|
+
job.body = body;
|
|
2925
|
+
if (deps.writeFileSync) {
|
|
2926
|
+
deps.writeFileSync(jobFile, JSON.stringify(job), { mode: 0o600 });
|
|
2927
|
+
}
|
|
2693
2928
|
}
|
|
2694
2929
|
await deps.post(job.url, body, job.token);
|
|
2695
2930
|
// success — drop a "last session captured" breadcrumb (before removing the
|
|
@@ -2842,3 +3077,9 @@ function transcriptReadSkipReason(err) {
|
|
|
2842
3077
|
return "no-transcript";
|
|
2843
3078
|
return `transcript-unreadable:${code || "unknown"}`;
|
|
2844
3079
|
}
|
|
3080
|
+
// Per-turn snapshots are backups, not session endings. Reporting their local
|
|
3081
|
+
// read/empty condition as a session skip would hide or close coverage while the
|
|
3082
|
+
// agent is still running; only final/legacy capture paths emit the skip ping.
|
|
3083
|
+
function shouldReportCaptureSkip(final) {
|
|
3084
|
+
return final !== false;
|
|
3085
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-beta.1",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|
|
@@ -12,7 +12,12 @@
|
|
|
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",
|
|
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",
|
|
17
|
+
"test:harness-binaries": "node scripts/checkHarnessBinaries.mjs",
|
|
18
|
+
"test:security-beta": "vitest run src/__tests__/security.test.ts src/__tests__/securityCommandEntitlement.test.ts src/__tests__/securityDeepScan.test.ts src/__tests__/securityDoctor.test.ts src/__tests__/securityEntitlementInstall.test.ts src/__tests__/securityFeature.test.ts src/__tests__/securityInstall.test.ts src/__tests__/securityInventory.test.ts src/__tests__/securityPilot.test.ts src/__tests__/securityPolicy.test.ts src/__tests__/securitySession.test.ts src/__tests__/codexHookTrust.test.ts src/__tests__/codieSecurityAdapterWiring.test.ts src/__tests__/limitEvent.functional.test.ts && tsc && node dist/cli.js security pilot --json",
|
|
19
|
+
"test:security-pilot": "vitest run src/__tests__/security.test.ts src/__tests__/securityInstall.test.ts src/__tests__/securityPilot.test.ts src/__tests__/codieSecurityAdapterWiring.test.ts && tsc && node dist/cli.js security pilot --json",
|
|
20
|
+
"test:opencode-attribution": "vitest run src/__tests__/repoResolver.test.ts src/__tests__/openCodeCapture.test.ts src/__tests__/openCodePluginGitRefs.test.ts src/__tests__/capturePayloadFinal.test.ts src/__tests__/captureDirectUpload.test.ts src/__tests__/resolveCaptureFinal.test.ts && tsc",
|
|
16
21
|
"test:watch": "vitest"
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
@@ -36,12 +41,18 @@
|
|
|
36
41
|
"session-capture"
|
|
37
42
|
],
|
|
38
43
|
"homepage": "https://codiedev.com",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/Signal-and-Code/codiedev.git",
|
|
47
|
+
"directory": "packages/codiedev-cli"
|
|
48
|
+
},
|
|
39
49
|
"license": "MIT",
|
|
40
50
|
"engines": {
|
|
41
51
|
"node": ">=18"
|
|
42
52
|
},
|
|
43
53
|
"publishConfig": {
|
|
44
|
-
"access": "public"
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
45
56
|
},
|
|
46
57
|
"devDependencies": {
|
|
47
58
|
"@types/node": "^20.0.0",
|
package/dist/commands/post.d.ts
DELETED