codiedev 0.9.0-beta.0 → 0.9.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/dist/connect.js +2 -2
- package/dist/hook.js +29 -4
- package/dist/utils.d.ts +21 -0
- package/dist/utils.js +33 -0
- package/package.json +1 -1
package/dist/connect.js
CHANGED
|
@@ -278,7 +278,7 @@ async function runConnect(args = []) {
|
|
|
278
278
|
}
|
|
279
279
|
};
|
|
280
280
|
if (hasClaude) {
|
|
281
|
-
runInstall("claude-code-hook", utils_1.installHook, "Claude Code SessionEnd
|
|
281
|
+
runInstall("claude-code-hook", utils_1.installHook, "Claude Code SessionEnd (capture) + UserPromptSubmit (skill discovery) hooks (~/.claude/settings.json)", "claudeCode-hook");
|
|
282
282
|
runInstall("claude-code-instructions", utils_1.installClaudeCodeInstructions, "Claude Code agent instructions (~/.claude/CLAUDE.md)", "claudeCode-instructions");
|
|
283
283
|
}
|
|
284
284
|
if (hasCodex) {
|
|
@@ -319,7 +319,7 @@ async function runConnect(args = []) {
|
|
|
319
319
|
console.log(`\nConnected to ${companyName}`);
|
|
320
320
|
console.log(`Tracking ${repos.length} repo${repos.length === 1 ? "" : "s"}`);
|
|
321
321
|
if (installed.length > 0) {
|
|
322
|
-
console.log("Installed
|
|
322
|
+
console.log("Installed hook(s):");
|
|
323
323
|
for (const target of installed) {
|
|
324
324
|
console.log(` - ${target}`);
|
|
325
325
|
}
|
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) {
|
|
@@ -86,9 +89,10 @@ async function runDiscover(prompt) {
|
|
|
86
89
|
.slice(0, 3)
|
|
87
90
|
.map((s) => `- ${s.key} — ${(s.score * 100).toFixed(0)}% match`)
|
|
88
91
|
.join("\n");
|
|
89
|
-
const additionalContext = "
|
|
92
|
+
const additionalContext = "Your team has a CodieDev skill that fits this task — prefer it over improvising:\n" +
|
|
90
93
|
lines +
|
|
91
|
-
"\
|
|
94
|
+
"\nPull and follow it now: `codiedev pull <key>`. It encodes the team's vetted " +
|
|
95
|
+
"approach. Skip it only if it clearly doesn't apply.";
|
|
92
96
|
process.stdout.write(JSON.stringify({
|
|
93
97
|
hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext },
|
|
94
98
|
}));
|
|
@@ -154,6 +158,20 @@ function resolveMode(arg) {
|
|
|
154
158
|
return "claude-code";
|
|
155
159
|
}
|
|
156
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
|
+
}
|
|
157
175
|
let hookInput = {};
|
|
158
176
|
try {
|
|
159
177
|
const raw = await readStdin();
|
|
@@ -225,11 +243,18 @@ async function main() {
|
|
|
225
243
|
capturedAt: Date.now(),
|
|
226
244
|
source: mode,
|
|
227
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.
|
|
228
251
|
try {
|
|
229
|
-
|
|
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 });
|
|
230
255
|
}
|
|
231
256
|
catch (err) {
|
|
232
|
-
process.stderr.write(`[codiedev-hook] Warning: Failed to
|
|
257
|
+
process.stderr.write(`[codiedev-hook] Warning: Failed to dispatch session capture — ${err.message}\n`);
|
|
233
258
|
}
|
|
234
259
|
process.exit(0);
|
|
235
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