careervivid 2.1.21 → 2.1.29
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/agent/QueryEngine.d.ts.map +1 -1
- package/dist/agent/QueryEngine.js +36 -4
- package/dist/agent/agentAuditLog.d.ts.map +1 -1
- package/dist/agent/agentAuditLog.js +45 -1
- package/dist/agent/instructions.d.ts +1 -0
- package/dist/agent/instructions.d.ts.map +1 -1
- package/dist/agent/instructions.js +96 -26
- package/dist/agent/memory.d.ts +28 -0
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +59 -0
- package/dist/agent/tools/coding.d.ts.map +1 -1
- package/dist/agent/tools/coding.js +27 -34
- package/dist/commands/agent/engineResolver.js +1 -1
- package/dist/commands/agent/index.d.ts.map +1 -1
- package/dist/commands/agent/index.js +11 -4
- package/dist/commands/agent/personas.d.ts +41 -0
- package/dist/commands/agent/personas.d.ts.map +1 -0
- package/dist/commands/agent/personas.js +323 -0
- package/dist/commands/agent/repl/engineLoop.d.ts +35 -0
- package/dist/commands/agent/repl/engineLoop.d.ts.map +1 -0
- package/dist/commands/agent/repl/engineLoop.js +168 -0
- package/dist/commands/agent/repl/input.d.ts +30 -0
- package/dist/commands/agent/repl/input.d.ts.map +1 -0
- package/dist/commands/agent/repl/input.js +86 -0
- package/dist/commands/agent/repl/slashCommands.d.ts +33 -0
- package/dist/commands/agent/repl/slashCommands.d.ts.map +1 -0
- package/dist/commands/agent/repl/slashCommands.js +193 -0
- package/dist/commands/agent/repl/toolHandlers.d.ts +33 -0
- package/dist/commands/agent/repl/toolHandlers.d.ts.map +1 -0
- package/dist/commands/agent/repl/toolHandlers.js +185 -0
- package/dist/commands/agent/repl.d.ts +12 -1
- package/dist/commands/agent/repl.d.ts.map +1 -1
- package/dist/commands/agent/repl.js +134 -636
- package/dist/commands/agent/toolRegistry.d.ts.map +1 -1
- package/dist/commands/agent/toolRegistry.js +4 -2
- package/dist/lib/tts.d.ts +19 -9
- package/dist/lib/tts.d.ts.map +1 -1
- package/dist/lib/tts.js +129 -50
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AASrD,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,QAAsC,CAAC;AAC9E,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,QAAoC,CAAC;AASpE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AASrD,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,QAAsC,CAAC;AAC9E,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,QAAoC,CAAC;AASpE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAwED;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,kBAAuB;IAyBrC,UAAU,IAAI,OAAO,EAAE;IAIvB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE;IAIpC,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE;IAW7B,OAAO,CAAC,mBAAmB;YAcb,YAAY;YAOZ,gBAAgB;IA6DjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAqG5E;;;;OAIG;IACU,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CAoHtF"}
|
|
@@ -18,14 +18,39 @@ function isRetryableError(e) {
|
|
|
18
18
|
// Google GenAI errors often have a status field
|
|
19
19
|
if (e?.status && RETRYABLE_STATUS_CODES.has(e.status))
|
|
20
20
|
return true;
|
|
21
|
-
if (e?.message && /rate.?limit|quota|too.?many.?requests|service.?unavailable/i.test(e.message))
|
|
21
|
+
if (e?.message && /rate.?limit|quota|too.?many.?requests|service.?unavailable|429/i.test(e.message))
|
|
22
22
|
return true;
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
|
+
/** Parse the retryDelay (in seconds) from a Gemini 429 error message or details array. */
|
|
26
|
+
function extractRetryDelayMs(e) {
|
|
27
|
+
// Try structured errorDetails array first (e.g. RetryInfo proto)
|
|
28
|
+
if (Array.isArray(e?.errorDetails)) {
|
|
29
|
+
for (const detail of e.errorDetails) {
|
|
30
|
+
const delay = detail?.retryDelay;
|
|
31
|
+
if (delay) {
|
|
32
|
+
// "16s" or "16.44s" → milliseconds
|
|
33
|
+
const secs = parseFloat(String(delay).replace('s', ''));
|
|
34
|
+
if (!isNaN(secs))
|
|
35
|
+
return Math.ceil(secs * 1000);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Fallback: parse from error message string ("Please retry in 16.44s")
|
|
40
|
+
if (e?.message) {
|
|
41
|
+
const match = e.message.match(/retry\s*in\s*([\d.]+)\s*s/i);
|
|
42
|
+
if (match) {
|
|
43
|
+
const secs = parseFloat(match[1]);
|
|
44
|
+
if (!isNaN(secs))
|
|
45
|
+
return Math.ceil(secs * 1000);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
25
50
|
async function sleep(ms) {
|
|
26
51
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
27
52
|
}
|
|
28
|
-
async function retryWithBackoff(fn, maxRetries =
|
|
53
|
+
async function retryWithBackoff(fn, maxRetries = 5, baseDelayMs = 5000) {
|
|
29
54
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
30
55
|
try {
|
|
31
56
|
return await fn();
|
|
@@ -33,8 +58,15 @@ async function retryWithBackoff(fn, maxRetries = 3, baseDelayMs = 1000) {
|
|
|
33
58
|
catch (e) {
|
|
34
59
|
if (attempt === maxRetries || !isRetryableError(e))
|
|
35
60
|
throw e;
|
|
36
|
-
|
|
37
|
-
|
|
61
|
+
// Honour the server-requested retry delay if present, use exponential backoff as floor
|
|
62
|
+
const serverDelayMs = extractRetryDelayMs(e);
|
|
63
|
+
const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
|
|
64
|
+
const delayMs = Math.max(serverDelayMs ?? 0, exponentialDelay);
|
|
65
|
+
// Add ±10% jitter to avoid thundering-herd on shared quota
|
|
66
|
+
const jitter = delayMs * 0.1 * (Math.random() * 2 - 1);
|
|
67
|
+
const finalDelay = Math.ceil(delayMs + jitter);
|
|
68
|
+
console.error(`\n⏳ Rate limited (attempt ${attempt + 1}/${maxRetries}). Retrying in ${(finalDelay / 1000).toFixed(1)}s…`);
|
|
69
|
+
await sleep(finalDelay);
|
|
38
70
|
}
|
|
39
71
|
}
|
|
40
72
|
// TypeScript requires this even though it's unreachable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentAuditLog.d.ts","sourceRoot":"","sources":["../../src/agent/agentAuditLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;CACb;AAID,eAAO,IAAI,UAAU,QAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"agentAuditLog.d.ts","sourceRoot":"","sources":["../../src/agent/agentAuditLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;CACb;AAID,eAAO,IAAI,UAAU,QAA2B,CAAC;AA0CjD,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGpE;AA8BD,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CA8BP;AA8KD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAMnD;AAID,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEhB"}
|
|
@@ -20,7 +20,7 @@ import { appendFileSync, existsSync, mkdirSync } from "fs";
|
|
|
20
20
|
import { resolve } from "path";
|
|
21
21
|
import { homedir } from "os";
|
|
22
22
|
import { randomUUID } from "crypto";
|
|
23
|
-
import { saveSessionMemory } from "./memory.js";
|
|
23
|
+
import { saveSessionMemory, saveCodingSession } from "./memory.js";
|
|
24
24
|
// ── Session-level state ───────────────────────────────────────────────────────
|
|
25
25
|
export let SESSION_ID = randomUUID().slice(0, 8);
|
|
26
26
|
const pendingFirestore = [];
|
|
@@ -38,6 +38,8 @@ const sessionState = {
|
|
|
38
38
|
coverLetters: [],
|
|
39
39
|
interviewPrep: [],
|
|
40
40
|
facts: {},
|
|
41
|
+
filesChanged: [],
|
|
42
|
+
lastBuilt: "",
|
|
41
43
|
};
|
|
42
44
|
/** Call once at startup so the session summary records the correct mode and model. */
|
|
43
45
|
export function initSessionContext(mode, model) {
|
|
@@ -196,6 +198,26 @@ function extractSessionFacts(tool, args, result) {
|
|
|
196
198
|
addHighlight(`Auto-filled application: ${company} (${role})`);
|
|
197
199
|
break;
|
|
198
200
|
}
|
|
201
|
+
// ── Coding file tools ───────────────────────────────────────────────────────────────────
|
|
202
|
+
case "write_file": {
|
|
203
|
+
const filePath = String(args.path || "");
|
|
204
|
+
if (filePath && !sessionState.filesChanged.includes(filePath)) {
|
|
205
|
+
sessionState.filesChanged.push(filePath);
|
|
206
|
+
}
|
|
207
|
+
// Track what was built from the path name
|
|
208
|
+
const fileName = filePath.split("/").pop() || filePath;
|
|
209
|
+
sessionState.lastBuilt = fileName;
|
|
210
|
+
addHighlight(`Created/wrote: ${filePath}`);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case "patch_file": {
|
|
214
|
+
const filePath = String(args.path || "");
|
|
215
|
+
if (filePath && !sessionState.filesChanged.includes(filePath)) {
|
|
216
|
+
sessionState.filesChanged.push(filePath);
|
|
217
|
+
}
|
|
218
|
+
addHighlight(`Patched: ${filePath} (lines ${args.start_line}–${args.end_line})`);
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
199
221
|
default:
|
|
200
222
|
break;
|
|
201
223
|
}
|
|
@@ -274,6 +296,28 @@ export async function writeSessionSummary(stats) {
|
|
|
274
296
|
facts: sessionState.facts,
|
|
275
297
|
};
|
|
276
298
|
saveSessionMemory(memoryInput);
|
|
299
|
+
// ── For coding mode: also save the fast pick-up file ──────────────────
|
|
300
|
+
if (sessionState.mode === "coding" && sessionState.filesChanged.length > 0) {
|
|
301
|
+
const { execSync } = await import("child_process");
|
|
302
|
+
let branch = "";
|
|
303
|
+
try {
|
|
304
|
+
branch = execSync("git branch --show-current", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
305
|
+
}
|
|
306
|
+
catch { /* no git */ }
|
|
307
|
+
const summaryParts = sessionState.highlights.slice(0, 5).map(h => h.outcome);
|
|
308
|
+
const summary = summaryParts.length > 0
|
|
309
|
+
? summaryParts.join(" · ")
|
|
310
|
+
: `Coding session — ${stats.turns} turns, ${sessionState.filesChanged.length} file(s) changed.`;
|
|
311
|
+
saveCodingSession({
|
|
312
|
+
cwd: process.cwd(),
|
|
313
|
+
savedAt: new Date().toISOString(),
|
|
314
|
+
mode: "coding",
|
|
315
|
+
summary,
|
|
316
|
+
filesChanged: sessionState.filesChanged,
|
|
317
|
+
lastBuilt: sessionState.lastBuilt,
|
|
318
|
+
branch,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
277
321
|
}
|
|
278
322
|
catch { /* memory write failure is non-fatal */ }
|
|
279
323
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
export declare const baseIdentity: string;
|
|
15
15
|
export declare const RESUME_SECTION: string;
|
|
16
16
|
export declare const CODING_SECTION: string;
|
|
17
|
+
export declare const BROWSER_CODING_SECTION: string;
|
|
17
18
|
export declare const JOBS_TOOLS_SECTION: string;
|
|
18
19
|
export declare const JOBS_HARNESS: string;
|
|
19
20
|
export declare const GREETING_PROTOCOL: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/agent/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsCH,mFAAmF;AACnF,eAAO,MAAM,YAAY,QAAsB,CAAC;AAMhD,eAAO,MAAM,cAAc,QAcnB,CAAC;AAMT,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/agent/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsCH,mFAAmF;AACnF,eAAO,MAAM,YAAY,QAAsB,CAAC;AAMhD,eAAO,MAAM,cAAc,QAcnB,CAAC;AAMT,eAAO,MAAM,cAAc,QAqDnB,CAAC;AAMT,eAAO,MAAM,sBAAsB,QAiD3B,CAAC;AAMT,eAAO,MAAM,kBAAkB,QA0DvB,CAAC;AAMT,eAAO,MAAM,YAAY,QA6CjB,CAAC;AAMT,eAAO,MAAM,iBAAiB,QAuBtB,CAAC;AAOT;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CAsCT"}
|
|
@@ -68,40 +68,109 @@ You have access to the **get_resume** tool which fetches the user's live CareerV
|
|
|
68
68
|
export const CODING_SECTION = `
|
|
69
69
|
## Coding Agent Mode
|
|
70
70
|
|
|
71
|
-
You
|
|
71
|
+
You are a fully autonomous software engineering agent — equivalent to Antigravity or Claude Code.
|
|
72
|
+
You write production-ready code directly to local files on disk using your file tools.
|
|
72
73
|
|
|
73
|
-
### ⚠️
|
|
74
|
+
### ⚠️ Critical: Write Code to Files — Never to Articles
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
When a user asks you to build something, create a feature, write a script, or fix a bug:
|
|
77
|
+
1. **USE write_file** to create actual code files in the user's working directory.
|
|
78
|
+
2. **USE patch_file** to surgically edit existing files without rewriting them.
|
|
79
|
+
3. **NEVER use publish_article** to output code. publish_article is ONLY for publishing markdown content to the CareerVivid community blog.
|
|
80
|
+
4. **NEVER just show the user a code block** in the chat and say "here you go". Always write the file.
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
- \`career-vivid/\` — your job tracker data, résumé drafts, and career pipeline files
|
|
79
|
-
- \`cli/\` — the CLI source code (your own code)
|
|
80
|
-
- \`tmp/\` or \`/tmp/\` — temporary scratch files
|
|
82
|
+
### Your Execution Loop (follow this exactly)
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
1. **Orient** — Call list_directory or get_file_tree on the relevant directory to understand the project structure.
|
|
85
|
+
2. **Read** — Call read_file on any existing files you need to understand before modifying them.
|
|
86
|
+
3. **Plan** — Emit a concise "Plan:" (2–5 bullets: what files you will create/change and why).
|
|
87
|
+
4. **Execute** — Call write_file or patch_file to create/modify every file in the plan.
|
|
88
|
+
5. **Verify** — Run the compiler or tests via run_command (e.g., \`tsc --noEmit\`, \`npm test\`, \`python -c "import script"\`).
|
|
89
|
+
6. **Fix** — If verification fails, read the error, patch the file, re-verify. Loop until clean.
|
|
90
|
+
7. **Report** — Summarise what was created or changed, with the exact file paths.
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
- \`src/\` — web app source code (React, components, pages)
|
|
88
|
-
- \`functions/\` — Firebase Cloud Functions source
|
|
89
|
-
- \`next-app/\` — Next.js application source
|
|
90
|
-
- Any file outside your allowed prefixes
|
|
92
|
+
### File Access
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
You have full read and write access to any file in the user's working directory EXCEPT:
|
|
95
|
+
- \`.env\` / \`.env.*\` files — contains secrets, never read or write these
|
|
96
|
+
- \`node_modules/\` — managed by npm, never write here
|
|
97
|
+
- \`.git/\` — git internals, use run_command for git operations
|
|
93
98
|
|
|
94
|
-
###
|
|
95
|
-
1. Read relevant files first (read_file). Never overwrite blindly.
|
|
96
|
-
2. Emit a short "Plan:" describing files you will touch and the approach.
|
|
97
|
-
3. Write data using write_file or patch_file (career-vivid/ only).
|
|
98
|
-
4. Verify with run_command (read-only commands like cat, ls, grep).
|
|
99
|
-
5. Fix errors and loop until clean; summarise all changes made.
|
|
99
|
+
### Code Quality Standards
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
- Follow existing code style.
|
|
104
|
-
-
|
|
101
|
+
- **TypeScript/JavaScript**: Strict types, no implicit \`any\`, ESM imports, JSDoc on public APIs.
|
|
102
|
+
- **Python**: Type hints, docstrings on public functions, PEP8.
|
|
103
|
+
- **All languages**: Follow existing code style in the project. Keep functions small and single-responsibility.
|
|
104
|
+
- Read the existing file style before writing new code in that file.
|
|
105
|
+
|
|
106
|
+
### Shell Commands
|
|
107
|
+
|
|
108
|
+
Use run_command for:
|
|
109
|
+
- Compiling: \`tsc --noEmit\`, \`npm run build\`, \`python -m py_compile\`
|
|
110
|
+
- Testing: \`npm test\`, \`pytest\`, \`go test ./...\`
|
|
111
|
+
- Formatting/linting: \`npm run lint\`, \`black .\`, \`gofmt\`
|
|
112
|
+
- Git: \`git status\`, \`git diff\`, \`git log --oneline -10\`
|
|
113
|
+
- Reading output: \`cat file\`, \`ls -la\`, \`grep -rn pattern dir/\`
|
|
114
|
+
|
|
115
|
+
### What You Must Never Do
|
|
116
|
+
|
|
117
|
+
- ❌ Show a code block in the chat and say "paste this" — always write the file
|
|
118
|
+
- ❌ Call publish_article with code content
|
|
119
|
+
- ❌ Ask "would you like me to write this file?" — just write it
|
|
120
|
+
- ❌ Write to .env, node_modules/, or .git/
|
|
121
|
+
`.trim();
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// §3b — Browser debugging section (added in --coding mode)
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
export const BROWSER_CODING_SECTION = `
|
|
126
|
+
## Browser Debugging Tools
|
|
127
|
+
|
|
128
|
+
You have a full browser automation suite for debugging web apps during development.
|
|
129
|
+
Use these tools when the user asks you to:
|
|
130
|
+
- Inspect a running local dev server (localhost:3000, :8080, etc.)
|
|
131
|
+
- Debug a visual layout bug, broken UI, or rendering error
|
|
132
|
+
- Check what the browser actually shows vs. what the code produces
|
|
133
|
+
- Verify an HTML/CSS/JS project looks correct after generating it
|
|
134
|
+
- Watch live errors or network failures in a running app
|
|
135
|
+
|
|
136
|
+
### Browser Debug Workflow
|
|
137
|
+
|
|
138
|
+
1. **browser_navigate** — Open the local server URL (e.g., \`http://localhost:3000\`)
|
|
139
|
+
2. **browser_state** — Read the page structure and all interactive elements
|
|
140
|
+
3. **browser_screenshot** — Visually inspect layout (returns description of current view)
|
|
141
|
+
4. **browser_scroll** — Scroll to reveal below-the-fold content
|
|
142
|
+
5. **browser_click** — Interact with elements by their index number from browser_state
|
|
143
|
+
6. **run_command** — Check server logs, console output, or network errors in parallel
|
|
144
|
+
7. **patch_file** — Fix the issue in the source file
|
|
145
|
+
8. **browser_navigate** (reload) — Verify the fix
|
|
146
|
+
|
|
147
|
+
### When to use browser tools vs. run_command
|
|
148
|
+
|
|
149
|
+
| Use browser tools when… | Use run_command when… |
|
|
150
|
+
|-----------------------------------|------------------------------------|
|
|
151
|
+
| Visual / layout bug | Compile error or type error |
|
|
152
|
+
| JS runtime error in the browser | Test suite failure |
|
|
153
|
+
| Form or interaction doesn't work | Lint warning |
|
|
154
|
+
| Need to see what page renders | File system inspection (ls, cat) |
|
|
155
|
+
| Verifying CSS / responsive design | Running build scripts |
|
|
156
|
+
|
|
157
|
+
### Browser Tool Quick Reference
|
|
158
|
+
|
|
159
|
+
- **browser_navigate(url)** — Go to a URL
|
|
160
|
+
- **browser_state()** — Get page title, URL, and numbered interactive element list
|
|
161
|
+
- **browser_click(element_index)** — Click by index from browser_state
|
|
162
|
+
- **browser_type(element_index, text)** — Type into a field
|
|
163
|
+
- **browser_scroll(direction, amount)** — Scroll up/down
|
|
164
|
+
- **browser_screenshot()** — Capture current view
|
|
165
|
+
- **browser_wait(seconds)** — Wait for async content to load
|
|
166
|
+
- **browser_close()** — Close the browser when done
|
|
167
|
+
|
|
168
|
+
### Rules
|
|
169
|
+
|
|
170
|
+
- Always call browser_state() after navigating to get the current element list
|
|
171
|
+
- Use browser_wait() after actions that trigger page loads or AJAX calls
|
|
172
|
+
- When you find a bug visually, fix it in the source file with patch_file, then reload
|
|
173
|
+
- Do NOT use browser_autofill_application in coding mode — that is for job applications only
|
|
105
174
|
`.trim();
|
|
106
175
|
// ---------------------------------------------------------------------------
|
|
107
176
|
// §4 — Jobs tools reference (appended in --jobs mode)
|
|
@@ -279,6 +348,7 @@ export function buildSystemPrompt(options) {
|
|
|
279
348
|
return [
|
|
280
349
|
baseIdentity,
|
|
281
350
|
CODING_SECTION,
|
|
351
|
+
BROWSER_CODING_SECTION,
|
|
282
352
|
...memorySections,
|
|
283
353
|
GREETING_PROTOCOL,
|
|
284
354
|
].join("\n\n---\n\n");
|
package/dist/agent/memory.d.ts
CHANGED
|
@@ -152,4 +152,32 @@ export declare function buildSessionSummary(opts: SessionSummaryInput): string;
|
|
|
152
152
|
* Append a session summary to the memory file, merge into KB, compact if needed.
|
|
153
153
|
*/
|
|
154
154
|
export declare function saveSessionMemory(opts: SessionSummaryInput): void;
|
|
155
|
+
export interface CodingSessionData {
|
|
156
|
+
/** Absolute path of the working directory when the session ended */
|
|
157
|
+
cwd: string;
|
|
158
|
+
/** ISO timestamp of when the session was saved */
|
|
159
|
+
savedAt: string;
|
|
160
|
+
/** Persona mode ("coding", "resume", etc.) */
|
|
161
|
+
mode: string;
|
|
162
|
+
/** Human-readable summary of what happened in the session */
|
|
163
|
+
summary: string;
|
|
164
|
+
/** Files the agent wrote or patched during the session */
|
|
165
|
+
filesChanged: string[];
|
|
166
|
+
/** Short description of the last thing that was built/created */
|
|
167
|
+
lastBuilt: string;
|
|
168
|
+
/** Git branch active during the session (empty if no git repo) */
|
|
169
|
+
branch: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Load the last coding session from ~/.careervivid/coding-session.md.
|
|
173
|
+
* Returns null if the file does not exist or cannot be parsed.
|
|
174
|
+
* Intentionally synchronous — called at startup before the agent loop.
|
|
175
|
+
*/
|
|
176
|
+
export declare function loadCodingSession(): CodingSessionData | null;
|
|
177
|
+
/**
|
|
178
|
+
* Save the current coding session to ~/.careervivid/coding-session.md.
|
|
179
|
+
* Overwrites the previous entry — only the most recent session is kept here.
|
|
180
|
+
* Full history is preserved in memory.md via saveSessionMemory().
|
|
181
|
+
*/
|
|
182
|
+
export declare function saveCodingSession(data: CodingSessionData): void;
|
|
155
183
|
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/memory.js
CHANGED
|
@@ -462,3 +462,62 @@ export function saveSessionMemory(opts) {
|
|
|
462
462
|
const updated = compactMemory(base + "\n" + entry);
|
|
463
463
|
saveMemory(updated);
|
|
464
464
|
}
|
|
465
|
+
// =============================================================================
|
|
466
|
+
// Coding session file — fast persona-specific pick-up context
|
|
467
|
+
// =============================================================================
|
|
468
|
+
const CODING_SESSION_FILE = join(MEMORY_DIR, "coding-session.md");
|
|
469
|
+
/**
|
|
470
|
+
* Load the last coding session from ~/.careervivid/coding-session.md.
|
|
471
|
+
* Returns null if the file does not exist or cannot be parsed.
|
|
472
|
+
* Intentionally synchronous — called at startup before the agent loop.
|
|
473
|
+
*/
|
|
474
|
+
export function loadCodingSession() {
|
|
475
|
+
if (!existsSync(CODING_SESSION_FILE))
|
|
476
|
+
return null;
|
|
477
|
+
try {
|
|
478
|
+
const raw = readFileSync(CODING_SESSION_FILE, "utf-8").trim();
|
|
479
|
+
const match = raw.match(/<!-- CODING_SESSION_START -->([\s\S]*?)<!-- CODING_SESSION_END -->/);
|
|
480
|
+
if (!match)
|
|
481
|
+
return null;
|
|
482
|
+
return JSON.parse(match[1].trim());
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Save the current coding session to ~/.careervivid/coding-session.md.
|
|
490
|
+
* Overwrites the previous entry — only the most recent session is kept here.
|
|
491
|
+
* Full history is preserved in memory.md via saveSessionMemory().
|
|
492
|
+
*/
|
|
493
|
+
export function saveCodingSession(data) {
|
|
494
|
+
try {
|
|
495
|
+
if (!existsSync(MEMORY_DIR))
|
|
496
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
497
|
+
const filesList = data.filesChanged.length > 0
|
|
498
|
+
? "## Files Changed\n" + data.filesChanged.map(f => `- \`${f}\``).join("\n")
|
|
499
|
+
: "";
|
|
500
|
+
const content = [
|
|
501
|
+
"# Last Coding Session",
|
|
502
|
+
"",
|
|
503
|
+
"> Auto-saved by cv agent --coding. Do not edit manually.",
|
|
504
|
+
"",
|
|
505
|
+
`**Saved:** ${data.savedAt}`,
|
|
506
|
+
`**Directory:** \`${data.cwd}\``,
|
|
507
|
+
`**Branch:** ${data.branch || "(no git)"}`,
|
|
508
|
+
"",
|
|
509
|
+
"## Summary",
|
|
510
|
+
data.summary,
|
|
511
|
+
"",
|
|
512
|
+
filesList,
|
|
513
|
+
"",
|
|
514
|
+
"<!-- CODING_SESSION_START -->",
|
|
515
|
+
JSON.stringify(data, null, 2),
|
|
516
|
+
"<!-- CODING_SESSION_END -->",
|
|
517
|
+
].join("\n");
|
|
518
|
+
writeFileSync(CODING_SESSION_FILE, content, "utf-8");
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
// Non-fatal — session save failure must never crash the agent
|
|
522
|
+
}
|
|
523
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/coding.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"coding.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/coding.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAmGlC,iBAAS,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG3C;AAMD,eAAO,MAAM,YAAY,EAAE,IAqC1B,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IA0B3B,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IA+C3B,CAAC;AAMF,eAAO,MAAM,iBAAiB,EAAE,IA8C/B,CAAC;AAMF,eAAO,MAAM,eAAe,EAAE,IA8C7B,CAAC;AAMF,eAAO,MAAM,cAAc,EAAE,IAmD5B,CAAC;AAEF,iEAAiE;AACjE,wBAAgB,yBAAyB,IAAI,IAAI,CAQhD;AAMD,eAAO,MAAM,eAAe,EAAE,IAkD7B,CAAC;AAMF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAQlC,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -5,45 +5,38 @@ import { Type } from '@google/genai';
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// Path-based Access Control (Least Privilege)
|
|
7
7
|
// ============================================================================
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Path-based Access Control
|
|
10
|
+
// ============================================================================
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// Scratch / temporary outputs produced by the agent
|
|
17
|
-
'tmp/',
|
|
18
|
-
'/tmp/',
|
|
19
|
-
// User's home config file is read by loadConfig() — not by read_file tool
|
|
20
|
-
];
|
|
21
|
-
/**
|
|
22
|
-
* Directories the agent is ALLOWED to WRITE.
|
|
23
|
-
* This is intentionally much narrower than the read list.
|
|
24
|
-
* Source code (src/, functions/, next-app/) is completely off-limits.
|
|
12
|
+
* In coding mode the agent operates like a real coding assistant (Antigravity-style).
|
|
13
|
+
* It can read and write files anywhere in the working directory tree.
|
|
14
|
+
*
|
|
15
|
+
* Hard-blocked regardless of mode:
|
|
16
|
+
* - .env / .env.* files (secrets)
|
|
17
|
+
* - node_modules/ (package artifacts — use npm install)
|
|
18
|
+
* - .git/ (git internals)
|
|
25
19
|
*/
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const HARD_BLOCKED_PATTERNS = [
|
|
21
|
+
/\/\.env(\.\w+)?$/, // .env, .env.local, .env.production, etc.
|
|
22
|
+
/\/node_modules\//, // node_modules anywhere in path
|
|
23
|
+
/\/\.git\//, // .git internals
|
|
30
24
|
];
|
|
25
|
+
function isHardBlocked(absPath) {
|
|
26
|
+
return HARD_BLOCKED_PATTERNS.some(re => re.test(absPath));
|
|
27
|
+
}
|
|
31
28
|
/**
|
|
32
|
-
* Resolve a raw path argument and
|
|
33
|
-
* @throws if the
|
|
29
|
+
* Resolve a raw path argument and enforce hard-block rules.
|
|
30
|
+
* @throws if the path matches a hard-blocked pattern.
|
|
34
31
|
*/
|
|
35
|
-
function guardPath(rawPath,
|
|
32
|
+
function guardPath(rawPath, _allowedPrefixes, mode) {
|
|
36
33
|
const cwd = process.cwd();
|
|
37
34
|
const absPath = normalize(resolve(rawPath));
|
|
38
|
-
|
|
39
|
-
const fullPrefix = prefix.startsWith('/') ? normalize(prefix) : normalize(join(cwd, prefix));
|
|
40
|
-
return absPath.startsWith(fullPrefix);
|
|
41
|
-
});
|
|
42
|
-
if (!allowed) {
|
|
35
|
+
if (isHardBlocked(absPath)) {
|
|
43
36
|
const rel = relative(cwd, absPath);
|
|
44
|
-
throw new Error(`⛔ Access denied: ${mode} access to "${rel}" is
|
|
45
|
-
`
|
|
46
|
-
`
|
|
37
|
+
throw new Error(`⛔ Access denied: ${mode} access to "${rel}" is blocked for safety.\n` +
|
|
38
|
+
`Blocked paths: .env files, node_modules/, .git/\n` +
|
|
39
|
+
`Edit secrets manually in your editor.`);
|
|
47
40
|
}
|
|
48
41
|
return absPath;
|
|
49
42
|
}
|
|
@@ -122,7 +115,7 @@ export const readFileTool = {
|
|
|
122
115
|
required: ['path'],
|
|
123
116
|
},
|
|
124
117
|
execute: async (args) => {
|
|
125
|
-
const absPath = guardPath(args.path,
|
|
118
|
+
const absPath = guardPath(args.path, [], 'read');
|
|
126
119
|
if (!existsSync(absPath)) {
|
|
127
120
|
throw new Error(`File not found: ${absPath}`);
|
|
128
121
|
}
|
|
@@ -159,7 +152,7 @@ export const writeFileTool = {
|
|
|
159
152
|
required: ['path', 'content'],
|
|
160
153
|
},
|
|
161
154
|
execute: async (args) => {
|
|
162
|
-
const absPath = guardPath(args.path,
|
|
155
|
+
const absPath = guardPath(args.path, [], 'write');
|
|
163
156
|
mkdirSync(dirname(absPath), { recursive: true });
|
|
164
157
|
writeFileSync(absPath, args.content, 'utf-8');
|
|
165
158
|
const lines = args.content.split('\n').length;
|
|
@@ -196,7 +189,7 @@ export const patchFileTool = {
|
|
|
196
189
|
required: ['path', 'start_line', 'end_line', 'new_content'],
|
|
197
190
|
},
|
|
198
191
|
execute: async (args) => {
|
|
199
|
-
const absPath = guardPath(args.path,
|
|
192
|
+
const absPath = guardPath(args.path, [], 'write');
|
|
200
193
|
if (!existsSync(absPath))
|
|
201
194
|
throw new Error(`File not found: ${absPath}`);
|
|
202
195
|
const lines = readFileSync(absPath, 'utf-8').split('\n');
|
|
@@ -50,6 +50,6 @@ export function printBanner(options, selectedProvider, selectedModel, _thinkingB
|
|
|
50
50
|
else {
|
|
51
51
|
console.log(chalk.dim(` Provider: ${selectedProvider} · Model: ${selectedModel} [0 credits]`));
|
|
52
52
|
}
|
|
53
|
-
const modeLabel = options.
|
|
53
|
+
const modeLabel = options.resume ? "Resume" : options.coding ? "Coding" : "Jobs & Applications";
|
|
54
54
|
console.log(chalk.dim(` Mode: ${modeLabel} · Type 'exit' to quit · /help for commands.\n`));
|
|
55
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAqJpD"}
|
|
@@ -6,6 +6,7 @@ import { getTools } from "./toolRegistry.js";
|
|
|
6
6
|
import { getSystemInstruction, buildEngine, printBanner } from "./engineResolver.js";
|
|
7
7
|
import { askLoop } from "./repl.js";
|
|
8
8
|
import { initSessionContext } from "../../agent/agentAuditLog.js";
|
|
9
|
+
import { resolvePersona } from "./personas.js";
|
|
9
10
|
const { prompt } = pkg;
|
|
10
11
|
export function registerAgentCommand(program) {
|
|
11
12
|
const agentCmd = program
|
|
@@ -13,7 +14,7 @@ export function registerAgentCommand(program) {
|
|
|
13
14
|
.description("Start an interactive autonomous AI agent in your terminal.")
|
|
14
15
|
.option("--coding", "Enable full coding tool suite (file I/O, shell, search).")
|
|
15
16
|
.option("--resume", "Add resume tools — load and discuss your CareerVivid resume.")
|
|
16
|
-
.option("--jobs", "Add job-hunting tools —
|
|
17
|
+
.option("--jobs", "Add job-hunting tools (default — enabled automatically).")
|
|
17
18
|
.option("--flash", "Use gemini-3-flash-preview — latest flash model (fast, 1 credit/turn).")
|
|
18
19
|
.option("--pro", "Use gemini-3.1-pro-preview with thinking mode (recommended for complex tasks).")
|
|
19
20
|
.option("--think <budget>", "Enable Gemini thinking mode with the given token budget (e.g. 8192).", parseInt)
|
|
@@ -24,6 +25,11 @@ export function registerAgentCommand(program) {
|
|
|
24
25
|
.option("--api-key <key>", "BYO API key for this session (not saved).")
|
|
25
26
|
.option("--base-url <url>", "Custom OpenAI-compatible base URL.")
|
|
26
27
|
.action(async (options) => {
|
|
28
|
+
// Jobs mode is the primary entry point — always enabled unless an explicit
|
|
29
|
+
// alternate mode flag (--resume, --coding) was passed.
|
|
30
|
+
if (!options.resume && !options.coding) {
|
|
31
|
+
options.jobs = true;
|
|
32
|
+
}
|
|
27
33
|
const cvApiKey = getApiKey();
|
|
28
34
|
const project = options.project ?? process.env.GOOGLE_CLOUD_PROJECT;
|
|
29
35
|
const wantsCareerVividCloud = !options.provider || options.provider === "careervivid";
|
|
@@ -114,10 +120,11 @@ export function registerAgentCommand(program) {
|
|
|
114
120
|
}
|
|
115
121
|
const engine = buildEngine(selectedProvider, selectedModel, systemInstruction, tools, thinkingBudget, includeThoughts, cvApiKey, geminiApiKey, project);
|
|
116
122
|
printBanner(options, selectedProvider, selectedModel, thinkingBudget);
|
|
117
|
-
|
|
118
|
-
const agentMode = options.jobs ? "jobs" : options.resume ? "resume" : "coding";
|
|
123
|
+
const agentMode = options.resume ? "resume" : options.coding ? "coding" : "jobs";
|
|
119
124
|
initSessionContext(agentMode, selectedModel);
|
|
120
|
-
|
|
125
|
+
// Resolve the active persona — drives menu, pick-up prompt, and context
|
|
126
|
+
const persona = resolvePersona(options);
|
|
127
|
+
await askLoop(engine, options, selectedProvider, selectedModel, cvApiKey, systemInstruction, tools, persona);
|
|
121
128
|
});
|
|
122
129
|
agentCmd.command("config")
|
|
123
130
|
.description("Configure the default LLM provider for cv agent")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* personas.ts — Extensible Persona Registry for cv agent
|
|
3
|
+
*
|
|
4
|
+
* Each persona defines:
|
|
5
|
+
* - id: unique key (matches the CLI flag name)
|
|
6
|
+
* - label: human-readable name shown in the banner
|
|
7
|
+
* - menuItems: first-turn autocomplete menu entries
|
|
8
|
+
* - pickUpPrompt: the system message injected when the user chooses
|
|
9
|
+
* "Pick up where we left off" — workspace-aware, not job-aware
|
|
10
|
+
* - contextGather: async function returning a markdown context snapshot
|
|
11
|
+
* of the current environment relevant to this persona
|
|
12
|
+
*
|
|
13
|
+
* ── Adding a new persona ──────────────────────────────────────────────────────
|
|
14
|
+
* 1. Add a new PersonaDefinition entry in PERSONAS below.
|
|
15
|
+
* 2. Register the matching --flag in index.ts (one line).
|
|
16
|
+
* 3. Done. The menu, context, and system prompt routing all work automatically.
|
|
17
|
+
*/
|
|
18
|
+
export interface PersonaDefinition {
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
/** Displayed in the first-turn autocomplete menu */
|
|
22
|
+
menuItems: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Optional per-item prompt overrides.
|
|
25
|
+
* Key = exact menu item string. Value = the prompt sent to the agent.
|
|
26
|
+
* Items without an entry get the default emoji-stripped text.
|
|
27
|
+
*/
|
|
28
|
+
menuPrompts?: Record<string, string>;
|
|
29
|
+
/** Used as the agent prompt when "Pick up where we left off" is selected */
|
|
30
|
+
pickUpPrompt: string;
|
|
31
|
+
/** Gathers live workspace context for the pick-up prompt */
|
|
32
|
+
contextGather: () => Promise<string>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the active PersonaDefinition from the CLI options object.
|
|
36
|
+
* Falls back to the jobs persona.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolvePersona(options: Record<string, any>): PersonaDefinition;
|
|
39
|
+
/** All registered personas — useful for dynamic --help generation. */
|
|
40
|
+
export declare const ALL_PERSONAS: PersonaDefinition[];
|
|
41
|
+
//# sourceMappingURL=personas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personas.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/personas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACtC;AAwTD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAK9E;AAED,sEAAsE;AACtE,eAAO,MAAM,YAAY,qBAA6B,CAAC"}
|