codiedev 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
File without changes
@@ -136,7 +136,13 @@ async function runPush(args) {
136
136
  const tagSummary = res.tags && res.tags.length > 0 ? ` tags=[${res.tags.join(", ")}]` : "";
137
137
  console.log(`✓ Pushed ${filename} (kind=${res.kind}${tagSummary}).`);
138
138
  console.log(` Visible under: ${res.destination}`);
139
- console.log(` Pull with: codiedev pull ${filename}`);
139
+ // Echo the *resolved* key the server stored, not the local filename —
140
+ // skills are keyed by their frontmatter name (so two `SKILL.md` files
141
+ // don't collide), so the stored key can differ from what was pushed.
142
+ if (res.key && res.key !== filename) {
143
+ console.log(` Stored as: ${res.key}`);
144
+ }
145
+ console.log(` Pull with: codiedev pull ${res.key ?? filename}`);
140
146
  }
141
147
  catch (err) {
142
148
  (0, shared_1.recordAgentEvent)(config, {
package/dist/hook.js CHANGED
@@ -35,8 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
38
40
  const https = __importStar(require("https"));
39
41
  const http = __importStar(require("http"));
42
+ const child_process_1 = require("child_process");
40
43
  const utils_1 = require("./utils");
41
44
  const version_1 = require("./version");
42
45
  function getJson(url, bearerToken) {
@@ -155,6 +158,20 @@ function resolveMode(arg) {
155
158
  return "claude-code";
156
159
  }
157
160
  async function main() {
161
+ // Detached uploader child (spawned by the capture path below). The job —
162
+ // including transcript and token — is on disk; no stdin is needed, so handle
163
+ // this before readStdin() to avoid blocking on the ignored stdin pipe.
164
+ if (process.argv[2] === "upload") {
165
+ const jobFile = process.argv[3];
166
+ if (jobFile) {
167
+ await (0, utils_1.runCaptureUpload)(jobFile, {
168
+ post: postJson,
169
+ readFileSync: fs.readFileSync,
170
+ unlinkSync: fs.unlinkSync,
171
+ });
172
+ }
173
+ process.exit(0);
174
+ }
158
175
  let hookInput = {};
159
176
  try {
160
177
  const raw = await readStdin();
@@ -226,11 +243,18 @@ async function main() {
226
243
  capturedAt: Date.now(),
227
244
  source: mode,
228
245
  };
246
+ // Fire the upload from a fully detached child and exit immediately. Awaiting
247
+ // it inline let Claude Code's SessionEnd shutdown window elapse and kill the
248
+ // still-running hook ("Hook cancelled") on large transcripts — even though
249
+ // the body had already reached the server. The detached child outlives this
250
+ // process and finishes the POST in the background.
229
251
  try {
230
- await postJson(`${config.backendUrl}/api/captureSession`, payload, config.token);
252
+ const hookScriptPath = process.argv[1];
253
+ const tmpFile = path.join(os.tmpdir(), `codiedev-capture-${session_id ?? process.pid}.json`);
254
+ (0, utils_1.dispatchCaptureDetached)({ url: `${config.backendUrl}/api/captureSession`, token: config.token, body: payload }, hookScriptPath, { spawn: child_process_1.spawn, writeFileSync: fs.writeFileSync, tmpFile });
231
255
  }
232
256
  catch (err) {
233
- process.stderr.write(`[codiedev-hook] Warning: Failed to capture session — ${err.message}\n`);
257
+ process.stderr.write(`[codiedev-hook] Warning: Failed to dispatch session capture — ${err.message}\n`);
234
258
  }
235
259
  process.exit(0);
236
260
  }
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as fs from "fs";
2
+ import { spawn as nodeSpawn } from "child_process";
1
3
  export interface CodiedevConfig {
2
4
  token: string;
3
5
  companyId: string;
@@ -101,3 +103,22 @@ export interface ParsedStats {
101
103
  export declare function parseClaudeCodeStats(content: string): ParsedStats;
102
104
  export declare function parseCodexStats(content: string): ParsedStats;
103
105
  export declare function extractCodexRepoUrl(content: string): string | null;
106
+ export interface CaptureUploadJob {
107
+ url: string;
108
+ token: string;
109
+ body: Record<string, unknown>;
110
+ }
111
+ interface DispatchDeps {
112
+ spawn: typeof nodeSpawn;
113
+ writeFileSync: typeof fs.writeFileSync;
114
+ tmpFile: string;
115
+ nodePath?: string;
116
+ }
117
+ export declare function dispatchCaptureDetached(job: CaptureUploadJob, hookScriptPath: string, deps: DispatchDeps): void;
118
+ interface UploadDeps {
119
+ post: (url: string, body: Record<string, unknown>, token: string) => Promise<void>;
120
+ readFileSync: typeof fs.readFileSync;
121
+ unlinkSync: typeof fs.unlinkSync;
122
+ }
123
+ export declare function runCaptureUpload(jobFile: string, deps: UploadDeps): Promise<void>;
124
+ export {};
package/dist/utils.js CHANGED
@@ -62,6 +62,8 @@ exports.installCursorHook = installCursorHook;
62
62
  exports.parseClaudeCodeStats = parseClaudeCodeStats;
63
63
  exports.parseCodexStats = parseCodexStats;
64
64
  exports.extractCodexRepoUrl = extractCodexRepoUrl;
65
+ exports.dispatchCaptureDetached = dispatchCaptureDetached;
66
+ exports.runCaptureUpload = runCaptureUpload;
65
67
  const fs = __importStar(require("fs"));
66
68
  const path = __importStar(require("path"));
67
69
  const os = __importStar(require("os"));
@@ -854,3 +856,34 @@ function extractCodexRepoUrl(content) {
854
856
  }
855
857
  return null;
856
858
  }
859
+ // Fire-and-forget the session-capture upload from a fully detached child so the
860
+ // SessionEnd hook can exit 0 immediately. Awaiting the upload inline let Claude
861
+ // Code's shutdown window elapse and kill the hook ("Hook cancelled") on large
862
+ // transcripts. The job (incl. bearer token) is written to a file rather than
863
+ // passed via argv so it never shows up in the process list.
864
+ function dispatchCaptureDetached(job, hookScriptPath, deps) {
865
+ deps.writeFileSync(deps.tmpFile, JSON.stringify(job), { mode: 0o600 });
866
+ const child = deps.spawn(deps.nodePath ?? process.execPath, [hookScriptPath, "upload", deps.tmpFile], { detached: true, stdio: "ignore" });
867
+ child.unref();
868
+ }
869
+ // Runs inside the detached child spawned by `dispatchCaptureDetached`: read the
870
+ // persisted job, POST it, and ALWAYS delete the temp file afterwards (even on
871
+ // failure) so we don't litter the tmp dir. Never throws — capture is
872
+ // best-effort.
873
+ async function runCaptureUpload(jobFile, deps) {
874
+ try {
875
+ const job = JSON.parse(deps.readFileSync(jobFile, "utf8"));
876
+ await deps.post(job.url, job.body, job.token);
877
+ }
878
+ catch {
879
+ // best-effort — the parent already exited; nothing to report to.
880
+ }
881
+ finally {
882
+ try {
883
+ deps.unlinkSync(jobFile);
884
+ }
885
+ catch {
886
+ // temp file already gone — fine.
887
+ }
888
+ }
889
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to Codiedev for org-wide session capture and artifact collaboration",
5
5
  "bin": {
6
6
  "codiedev": "dist/cli.js",