careervivid 2.1.22 → 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/index.d.ts.map +1 -1
- package/dist/commands/agent/index.js +4 -2
- 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/input.d.ts +12 -3
- package/dist/commands/agent/repl/input.d.ts.map +1 -1
- package/dist/commands/agent/repl/input.js +25 -17
- package/dist/commands/agent/repl.d.ts +2 -1
- package/dist/commands/agent/repl.d.ts.map +1 -1
- package/dist/commands/agent/repl.js +2 -2
- package/dist/commands/agent/toolRegistry.d.ts.map +1 -1
- package/dist/commands/agent/toolRegistry.js +4 -2
- 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');
|
|
@@ -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
|
|
@@ -119,10 +120,11 @@ export function registerAgentCommand(program) {
|
|
|
119
120
|
}
|
|
120
121
|
const engine = buildEngine(selectedProvider, selectedModel, systemInstruction, tools, thinkingBudget, includeThoughts, cvApiKey, geminiApiKey, project);
|
|
121
122
|
printBanner(options, selectedProvider, selectedModel, thinkingBudget);
|
|
122
|
-
// agentMode: jobs is now always the default
|
|
123
123
|
const agentMode = options.resume ? "resume" : options.coding ? "coding" : "jobs";
|
|
124
124
|
initSessionContext(agentMode, selectedModel);
|
|
125
|
-
|
|
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);
|
|
126
128
|
});
|
|
127
129
|
agentCmd.command("config")
|
|
128
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"}
|
|
@@ -0,0 +1,323 @@
|
|
|
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
|
+
import { execSync } from "child_process";
|
|
19
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
import { loadCodingSession } from "../../agent/memory.js";
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
// Shared context helpers
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
function tryExec(cmd) {
|
|
26
|
+
try {
|
|
27
|
+
return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getRecentlyModifiedFiles(dir, maxFiles = 8) {
|
|
34
|
+
const results = [];
|
|
35
|
+
function walk(current, depth = 0) {
|
|
36
|
+
if (depth > 3)
|
|
37
|
+
return;
|
|
38
|
+
let entries;
|
|
39
|
+
try {
|
|
40
|
+
entries = readdirSync(current);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const name of entries) {
|
|
46
|
+
if (name.startsWith(".") || name === "node_modules" || name === "__pycache__" || name === ".git")
|
|
47
|
+
continue;
|
|
48
|
+
const full = join(current, name);
|
|
49
|
+
try {
|
|
50
|
+
const st = statSync(full);
|
|
51
|
+
if (st.isDirectory()) {
|
|
52
|
+
walk(full, depth + 1);
|
|
53
|
+
}
|
|
54
|
+
else if (/\.(ts|tsx|js|jsx|py|go|rs|java|cs|cpp|c|md|json|yaml|yml|sh|toml)$/.test(name)) {
|
|
55
|
+
results.push({ path: full.replace(dir + "/", ""), mtime: st.mtimeMs });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// skip
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
walk(dir);
|
|
64
|
+
return results
|
|
65
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
66
|
+
.slice(0, maxFiles)
|
|
67
|
+
.map((f) => f.path);
|
|
68
|
+
}
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Coding Persona
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
async function gatherCodingContext() {
|
|
73
|
+
const cwd = process.cwd();
|
|
74
|
+
const lines = [];
|
|
75
|
+
// Git branch + last commit
|
|
76
|
+
const branch = tryExec("git branch --show-current");
|
|
77
|
+
const lastCommit = tryExec("git log --oneline -1");
|
|
78
|
+
const gitStatus = tryExec("git status --short").split("\n").slice(0, 10).join("\n");
|
|
79
|
+
if (branch)
|
|
80
|
+
lines.push(`**Git branch:** \`${branch}\``);
|
|
81
|
+
if (lastCommit)
|
|
82
|
+
lines.push(`**Last commit:** ${lastCommit}`);
|
|
83
|
+
if (gitStatus)
|
|
84
|
+
lines.push(`**Uncommitted changes:**\n\`\`\`\n${gitStatus}\n\`\`\``);
|
|
85
|
+
// Recent files
|
|
86
|
+
const recentFiles = getRecentlyModifiedFiles(cwd);
|
|
87
|
+
if (recentFiles.length > 0) {
|
|
88
|
+
lines.push(`**Recently modified files:**\n${recentFiles.map((f) => ` • \`${f}\``).join("\n")}`);
|
|
89
|
+
}
|
|
90
|
+
// Package.json / pyproject.toml project name
|
|
91
|
+
const pkgPath = join(cwd, "package.json");
|
|
92
|
+
const pyPath = join(cwd, "pyproject.toml");
|
|
93
|
+
if (existsSync(pkgPath)) {
|
|
94
|
+
try {
|
|
95
|
+
const pkg = JSON.parse(require("fs").readFileSync(pkgPath, "utf-8"));
|
|
96
|
+
lines.push(`**Project:** ${pkg.name ?? "unknown"} v${pkg.version ?? "?"} (Node.js)`);
|
|
97
|
+
}
|
|
98
|
+
catch { /* ok */ }
|
|
99
|
+
}
|
|
100
|
+
else if (existsSync(pyPath)) {
|
|
101
|
+
const name = tryExec("grep '^name' pyproject.toml | head -1");
|
|
102
|
+
if (name)
|
|
103
|
+
lines.push(`**Project (Python):** ${name}`);
|
|
104
|
+
}
|
|
105
|
+
return lines.length > 0 ? lines.join("\n") : "No git repo or recognised project found in current directory.";
|
|
106
|
+
}
|
|
107
|
+
const CODING_PERSONA = {
|
|
108
|
+
id: "coding",
|
|
109
|
+
label: "Coding",
|
|
110
|
+
menuItems: [
|
|
111
|
+
"🔍 Analyze the current codebase or a specific file",
|
|
112
|
+
"🛠️ Debug an error or failing test",
|
|
113
|
+
"🏗️ Scaffold a new feature or module into an existing project",
|
|
114
|
+
"🔄 Refactor or optimize existing code",
|
|
115
|
+
"📖 Explain how a piece of code works",
|
|
116
|
+
"🧪 Write or improve tests",
|
|
117
|
+
"🆕 Create a brand-new project or app in a new folder",
|
|
118
|
+
"🌐 Debug or test in the browser",
|
|
119
|
+
"🗓️ Pick up where we left off",
|
|
120
|
+
],
|
|
121
|
+
menuPrompts: {
|
|
122
|
+
"🔍 Analyze the current codebase or a specific file": [
|
|
123
|
+
`The user wants to analyze their codebase. Current working directory: \`${process.cwd()}\`.`,
|
|
124
|
+
`1. Run get_file_tree on the current directory to show the project structure.`,
|
|
125
|
+
`2. Ask the user: "Which file or directory would you like me to analyze? Or should I give you an overview of the whole project?"`,
|
|
126
|
+
`3. Wait for their answer before reading any files.`,
|
|
127
|
+
].join("\n"),
|
|
128
|
+
"🛠️ Debug an error or failing test": [
|
|
129
|
+
`The user wants to debug an error or failing test. Current working directory: \`${process.cwd()}\`.`,
|
|
130
|
+
`1. Ask the user: "Please paste the full error message or test output, and tell me which file it's coming from."`,
|
|
131
|
+
`2. Once you have the error, read the relevant file(s) and identify the root cause.`,
|
|
132
|
+
`3. Apply a targeted fix with patch_file. Verify with run_command (e.g., npm test or tsc --noEmit).`,
|
|
133
|
+
].join("\n"),
|
|
134
|
+
"🏗️ Scaffold a new feature or module into an existing project": [
|
|
135
|
+
`The user wants to scaffold a feature or module into their EXISTING project. Current working directory: \`${process.cwd()}\`.`,
|
|
136
|
+
``,
|
|
137
|
+
`Before writing ANY files, ask the user these two questions in a single message:`,
|
|
138
|
+
`1. "What feature or module would you like to add?" — e.g., authentication, API route, React component, database model`,
|
|
139
|
+
`2. "Which directory should I add it to?" — e.g., src/components, src/api, lib/`,
|
|
140
|
+
``,
|
|
141
|
+
`Do NOT write any files until you have both answers.`,
|
|
142
|
+
`Once you have the answers: read the relevant existing files first → Plan → write_file/patch_file → run_command to verify → Report all changed paths.`,
|
|
143
|
+
].join("\n"),
|
|
144
|
+
"🆕 Create a brand-new project or app in a new folder": [
|
|
145
|
+
`The user wants to create a BRAND-NEW standalone project in a new folder. Current working directory: \`${process.cwd()}\`.`,
|
|
146
|
+
``,
|
|
147
|
+
`Ask the user ONE message with these two questions:`,
|
|
148
|
+
`1. "What would you like to build?"`,
|
|
149
|
+
` Give them examples to spark ideas:`,
|
|
150
|
+
` • A modern HTML/CSS/JS dashboard visualising my CareerVivid job pipeline`,
|
|
151
|
+
` • A React + Vite app with TypeScript`,
|
|
152
|
+
` • A FastAPI Python REST API`,
|
|
153
|
+
` • A Next.js full-stack app`,
|
|
154
|
+
` • A Node.js CLI tool`,
|
|
155
|
+
` • A static landing page`,
|
|
156
|
+
` • Or describe anything else`,
|
|
157
|
+
`2. "What should the folder be called?" (e.g., job-dashboard, my-api, cool-app)`,
|
|
158
|
+
``,
|
|
159
|
+
`Do NOT create any files or run any commands until you have both answers.`,
|
|
160
|
+
``,
|
|
161
|
+
`Once you have the answers, follow this execution loop:`,
|
|
162
|
+
` a. Plan: list every file you will create with a one-line description of each`,
|
|
163
|
+
` b. Create the folder and write EVERY file using write_file`,
|
|
164
|
+
` — For HTML/CSS/JS projects: write index.html, style.css, app.js (and any sub-files) with REAL, complete, production-quality code — NO placeholders`,
|
|
165
|
+
` — For Node/Python projects: write package.json/pyproject.toml, README.md, and all source files`,
|
|
166
|
+
` — For React/Next/Vite apps: write all config files AND source components`,
|
|
167
|
+
` c. Run run_command to verify (e.g., \`tsc --noEmit\`, \`python -m py_compile\`, or \`open index.html\`)`,
|
|
168
|
+
` d. Report: list every file created with its absolute path so the user can open them immediately`,
|
|
169
|
+
``,
|
|
170
|
+
`Special case — Job Pipeline Dashboard:`,
|
|
171
|
+
`If the user asks for a dashboard based on their job application pipeline or CareerVivid data:`,
|
|
172
|
+
` 1. Run run_command with \`cat ~/career-vivid/jobs.csv\` to read their real job data`,
|
|
173
|
+
` 2. Parse the CSV and embed the actual data as a JavaScript array in the dashboard`,
|
|
174
|
+
` 3. Build a beautiful, modern HTML/CSS/JS single-file dashboard with:`,
|
|
175
|
+
` — Kanban-style status columns (To Apply / Applied / Interview / Offer / Rejected)`,
|
|
176
|
+
` — Cards for each job showing company, role, status, excitement score`,
|
|
177
|
+
` — Summary stats bar (total jobs, applied %, top companies)`,
|
|
178
|
+
` — A color-coded priority chart`,
|
|
179
|
+
` — Responsive design that works on mobile`,
|
|
180
|
+
` 4. Write it to \`job-dashboard/index.html\` as a self-contained file (inline CSS + JS)`,
|
|
181
|
+
` 5. Run \`open job-dashboard/index.html\` so the user sees it immediately in their browser`,
|
|
182
|
+
].join("\n"),
|
|
183
|
+
"🔄 Refactor or optimize existing code": [
|
|
184
|
+
`The user wants to refactor or optimize code. Current working directory: \`${process.cwd()}\`.`,
|
|
185
|
+
`1. Ask: "Which file or function would you like me to refactor? What's the goal — performance, readability, type safety, or something else?"`,
|
|
186
|
+
`2. Wait for their answer. Then read the file, propose a brief Plan, and apply changes with patch_file.`,
|
|
187
|
+
].join("\n"),
|
|
188
|
+
"📖 Explain how a piece of code works": [
|
|
189
|
+
`The user wants an explanation of some code. Current working directory: \`${process.cwd()}\`.`,
|
|
190
|
+
`Ask: "Which file, function, or class would you like me to explain?"`,
|
|
191
|
+
`Once they answer, read the file and give a clear, structured explanation.`,
|
|
192
|
+
].join("\n"),
|
|
193
|
+
"🧪 Write or improve tests": [
|
|
194
|
+
`The user wants to write or improve tests. Current working directory: \`${process.cwd()}\`.`,
|
|
195
|
+
`1. Ask: "Which file or function should I write tests for? And do you want unit tests, integration tests, or browser-based E2E tests?"`,
|
|
196
|
+
`2. Once they answer:`,
|
|
197
|
+
` • For unit/integration tests: read the source file, detect the test framework (check package.json), write test files to disk with write_file, run with run_command.`,
|
|
198
|
+
` • For browser E2E tests: use browser_navigate to open the app, browser_state to inspect, browser_click/browser_type to simulate user actions, browser_screenshot to verify results.`,
|
|
199
|
+
`3. Report all test files created and test results.`,
|
|
200
|
+
].join("\n"),
|
|
201
|
+
"🌐 Debug or test in the browser": [
|
|
202
|
+
`The user wants to debug a visual/runtime issue OR test a feature directly in the browser. Current working directory: \`${process.cwd()}\`.`,
|
|
203
|
+
``,
|
|
204
|
+
`First, ask ONE clarifying question: "What would you like to do?"`,
|
|
205
|
+
`Give these options:`,
|
|
206
|
+
` • Debug a visual or layout bug in my running app`,
|
|
207
|
+
` • Debug a JavaScript runtime error`,
|
|
208
|
+
` • Smoke test / manually test a feature in the browser`,
|
|
209
|
+
` • Run a browser-based E2E test on my app`,
|
|
210
|
+
` • Verify my generated HTML/CSS/JS file looks correct`,
|
|
211
|
+
``,
|
|
212
|
+
`Once you know what they want, follow the browser debug/test loop:`,
|
|
213
|
+
`1. Ask for the URL if not provided (e.g., http://localhost:3000 or a file path like ./index.html)`,
|
|
214
|
+
`2. Call browser_navigate(url) to open it`,
|
|
215
|
+
`3. Call browser_state() to get the page structure`,
|
|
216
|
+
`4. Call browser_screenshot() to visually inspect the page`,
|
|
217
|
+
`5. For debugging: identify the root cause, patch_file to fix it, browser_navigate to reload and verify`,
|
|
218
|
+
`6. For testing: browser_click / browser_type to simulate user interactions, browser_screenshot to confirm the expected result`,
|
|
219
|
+
`7. Report what was found and fixed/verified`,
|
|
220
|
+
``,
|
|
221
|
+
`If the user's dev server is not running, offer to start it: run_command with \`npm run dev\` or equivalent.`,
|
|
222
|
+
].join("\n"),
|
|
223
|
+
},
|
|
224
|
+
pickUpPrompt: (() => {
|
|
225
|
+
// Read the coding session file synchronously — this is O(1), no tool calls.
|
|
226
|
+
const last = loadCodingSession();
|
|
227
|
+
if (last) {
|
|
228
|
+
const fileList = last.filesChanged.length > 0
|
|
229
|
+
? `Files changed last session:\n${last.filesChanged.slice(0, 10).map(f => ` • \`${f}\``).join("\n")}`
|
|
230
|
+
: "";
|
|
231
|
+
return [
|
|
232
|
+
`## Resuming Last Coding Session`,
|
|
233
|
+
``,
|
|
234
|
+
`Here is the context from the last coding session (read from local memory — no tool calls needed):`,
|
|
235
|
+
``,
|
|
236
|
+
`- **Directory:** \`${last.cwd}\``,
|
|
237
|
+
`- **Git branch:** ${last.branch || "(no git repo)"}`,
|
|
238
|
+
`- **Last built:** ${last.lastBuilt || "(unknown)"}`,
|
|
239
|
+
`- **Session saved:** ${new Date(last.savedAt).toLocaleString()}`,
|
|
240
|
+
``,
|
|
241
|
+
`**What happened last time:**`,
|
|
242
|
+
last.summary,
|
|
243
|
+
``,
|
|
244
|
+
fileList,
|
|
245
|
+
``,
|
|
246
|
+
`The current working directory is now: \`${process.cwd()}\``,
|
|
247
|
+
``,
|
|
248
|
+
`Greet the user with a ONE-LINE recap of what was worked on last time and ask what they want to continue or start next.`,
|
|
249
|
+
`Do NOT run any git commands or scan the filesystem — you already have the context above.`,
|
|
250
|
+
`Only call tools if the user asks for something new.`,
|
|
251
|
+
].filter(Boolean).join("\n");
|
|
252
|
+
}
|
|
253
|
+
// No previous session — do a lightweight orientation
|
|
254
|
+
return [
|
|
255
|
+
`No previous coding session found in memory.`,
|
|
256
|
+
`Current working directory: \`${process.cwd()}\`.`,
|
|
257
|
+
``,
|
|
258
|
+
`Ask the user: "What would you like to work on today?" — give them the menu options as a reminder.`,
|
|
259
|
+
`Do NOT run any commands or scan files until they answer.`,
|
|
260
|
+
].join("\n");
|
|
261
|
+
})(),
|
|
262
|
+
contextGather: gatherCodingContext,
|
|
263
|
+
};
|
|
264
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
265
|
+
// Jobs Persona (default)
|
|
266
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
267
|
+
const JOBS_PERSONA = {
|
|
268
|
+
id: "jobs",
|
|
269
|
+
label: "Jobs & Applications",
|
|
270
|
+
menuItems: [
|
|
271
|
+
"📄 View or update my resume",
|
|
272
|
+
"🔍 Search for job opportunities",
|
|
273
|
+
"📊 Check my job pipeline / tracker",
|
|
274
|
+
"✉️ Draft a cover letter or tailor my resume",
|
|
275
|
+
"🎙 Start an AI mock interview (voice or text)",
|
|
276
|
+
"📈 Get an overview of my job search progress",
|
|
277
|
+
"🗓️ Pick up where we left off",
|
|
278
|
+
],
|
|
279
|
+
pickUpPrompt: "The user wants to pick up their last job-search session. Check their resume list (cv tracker resume list), review their job tracker pipeline (cv tracker list), and provide a concise overview of where they left off. Suggest the most impactful next action.",
|
|
280
|
+
contextGather: async () => "",
|
|
281
|
+
};
|
|
282
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
283
|
+
// Resume Persona
|
|
284
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
285
|
+
const RESUME_PERSONA = {
|
|
286
|
+
id: "resume",
|
|
287
|
+
label: "Resume Builder",
|
|
288
|
+
menuItems: [
|
|
289
|
+
"📄 View or load my current resume",
|
|
290
|
+
"✏️ Update a section of my resume",
|
|
291
|
+
"🎯 Tailor my resume for a specific job",
|
|
292
|
+
"🔗 Sync resume to my portfolio",
|
|
293
|
+
"💡 Get suggestions to improve my resume",
|
|
294
|
+
"🗓️ Pick up where we left off",
|
|
295
|
+
],
|
|
296
|
+
pickUpPrompt: "The user wants to pick up their last resume session. List their resumes with get_resume or list_resumes, identify the most recently updated one, and summarise what was last worked on. Suggest the next improvement.",
|
|
297
|
+
contextGather: async () => "",
|
|
298
|
+
};
|
|
299
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
300
|
+
// Persona registry
|
|
301
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
302
|
+
const PERSONA_MAP = {
|
|
303
|
+
coding: CODING_PERSONA,
|
|
304
|
+
jobs: JOBS_PERSONA,
|
|
305
|
+
resume: RESUME_PERSONA,
|
|
306
|
+
// Future:
|
|
307
|
+
// ceo: CEO_PERSONA,
|
|
308
|
+
// finance: FINANCE_PERSONA,
|
|
309
|
+
// marketing: MARKETING_PERSONA,
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* Resolve the active PersonaDefinition from the CLI options object.
|
|
313
|
+
* Falls back to the jobs persona.
|
|
314
|
+
*/
|
|
315
|
+
export function resolvePersona(options) {
|
|
316
|
+
for (const key of Object.keys(PERSONA_MAP)) {
|
|
317
|
+
if (options[key])
|
|
318
|
+
return PERSONA_MAP[key];
|
|
319
|
+
}
|
|
320
|
+
return JOBS_PERSONA;
|
|
321
|
+
}
|
|
322
|
+
/** All registered personas — useful for dynamic --help generation. */
|
|
323
|
+
export const ALL_PERSONAS = Object.values(PERSONA_MAP);
|
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
* repl/input.ts
|
|
3
3
|
*
|
|
4
4
|
* Handles all user input collection:
|
|
5
|
-
* - First-turn quick-action menu (autocomplete)
|
|
5
|
+
* - First-turn quick-action menu (autocomplete) — persona-aware
|
|
6
6
|
* - Subsequent turn free-text input
|
|
7
7
|
* - Multi-line paste mode (<<<)
|
|
8
8
|
* - Fast-paste buffer accumulation
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import type { PersonaDefinition } from "../personas.js";
|
|
11
|
+
/**
|
|
12
|
+
* Read the first-turn menu selection.
|
|
13
|
+
* Returns the cleaned user-intent string ready to send to the agent.
|
|
14
|
+
*
|
|
15
|
+
* When the user picks "Pick up where we left off" the persona's
|
|
16
|
+
* `pickUpPrompt` is returned instead of the raw menu label — so
|
|
17
|
+
* the agent receives a rich context-gathering instruction specific
|
|
18
|
+
* to the active mode (coding, jobs, resume, …).
|
|
19
|
+
*/
|
|
20
|
+
export declare function readFirstTurnInput(persona: PersonaDefinition): Promise<string>;
|
|
12
21
|
/** Reads a single multi-line paste block. User ends with an empty Enter. */
|
|
13
22
|
export declare function readMultiLineInput(prefix: string): Promise<string>;
|
|
14
23
|
export interface InputResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../../src/commands/agent/repl/input.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../../src/commands/agent/repl/input.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMxD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCpF;AAED,4EAA4E;AAC5E,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBxE;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,gFAAgF;AAChF,wBAAsB,eAAe,CAAC,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAUrF"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* repl/input.ts
|
|
3
3
|
*
|
|
4
4
|
* Handles all user input collection:
|
|
5
|
-
* - First-turn quick-action menu (autocomplete)
|
|
5
|
+
* - First-turn quick-action menu (autocomplete) — persona-aware
|
|
6
6
|
* - Subsequent turn free-text input
|
|
7
7
|
* - Multi-line paste mode (<<<)
|
|
8
8
|
* - Fast-paste buffer accumulation
|
|
@@ -10,19 +10,19 @@
|
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import pkg from "enquirer";
|
|
12
12
|
const { prompt } = pkg;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export async function readFirstTurnInput() {
|
|
13
|
+
const PICK_UP_LABEL = "🗓️ Pick up where we left off";
|
|
14
|
+
/**
|
|
15
|
+
* Read the first-turn menu selection.
|
|
16
|
+
* Returns the cleaned user-intent string ready to send to the agent.
|
|
17
|
+
*
|
|
18
|
+
* When the user picks "Pick up where we left off" the persona's
|
|
19
|
+
* `pickUpPrompt` is returned instead of the raw menu label — so
|
|
20
|
+
* the agent receives a rich context-gathering instruction specific
|
|
21
|
+
* to the active mode (coding, jobs, resume, …).
|
|
22
|
+
*/
|
|
23
|
+
export async function readFirstTurnInput(persona) {
|
|
24
24
|
console.log(chalk.dim(" What would you like to do today?\n"));
|
|
25
|
-
for (const item of
|
|
25
|
+
for (const item of persona.menuItems) {
|
|
26
26
|
console.log(chalk.dim(` ${item}`));
|
|
27
27
|
}
|
|
28
28
|
console.log("");
|
|
@@ -30,17 +30,25 @@ export async function readFirstTurnInput() {
|
|
|
30
30
|
type: "autocomplete",
|
|
31
31
|
name: "choice",
|
|
32
32
|
message: chalk.bold.hex("#6366f1")("❯") + chalk.dim(" ·"),
|
|
33
|
-
limit:
|
|
33
|
+
limit: persona.menuItems.length,
|
|
34
34
|
suggest(input, choices) {
|
|
35
35
|
if (!input)
|
|
36
36
|
return choices;
|
|
37
37
|
return choices.filter((c) => c.value.toLowerCase().includes(input.toLowerCase()));
|
|
38
38
|
},
|
|
39
|
-
choices:
|
|
39
|
+
choices: persona.menuItems.map((item) => ({ name: item, value: item })),
|
|
40
40
|
footer: chalk.dim(" ↑↓ to navigate · type to filter · Enter to send"),
|
|
41
41
|
});
|
|
42
|
-
const raw = firstResp.choice?.trim()
|
|
43
|
-
//
|
|
42
|
+
const raw = firstResp.choice?.trim() ?? "";
|
|
43
|
+
// 1. "Pick up where we left off" → persona-specific context-gathering prompt
|
|
44
|
+
if (raw === PICK_UP_LABEL || raw.toLowerCase().includes("pick up")) {
|
|
45
|
+
return persona.pickUpPrompt;
|
|
46
|
+
}
|
|
47
|
+
// 2. Per-item prompt override (e.g. scaffold, debug) → rich targeted prompt
|
|
48
|
+
if (persona.menuPrompts?.[raw]) {
|
|
49
|
+
return persona.menuPrompts[raw];
|
|
50
|
+
}
|
|
51
|
+
// 3. Default: strip emoji prefix and send clean text
|
|
44
52
|
return raw.replace(/^[\p{Emoji}\s]+/u, "").trim() || raw;
|
|
45
53
|
}
|
|
46
54
|
/** Reads a single multi-line paste block. User ends with an empty Enter. */
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { CareerVividProxyEngine } from "../../agent/CareerVividProxyEngine.js";
|
|
12
12
|
import { QueryEngine } from "../../agent/QueryEngine.js";
|
|
13
13
|
import { type LLMProvider } from "../../config.js";
|
|
14
|
+
import type { PersonaDefinition } from "./personas.js";
|
|
14
15
|
export declare function printCreditStatus(remaining: number | null, limit?: number | null): void;
|
|
15
16
|
export declare function askLoop(engine: QueryEngine | CareerVividProxyEngine | null, options: {
|
|
16
17
|
verbose?: boolean;
|
|
@@ -22,5 +23,5 @@ export declare function askLoop(engine: QueryEngine | CareerVividProxyEngine | n
|
|
|
22
23
|
jobs?: boolean;
|
|
23
24
|
resume?: boolean;
|
|
24
25
|
coding?: boolean;
|
|
25
|
-
}, selectedProvider: LLMProvider, selectedModel: string, cvApiKey: string | undefined, systemInstruction: string, tools: any[]): Promise<void>;
|
|
26
|
+
}, selectedProvider: LLMProvider, selectedModel: string, cvApiKey: string | undefined, systemInstruction: string, tools: any[], persona: PersonaDefinition): Promise<void>;
|
|
26
27
|
//# sourceMappingURL=repl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAInD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAUvD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,QAsBtF;AA+DD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,WAAW,GAAG,sBAAsB,GAAG,IAAI,EACnD,OAAO,EAAE;IACP,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,EACD,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,GAAG,EAAE,EACZ,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CA4Kf"}
|
|
@@ -95,7 +95,7 @@ async function handle401Error(selectedProvider, options) {
|
|
|
95
95
|
return true; // re-prompt
|
|
96
96
|
}
|
|
97
97
|
// ── Main REPL loop ────────────────────────────────────────────────────────────
|
|
98
|
-
export async function askLoop(engine, options, selectedProvider, selectedModel, cvApiKey, systemInstruction, tools) {
|
|
98
|
+
export async function askLoop(engine, options, selectedProvider, selectedModel, cvApiKey, systemInstruction, tools, persona) {
|
|
99
99
|
let sessionTurns = 0;
|
|
100
100
|
let sessionLimit = null;
|
|
101
101
|
let currentModel = selectedModel;
|
|
@@ -123,7 +123,7 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
123
123
|
let userInput;
|
|
124
124
|
// ── Collect user input ──────────────────────────────────────────────
|
|
125
125
|
if (isFirstTurn) {
|
|
126
|
-
userInput = await readFirstTurnInput();
|
|
126
|
+
userInput = await readFirstTurnInput(persona);
|
|
127
127
|
}
|
|
128
128
|
else {
|
|
129
129
|
const { text, isFastLine } = await readNormalInput(pasteBuffer.length > 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolRegistry.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/toolRegistry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAyO3C,wBAAgB,QAAQ,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"toolRegistry.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/toolRegistry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAyO3C,wBAAgB,QAAQ,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,EAAE,CAsDhG"}
|
|
@@ -274,6 +274,8 @@ export function getTools(options) {
|
|
|
274
274
|
}
|
|
275
275
|
return tools;
|
|
276
276
|
}
|
|
277
|
-
// Default coding mode: file system +
|
|
278
|
-
|
|
277
|
+
// Default coding mode: file system tools + browser debugging tools
|
|
278
|
+
// Browser tools are included for dev debugging (inspect layout, check errors, verify local server).
|
|
279
|
+
// publish_article is NOT included — agent writes code to disk, not to community posts.
|
|
280
|
+
return [...ALL_CODING_TOOLS, ...ALL_BROWSER_TOOLS];
|
|
279
281
|
}
|
package/package.json
CHANGED