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
package/README.md CHANGED
@@ -5,14 +5,20 @@ Connect Claude Code, Codex, Cursor, or OpenCode to Codiedev for team session cap
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- npx codiedev connect
8
+ npm install -g codiedev@latest
9
+ codiedev connect
10
+ codiedev doctor
9
11
  ```
10
12
 
13
+ If global npm installs are blocked, use `npx codiedev@latest connect` and
14
+ `npx codiedev@latest doctor` instead. The agent instructions include the same
15
+ fallback for other commands.
16
+
11
17
  The CLI will prompt you for an API token (get one at https://codiedev.com/portal/integrations/claude-code), then install a capture hook into whichever agent CLIs are present on your machine:
12
18
 
13
19
  - **Claude Code** — `SessionEnd` hook in `~/.claude/settings.json`
14
20
  - **Codex** — `Stop` hook in `~/.codex/hooks.json`
15
- - **Cursor** — native session, backup, and capture hooks in `~/.cursor/hooks.json`
21
+ - **Cursor** — native session, subagent/tool observation, backup, and capture hooks in `~/.cursor/hooks.json`
16
22
  - **OpenCode / Codie** — plugin in `~/.config/opencode/plugins/codiedev.js`
17
23
 
18
24
  Installed clients are wired automatically. The OpenCode adapter is staged even
@@ -51,9 +57,10 @@ Codex doesn't currently expose a `SessionEnd` event, so the hook runs on `Stop`
51
57
 
52
58
  ## Cursor note
53
59
 
54
- Cursor uses its native hook events for session start/end and stop-time backup.
55
- Cursor's `beforeSubmitPrompt` currently cannot inject dynamic context, so task-
56
- specific discovery remains unavailable there. Cursor sessions keep their source
60
+ Cursor uses its native hook events for session start/end, tool and subagent
61
+ observation, and stop-time backup. Cursor's `beforeSubmitPrompt` cannot inject
62
+ dynamic context today, so task-specific discovery is not claimed.
63
+ Cursor sessions keep their source
57
64
  label and real transcript statistics through the shared capture pipeline.
58
65
 
59
66
  ## OpenCode note
@@ -49,7 +49,7 @@ function loadCache(readFileSync = fs.readFileSync) {
49
49
  return JSON.parse(readFileSync(CACHE_PATH, "utf8"));
50
50
  }
51
51
  catch {
52
- return {}; // missing or corrupt → empty
52
+ return {};
53
53
  }
54
54
  }
55
55
  function saveCache(cache, deps = fs) {
@@ -58,12 +58,8 @@ function saveCache(cache, deps = fs) {
58
58
  deps.writeFileSync(CACHE_PATH, JSON.stringify(cache), { mode: 0o600 });
59
59
  }
60
60
  catch {
61
- // best-effort — a failed cache write just means a redundant push next turn,
62
- // which the server idempotently no-ops.
63
61
  }
64
62
  }
65
- // Pure: given the prior cache, a repo key, and this turn's candidates, return
66
- // the subset whose hash changed plus the cache to persist.
67
63
  function applyCache(prev, repoKey, candidates) {
68
64
  const next = { ...prev };
69
65
  const changed = [];
@@ -1,8 +1,4 @@
1
1
  "use strict";
2
- // Pure path matcher for the auto-backup hook. Decides whether a repo-relative
3
- // path is an "artifact" worth backing up. Deny rules win over allow rules.
4
- // `getContent` is lazy: only called when a path-level allow needs the
5
- // skill-frontmatter exclusion (skills are pushed deliberately, not auto-backed).
6
2
  Object.defineProperty(exports, "__esModule", { value: true });
7
3
  exports.hasSkillFrontmatter = hasSkillFrontmatter;
8
4
  exports.isArtifactPath = isArtifactPath;
@@ -15,7 +11,6 @@ function basename(p) {
15
11
  const i = p.lastIndexOf("/");
16
12
  return i === -1 ? p : p.slice(i + 1);
17
13
  }
18
- // A leading `---` block containing both `name:` and `description:` → skill.
19
14
  function hasSkillFrontmatter(md) {
20
15
  const m = md.match(/^?---\s*\n([\s\S]*?)\n---/);
21
16
  if (!m)
@@ -62,7 +57,6 @@ function isArtifactPath(rel, getContent) {
62
57
  return false;
63
58
  }
64
59
  catch {
65
- // unreadable → treat as non-skill; the push will fail harmlessly later
66
60
  }
67
61
  return true;
68
62
  }
@@ -40,7 +40,6 @@ exports.runBackup = runBackup;
40
40
  exports.runBackupUpload = runBackupUpload;
41
41
  const match_1 = require("./match");
42
42
  const cache_1 = require("./cache");
43
- // Pure: merge two path lists into one, deduped, preserving first-seen order.
44
43
  function unionPaths(a, b) {
45
44
  const seen = new Set();
46
45
  const out = [];
@@ -56,8 +55,6 @@ function unionPaths(a, b) {
56
55
  }
57
56
  return out;
58
57
  }
59
- // Pure: from this turn's git-status paths, keep artifact files whose content
60
- // hash changed since the cache. Returns the changed candidates + next cache.
61
58
  function selectBackupFiles(input) {
62
59
  const candidates = [];
63
60
  for (const rel of input.statusPaths) {
@@ -70,7 +67,7 @@ function selectBackupFiles(input) {
70
67
  c = getContent();
71
68
  }
72
69
  catch {
73
- continue; // deleted/unreadable → skip
70
+ continue;
74
71
  }
75
72
  if (c === undefined || c === null)
76
73
  continue;
@@ -99,11 +96,7 @@ function gitRoot(cwd) {
99
96
  }
100
97
  function gitStatusPaths(root) {
101
98
  try {
102
- // --untracked-files=all is REQUIRED: the default collapses untracked files
103
- // into their top-level directory (a new docs/superpowers/plans/x.md shows as
104
- // just "docs/"), which would make the hook miss brand-new artifact files.
105
99
  const out = (0, child_process_1.execFileSync)("git", ["-C", root, "status", "--porcelain", "-z", "--untracked-files=all"], { encoding: "utf8" });
106
- // -z: NUL-separated, each entry "XY <path>"; rename entries have two paths.
107
100
  return out
108
101
  .split("\0")
109
102
  .filter(Boolean)
@@ -114,8 +107,6 @@ function gitStatusPaths(root) {
114
107
  return [];
115
108
  }
116
109
  }
117
- // All git-tracked files (committed, regardless of dirty state). `git ls-files`
118
- // emits bare NUL-separated paths — no "XY " status prefix — so we don't slice.
119
110
  function gitTrackedPaths(root) {
120
111
  try {
121
112
  const out = (0, child_process_1.execFileSync)("git", ["-C", root, "ls-files", "-z"], {
@@ -127,16 +118,10 @@ function gitTrackedPaths(root) {
127
118
  return [];
128
119
  }
129
120
  }
130
- // The hook's candidate set: dirty/untracked files ∪ all tracked files. Feeding
131
- // tracked files in is what lets a COMMITTED artifact (clean status) still get
132
- // backed up — the hash cache in selectBackupFiles dedups so already-backed-up
133
- // files don't re-upload. selectBackupFiles rejects non-artifact paths cheaply
134
- // (by regex, no content read), so the full tracked list is fine to pass.
135
121
  function gitCandidatePaths(root) {
136
122
  return unionPaths(gitStatusPaths(root), gitTrackedPaths(root));
137
123
  }
138
124
  const sha256 = (s) => crypto.createHash("sha256").update(s, "utf8").digest("hex");
139
- // Stop/sessionEnd hook entry. Best-effort, never throws.
140
125
  function runBackup(cwd, hookScriptPath) {
141
126
  try {
142
127
  if (process.env.CODIEDEV_NO_AUTOBACKUP)
@@ -150,8 +135,6 @@ function runBackup(cwd, hookScriptPath) {
150
135
  if (!root)
151
136
  return;
152
137
  const repoKey = (0, utils_1.getGitRemoteUrl)(root) ?? root;
153
- // Dirty files ∪ tracked files — so committed-but-unbacked artifacts are
154
- // caught too, not just unsaved drafts. Hash cache dedups the rest.
155
138
  const statusPaths = gitCandidatePaths(root);
156
139
  if (statusPaths.length === 0)
157
140
  return;
@@ -164,12 +147,6 @@ function runBackup(cwd, hookScriptPath) {
164
147
  });
165
148
  if (changed.length === 0)
166
149
  return;
167
- // The detached child owns the cache: it writes each file's hash ONLY after a
168
- // successful push, so a genuine push failure (network/500) leaves the hash
169
- // uncached and the file retries next turn. (Persisting here optimistically
170
- // would suppress that retry, since the server no-op only covers successes.)
171
- // Job carries root + repoKey + files; the child re-reads config from disk
172
- // via readConfig() (same machine), avoiding any config-shape guessing.
173
150
  const job = {
174
151
  root,
175
152
  repoKey,
@@ -184,18 +161,13 @@ function runBackup(cwd, hookScriptPath) {
184
161
  child.unref();
185
162
  }
186
163
  catch {
187
- // best-effort
188
164
  }
189
165
  }
190
166
  const DEFAULT_BACKUP_UPLOAD_DEPS = { readConfig: utils_1.readConfig, loadCache: cache_2.loadCache, saveCache: cache_2.saveCache };
191
- // Detached child: push each queued file privately, caching its hash ONLY on a
192
- // successful push (so a failed push retries next turn), then delete the job
193
- // file. Deps are injectable so the cache write is testable without touching the
194
- // global ~/.codiedev cache.
195
167
  async function runBackupUpload(jobFile, deps = DEFAULT_BACKUP_UPLOAD_DEPS) {
196
168
  try {
197
169
  const job = JSON.parse(fs.readFileSync(jobFile, "utf8"));
198
- const config = deps.readConfig(); // same machine — read full config from disk
170
+ const config = deps.readConfig();
199
171
  if (!config)
200
172
  return;
201
173
  if ((0, captureExclusions_1.isCaptureIgnored)(job.root, config.captureIgnoredPaths))
@@ -210,25 +182,21 @@ async function runBackupUpload(jobFile, deps = DEFAULT_BACKUP_UPLOAD_DEPS) {
210
182
  visibility: "private",
211
183
  contentHash: f.hash,
212
184
  });
213
- // Only now that the push landed do we record the hash — a prior failure
214
- // leaves it uncached so the next turn retries it.
215
185
  cache[(0, cache_2.cacheKey)(job.repoKey, f.rel)] = f.hash;
216
186
  dirty = true;
217
187
  }
218
188
  catch {
219
- // one file failing must not abort the rest, and its hash stays uncached
220
189
  }
221
190
  }
222
191
  if (dirty)
223
192
  deps.saveCache(cache);
224
193
  }
225
194
  catch {
226
- // best-effort
227
195
  }
228
196
  finally {
229
197
  try {
230
198
  fs.unlinkSync(jobFile);
231
199
  }
232
- catch { /* gone */ }
200
+ catch { }
233
201
  }
234
202
  }
@@ -47,12 +47,6 @@ const DEFAULT_NORMALIZE_DEPS = {
47
47
  homedir: () => os.homedir(),
48
48
  realpathSync: (value) => fs.realpathSync.native(value),
49
49
  };
50
- /**
51
- * Convert a user-entered path into the canonical absolute form persisted in
52
- * ~/.codiedev/config.json. Existing symlinks resolve to their real target so a
53
- * session cannot bypass an exclusion by entering the same folder through a
54
- * different alias. Non-existent paths still normalize deterministically.
55
- */
56
50
  function normalizeCapturePath(input, deps = {}) {
57
51
  const d = { ...DEFAULT_NORMALIZE_DEPS, ...deps };
58
52
  const trimmed = input.trim();
@@ -94,7 +88,6 @@ function findMatchingCaptureExclusion(candidate, ignoredPaths) {
94
88
  function isCaptureIgnored(candidate, ignoredPaths) {
95
89
  return findMatchingCaptureExclusion(candidate, ignoredPaths) !== null;
96
90
  }
97
- /** Every local root associated with a hook event, deduped in stable order. */
98
91
  function captureScopePaths(input, fallback = process.cwd()) {
99
92
  const values = [input.cwd, ...(input.workspace_roots ?? [])]
100
93
  .filter((value) => typeof value === "string" && value.length > 0);
@@ -103,11 +96,6 @@ function captureScopePaths(input, fallback = process.cwd()) {
103
96
  function shouldIgnoreCaptureInput(config, input, fallback) {
104
97
  return isCaptureIgnored(captureScopePaths(input, fallback), config?.captureIgnoredPaths);
105
98
  }
106
- /**
107
- * Final egress decision for queued transcript uploads. Jobs written by older
108
- * CLI versions have no scope metadata; once any exclusion exists, dropping
109
- * those legacy jobs is safer than guessing that they are public.
110
- */
111
99
  function shouldSkipCaptureUploadJob(job, config) {
112
100
  const ignoredPaths = config?.captureIgnoredPaths ?? [];
113
101
  if (ignoredPaths.length === 0)
@@ -36,47 +36,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CAPTURE_LOG_PATH = exports.CAPTURE_LOG_MAX_BYTES = void 0;
37
37
  exports.appendCaptureLog = appendCaptureLog;
38
38
  exports.readCaptureLogTail = readCaptureLogTail;
39
- // Rotating capture diagnostic log at <codiedevStateDir()>/capture.log
40
- // (~/.codiedev by default; ~/.codiedev-desktop under CODIEDEV_CONFIG_DIR).
41
- //
42
- // Every stage of the capture pipeline (capture-finish child, upload child,
43
- // stderr from detached children) writes one line here so users — and
44
- // `codiedev doctor` — can see exactly why a capture succeeded or failed
45
- // without having to file a support ticket or grep /tmp. The log is
46
- // best-effort throughout: appendCaptureLog NEVER throws into the hook path.
47
39
  const fs = __importStar(require("fs"));
48
40
  const path = __importStar(require("path"));
49
- // Imported from configDir (not utils) — utils.ts imports this module, so
50
- // importing utils.ts here would create a circular import.
51
41
  const configDir_1 = require("./configDir");
52
- // Rotate the log when it exceeds this size (keeps the newest ~half). 256 KB
53
- // holds thousands of entries; a user would have to see hundreds of errors
54
- // before hitting this limit, so it's a generous safety valve.
55
42
  exports.CAPTURE_LOG_MAX_BYTES = 256 * 1024;
56
43
  exports.CAPTURE_LOG_PATH = path.join((0, configDir_1.codiedevStateDir)(), "capture.log");
57
- // Append one structured line to the capture log. Rotates the file when it
58
- // grows past CAPTURE_LOG_MAX_BYTES by truncating it to the newest half (keeps
59
- // the most recent diagnostics). Best-effort: NEVER throws into the hook path —
60
- // a diagnostic log must not break the thing it's diagnosing.
61
44
  function appendCaptureLog(entry, deps = fs, filePath = exports.CAPTURE_LOG_PATH, now = Date.now) {
62
45
  try {
63
46
  const dir = path.dirname(filePath);
64
47
  if (!deps.existsSync(dir))
65
48
  deps.mkdirSync(dir, { recursive: true });
66
49
  if (deps.existsSync(filePath) && deps.statSync(filePath).size >= exports.CAPTURE_LOG_MAX_BYTES) {
67
- // Rotation: keep the newest half. Slice from the middle to a line
68
- // boundary so we never persist a torn (partial) first line.
69
50
  const content = deps.readFileSync(filePath, "utf8");
70
51
  const half = content.slice(Math.floor(content.length / 2));
71
52
  const firstNewline = half.indexOf("\n");
72
- // If no newline is found in the second half, drop it all and start
73
- // fresh — better than keeping a torn line.
74
53
  const fromLine = firstNewline === -1 ? "" : half.slice(firstNewline + 1);
75
54
  deps.writeFileSync(filePath, fromLine);
76
55
  }
77
- // Sanitize before composing: error messages can carry newlines (e.g. a
78
- // proxy's HTML error body in err.message) which would tear the one-line
79
- // format that doctor's parser depends on.
80
56
  const outcome = entry.outcome.replace(/\s+/g, " ").trim();
81
57
  const detail = entry.detail?.replace(/\s+/g, " ").trim();
82
58
  const line = `${new Date(now()).toISOString()} ${entry.stage} session=${entry.sessionId ?? "-"} ${outcome}` +
@@ -85,12 +61,8 @@ function appendCaptureLog(entry, deps = fs, filePath = exports.CAPTURE_LOG_PATH,
85
61
  deps.appendFileSync(filePath, line);
86
62
  }
87
63
  catch {
88
- // diagnostics must never break capture
89
64
  }
90
65
  }
91
- // Read the last N lines from the capture log. Returns [] when the file is
92
- // missing or unreadable — callers (e.g. `codiedev doctor`) treat that as
93
- // "nothing captured yet" rather than an error.
94
66
  function readCaptureLogTail(n, deps = fs, filePath = exports.CAPTURE_LOG_PATH) {
95
67
  try {
96
68
  if (!deps.existsSync(filePath))
@@ -1,11 +1,4 @@
1
1
  "use strict";
2
- // Capture-readiness assessment for `codiedev doctor`. Answers: "from where I'm
3
- // standing right now, would a session actually capture?" — the blind spot that
4
- // let lean-container / submodule / no-remote setups silently produce zero
5
- // sessions while doctor's setup checks stayed green.
6
- //
7
- // Pure + fully injectable so it unit-tests without a real git repo. The default
8
- // production wiring (in doctor.ts) plugs in execSync + the real git helpers.
9
2
  Object.defineProperty(exports, "__esModule", { value: true });
10
3
  exports.assessCaptureReadiness = assessCaptureReadiness;
11
4
  exports.assessWorkspaceRepos = assessWorkspaceRepos;
@@ -79,9 +72,6 @@ function formatRelativeAge(ms) {
79
72
  return `${h}h`;
80
73
  return `${Math.floor(h / 24)}d`;
81
74
  }
82
- // Turns the "last session captured" breadcrumb into a doctor line. This is the
83
- // payoff check: it lets a user confirm their most recent session actually
84
- // landed without opening the portal.
85
75
  function summarizeLastCapture(record, nowMs) {
86
76
  if (!record) {
87
77
  return {
@@ -98,44 +88,18 @@ function summarizeLastCapture(record, nowMs) {
98
88
  }
99
89
  return { status: "pass", detail: `last session captured ${age} ago` };
100
90
  }
101
- // ─── Capture-activity summarizer ──────────────────────────────────────────────
102
- // Turns the tail of ~/.codiedev/capture.log + a count of pending upload jobs
103
- // into a single doctor CheckResult-compatible {status, detail} object.
104
- //
105
- // Pure (no fs/os imports) — all data is injected by the caller (doctor.ts).
106
91
  const LOG_PATH_SUFFIX = "— details: ~/.codiedev/capture.log";
107
- // Parse regex: a valid structured line has exactly this shape.
108
- // stage is one of the four known stages; outcome is a bare token.
109
92
  const STRUCTURED_LINE_RE = /^(\S+) (capture-finish|upload|upload-deferred|stderr) session=(\S+) (\S+)(?: (.*))?$/;
110
- /** Append the shared log-path suffix to any detail string. */
111
93
  function withSuffix(detail) {
112
94
  return `${detail} ${LOG_PATH_SUFFIX}`;
113
95
  }
114
- // TLS-trust failure phrases/codes. These are what Node's TLS stack emits when
115
- // a middlebox (corporate proxy / VPN doing SSL inspection) re-signs HTTPS with
116
- // an internal CA. Node uses its OWN bundled Mozilla trust store — NOT the OS
117
- // store — so installing the corporate cert system-wide fixes curl/git but not
118
- // the CLI; only NODE_EXTRA_CA_CERTS (or an IT bypass) does.
119
- // "certificate has expired" is deliberately absent: that's a server-side cert
120
- // problem, not interception, and the hint would mislead.
121
96
  const TLS_INTERCEPTION_RE = /self.signed certificate|unable to verify the first certificate|unable to get local issuer certificate|SELF_SIGNED_CERT_IN_CHAIN|UNABLE_TO_VERIFY_LEAF_SIGNATURE|UNABLE_TO_GET_ISSUER_CERT_LOCALLY|DEPTH_ZERO_SELF_SIGNED_CERT|CERT_UNTRUSTED/i;
122
- /**
123
- * When `text` (an error message, code, or capture.log detail) indicates a
124
- * TLS-trust failure from SSL inspection, return the remediation hint;
125
- * otherwise null.
126
- */
127
97
  function tlsInterceptionHint(text, host) {
128
98
  if (!TLS_INTERCEPTION_RE.test(text))
129
99
  return null;
130
100
  return (`your network is intercepting TLS (corporate proxy/VPN). Node ignores the OS cert store — ` +
131
101
  `set NODE_EXTRA_CA_CERTS to your corporate root CA file, or ask IT to bypass SSL inspection for ${host}`);
132
102
  }
133
- /**
134
- * Flatten a thrown fetch/network error to "<message> (<code>)". undici's
135
- * fetch wraps the real failure in TypeError("fetch failed") with the cause
136
- * chained on `.cause` — walk to the deepest cause so doctor reports the
137
- * actual TLS/socket error instead of "fetch failed".
138
- */
139
103
  function describeFetchError(err) {
140
104
  if (err === null || err === undefined)
141
105
  return "unknown error";
@@ -148,10 +112,6 @@ function describeFetchError(err) {
148
112
  const message = deepest.message || String(deepest);
149
113
  return deepest.code ? `${message} (${deepest.code})` : message;
150
114
  }
151
- /**
152
- * Derive remediation text and status from the last failing upload-ish line.
153
- * Inspects the `detail` field of the log line for http= and msg= tokens.
154
- */
155
115
  function failDetail(rawLine, outcome, lineDetail, backendHost) {
156
116
  const d = lineDetail ?? "";
157
117
  const truncated = rawLine.slice(0, 140);
@@ -182,10 +142,7 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
182
142
  continue;
183
143
  parsed.push({ ts: m[1], stage: m[2], session: m[3], outcome: m[4], detail: m[5], raw });
184
144
  }
185
- // We care only about upload-ish lines (upload + upload-deferred).
186
- // capture-finish and stderr lines are noise for this check.
187
145
  const uploadLines = parsed.filter((p) => p.stage === "upload" || p.stage === "upload-deferred");
188
- // Rule 1: last upload-ish outcome is ERROR or gave-up → fail.
189
146
  if (uploadLines.length > 0) {
190
147
  const last = uploadLines[uploadLines.length - 1];
191
148
  if (last.outcome === "ERROR" || last.outcome === "gave-up") {
@@ -195,8 +152,6 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
195
152
  };
196
153
  }
197
154
  }
198
- // Rule 2: pending jobs queued (last upload wasn't a hard failure, but
199
- // retries are still in-flight).
200
155
  if (pendingJobCount > 0) {
201
156
  const noun = pendingJobCount === 1 ? "job" : "jobs";
202
157
  return {
@@ -204,15 +159,12 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
204
159
  detail: withSuffix(`${pendingJobCount} pending upload ${noun} queued for retry`),
205
160
  };
206
161
  }
207
- // Rule 3: no upload-ish lines at all — log is empty, garbage-only, or
208
- // only capture-finish lines.
209
162
  if (uploadLines.length === 0) {
210
163
  return {
211
164
  status: "warn",
212
165
  detail: withSuffix("no capture uploads recorded yet on this machine — run a session and exit, then re-run `codiedev doctor`"),
213
166
  };
214
167
  }
215
- // Rule 4a: last upload-ish outcome is ok → pass.
216
168
  const last = uploadLines[uploadLines.length - 1];
217
169
  const hasOk = uploadLines.some((p) => p.outcome === "ok");
218
170
  if (last.outcome === "ok") {
@@ -221,9 +173,6 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
221
173
  detail: withSuffix(`last upload ok (${last.ts})`),
222
174
  };
223
175
  }
224
- // Rule 4b: last is "deferred" — a throttled/scheduled defer was queued.
225
- // If an ok exists somewhere in the tail → pass (next upload deferred is fine).
226
- // If no ok exists → warn.
227
176
  if (last.outcome === "deferred") {
228
177
  if (hasOk) {
229
178
  return {
@@ -236,7 +185,6 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
236
185
  detail: withSuffix("deferred uploads were superseded but no completed upload found in the recent log"),
237
186
  };
238
187
  }
239
- // Rule 4c: last is "superseded".
240
188
  if (hasOk) {
241
189
  return {
242
190
  status: "pass",
@@ -248,12 +196,7 @@ function summarizeCaptureActivity(tailLines, pendingJobCount, backendHost) {
248
196
  detail: withSuffix("deferred uploads were superseded but no completed upload found in the recent log"),
249
197
  };
250
198
  }
251
- // Server-confirmed "did my last session land?". Prefers portal truth over the
252
- // local 2xx breadcrumb; the highest-value branch is "uploaded locally but the
253
- // portal has no record" — the exact silent failure devs hit (2xx to nothing,
254
- // wrong identity, backend drop).
255
199
  function summarizeServerCapture(server, local, nowMs) {
256
- // Endpoint unavailable / old backend → fall back to the local breadcrumb.
257
200
  if (server === null)
258
201
  return summarizeLastCapture(local, nowMs);
259
202
  if (server.found && server.latest) {
@@ -266,7 +209,6 @@ function summarizeServerCapture(server, local, nowMs) {
266
209
  }
267
210
  return { status: "pass", detail: `confirmed in the portal — last session captured ${age} ago` };
268
211
  }
269
- // Server reachable but has no session for this user.
270
212
  if (local) {
271
213
  const age = formatRelativeAge(nowMs - local.recordedAt);
272
214
  return {
@@ -15,7 +15,6 @@ const skillKey = (value) => typeof value === "string"
15
15
  .trim()
16
16
  .toLowerCase()
17
17
  : "";
18
- /** Compact diagnostic inputs only: never persist prompts, commands or tool output. */
19
18
  function parseCaptureSignals(content, source) {
20
19
  const timestamps = new Set();
21
20
  const invocationCounts = new Map();
@@ -24,8 +23,6 @@ function parseCaptureSignals(content, source) {
24
23
  let parsedRecords = 0, truncated = false;
25
24
  let premiumRequests, nanoAiu;
26
25
  let cursorSubagents;
27
- // The skill content a harness loads after an invocation follows it in the
28
- // transcript; a marker there names the artifact the invocation ran.
29
26
  const artifacts = new Map();
30
27
  let lastInvoked = null;
31
28
  const marker = (text) => {
@@ -91,10 +88,7 @@ function parseCaptureSignals(content, source) {
91
88
  /^[a-z0-9][a-z0-9_.:-]{0,39}$/i.test(value))
92
89
  entrypoints.add(value.toLowerCase());
93
90
  };
94
- // Claude Code: "cli" at the terminal, "sdk-cli" for `claude -p` / SDK.
95
91
  entrypoint(e.entrypoint);
96
- // Codex: session_meta.source is "cli", "vscode", "exec" (`codex exec`),
97
- // "mcp", or an object for sub-agents. Namespaced so values never collide.
98
92
  if (e.type === "session_meta" && e.payload) {
99
93
  const src = e.payload.source;
100
94
  entrypoint(typeof src === "string"
@@ -106,12 +100,8 @@ function parseCaptureSignals(content, source) {
106
100
  if (e.type === "cursor.subagent_summary" && Number.isSafeInteger(e.count) && e.count > 0) {
107
101
  cursorSubagents = { count: e.count, truncated: e.truncated === true };
108
102
  }
109
- // GitHub Copilot (normalized): one skill.invoked event per skill run,
110
- // whether the user or the agent invoked it.
111
103
  if (e.type === "copilot.skill_invoked")
112
104
  addSkill(invocations, e.skill);
113
- // pi (normalized): a `/skill:name` expansion or the agent reading a
114
- // listed skill's SKILL.md, which is how pi runs skills.
115
105
  if (e.type === "pi.skill_invoked")
116
106
  addSkill(invocations, e.skill);
117
107
  if (e.type === "copilot.usage_summary") {
@@ -186,7 +176,6 @@ function parseCaptureSignals(content, source) {
186
176
  truncated,
187
177
  };
188
178
  }
189
- /** Add a launch value the hook detected (harnesses that do not record one). */
190
179
  function withLaunch(signals, launch) {
191
180
  if (!launch || !/^[a-z0-9][a-z0-9_.:-]{0,39}$/.test(launch))
192
181
  return signals;
package/dist/cli.js CHANGED
@@ -1,18 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- /**
4
- * Codiedev CLI dispatcher.
5
- *
6
- * Subcommands:
7
- * codiedev connect — link agent CLIs to your org
8
- * codiedev push <file> — author/update a spec/review/decision/etc.
9
- * codiedev pull <key> — fetch an artifact by filename key
10
- * codiedev ping <user> "<msg>" — send a message (optionally --with <key>)
11
- * codiedev inbox — list pings (use --unread to filter)
12
- * codiedev read <ping-id> — mark read + show full message
13
- * codiedev note "<text>" — capture a thought
14
- * codiedev help — show this help
15
- */
16
3
  Object.defineProperty(exports, "__esModule", { value: true });
17
4
  const connect_1 = require("./connect");
18
5
  const cliSession_1 = require("./cliSession");
@@ -33,11 +20,10 @@ const send_1 = require("./commands/send");
33
20
  const capture_1 = require("./commands/capture");
34
21
  const pack_1 = require("./commands/pack");
35
22
  const traces_1 = require("./commands/traces");
23
+ const agents_1 = require("./commands/agents");
36
24
  const evals_1 = require("./commands/evals");
37
- // --- starter packs phase 2 ---
38
25
  const hookGate_1 = require("./packs/hookGate");
39
26
  const sync_1 = require("./packs/sync");
40
- // --- end starter packs ---
41
27
  const version_1 = require("./version");
42
28
  const HELP = `
43
29
  Codiedev CLI
@@ -51,7 +37,7 @@ Connect:
51
37
  Claude Code / Codex / Cursor /
52
38
  GitHub Copilot / pi to your org
53
39
  codiedev logout Sign this machine out
54
- codiedev doctor Verify your setup after connecting
40
+ codiedev doctor Verify setup and capture
55
41
 
56
42
  Harness:
57
43
  codie Launch the governed Codie harness.
@@ -98,6 +84,9 @@ Starter packs:
98
84
  codiedev pack status Installed packs, up to date or not
99
85
 
100
86
  Traces (agents on the gateway):
87
+ codiedev agents init | deploy | run | runs | show | secrets | webhook
88
+ Deploy agents and run them in fresh sandboxes
89
+ (codiedev agents help)
101
90
  codiedev traces agents Agents you maintain, with saved test cases
102
91
  codiedev traces cases <agent> [--dataset name [--version n]] [--model id]
103
92
  Pull an agent's test cases (or one dataset)
@@ -172,6 +161,7 @@ status sync never weakens it.
172
161
  const COMMAND_HELP = {
173
162
  pack: pack_1.PACK_HELP,
174
163
  traces: traces_1.TRACES_HELP,
164
+ agents: agents_1.AGENTS_HELP,
175
165
  evals: evals_1.EVALS_HELP,
176
166
  connect: `
177
167
  codiedev connect — link your agent CLI to Codiedev
@@ -183,10 +173,10 @@ codiedev connect — link your agent CLI to Codiedev
183
173
  - ~/.claude/CLAUDE.md instructions (teaches Claude the commands)
184
174
  - ~/.codex/hooks.json hook (same, for Codex)
185
175
  - ~/.codex/AGENTS.md instructions (same, for Codex)
186
- - ~/.cursor/hooks.json hooks (session, prompt, security, capture)
176
+ - ~/.cursor/hooks.json hooks (session, backup, capture)
187
177
  - ~/.cursor/rules/codiedev.mdc (teaches Cursor the commands)
188
178
  - ~/.copilot/hooks/codiedev.json (GitHub Copilot: VS Code + CLI capture)
189
- - ~/.pi/agent/extensions/codiedev.ts (pi: session, prompt, security, capture)
179
+ - ~/.pi/agent/extensions/codiedev.ts (pi: session, prompt, capture)
190
180
  - ~/.pi/agent/AGENTS.md (teaches pi the commands)
191
181
 
192
182
  Re-run anytime to swap accounts or refresh the instruction block.
@@ -440,8 +430,6 @@ Run 'codiedev help <command>' for each, or see the quick list:
440
430
  https://codiedev.com/portal/integrations/claude-code and run
441
431
  'codiedev connect --token'.
442
432
  `.trim();
443
- // Retired commands: say so instead of "Unknown command", since agent
444
- // instructions written by older installs may still reference them.
445
433
  const REMOVED_COMMANDS = {
446
434
  promote: "Share an auto-saved spec with 'Promote to team' in the portal's My artifacts list.",
447
435
  "reverse-ticket": "Use 'codiedev create-ticket' to draft Jira tickets.",
@@ -454,20 +442,15 @@ function removedCommand(command) {
454
442
  }
455
443
  async function main() {
456
444
  const [, , command, ...rest] = process.argv;
457
- // --- starter packs: hook-gate runs inside agent hooks; stdout/stderr and
458
- // the exit code belong to the wrapped command, so nothing else may run. ---
459
445
  if (command === "hook-gate") {
460
446
  (0, hookGate_1.runHookGateCli)(rest);
461
447
  return;
462
448
  }
463
- // Daily "pack has updates" nudge from the cached status (local read only).
464
- // stderr, so piped/--json output stays clean.
465
449
  if (command && !["pack", "packs", "help", "docs", "version", "--version", "-v", "--help", "-h"].includes(command) && !rest.includes("--json")) {
466
450
  const nag = (0, sync_1.packNagLine)();
467
451
  if (nag)
468
452
  console.error(`\n${nag}\n`);
469
453
  }
470
- // --- end starter packs ---
471
454
  if (!command || command === "--help" || command === "-h") {
472
455
  console.log(HELP);
473
456
  return;
@@ -490,7 +473,6 @@ async function main() {
490
473
  console.log(DOCS);
491
474
  return;
492
475
  }
493
- // Support `codiedev <command> --help` too.
494
476
  if (rest.includes("--help") || rest.includes("-h")) {
495
477
  if (COMMAND_HELP[command]) {
496
478
  console.log(COMMAND_HELP[command]);
@@ -501,8 +483,6 @@ async function main() {
501
483
  console.log(`codiedev v${version_1.CLI_VERSION}`);
502
484
  return;
503
485
  }
504
- // Browser sign-in: refresh the access token (under the shared lock) before
505
- // the command reads config. No-op for pasted API tokens.
506
486
  if (command !== "connect" && command !== "logout") {
507
487
  const fresh = await (0, cliSession_1.ensureFreshToken)({ configPath: utils_1.CONFIG_PATH }).catch(() => null);
508
488
  if (fresh?.status === "signed_out") {
@@ -515,9 +495,6 @@ async function main() {
515
495
  await (0, connect_1.runConnect)(rest);
516
496
  return;
517
497
  case "session-refresh": {
518
- // Internal: the Codie engine's per-request hook asks for a fresh
519
- // token when the current one has under 30 minutes left. Refreshes
520
- // under the shared lock; prints nothing.
521
498
  await (0, cliSession_1.ensureFreshToken)({ configPath: utils_1.CONFIG_PATH }, 60 * 60_000).catch(() => null);
522
499
  return;
523
500
  }
@@ -558,7 +535,6 @@ async function main() {
558
535
  return;
559
536
  case "inbox":
560
537
  case "check":
561
- // Support `codiedev check inbox` as an alias.
562
538
  if (command === "check" && rest[0] === "inbox") {
563
539
  await (0, inbox_1.runInbox)(rest.slice(1));
564
540
  }
@@ -592,6 +568,9 @@ async function main() {
592
568
  case "traces":
593
569
  await (0, traces_1.runTraces)(rest);
594
570
  return;
571
+ case "agents":
572
+ await (0, agents_1.runAgents)(rest);
573
+ return;
595
574
  case "evals":
596
575
  await (0, evals_1.runEvals)(rest);
597
576
  return;
@@ -611,7 +590,6 @@ async function main() {
611
590
  await (0, pack_1.runPack)(rest);
612
591
  return;
613
592
  case "capture":
614
- // Back-compat alias for the earlier `codiedev capture <sub>` form.
615
593
  await (0, capture_1.runCaptureCommand)(rest);
616
594
  return;
617
595
  default: