codiedev 0.9.80 → 0.10.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +12 -5
  2. package/dist/backup/cache.js +1 -5
  3. package/dist/backup/match.js +0 -6
  4. package/dist/backup/run.js +3 -35
  5. package/dist/captureExclusions.js +0 -12
  6. package/dist/captureLog.js +0 -28
  7. package/dist/captureReadiness.js +0 -58
  8. package/dist/captureSignals.js +0 -11
  9. package/dist/cli.js +11 -33
  10. package/dist/cliSession.js +3 -60
  11. package/dist/codexHookTrust.js +0 -28
  12. package/dist/codexLimitScan.js +0 -107
  13. package/dist/codie.js +64 -57
  14. package/dist/codieBrandInstall.js +0 -18
  15. package/dist/codieLauncher.js +1 -72
  16. package/dist/codieRuntime.js +27 -32
  17. package/dist/codieRuntimeInstall.js +11 -43
  18. package/dist/codieUsage.js +0 -49
  19. package/dist/commands/agentTemplates.generated.js +136 -0
  20. package/dist/commands/agents.js +1088 -0
  21. package/dist/commands/agentsSdk.generated.js +7 -0
  22. package/dist/commands/ask.js +0 -3
  23. package/dist/commands/capture.js +0 -2
  24. package/dist/commands/createTicket.js +0 -10
  25. package/dist/commands/doctor.js +2 -72
  26. package/dist/commands/evals.js +3 -9
  27. package/dist/commands/inbox.js +0 -2
  28. package/dist/commands/library.js +0 -3
  29. package/dist/commands/pack.js +0 -29
  30. package/dist/commands/ping.js +0 -2
  31. package/dist/commands/pull.js +0 -9
  32. package/dist/commands/push.js +0 -12
  33. package/dist/commands/search.js +0 -4
  34. package/dist/commands/send.js +0 -5
  35. package/dist/commands/share.js +0 -4
  36. package/dist/commands/shared.js +0 -35
  37. package/dist/commands/traces.js +0 -13
  38. package/dist/commands/tracesLabel.js +0 -8
  39. package/dist/configDir.js +1 -18
  40. package/dist/connect.js +3 -75
  41. package/dist/connectFlow.js +0 -15
  42. package/dist/copilotHooks.js +0 -67
  43. package/dist/copilotInstallation.js +0 -1
  44. package/dist/cursorHooks.js +1 -11
  45. package/dist/cursorLifecycle.js +1 -7
  46. package/dist/detection.js +0 -30
  47. package/dist/harnessDirs.js +0 -19
  48. package/dist/hook.js +43 -293
  49. package/dist/inboxFormat.js +0 -24
  50. package/dist/launchContext.js +0 -50
  51. package/dist/launcher.js +0 -33
  52. package/dist/limitEvent.js +2 -34
  53. package/dist/managedPi.js +2 -50
  54. package/dist/mcp.js +0 -48
  55. package/dist/nativeSteering.js +1 -4
  56. package/dist/packs/codie.js +0 -15
  57. package/dist/packs/harnesses.js +1 -38
  58. package/dist/packs/hookGate.js +0 -33
  59. package/dist/packs/install.js +0 -20
  60. package/dist/packs/manifest.js +1 -15
  61. package/dist/packs/paths.js +0 -8
  62. package/dist/packs/shim.js +0 -8
  63. package/dist/packs/store.js +0 -12
  64. package/dist/packs/sync.js +0 -20
  65. package/dist/piHooks.js +4 -45
  66. package/dist/piRuntime.js +0 -18
  67. package/dist/repoResolver.js +0 -19
  68. package/dist/secret.js +0 -3
  69. package/dist/selfUpdate.js +2 -77
  70. package/dist/skillMarker.js +0 -14
  71. package/dist/subagents.js +0 -18
  72. package/dist/teamSkillContext.js +0 -1
  73. package/dist/transcriptModels.js +0 -8
  74. package/dist/upgradeNudge.js +1 -30
  75. package/dist/utils.js +165 -768
  76. package/dist/version.js +0 -10
  77. package/dist/versionCheck.js +0 -33
  78. package/package.json +13 -8
  79. package/dist/backup/cache.d.ts +0 -14
  80. package/dist/backup/match.d.ts +0 -2
  81. package/dist/backup/run.d.ts +0 -25
  82. package/dist/captureExclusions.d.ts +0 -30
  83. package/dist/captureLog.d.ts +0 -20
  84. package/dist/captureReadiness.d.ts +0 -65
  85. package/dist/captureSignals.d.ts +0 -47
  86. package/dist/cli.d.ts +0 -15
  87. package/dist/cliSession.d.ts +0 -108
  88. package/dist/codexHookTrust.d.ts +0 -49
  89. package/dist/codexLimitScan.d.ts +0 -64
  90. package/dist/codie.d.ts +0 -2
  91. package/dist/codieBrandInstall.d.ts +0 -11
  92. package/dist/codieLauncher.d.ts +0 -87
  93. package/dist/codieRuntime.d.ts +0 -35
  94. package/dist/codieRuntimeInstall.d.ts +0 -56
  95. package/dist/codieUsage.d.ts +0 -64
  96. package/dist/commands/ask.d.ts +0 -1
  97. package/dist/commands/capture.d.ts +0 -14
  98. package/dist/commands/createTicket.d.ts +0 -27
  99. package/dist/commands/delete.d.ts +0 -1
  100. package/dist/commands/doctor.d.ts +0 -9
  101. package/dist/commands/evals.d.ts +0 -3
  102. package/dist/commands/inbox.d.ts +0 -3
  103. package/dist/commands/library.d.ts +0 -2
  104. package/dist/commands/note.d.ts +0 -1
  105. package/dist/commands/pack.d.ts +0 -54
  106. package/dist/commands/ping.d.ts +0 -1
  107. package/dist/commands/pull.d.ts +0 -2
  108. package/dist/commands/push.d.ts +0 -33
  109. package/dist/commands/search.d.ts +0 -2
  110. package/dist/commands/send.d.ts +0 -2
  111. package/dist/commands/share.d.ts +0 -2
  112. package/dist/commands/shared.d.ts +0 -64
  113. package/dist/commands/traces.d.ts +0 -72
  114. package/dist/commands/tracesLabel.d.ts +0 -24
  115. package/dist/configDir.d.ts +0 -10
  116. package/dist/connect.d.ts +0 -1
  117. package/dist/connectFlow.d.ts +0 -34
  118. package/dist/copilotHooks.d.ts +0 -85
  119. package/dist/copilotInstallation.d.ts +0 -10
  120. package/dist/cursorHooks.d.ts +0 -13
  121. package/dist/cursorLifecycle.d.ts +0 -2
  122. package/dist/detection.d.ts +0 -30
  123. package/dist/harnessDirs.d.ts +0 -15
  124. package/dist/hook.d.ts +0 -6
  125. package/dist/inboxFormat.d.ts +0 -38
  126. package/dist/launchContext.d.ts +0 -46
  127. package/dist/launcher.d.ts +0 -25
  128. package/dist/limitEvent.d.ts +0 -15
  129. package/dist/limitEventReporter.d.ts +0 -21
  130. package/dist/managedPi.d.ts +0 -48
  131. package/dist/mcp.d.ts +0 -12
  132. package/dist/nativeSteering.d.ts +0 -5
  133. package/dist/packs/codie.d.ts +0 -45
  134. package/dist/packs/harnesses.d.ts +0 -74
  135. package/dist/packs/hookGate.d.ts +0 -53
  136. package/dist/packs/install.d.ts +0 -46
  137. package/dist/packs/manifest.d.ts +0 -52
  138. package/dist/packs/paths.d.ts +0 -17
  139. package/dist/packs/shim.d.ts +0 -26
  140. package/dist/packs/store.d.ts +0 -42
  141. package/dist/packs/sync.d.ts +0 -40
  142. package/dist/piHooks.d.ts +0 -22
  143. package/dist/piRuntime.d.ts +0 -26
  144. package/dist/repoResolver.d.ts +0 -17
  145. package/dist/secret.d.ts +0 -1
  146. package/dist/selfUpdate.d.ts +0 -64
  147. package/dist/skillMarker.d.ts +0 -6
  148. package/dist/subagents.d.ts +0 -36
  149. package/dist/teamSkillContext.d.ts +0 -9
  150. package/dist/transcriptModels.d.ts +0 -20
  151. package/dist/upgradeNudge.d.ts +0 -24
  152. package/dist/utils.d.ts +0 -396
  153. package/dist/version.d.ts +0 -1
  154. package/dist/versionCheck.d.ts +0 -68
@@ -42,11 +42,6 @@ exports.decideSync = decideSync;
42
42
  exports.cliEntry = cliEntry;
43
43
  exports.maybeSpawnPackSync = maybeSpawnPackSync;
44
44
  exports.packNagLine = packNagLine;
45
- // Background auto-update and the "pack has updates" nudge.
46
- //
47
- // Hot-path rule: maybeSpawnPackSync and packNagLine run inside session-start
48
- // hooks and every `codiedev` command, so they only stat/read tiny local files
49
- // and, at most once an hour, spawn a detached child. No network here, ever.
50
45
  const fs = __importStar(require("fs"));
51
46
  const os = __importStar(require("os"));
52
47
  const path = __importStar(require("path"));
@@ -54,16 +49,11 @@ const child_process_1 = require("child_process");
54
49
  const paths_1 = require("./paths");
55
50
  const store_1 = require("./store");
56
51
  exports.SYNC_INTERVAL_MS = 60 * 60 * 1000;
57
- /** CI mode: a pack-scoped CI key (`cdvci_…`) or CI=true. No auto-sync, no
58
- * nudge, no server writes, no pruning — CI machines are ephemeral and CI keys
59
- * can only read packs. */
60
52
  function isPackCiMode(env = process.env) {
61
53
  const ci = (env.CI ?? "").trim().toLowerCase();
62
54
  return (env.CODIEDEV_TOKEN ?? "").trim().startsWith("cdvci_") || ci === "true" || ci === "1";
63
55
  }
64
56
  exports.NAG_INTERVAL_MS = 24 * 60 * 60 * 1000;
65
- // With nothing installed and a fresh "your team has no packs" answer, only
66
- // re-ask once a day so a newly assigned pack still arrives.
67
57
  exports.EMPTY_RECHECK_MS = 24 * 60 * 60 * 1000;
68
58
  function readStatusCache(root) {
69
59
  try {
@@ -85,7 +75,6 @@ function writeStatusCache(root, cache) {
85
75
  (0, store_1.writeFileAtomic)((0, paths_1.statusCachePath)(root), JSON.stringify(cache) + "\n");
86
76
  }
87
77
  catch {
88
- // best-effort
89
78
  }
90
79
  }
91
80
  function mtime(file) {
@@ -97,7 +86,6 @@ function mtime(file) {
97
86
  }
98
87
  }
99
88
  function registryNonEmpty(root) {
100
- // installed.json is tiny; `{"packs":{}}` means nothing is installed.
101
89
  try {
102
90
  const parsed = JSON.parse(fs.readFileSync((0, paths_1.registryPath)(root), "utf8"));
103
91
  return Boolean(parsed?.packs && Object.keys(parsed.packs).length > 0);
@@ -110,7 +98,6 @@ function autosyncDisabled(env) {
110
98
  const v = (env.CODIEDEV_PACK_AUTOSYNC ?? "").trim().toLowerCase();
111
99
  return v === "0" || v === "false" || v === "off" || v === "no";
112
100
  }
113
- /** Pure decision for the hourly background sync. */
114
101
  function decideSync(root, env, now) {
115
102
  const last = mtime((0, paths_1.lastSyncPath)(root));
116
103
  if (last !== null && now - last < exports.SYNC_INTERVAL_MS && last <= now)
@@ -120,15 +107,11 @@ function decideSync(root, env, now) {
120
107
  if (cache && cache.available === 0 && now - cache.checkedAt < exports.EMPTY_RECHECK_MS)
121
108
  return "no-packs";
122
109
  }
123
- // Opted out of auto-install: still refresh the cached status (read-only,
124
- // no install, no pruning) so the outdated nudge can tell them.
125
110
  return autosyncDisabled(env) ? "spawn-check" : "spawn-install";
126
111
  }
127
112
  function cliEntry() {
128
- // dist/packs/sync.js → dist/cli.js
129
113
  return path.join(__dirname, "..", "cli.js");
130
114
  }
131
- /** Called from session-start hooks. Never throws, never waits. */
132
115
  function maybeSpawnPackSync(options = {}) {
133
116
  try {
134
117
  if (options.connected === false)
@@ -141,7 +124,6 @@ function maybeSpawnPackSync(options = {}) {
141
124
  const decision = decideSync(root, env, now);
142
125
  if (decision !== "spawn-install" && decision !== "spawn-check")
143
126
  return decision;
144
- // Stamp first so concurrent session starts don't each spawn a sync.
145
127
  fs.mkdirSync(root, { recursive: true });
146
128
  fs.writeFileSync((0, paths_1.lastSyncPath)(root), String(now));
147
129
  const time = new Date(now);
@@ -162,8 +144,6 @@ function maybeSpawnPackSync(options = {}) {
162
144
  return "error";
163
145
  }
164
146
  }
165
- /** One-line nudge when the cached status shows an outdated pack, at most once
166
- * a day. Returns null (and records nothing) when there's nothing to say. */
167
147
  function packNagLine(options = {}) {
168
148
  try {
169
149
  const env = options.env ?? process.env;
package/dist/piHooks.js CHANGED
@@ -39,21 +39,9 @@ exports.isPiTranscriptEmpty = isPiTranscriptEmpty;
39
39
  exports.piLaunchFromArgv = piLaunchFromArgv;
40
40
  exports.piExtensionPath = piExtensionPath;
41
41
  exports.inspectPiExtension = inspectPiExtension;
42
- // pi (@mariozechner/pi-coding-agent). pi has no hook config; it loads
43
- // TypeScript extensions from <agentDir>/extensions/, so Codiedev installs one
44
- // there (see installPiExtension in utils.ts) that forwards pi's events to the
45
- // same hook subcommands every other harness uses.
46
- //
47
- // pi writes its own session JSONL (<agentDir>/sessions/--<cwd>--/<ts>_<id>.jsonl)
48
- // and the extension passes that path as transcript_path. This file turns it
49
- // into the Claude Code shape the shared capture pipeline parses.
50
- //
51
- // Imports only node built-ins and harnessDirs (no utils.ts) so hook.ts,
52
- // utils.ts and securityInstall.ts can all import it without a cycle.
53
42
  const fs = __importStar(require("fs"));
54
43
  const path = __importStar(require("path"));
55
44
  const harnessDirs_1 = require("./harnessDirs");
56
- /** customType of the context message the Codiedev extension injects. */
57
45
  exports.PI_CONTEXT_CUSTOM_TYPE = "codiedev";
58
46
  const SKILL_EXPANSION = /^<skill name="([^"]{1,200})" location="[^"]*">/;
59
47
  const num = (n) => (typeof n === "number" && Number.isFinite(n) ? Math.max(0, n) : 0);
@@ -74,36 +62,20 @@ function userBlocks(content) {
74
62
  return [];
75
63
  return content.flatMap((block) => block?.type === "text" && typeof block.text === "string"
76
64
  ? [{ type: "text", text: block.text }]
77
- // Image bytes never leave the machine; keep that one was attached.
78
65
  : block?.type === "image" ? [{ type: "text", text: "[image]" }] : []);
79
66
  }
80
- /** `/x/skills/review/SKILL.md` → "review". pi's agent loads a listed skill by
81
- * reading its SKILL.md; that read IS the invocation (docs/skills.md). */
82
67
  function skillFromPath(value) {
83
68
  if (typeof value !== "string")
84
69
  return null;
85
70
  const m = value.replace(/\\/g, "/").match(/\/([^/]+)\/SKILL\.md$/);
86
71
  return m ? m[1] : null;
87
72
  }
88
- /** A provider error's HTTP status, when pi kept it at the front ("429 …"). */
89
73
  function errorCode(message) {
90
74
  return message.match(/^\s*(\d{3})\b/)?.[1] ?? "";
91
75
  }
92
- /**
93
- * pi session JSONL → Claude Code-shaped JSONL.
94
- *
95
- * Every message entry is kept in file order, abandoned /tree branches
96
- * included: they were real model calls and real tokens. Usage is pi's own
97
- * per-call record (`input` is already the uncached part). Skill runs, user
98
- * interrupts and provider errors become the records the shared parsers read:
99
- * `pi.skill_invoked`, Claude's interrupt marker and a native `agent.error`.
100
- */
101
76
  function normalizePiTranscript(content) {
102
77
  const out = [];
103
78
  const skillReads = new Map();
104
- // pi retries a failed provider call on its own (and fires agent_end after
105
- // each try). Every error in one retry streak carries the streak's first
106
- // time, so limit events, which dedupe on time, count the hit once.
107
79
  let errorStreakAt;
108
80
  const push = (record) => out.push(JSON.stringify(record));
109
81
  for (const line of content.split("\n")) {
@@ -114,7 +86,7 @@ function normalizePiTranscript(content) {
114
86
  entry = JSON.parse(line);
115
87
  }
116
88
  catch {
117
- continue; // pi may be mid-append
89
+ continue;
118
90
  }
119
91
  if (!entry || typeof entry !== "object")
120
92
  continue;
@@ -150,7 +122,6 @@ function normalizePiTranscript(content) {
150
122
  }
151
123
  const usage = message.usage ?? {};
152
124
  const tokens = num(usage.input) + num(usage.output) + num(usage.cacheRead) + num(usage.cacheWrite);
153
- // A failed call records all-zero usage; it was not a billed request.
154
125
  const measured = tokens > 0 || message.stopReason !== "error";
155
126
  push({
156
127
  type: "assistant",
@@ -175,8 +146,6 @@ function normalizePiTranscript(content) {
175
146
  push({ type: "user", timestamp, message: { role: "user", content: [{ type: "text", text: "[Request interrupted by user]" }] } });
176
147
  }
177
148
  if (message.stopReason === "error" && typeof message.errorMessage === "string") {
178
- // The call's own time, not the entry's write time, so interim and
179
- // final captures agree.
180
149
  errorStreakAt ??= typeof message.timestamp === "number" ? new Date(message.timestamp).toISOString() : timestamp;
181
150
  push({
182
151
  type: "agent.error",
@@ -202,18 +171,15 @@ function normalizePiTranscript(content) {
202
171
  });
203
172
  continue;
204
173
  }
205
- // `!cmd` typed by the user: shown to the model like any user turn.
206
174
  if (message.role === "bashExecution" && typeof message.command === "string") {
207
175
  push({ type: "user", uuid, timestamp, message: { role: "user", content: [{ type: "text", text: `<bash-input>${message.command}</bash-input>` }] } });
208
176
  }
209
177
  }
210
178
  return out.join("\n") + (out.length ? "\n" : "");
211
179
  }
212
- /** pi writes the session header at startup; no message yet means nothing to capture. */
213
180
  function isPiTranscriptEmpty(content) {
214
181
  return !content.split("\n").some((line) => line.includes('"type":"message"'));
215
182
  }
216
- /** How pi was launched, from its own argv (the extension runs in pi's process). */
217
183
  function piLaunchFromArgv(argv) {
218
184
  for (let i = 0; i < argv.length; i++) {
219
185
  const a = argv[i];
@@ -232,8 +198,7 @@ function piLaunchFromArgv(argv) {
232
198
  function piExtensionPath(agentDir = (0, harnessDirs_1.piAgentDir)()) {
233
199
  return path.join(agentDir, "extensions", "codiedev.ts");
234
200
  }
235
- /** What `doctor` and the security install check report for pi. */
236
- function inspectPiExtension(file = piExtensionPath(), security = false) {
201
+ function inspectPiExtension(file = piExtensionPath()) {
237
202
  if (!fs.existsSync(file))
238
203
  return { status: "not-installed", detail: `${file} not found` };
239
204
  let source;
@@ -243,15 +208,9 @@ function inspectPiExtension(file = piExtensionPath(), security = false) {
243
208
  catch {
244
209
  return { status: "fail", detail: `${file} unreadable` };
245
210
  }
246
- const expected = security
247
- ? ["discover-pi", "security-pre-pi", "security-post-pi"]
248
- : ["session-start-pi", "backup-pi", "capture-pi-final"];
249
- // Quoted: "backup-pi" must not match inside some longer subcommand.
211
+ const expected = ["session-start-pi", "backup-pi", "capture-pi-final"];
250
212
  const missing = expected.filter((subcommand) => !source.includes(JSON.stringify(subcommand)));
251
213
  if (missing.length)
252
214
  return { status: "fail", detail: `missing ${missing.join(", ")} — re-run \`codiedev connect\`` };
253
- if (security && source.includes("const AGENT_SECURITY_ENABLED = false;")) {
254
- return { status: "fail", detail: "installed with agent security off — re-run `codiedev connect`" };
255
- }
256
- return { status: "pass", detail: security ? "prompt guard and tool telemetry installed" : `session start, capture and backup events (${(0, harnessDirs_1.displayPath)(file)})` };
215
+ return { status: "pass", detail: `session start, capture and backup events (${(0, harnessDirs_1.displayPath)(file)})` };
257
216
  }
package/dist/piRuntime.js CHANGED
@@ -37,13 +37,6 @@ exports.PI_RUNTIME_SPEC = exports.PI_VERSION = exports.PI_PACKAGE = void 0;
37
37
  exports.piRuntimeDir = piRuntimeDir;
38
38
  exports.installedPiEntry = installedPiEntry;
39
39
  exports.ensurePiRuntime = ensurePiRuntime;
40
- // pi for `codie --pi`, installed on first use like the Codie engine
41
- // (codieRuntimeInstall.ts): developers never run a second npm install, and
42
- // every machine runs the version Codiedev tested its capture against.
43
- //
44
- // pi is plain JavaScript, so it runs on the same Node as `codie` from its
45
- // package entry: no PATH lookup, no Windows .cmd shim, and no dependency on
46
- // whatever `pi` (if any) the developer installed globally.
47
40
  const fs = __importStar(require("fs"));
48
41
  const path = __importStar(require("path"));
49
42
  const child_process_1 = require("child_process");
@@ -57,7 +50,6 @@ function piRuntimeDir(stateDir) {
57
50
  function packageDir(installDir) {
58
51
  return path.join(installDir, "node_modules", ...exports.PI_PACKAGE.split("/"));
59
52
  }
60
- /** The installed pi's CLI entry, when it is the pinned version. */
61
53
  function installedPiEntry(installDir, readFile = (f) => fs.readFileSync(f, "utf8")) {
62
54
  try {
63
55
  const pkg = JSON.parse(readFile(path.join(packageDir(installDir), "package.json")));
@@ -70,15 +62,8 @@ function installedPiEntry(installDir, readFile = (f) => fs.readFileSync(f, "utf8
70
62
  return null;
71
63
  }
72
64
  }
73
- // One retry rides out a dropped connection or registry blip; a second
74
- // failure is a real problem (offline, blocked registry) and is reported.
75
65
  const INSTALL_ATTEMPTS = 2;
76
66
  const INSTALL_RETRY_DELAY_MS = 5_000;
77
- /**
78
- * The pi CLI entry to run, installing the pinned version first if it is
79
- * missing or a different version. Staged then renamed, so an interrupted
80
- * download never leaves a half-installed pi that looks ready.
81
- */
82
67
  function ensurePiRuntime(deps) {
83
68
  const installDir = piRuntimeDir(deps.stateDir);
84
69
  const exists = deps.exists ?? fs.existsSync;
@@ -109,7 +94,6 @@ function ensurePiRuntime(deps) {
109
94
  const result = run((0, codieRuntimeInstall_1.buildRuntimeInstallArgs)(staging, exports.PI_RUNTIME_SPEC));
110
95
  if (result.status !== 0)
111
96
  throw new Error(result.stderr?.trim() || `npm exited with ${result.status}`);
112
- // Trust the artifact, not the exit code.
113
97
  const staged = installedPiEntry(staging, readFile);
114
98
  if (!staged || !exists(staged)) {
115
99
  throw new Error(`the install did not produce pi. Check that ${exports.PI_RUNTIME_SPEC} is available from your npm registry.`);
@@ -125,8 +109,6 @@ function ensurePiRuntime(deps) {
125
109
  log(`pi install failed (${message.split("\n")[0]}); retrying.`);
126
110
  }
127
111
  finally {
128
- // No-op after a successful promote; otherwise it keeps a ~200MB staging
129
- // dir from being orphaned.
130
112
  remove(staging);
131
113
  }
132
114
  sleep(INSTALL_RETRY_DELAY_MS);
@@ -1,12 +1,4 @@
1
1
  "use strict";
2
- // Pure resolver — given a captured remoteUrl, an optional client-cached
3
- // repoId, and the company's current repo list, decide which repoId (if any)
4
- // the session belongs to. Used both inline by the CLI hook and (mirrored
5
- // in convex/repoResolver.ts) by the /api/captureSession endpoint so that
6
- // repos added after the user's last `connect` still get matched.
7
- //
8
- // "unmatched" is intentional — the backend will accept the session anyway
9
- // and surface it for triage rather than silently dropping it.
10
2
  Object.defineProperty(exports, "__esModule", { value: true });
11
3
  exports.resolveRepoForCapture = resolveRepoForCapture;
12
4
  function parseRemoteUrl(url) {
@@ -17,12 +9,6 @@ function parseRemoteUrl(url) {
17
9
  if (https) {
18
10
  return { host: https[1].toLowerCase(), ownerName: https[2].toLowerCase() };
19
11
  }
20
- // Codie strips SSH userinfo before putting a remote in the gateway header,
21
- // so `git@gitlab.com:owner/repo.git` arrives here as
22
- // `gitlab.com:owner/repo.git`. Accept both forms; otherwise every SSH-cloned
23
- // Harness checkout loses its repoId even though transcript capture can
24
- // resolve the same raw remote correctly. Check this after URL forms so the
25
- // colon in `https://` is never mistaken for an SCP separator.
26
12
  const scp = s.match(/^(?:[^@\s]+@)?([^:/\s]+):([^/]+\/.+)$/);
27
13
  if (scp) {
28
14
  return { host: scp[1].toLowerCase(), ownerName: scp[2].toLowerCase() };
@@ -31,11 +17,6 @@ function parseRemoteUrl(url) {
31
17
  }
32
18
  function resolveRepoForCapture(input) {
33
19
  const { remoteUrl, providedRepoId, companyRepos } = input;
34
- // The remoteUrl is the ground truth of where the work happened — resolve it
35
- // FIRST. A buggy client can send a wrong-but-valid providedRepoId (the Sonifi
36
- // case: client matchRepo stamped every session with the org's first repo), so
37
- // we must not blindly trust it over the URL. Keep providedRepoId only when it
38
- // AGREES with the URL match, or when the URL can't be resolved at all.
39
20
  const parsed = remoteUrl ? parseRemoteUrl(remoteUrl) : null;
40
21
  const urlMatch = parsed
41
22
  ? companyRepos.find((r) => `${r.owner}/${r.name}`.toLowerCase() === parsed.ownerName)
package/dist/secret.js CHANGED
@@ -1,7 +1,4 @@
1
1
  "use strict";
2
- // Display helpers for credential-shaped strings. Use anywhere a token /
3
- // secret might end up in user-visible output (logs, success lines, error
4
- // messages) so a stray `console.log` can't leak the full value.
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.maskToken = maskToken;
7
4
  const PREFIX_LEN = 12;
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- // Background self-update for the hook path.
3
- //
4
- // Onboarded developers never run `connect`/`doctor`/`codie` again — only the
5
- // harness hooks fire — so the interactive nudges in upgradeNudge.ts never
6
- // reach them. This module lets any hook invocation spawn a detached child
7
- // (at most once per day) that upgrades the exact install the hook is running
8
- // from. Harness configs bake the absolute path to dist/hook.js, so the next
9
- // hook call picks up the new code with no config change.
10
- //
11
- // Split like versionCheck.ts: `planSelfUpdate` is pure and unit-tested; the IO
12
- // (state file, lock, registry fetch, package-manager spawn) is below it.
13
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
3
  if (k2 === undefined) k2 = k;
15
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -63,16 +52,10 @@ const captureLog_1 = require("./captureLog");
63
52
  const launcher_1 = require("./launcher");
64
53
  const utils_1 = require("./utils");
65
54
  const PKG = "codiedev";
66
- const DAY_MS = 24 * 60 * 60 * 1000;
55
+ const SELF_UPDATE_INTERVAL_MS = 2 * 60 * 60 * 1000;
67
56
  const LOCK_STALE_MS = 10 * 60 * 1000;
68
57
  const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
69
58
  const FETCH_TIMEOUT_MS = 5_000;
70
- /**
71
- * Decide which package manager owns this install, from where hook.js really
72
- * lives. We only ever upgrade the install we are running from: running
73
- * whatever `npm` is first on PATH would, under nvm/Homebrew mixes, upgrade a
74
- * different global tree than the one the harness config points at.
75
- */
76
59
  function planSelfUpdate(deps) {
77
60
  const { env } = deps;
78
61
  if (env.CODIEDEV_NO_UPDATE_CHECK || env.CODIEDEV_NO_AUTO_UPDATE) {
@@ -89,30 +72,21 @@ function planSelfUpdate(deps) {
89
72
  const pkgDir = path.dirname(path.dirname(deps.hookScriptReal));
90
73
  if (!deps.isWritable(pkgDir))
91
74
  return { kind: "skip", reason: "readonly" };
92
- // pnpm puts every global install in a fresh hashed dir
93
- // (global/v11/<hash>/node_modules/codiedev). That is only safe to upgrade
94
- // when the hook runs through the stable launcher, which runSelfUpdate
95
- // re-points at the new dir; a direct-path config would stay on the old copy.
96
75
  if (/\/\.pnpm\/|\/pnpm\/global\/|\/global\/v\d+\/[^/]+\/node_modules\/codiedev\/|\/store\/v\d+\/links\//.test(p)) {
97
76
  return deps.viaLauncher ? { kind: "pnpm" } : { kind: "skip", reason: "pnpm-direct" };
98
77
  }
99
- // $BUN_INSTALL/install/global — ~/.bun by default, but BUN_INSTALL is often
100
- // customized, so match the layout rather than the directory name.
101
78
  if (/\/install\/global\/node_modules\/codiedev\/dist\/hook\.js$/.test(p))
102
79
  return { kind: "bun" };
103
- // npm global: <prefix>/lib/node_modules/codiedev/dist/hook.js on unix,
104
- // <prefix>/node_modules/codiedev/dist/hook.js on Windows.
105
80
  const unix = p.match(/^(.*)\/lib\/node_modules\/codiedev\/dist\/hook\.js$/);
106
81
  if (unix && deps.platform !== "win32")
107
82
  return { kind: "npm", prefix: unix[1] };
108
83
  const win = p.match(/^(.*)\/node_modules\/codiedev\/dist\/hook\.js$/);
109
84
  if (win && deps.platform === "win32")
110
85
  return { kind: "npm", prefix: win[1] };
111
- // A repo checkout, a project-local install, or a layout we don't know.
112
86
  return { kind: "skip", reason: "unmanaged" };
113
87
  }
114
88
  function isSelfUpdateDue(state, nowMs) {
115
- return !state || nowMs - state.lastAttemptAt > DAY_MS;
89
+ return !state || nowMs - state.lastAttemptAt > SELF_UPDATE_INTERVAL_MS;
116
90
  }
117
91
  function statePath() {
118
92
  return path.join((0, configDir_1.codiedevStateDir)(), ".self-update.json");
@@ -139,11 +113,6 @@ function writeState(state) {
139
113
  return false;
140
114
  }
141
115
  }
142
- /**
143
- * Called on every hook invocation, so the not-due path is one small file read.
144
- * Stamps the attempt before spawning so a burst of parallel hooks spawns one
145
- * child, not one each; the child's lock covers whatever still slips through.
146
- */
147
116
  function maybeSpawnSelfUpdate(hookEntry) {
148
117
  try {
149
118
  if (!hookEntry || process.env.CODIEDEV_NO_AUTO_UPDATE || process.env.CODIEDEV_NO_UPDATE_CHECK)
@@ -156,24 +125,16 @@ function maybeSpawnSelfUpdate(hookEntry) {
156
125
  const child = (0, child_process_1.spawn)(process.execPath, [hookEntry, "self-update"], {
157
126
  detached: true,
158
127
  stdio: "ignore",
159
- // detached on Windows opens a console window unless hidden.
160
128
  windowsHide: true,
161
129
  });
162
130
  child.unref();
163
131
  }
164
132
  catch {
165
- // Best-effort; a failed spawn retries tomorrow.
166
133
  }
167
134
  }
168
135
  function instructionsVersionPath() {
169
136
  return path.join((0, configDir_1.codiedevStateDir)(), ".instructions-version");
170
137
  }
171
- /**
172
- * First hook call on a new CLI version (self-updated or installed by hand)
173
- * re-renders the codiedev block in the harness instruction files, so they
174
- * stop listing commands this version no longer has. The steady-state cost is
175
- * one small file read. Honors the same opt-outs as the self-update.
176
- */
177
138
  function maybeRefreshInstructions(refresh = utils_1.refreshInstalledInstructions) {
178
139
  if (process.env.CODIEDEV_NO_AUTO_UPDATE || process.env.CODIEDEV_NO_UPDATE_CHECK)
179
140
  return;
@@ -184,7 +145,6 @@ function maybeRefreshInstructions(refresh = utils_1.refreshInstalledInstructions
184
145
  return;
185
146
  }
186
147
  catch {
187
- // No marker yet: first run on a version that ships this check.
188
148
  }
189
149
  const refreshed = refresh();
190
150
  fs.mkdirSync((0, configDir_1.codiedevStateDir)(), { recursive: true });
@@ -199,7 +159,6 @@ function maybeRefreshInstructions(refresh = utils_1.refreshInstalledInstructions
199
159
  }
200
160
  }
201
161
  catch {
202
- // Best-effort; retried on the next hook call.
203
162
  }
204
163
  }
205
164
  function acquireLock() {
@@ -217,7 +176,6 @@ function acquireLock() {
217
176
  }
218
177
  }
219
178
  catch {
220
- // Lost the race to another child.
221
179
  }
222
180
  return false;
223
181
  }
@@ -225,8 +183,6 @@ function acquireLock() {
225
183
  function detectContainer(env) {
226
184
  if (env.KUBERNETES_SERVICE_HOST)
227
185
  return true;
228
- // /run/.containerenv is podman's marker (e.g. Sonifi's dev containers);
229
- // cgroup v2 hosts report a bare "0::/" there, so the cgroup probe misses it.
230
186
  if (fs.existsSync("/.dockerenv") || fs.existsSync("/run/.containerenv"))
231
187
  return true;
232
188
  try {
@@ -262,18 +218,11 @@ async function fetchLatest(env) {
262
218
  }
263
219
  function installCommand(plan, version, env) {
264
220
  const spec = `${PKG}@${version}`;
265
- // Pass an env-level registry override explicitly so the install always
266
- // hits the registry fetchLatest just read from (not every manager honors
267
- // npm_config_registry for global installs).
268
221
  const configured = env.npm_config_registry || env.NPM_CONFIG_REGISTRY;
269
222
  const registry = configured ? ["--registry", registryBase(env)] : [];
270
223
  switch (plan.kind) {
271
224
  case "npm": {
272
225
  const args = ["i", "-g", "--prefix", plan.prefix, "--no-fund", "--no-audit", ...registry, spec];
273
- // Run the npm that ships beside the node running us (it owns this
274
- // prefix even when PATH resolves a different node), through its JS
275
- // entry. That avoids spawning npm.cmd, which on Windows needs a shell
276
- // and would split a prefix like C:\Users\First Last\… on the space.
277
226
  const execDir = path.dirname(process.execPath);
278
227
  const npmCli = [
279
228
  path.join(execDir, "node_modules", "npm", "bin", "npm-cli.js"),
@@ -284,7 +233,6 @@ function installCommand(plan, version, env) {
284
233
  return { cmd: process.platform === "win32" ? "npm.cmd" : "npm", args, shell: process.platform === "win32" };
285
234
  }
286
235
  case "pnpm":
287
- // pnpm on Windows is usually a .cmd shim; these args carry no paths.
288
236
  return { cmd: "pnpm", args: ["add", "-g", ...registry, spec], shell: process.platform === "win32" };
289
237
  case "bun":
290
238
  return { cmd: "bun", args: ["add", "-g", ...registry, spec] };
@@ -292,14 +240,7 @@ function installCommand(plan, version, env) {
292
240
  return null;
293
241
  }
294
242
  }
295
- // package.json "bin" names. npm writes a .cmd, a .ps1 and a bare shim for each.
296
243
  exports.BIN_NAMES = ["codiedev", "codiedev-hook", "codie"];
297
- /**
298
- * The .ps1 shims missing from a Windows npm prefix. The IT kit deletes them so
299
- * PowerShell falls back to the .cmd shims, which run under the Restricted
300
- * execution policy Windows client machines default to; `npm i -g` puts them
301
- * back, so an update must delete again exactly the ones that were gone.
302
- */
303
244
  function missingPs1Shims(prefix, exists = fs.existsSync) {
304
245
  return exports.BIN_NAMES.map((name) => path.join(prefix, `${name}.ps1`)).filter((p) => !exists(p));
305
246
  }
@@ -312,12 +253,6 @@ function readVersionAt(hookScript) {
312
253
  return null;
313
254
  }
314
255
  }
315
- /**
316
- * Where the just-installed copy lives. npm/bun reinstall in place. pnpm's
317
- * `root -g` is <root>/node_modules up to v10 but the bare global/v11 parent
318
- * from v11 on, with each install in a hashed child — so check both shapes and
319
- * take the copy that is actually at the version we just installed.
320
- */
321
256
  function findInstalledHookScript(plan, hookEntry, version, pnpmRoot = pnpmGlobalRoot) {
322
257
  if (plan.kind !== "pnpm")
323
258
  return readVersionAt(hookEntry) === version ? hookEntry : null;
@@ -331,7 +266,6 @@ function findInstalledHookScript(plan, hookEntry, version, pnpmRoot = pnpmGlobal
331
266
  }
332
267
  }
333
268
  catch {
334
- // root vanished between calls
335
269
  }
336
270
  for (const candidate of candidates) {
337
271
  if (readVersionAt(candidate) !== version)
@@ -340,7 +274,6 @@ function findInstalledHookScript(plan, hookEntry, version, pnpmRoot = pnpmGlobal
340
274
  return fs.realpathSync(candidate);
341
275
  }
342
276
  catch {
343
- // dangling link; keep looking
344
277
  }
345
278
  }
346
279
  return null;
@@ -355,7 +288,6 @@ function pnpmGlobalRoot() {
355
288
  const dir = root.status === 0 ? root.stdout.trim() : "";
356
289
  return dir || null;
357
290
  }
358
- /** Body of the detached `codiedev-hook self-update` child. Never throws. */
359
291
  async function runSelfUpdate(hookEntry) {
360
292
  const env = process.env;
361
293
  const now = Date.now();
@@ -396,8 +328,6 @@ async function runSelfUpdate(hookEntry) {
396
328
  if (plan.kind === "skip")
397
329
  return finish(`skip:${plan.reason}`);
398
330
  if (!acquireLock()) {
399
- // Another child is mid-install and owns the state file; overwriting it
400
- // here would report "skip:locked" while that install is still running.
401
331
  (0, captureLog_1.appendCaptureLog)({ stage: "self-update", sessionId: undefined, outcome: "ok", detail: "result=skip:locked" });
402
332
  return { lastAttemptAt: now, lastResult: "skip:locked" };
403
333
  }
@@ -426,12 +356,8 @@ async function runSelfUpdate(hookEntry) {
426
356
  fs.rmSync(shim, { force: true });
427
357
  }
428
358
  catch {
429
- // Best-effort: a leftover .ps1 only matters under a Restricted policy.
430
359
  }
431
360
  }
432
- // Re-point the launcher. npm/bun reinstall in place; pnpm lands in a new
433
- // hashed dir. Only switch once the new copy is on disk at the new version,
434
- // so a half-finished install can never strand the hooks.
435
361
  const installed = findInstalledHookScript(plan, hookScriptReal, latest);
436
362
  if (!installed) {
437
363
  return finish("error:not-found-after-install", { toVersion: latest });
@@ -445,7 +371,6 @@ async function runSelfUpdate(hookEntry) {
445
371
  fs.rmSync(lockPath(), { recursive: true, force: true });
446
372
  }
447
373
  catch {
448
- // A stale lock self-expires after LOCK_STALE_MS.
449
374
  }
450
375
  }
451
376
  }
@@ -1,33 +1,19 @@
1
1
  "use strict";
2
- // Skill identity marker. Two authors can each publish a skill with the same
3
- // name ("/ticket"); a session's transcript only records the name it ran. A
4
- // company skill written to disk therefore carries the artifact it came from:
5
- //
6
- // <!-- codiedev:artifact=<artifact id> -->
7
- //
8
- // appended after the body (frontmatter untouched, invisible when rendered).
9
- // When the harness loads the skill, the marker lands in the transcript and
10
- // parseCaptureSignals reports it as signals.skillInvocationArtifacts, so the
11
- // portal attributes the run to the exact skill. `push` strips it, so a
12
- // re-pushed copy never claims someone else's artifact id.
13
2
  Object.defineProperty(exports, "__esModule", { value: true });
14
3
  exports.looksLikeSkill = looksLikeSkill;
15
4
  exports.stripSkillMarker = stripSkillMarker;
16
5
  exports.stampSkillMarker = stampSkillMarker;
17
6
  const ID = /^[a-z0-9]{16,64}$/i;
18
7
  const LINE = /^[ \t]*<!--\s*codiedev:artifact=[a-z0-9]+\s*-->[ \t]*(?:\r?\n|$)/gim;
19
- /** Is this a SKILL.md-style file (frontmatter with name + description)? */
20
8
  function looksLikeSkill(markdown) {
21
9
  const fm = markdown.match(/^?---\r?\n([\s\S]*?)\r?\n---/);
22
10
  if (!fm)
23
11
  return false;
24
12
  return /^name\s*:/im.test(fm[1]) && /^description\s*:/im.test(fm[1]);
25
13
  }
26
- /** Remove every marker line (idempotent). */
27
14
  function stripSkillMarker(markdown) {
28
15
  return markdown.replace(LINE, "");
29
16
  }
30
- /** Stamp (or re-stamp) the marker. Leaves the text alone for a bad id. */
31
17
  function stampSkillMarker(markdown, artifactId) {
32
18
  if (!artifactId || !ID.test(artifactId))
33
19
  return markdown;
package/dist/subagents.js CHANGED
@@ -12,27 +12,12 @@ const node_fs_1 = __importDefault(require("node:fs"));
12
12
  const node_path_1 = __importDefault(require("node:path"));
13
13
  const captureSignals_1 = require("./captureSignals");
14
14
  const transcriptModels_1 = require("./transcriptModels");
15
- /**
16
- * Claude Code writes each subagent's transcript next to its parent's:
17
- *
18
- * ~/.claude/projects/<project>/<sessionId>.jsonl (parent)
19
- * ~/.claude/projects/<project>/<sessionId>/subagents/agent-*.jsonl
20
- *
21
- * Lines there carry `isSidechain: true` and the parent's sessionId. They are
22
- * part of the parent session's work, not sessions of their own: their skill
23
- * runs, tool calls and token usage fold into the parent's signals and usage,
24
- * and they are never uploaded separately. Other harnesses (Codex, Codie,
25
- * Cursor) keep sub-agents inside the one transcript or don't have them, so
26
- * this is Claude Code only.
27
- */
28
15
  exports.SUBAGENT_LIMITS = { files: 20, bytes: 20 * 1024 * 1024 };
29
16
  function subagentDir(transcriptPath, sessionId) {
30
17
  const dir = node_path_1.default.dirname(transcriptPath);
31
18
  const id = sessionId || node_path_1.default.basename(transcriptPath).replace(/\.jsonl$/i, "");
32
19
  return node_path_1.default.join(dir, id, "subagents");
33
20
  }
34
- /** Read the session's subagent transcripts in full, up to 20 files / 20 MB
35
- * in total (oldest first); anything past that is counted as truncated. */
36
21
  function readSubagentTranscripts(transcriptPath, sessionId, limits = exports.SUBAGENT_LIMITS) {
37
22
  const dir = subagentDir(transcriptPath, sessionId);
38
23
  let names;
@@ -73,8 +58,6 @@ function readSubagentTranscripts(transcriptPath, sessionId, limits = exports.SUB
73
58
  }
74
59
  return { contents, count: files.length, truncated };
75
60
  }
76
- /** Parent signals plus every subagent's skill runs, pulls and tool calls.
77
- * Launch values, timing and record counts stay the parent's own. */
78
61
  function foldSubagentSignals(parent, subs, source) {
79
62
  if (subs.count === 0)
80
63
  return parent;
@@ -121,7 +104,6 @@ function foldSubagentSignals(parent, subs, source) {
121
104
  truncated,
122
105
  };
123
106
  }
124
- /** Parent model usage plus each subagent's, summed per model. */
125
107
  function foldSubagentModelUsage(parent, subs, source) {
126
108
  if (subs.contents.length === 0)
127
109
  return parent;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TEAM_SKILL_CONTEXT = void 0;
4
4
  exports.teamSkillSessionOutput = teamSkillSessionOutput;
5
- /** Session-start context uses each client's supported output contract. */
6
5
  exports.TEAM_SKILL_CONTEXT = `This machine is connected to Codiedev, the team's artifact and skill library.
7
6
  Before a substantial coding task, run codiedev search "<brief task description>" and reuse a relevant team skill by running codiedev pull <exact-key> and following its instructions. If no skill fits, continue the task.
8
7
  Use codiedev push <file.md> to share an artifact when asked, codiedev ask "<question>" for team knowledge, and codiedev inbox to check messages. Use codiedev --help for other commands. If codiedev is unavailable, use npx codiedev@latest.`;