career-compass-mcp 2.0.0 → 2.1.0
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/README.md +368 -340
- package/build/bin/cli.d.ts +5 -5
- package/build/bin/cli.d.ts.map +1 -1
- package/build/bin/cli.js +55 -49
- package/build/bin/cli.js.map +1 -1
- package/build/src/dashboard-lite/render.d.ts +21 -0
- package/build/src/dashboard-lite/render.d.ts.map +1 -0
- package/build/src/dashboard-lite/render.js +216 -0
- package/build/src/dashboard-lite/render.js.map +1 -0
- package/build/src/dashboard-lite/server.d.ts +14 -0
- package/build/src/dashboard-lite/server.d.ts.map +1 -0
- package/build/src/dashboard-lite/server.js +50 -0
- package/build/src/dashboard-lite/server.js.map +1 -0
- package/build/src/schemas/career-schema.d.ts +108 -0
- package/build/src/schemas/career-schema.d.ts.map +1 -1
- package/build/src/schemas/career-schema.js +43 -0
- package/build/src/schemas/career-schema.js.map +1 -1
- package/build/src/server.d.ts.map +1 -1
- package/build/src/server.js +4 -1
- package/build/src/server.js.map +1 -1
- package/build/src/storage/file-store.d.ts +43 -0
- package/build/src/storage/file-store.d.ts.map +1 -1
- package/build/src/storage/file-store.js +133 -4
- package/build/src/storage/file-store.js.map +1 -1
- package/build/src/tools/career-kb.d.ts.map +1 -1
- package/build/src/tools/career-kb.js +94 -8
- package/build/src/tools/career-kb.js.map +1 -1
- package/build/src/tools/interview.d.ts.map +1 -1
- package/build/src/tools/interview.js +5 -2
- package/build/src/tools/interview.js.map +1 -1
- package/build/src/tools/opportunity.d.ts.map +1 -1
- package/build/src/tools/opportunity.js +5 -2
- package/build/src/tools/opportunity.js.map +1 -1
- package/build/src/tools/pipeline.d.ts.map +1 -1
- package/build/src/tools/pipeline.js +28 -15
- package/build/src/tools/pipeline.js.map +1 -1
- package/build/src/tools/resume.d.ts.map +1 -1
- package/build/src/tools/resume.js +6 -3
- package/build/src/tools/resume.js.map +1 -1
- package/build/src/tools/signal-digest.d.ts +15 -0
- package/build/src/tools/signal-digest.d.ts.map +1 -0
- package/build/src/tools/signal-digest.js +45 -0
- package/build/src/tools/signal-digest.js.map +1 -0
- package/build/src/untrusted.d.ts +63 -0
- package/build/src/untrusted.d.ts.map +1 -0
- package/build/src/untrusted.js +83 -0
- package/build/src/untrusted.js.map +1 -0
- package/build/src/version.d.ts +23 -0
- package/build/src/version.d.ts.map +1 -0
- package/build/src/version.js +66 -0
- package/build/src/version.js.map +1 -0
- package/data/example/career/education.yaml +20 -0
- package/data/example/career/journal.yaml +56 -0
- package/data/example/career/projects.yaml +47 -0
- package/package.json +3 -13
package/build/bin/cli.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* "unknown"). Returns "unknown" only when package.json is missing/unreadable.
|
|
3
|
+
* Re-exported from src/version.ts so the CLI and the MCP server resolve the
|
|
4
|
+
* version through exactly one code path. Kept as a named export here because
|
|
5
|
+
* cli-version.test.ts exercises it directly.
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
import { readPkgVersion } from "../src/version.js";
|
|
8
|
+
export { readPkgVersion };
|
|
9
9
|
//# sourceMappingURL=cli.d.ts.map
|
package/build/bin/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAaA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAaA;;;;GAIG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/build/bin/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
import { existsSync, mkdirSync
|
|
4
|
+
import { existsSync, mkdirSync } from "fs";
|
|
5
5
|
import { createServer as createNetServer } from "net";
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -9,20 +9,12 @@ const args = process.argv.slice(2);
|
|
|
9
9
|
// ─── Version ─────────────────────────────────────────────────────────────────
|
|
10
10
|
const __cliDir = fileURLToPath(new URL(".", import.meta.url));
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* "unknown"). Returns "unknown" only when package.json is missing/unreadable.
|
|
12
|
+
* Re-exported from src/version.ts so the CLI and the MCP server resolve the
|
|
13
|
+
* version through exactly one code path. Kept as a named export here because
|
|
14
|
+
* cli-version.test.ts exercises it directly.
|
|
16
15
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const pkgJson = JSON.parse(readFileSync(join(cliDir, "..", "..", "package.json"), "utf-8"));
|
|
20
|
-
return typeof pkgJson.version === "string" ? pkgJson.version : "unknown";
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return "unknown"; // package.json not found — version will show as "unknown"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
16
|
+
import { readPkgVersion } from "../src/version.js";
|
|
17
|
+
export { readPkgVersion };
|
|
26
18
|
const pkgVersion = readPkgVersion(__cliDir);
|
|
27
19
|
if (args.includes("--version") || args.includes("-v")) {
|
|
28
20
|
console.log(`career-compass-mcp v${pkgVersion}`);
|
|
@@ -35,13 +27,15 @@ career-compass-mcp v${pkgVersion}
|
|
|
35
27
|
|
|
36
28
|
Usage:
|
|
37
29
|
career-compass-mcp Run MCP server (stdio)
|
|
38
|
-
career-compass-mcp dashboard Open web dashboard
|
|
30
|
+
career-compass-mcp dashboard Open web dashboard (full if built, else lite)
|
|
31
|
+
career-compass-mcp dashboard --lite Force the zero-build lite dashboard
|
|
39
32
|
career-compass-mcp dashboard --port 3000 Specify port (default: 3141)
|
|
40
33
|
career-compass-mcp dashboard --no-open Start without opening browser
|
|
41
34
|
|
|
42
35
|
Options:
|
|
43
36
|
-h, --help Show this help message
|
|
44
37
|
-v, --version Show version number
|
|
38
|
+
--lite Use the built-in zero-build dashboard (no Next.js build needed)
|
|
45
39
|
--port <number> Dashboard port (default: 3141)
|
|
46
40
|
--no-open Don't auto-open browser
|
|
47
41
|
`);
|
|
@@ -65,6 +59,7 @@ else {
|
|
|
65
59
|
requestedPort = portValue;
|
|
66
60
|
}
|
|
67
61
|
const noOpen = args.includes("--no-open");
|
|
62
|
+
const forceLite = args.includes("--lite");
|
|
68
63
|
// Resolve data path
|
|
69
64
|
const dataPath = process.env.CAREER_DATA_PATH ?? join(homedir(), ".career-compass");
|
|
70
65
|
if (!existsSync(dataPath)) {
|
|
@@ -76,42 +71,53 @@ else {
|
|
|
76
71
|
// Resolve standalone server path
|
|
77
72
|
// __cliDir is build/bin/ at runtime; go up two levels to repo root
|
|
78
73
|
const standalonePath = join(__cliDir, "..", "..", "dashboard", ".next", "standalone", "dashboard", "server.js");
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
74
|
+
// Decide which dashboard to serve:
|
|
75
|
+
// --lite always uses the built-in zero-build dashboard.
|
|
76
|
+
// Otherwise prefer the full Next.js dashboard when it has been built.
|
|
77
|
+
// If the full build is absent (e.g. the npm install), fall back to lite
|
|
78
|
+
// instead of erroring — the dashboard now always works.
|
|
79
|
+
const useLite = forceLite || !existsSync(standalonePath);
|
|
80
|
+
if (useLite) {
|
|
81
|
+
process.env.CAREER_DATA_PATH = dataPath; // loadPipeline() reads this
|
|
82
|
+
const { startLiteDashboard } = await import("../src/dashboard-lite/server.js");
|
|
83
|
+
if (!forceLite) {
|
|
84
|
+
console.error("Full dashboard isn't built in this install — starting the built-in lite dashboard.");
|
|
85
|
+
console.error("(Run `npm run build` from source for the full Next.js dashboard with kanban drag, analytics, and Career KB views.)");
|
|
86
|
+
}
|
|
87
|
+
const server = await startLiteDashboard(port);
|
|
88
|
+
console.error(`Lite dashboard running at http://localhost:${port}`);
|
|
89
|
+
if (!noOpen)
|
|
90
|
+
openBrowser(`http://localhost:${port}`);
|
|
91
|
+
const shutdownLite = () => { server.close(); process.exit(0); };
|
|
92
|
+
process.on("SIGINT", shutdownLite);
|
|
93
|
+
process.on("SIGTERM", shutdownLite);
|
|
94
|
+
// Keep the process alive on the listening server.
|
|
95
|
+
await new Promise(() => { });
|
|
92
96
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
97
|
+
else {
|
|
98
|
+
// Start Next.js standalone server
|
|
99
|
+
const child = spawn("node", [standalonePath], {
|
|
100
|
+
env: {
|
|
101
|
+
...process.env,
|
|
102
|
+
PORT: String(port),
|
|
103
|
+
HOSTNAME: "localhost",
|
|
104
|
+
CAREER_DATA_PATH: dataPath,
|
|
105
|
+
},
|
|
106
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
107
|
+
});
|
|
108
|
+
child.stdout?.on("data", (data) => {
|
|
109
|
+
const output = data.toString();
|
|
110
|
+
if (output.includes("Ready") || output.includes("started")) {
|
|
111
|
+
console.error(`Dashboard running at http://localhost:${port}`);
|
|
112
|
+
if (!noOpen) {
|
|
113
|
+
openBrowser(`http://localhost:${port}`);
|
|
114
|
+
}
|
|
109
115
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
});
|
|
117
|
+
const shutdown = () => { child.kill("SIGTERM"); process.exit(0); };
|
|
118
|
+
process.on("SIGINT", shutdown);
|
|
119
|
+
process.on("SIGTERM", shutdown);
|
|
120
|
+
}
|
|
115
121
|
}
|
|
116
122
|
function findPort(preferred) {
|
|
117
123
|
return new Promise((resolve) => {
|
package/build/bin/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Pipeline, Application } from "../schemas/career-schema.js";
|
|
2
|
+
interface NextAction {
|
|
3
|
+
app: Application;
|
|
4
|
+
label: string;
|
|
5
|
+
urgency: "overdue" | "soon" | "info";
|
|
6
|
+
}
|
|
7
|
+
/** Derive next actions from follow-up dates, upcoming interviews, and expiring offers. */
|
|
8
|
+
export declare function deriveNextActions(apps: Application[], today?: Date): NextAction[];
|
|
9
|
+
export interface PipelineStats {
|
|
10
|
+
total: number;
|
|
11
|
+
active: number;
|
|
12
|
+
inConversation: number;
|
|
13
|
+
offers: number;
|
|
14
|
+
responseRate: number;
|
|
15
|
+
ghostRate: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function computeStats(apps: Application[]): PipelineStats;
|
|
18
|
+
/** Render the full self-contained HTML document. */
|
|
19
|
+
export declare function renderLiteDashboard(pipeline: Pipeline): string;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/dashboard-lite/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAqB,MAAM,6BAA6B,CAAC;AAmC5F,UAAU,UAAU;IAAG,GAAG,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;CAAE;AAE/F,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,KAAK,OAAa,GAAG,UAAU,EAAE,CAwBvF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IACtE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;CACzC;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,aAAa,CAa/D;AAkBD,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAuI9D"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-build "lite" dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Renders the pipeline into a single self-contained HTML document — all CSS and
|
|
5
|
+
* JS inlined, no external assets, no build step. This is what ships in the npm
|
|
6
|
+
* package so `career-compass-mcp dashboard` works without the Next.js standalone
|
|
7
|
+
* (which is source-build only). The lite server (server.ts) re-reads the YAML on
|
|
8
|
+
* every request and calls this, so the page is always live, never prerendered.
|
|
9
|
+
*
|
|
10
|
+
* Design note: in a plain browser there is no Claude chat bridge, so the
|
|
11
|
+
* "Ask Claude" affordances copy a ready-to-paste prompt to the clipboard rather
|
|
12
|
+
* than dispatching it. In Cowork, the artifact variant wires the same prompts to
|
|
13
|
+
* sendPrompt() instead.
|
|
14
|
+
*/
|
|
15
|
+
const STAGE_ORDER = [
|
|
16
|
+
"discovered", "applied", "screening", "interviewing",
|
|
17
|
+
"offer", "negotiating", "accepted", "rejected", "withdrawn", "ghosted",
|
|
18
|
+
];
|
|
19
|
+
const ACTIVE = [
|
|
20
|
+
"discovered", "applied", "screening", "interviewing", "offer", "negotiating",
|
|
21
|
+
];
|
|
22
|
+
const STAGE_COLOR = {
|
|
23
|
+
discovered: "#64748b", applied: "#0ea5e9", screening: "#8b5cf6",
|
|
24
|
+
interviewing: "#f59e0b", offer: "#10b981", negotiating: "#14b8a6",
|
|
25
|
+
accepted: "#059669", rejected: "#ef4444", withdrawn: "#9ca3af", ghosted: "#a1a1aa",
|
|
26
|
+
};
|
|
27
|
+
function esc(s) {
|
|
28
|
+
return String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
29
|
+
}
|
|
30
|
+
/** Derive next actions from follow-up dates, upcoming interviews, and expiring offers. */
|
|
31
|
+
export function deriveNextActions(apps, today = new Date()) {
|
|
32
|
+
const out = [];
|
|
33
|
+
const t0 = today.getTime();
|
|
34
|
+
const days = (iso) => (iso ? Math.round((new Date(iso).getTime() - t0) / 86400000) : NaN);
|
|
35
|
+
for (const app of apps) {
|
|
36
|
+
if (["accepted", "rejected", "withdrawn", "ghosted"].includes(app.status))
|
|
37
|
+
continue;
|
|
38
|
+
const fu = days(app.followUpDue);
|
|
39
|
+
if (!Number.isNaN(fu)) {
|
|
40
|
+
if (fu < 0)
|
|
41
|
+
out.push({ app, label: `Follow-up overdue by ${Math.abs(fu)}d — ${app.company}`, urgency: "overdue" });
|
|
42
|
+
else if (fu <= 2)
|
|
43
|
+
out.push({ app, label: `Follow-up due ${fu === 0 ? "today" : `in ${fu}d`} — ${app.company}`, urgency: "soon" });
|
|
44
|
+
}
|
|
45
|
+
const nextInterview = (app.interviewRounds ?? [])
|
|
46
|
+
.map((r) => days(r.date)).filter((d) => !Number.isNaN(d) && d >= 0).sort((a, b) => a - b)[0];
|
|
47
|
+
if (nextInterview != null && nextInterview <= 7) {
|
|
48
|
+
out.push({ app, label: `Interview ${nextInterview === 0 ? "today" : `in ${nextInterview}d`} — ${app.company}`, urgency: nextInterview <= 2 ? "soon" : "info" });
|
|
49
|
+
}
|
|
50
|
+
if (app.offer?.expiresDate) {
|
|
51
|
+
const exp = days(app.offer.expiresDate);
|
|
52
|
+
if (!Number.isNaN(exp) && exp >= 0 && exp <= 14)
|
|
53
|
+
out.push({ app, label: `Offer expires ${exp === 0 ? "today" : `in ${exp}d`} — ${app.company}`, urgency: exp <= 4 ? "overdue" : "soon" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const rank = { overdue: 0, soon: 1, info: 2 };
|
|
57
|
+
return out.sort((a, b) => rank[a.urgency] - rank[b.urgency]);
|
|
58
|
+
}
|
|
59
|
+
export function computeStats(apps) {
|
|
60
|
+
const total = apps.length;
|
|
61
|
+
const active = apps.filter((a) => ACTIVE.includes(a.status)).length;
|
|
62
|
+
const inConversation = apps.filter((a) => ["screening", "interviewing"].includes(a.status)).length;
|
|
63
|
+
const offers = apps.filter((a) => ["offer", "negotiating", "accepted"].includes(a.status)).length;
|
|
64
|
+
const applied = apps.filter((a) => a.status !== "discovered").length;
|
|
65
|
+
const responded = apps.filter((a) => !["discovered", "applied", "ghosted"].includes(a.status)).length;
|
|
66
|
+
const ghosted = apps.filter((a) => a.status === "ghosted").length;
|
|
67
|
+
return {
|
|
68
|
+
total, active, inConversation, offers,
|
|
69
|
+
responseRate: applied ? Math.round((responded / applied) * 100) : 0,
|
|
70
|
+
ghostRate: applied ? Math.round((ghosted / applied) * 100) : 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function kpi(n, label, foot = "") {
|
|
74
|
+
return `<div class="kpi"><div class="n">${esc(n)}</div><div class="l">${esc(label)}</div>${foot ? `<div class="foot">${esc(foot)}</div>` : ""}</div>`;
|
|
75
|
+
}
|
|
76
|
+
function jobCard(a) {
|
|
77
|
+
const color = STAGE_COLOR[a.status];
|
|
78
|
+
const pr = a.priority ? `<span class="pill p-${esc(a.priority)}">${esc(a.priority)}</span>` : "";
|
|
79
|
+
const exc = a.excitement != null ? `<span class="exc">🔥 ${esc(a.excitement)}/10</span>` : "";
|
|
80
|
+
const prompt = `Give me a full status on my ${a.company} application (${a.role}) — where it stands, what's next, and anything I'm at risk of dropping.`;
|
|
81
|
+
return `<div class="jc" style="--stage:${color}" data-prompt="${esc(prompt)}">
|
|
82
|
+
<div class="co">${esc(a.company)}</div>
|
|
83
|
+
<div class="ro">${esc(a.role)}</div>
|
|
84
|
+
<div class="meta">${pr}${exc}</div>
|
|
85
|
+
</div>`;
|
|
86
|
+
}
|
|
87
|
+
/** Render the full self-contained HTML document. */
|
|
88
|
+
export function renderLiteDashboard(pipeline) {
|
|
89
|
+
const apps = [...(pipeline.applications ?? [])].sort((a, b) => (b.dateUpdated ?? "").localeCompare(a.dateUpdated ?? ""));
|
|
90
|
+
const s = computeStats(apps);
|
|
91
|
+
const actions = deriveNextActions(apps);
|
|
92
|
+
const lastUpdated = pipeline.lastUpdated ? new Date(pipeline.lastUpdated).toLocaleString() : "—";
|
|
93
|
+
const kpis = [
|
|
94
|
+
kpi(s.total, "Total applications"),
|
|
95
|
+
kpi(s.active, "Active", "in play right now"),
|
|
96
|
+
kpi(s.inConversation, "In conversation", "screening + interviewing"),
|
|
97
|
+
kpi(s.offers, "Offers", s.offers > 0 ? "🎉 decision time" : ""),
|
|
98
|
+
kpi(`${s.responseRate}%`, "Response rate"),
|
|
99
|
+
kpi(`${s.ghostRate}%`, "Ghost rate"),
|
|
100
|
+
].join("");
|
|
101
|
+
const presentStages = STAGE_ORDER.filter((st) => apps.some((a) => a.status === st) || ACTIVE.includes(st));
|
|
102
|
+
const board = presentStages.map((st) => {
|
|
103
|
+
const items = apps.filter((a) => a.status === st);
|
|
104
|
+
return `<div class="col">
|
|
105
|
+
<div class="h"><span class="sw" style="background:${STAGE_COLOR[st]}"></span> ${st} <span class="count">${items.length}</span></div>
|
|
106
|
+
<div class="stack">${items.map(jobCard).join("") || '<div class="none">—</div>'}</div>
|
|
107
|
+
</div>`;
|
|
108
|
+
}).join("");
|
|
109
|
+
const actionsHtml = actions.length
|
|
110
|
+
? actions.map((x) => `<div class="action ${x.urgency}"><span class="dot"></span><div class="t"><b>${esc(x.label)}</b><span>${esc(x.app.role)}</span></div></div>`).join("")
|
|
111
|
+
: `<div class="none-lg">✅ Nothing overdue. Follow-ups, upcoming interviews, and expiring offers surface here.</div>`;
|
|
112
|
+
const emptyState = `<div class="panel"><div class="state">
|
|
113
|
+
<h3>Your pipeline is empty — let's fix that</h3>
|
|
114
|
+
<div>Career Compass builds everything off your pipeline. Add your first opportunity and this dashboard lights up.</div>
|
|
115
|
+
<div class="btns">
|
|
116
|
+
<button class="btn primary" data-prompt="I found a job posting I want to track. Here it is: [paste posting]. Add it to my pipeline and give me a fit analysis.">Copy: track a job posting</button>
|
|
117
|
+
<button class="btn" data-prompt="Add an application to my pipeline — I'll give you the company and role.">Copy: add manually</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div></div>`;
|
|
120
|
+
const chartData = JSON.stringify(ACTIVE.filter((st) => apps.some((a) => a.status === st))
|
|
121
|
+
.map((st) => ({ label: st[0].toUpperCase() + st.slice(1), value: apps.filter((a) => a.status === st).length, color: STAGE_COLOR[st] })));
|
|
122
|
+
return `<!doctype html>
|
|
123
|
+
<html lang="en"><head>
|
|
124
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
125
|
+
<title>Career Compass — Pipeline</title>
|
|
126
|
+
<style>
|
|
127
|
+
:root{color-scheme:light;--bg:#f7f8fa;--card:#fff;--ink:#14161c;--muted:#6b7280;--line:#e6e8ee;--accent:#4f46e5;--accent-soft:#eef0fe;--shadow:0 1px 2px rgba(20,22,28,.06),0 4px 14px rgba(20,22,28,.05)}
|
|
128
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}
|
|
129
|
+
.wrap{max-width:1180px;margin:0 auto;padding:20px 20px 64px}
|
|
130
|
+
header.top{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:18px}
|
|
131
|
+
h1{font-size:20px;margin:0 0 2px;letter-spacing:-.01em}.sub{color:var(--muted);font-size:12.5px}
|
|
132
|
+
.chip{display:inline-flex;align-items:center;gap:6px;background:var(--accent-soft);color:var(--accent);border-radius:999px;padding:4px 10px;font-size:12px;font-weight:600}
|
|
133
|
+
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:20px}
|
|
134
|
+
.kpi{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;box-shadow:var(--shadow)}
|
|
135
|
+
.kpi .n{font-size:26px;font-weight:700;letter-spacing:-.02em}.kpi .l{color:var(--muted);font-size:12px;margin-top:2px}.kpi .foot{font-size:11.5px;margin-top:6px;color:var(--muted)}
|
|
136
|
+
.grid2{display:grid;grid-template-columns:1.55fr 1fr;gap:16px;align-items:start}@media(max-width:900px){.grid2{grid-template-columns:1fr}}
|
|
137
|
+
.panel{background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}
|
|
138
|
+
.panel h2{font-size:13px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin:0;padding:14px 16px;border-bottom:1px solid var(--line)}
|
|
139
|
+
.panel .body{padding:14px 16px}.chart-box{position:relative;height:230px;display:flex;align-items:flex-end;gap:10px}
|
|
140
|
+
.bar-row{display:flex;flex-direction:column;gap:9px;width:100%}
|
|
141
|
+
.bar{display:flex;align-items:center;gap:10px}.bar .lab{width:96px;font-size:12px;color:var(--muted);text-align:right;flex:0 0 auto}
|
|
142
|
+
.bar .track{flex:1;background:#f0f1f5;border-radius:6px;height:22px;overflow:hidden}.bar .fill{height:100%;border-radius:6px}
|
|
143
|
+
.bar .val{font-size:12px;font-weight:600;width:22px}
|
|
144
|
+
.action{display:flex;gap:10px;padding:11px 0;border-bottom:1px dashed var(--line)}.action:last-child{border-bottom:0}
|
|
145
|
+
.action .dot{width:8px;height:8px;border-radius:50%;margin-top:6px;flex:0 0 auto;background:var(--accent)}
|
|
146
|
+
.action.overdue .dot{background:#ef4444}.action.soon .dot{background:#f59e0b}
|
|
147
|
+
.action .t{flex:1}.action .t b{display:block;font-size:13.5px}.action .t span{color:var(--muted);font-size:12px}
|
|
148
|
+
.board{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(220px,1fr);gap:12px;overflow-x:auto;padding-bottom:8px}
|
|
149
|
+
.col{background:#fbfbfd;border:1px solid var(--line);border-radius:12px;min-height:80px}
|
|
150
|
+
.col .h{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line);font-weight:600;font-size:12.5px;text-transform:capitalize}
|
|
151
|
+
.col .h .sw{width:9px;height:9px;border-radius:50%}.col .h .count{margin-left:auto;background:#eef0f4;color:var(--muted);border-radius:999px;font-size:11px;padding:1px 8px}
|
|
152
|
+
.col .stack{padding:10px;display:flex;flex-direction:column;gap:9px}.none{color:var(--muted);font-size:11.5px;padding:4px 2px}.none-lg{color:var(--muted);font-size:13px}
|
|
153
|
+
.jc{background:var(--card);border:1px solid var(--line);border-left:3px solid var(--stage,#ccc);border-radius:10px;padding:10px 11px;cursor:pointer;transition:.12s}
|
|
154
|
+
.jc:hover{box-shadow:var(--shadow);transform:translateY(-1px)}.jc .co{font-weight:650;font-size:13px}.jc .ro{color:var(--muted);font-size:12px;margin-top:1px}
|
|
155
|
+
.jc .meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;align-items:center}
|
|
156
|
+
.pill{font-size:10.5px;padding:2px 7px;border-radius:999px;font-weight:600}.p-high{background:#fee2e2;color:#b91c1c}.p-medium{background:#fef3c7;color:#92400e}.p-low{background:#e5e7eb;color:#4b5563}
|
|
157
|
+
.exc{font-size:11px;color:var(--muted);margin-left:auto}
|
|
158
|
+
.btn{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:9px;padding:7px 12px;font-size:12.5px;font-weight:600;cursor:pointer;transition:.12s}
|
|
159
|
+
.btn:hover{border-color:var(--accent);color:var(--accent)}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.primary:hover{background:#4338ca}
|
|
160
|
+
.toolbar{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 14px}
|
|
161
|
+
.state{text-align:center;padding:48px 20px;color:var(--muted)}.state h3{color:var(--ink);font-size:16px;margin:0 0 6px}.state .btns{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:16px}
|
|
162
|
+
.foot-note{color:var(--muted);font-size:11.5px;margin-top:22px;text-align:center}
|
|
163
|
+
#toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--ink);color:#fff;padding:10px 16px;border-radius:10px;font-size:13px;opacity:0;transition:.2s;pointer-events:none}
|
|
164
|
+
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
|
|
165
|
+
.hidden{display:none!important}
|
|
166
|
+
</style></head>
|
|
167
|
+
<body><div class="wrap">
|
|
168
|
+
<header class="top">
|
|
169
|
+
<div><h1>🧭 Career Compass — Pipeline</h1>
|
|
170
|
+
<div class="sub">Live view of your job search · re-read from disk on every load · last write ${esc(lastUpdated)}</div></div>
|
|
171
|
+
<div class="chip">● local</div>
|
|
172
|
+
</header>
|
|
173
|
+
<div class="toolbar">
|
|
174
|
+
<button class="btn primary" data-prompt="What should I focus on in my job search today? Look at my pipeline and give me the 3 highest-leverage moves.">🎯 Copy: what should I do today?</button>
|
|
175
|
+
<button class="btn" data-prompt="Review my whole pipeline and flag anything stale, single-threaded, or at risk of going cold.">🔍 Copy: health check</button>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="kpis">${kpis}</div>
|
|
178
|
+
${apps.length === 0 ? emptyState : `
|
|
179
|
+
<div class="grid2">
|
|
180
|
+
<div class="panel">
|
|
181
|
+
<h2>Pipeline by stage</h2>
|
|
182
|
+
<div class="body"><div class="board">${board}</div></div>
|
|
183
|
+
</div>
|
|
184
|
+
<div style="display:flex;flex-direction:column;gap:16px">
|
|
185
|
+
<div class="panel"><h2>Next actions</h2><div class="body">${actionsHtml}</div></div>
|
|
186
|
+
<div class="panel"><h2>Stage distribution</h2><div class="body"><div id="chart" class="chart-box"></div></div></div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>`}
|
|
189
|
+
<div class="foot-note">Data stays local (<code>~/.career-compass</code>). Click any card to copy a prompt for Claude — that's where the work happens. Refresh the page to re-read from disk.</div>
|
|
190
|
+
</div>
|
|
191
|
+
<div id="toast"></div>
|
|
192
|
+
<script>
|
|
193
|
+
const CHART=${chartData};
|
|
194
|
+
(function(){
|
|
195
|
+
const box=document.getElementById("chart");
|
|
196
|
+
if(box&&CHART.length){
|
|
197
|
+
const max=Math.max(...CHART.map(d=>d.value),1);
|
|
198
|
+
box.innerHTML='<div class="bar-row">'+CHART.map(d=>
|
|
199
|
+
'<div class="bar"><span class="lab">'+d.label+'</span><span class="track"><span class="fill" style="width:'+(d.value/max*100)+'%;background:'+d.color+'"></span></span><span class="val">'+d.value+'</span></div>'
|
|
200
|
+
).join("")+'</div>';
|
|
201
|
+
} else if(box){ box.innerHTML='<div class="none-lg">No active applications yet.</div>'; }
|
|
202
|
+
const toast=document.getElementById("toast"); let tmr;
|
|
203
|
+
function flash(m){ toast.textContent=m; toast.classList.add("show"); clearTimeout(tmr); tmr=setTimeout(()=>toast.classList.remove("show"),1900); }
|
|
204
|
+
document.querySelectorAll("[data-prompt]").forEach(el=>{
|
|
205
|
+
el.addEventListener("click",()=>{
|
|
206
|
+
const p=el.getAttribute("data-prompt");
|
|
207
|
+
if(navigator.clipboard&&navigator.clipboard.writeText){
|
|
208
|
+
navigator.clipboard.writeText(p).then(()=>flash("Prompt copied — paste it to Claude 🧭")).catch(()=>flash(p));
|
|
209
|
+
} else { flash("Copy this: "+p); }
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
})();
|
|
213
|
+
</script>
|
|
214
|
+
</body></html>`;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/dashboard-lite/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Server } from "http";
|
|
2
|
+
/**
|
|
3
|
+
* Zero-build "lite" dashboard server.
|
|
4
|
+
*
|
|
5
|
+
* A dependency-free Node HTTP server that, on every request, re-reads the
|
|
6
|
+
* pipeline YAML from disk and renders it via renderLiteDashboard(). Nothing is
|
|
7
|
+
* cached or prerendered — refreshing the page always reflects the current state
|
|
8
|
+
* of ~/.career-compass, mirroring the force-dynamic guarantee of the Next.js
|
|
9
|
+
* dashboard. Ships in the npm package (pure JS, no framework, no build).
|
|
10
|
+
*/
|
|
11
|
+
export declare function createLiteDashboardServer(): Server;
|
|
12
|
+
/** Start the lite dashboard on `port`, resolving once it's listening. */
|
|
13
|
+
export declare function startLiteDashboard(port: number, hostname?: string): Promise<Server>;
|
|
14
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/dashboard-lite/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAIjD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA2BlD;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
import { loadPipeline, isCorruptDataError } from "../storage/file-store.js";
|
|
3
|
+
import { renderLiteDashboard } from "./render.js";
|
|
4
|
+
/**
|
|
5
|
+
* Zero-build "lite" dashboard server.
|
|
6
|
+
*
|
|
7
|
+
* A dependency-free Node HTTP server that, on every request, re-reads the
|
|
8
|
+
* pipeline YAML from disk and renders it via renderLiteDashboard(). Nothing is
|
|
9
|
+
* cached or prerendered — refreshing the page always reflects the current state
|
|
10
|
+
* of ~/.career-compass, mirroring the force-dynamic guarantee of the Next.js
|
|
11
|
+
* dashboard. Ships in the npm package (pure JS, no framework, no build).
|
|
12
|
+
*/
|
|
13
|
+
export function createLiteDashboardServer() {
|
|
14
|
+
return createServer(async (req, res) => {
|
|
15
|
+
// Only serve the dashboard at "/"; everything else 404s (favicon, etc.).
|
|
16
|
+
const path = (req.url ?? "/").split("?")[0];
|
|
17
|
+
if (path !== "/" && path !== "/index.html") {
|
|
18
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
19
|
+
res.end("Not found");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const pipeline = await loadPipeline();
|
|
24
|
+
const html = renderLiteDashboard(pipeline);
|
|
25
|
+
res.writeHead(200, {
|
|
26
|
+
"content-type": "text/html; charset=utf-8",
|
|
27
|
+
// Never cache: the whole point is a live read of local data.
|
|
28
|
+
"cache-control": "no-store, must-revalidate",
|
|
29
|
+
});
|
|
30
|
+
res.end(html);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const corrupt = isCorruptDataError(err);
|
|
34
|
+
const msg = corrupt
|
|
35
|
+
? `Your pipeline file exists but couldn't be parsed. Fix it or restore a .bak backup, then refresh.\n\n${err.message}`
|
|
36
|
+
: `Failed to render dashboard: ${err?.message ?? String(err)}`;
|
|
37
|
+
res.writeHead(corrupt ? 422 : 500, { "content-type": "text/plain; charset=utf-8" });
|
|
38
|
+
res.end(msg);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Start the lite dashboard on `port`, resolving once it's listening. */
|
|
43
|
+
export function startLiteDashboard(port, hostname = "localhost") {
|
|
44
|
+
const server = createLiteDashboardServer();
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
server.once("error", reject);
|
|
47
|
+
server.listen(port, hostname, () => resolve(server));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/dashboard-lite/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACrC,yEAAyE;QACzE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;gBACjB,cAAc,EAAE,0BAA0B;gBAC1C,6DAA6D;gBAC7D,eAAe,EAAE,2BAA2B;aAC7C,CAAC,CAAC;YACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,GAAG,GAAG,OAAO;gBACjB,CAAC,CAAC,uGAAwG,GAAa,CAAC,OAAO,EAAE;gBACjI,CAAC,CAAC,+BAAgC,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5E,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,2BAA2B,EAAE,CAAC,CAAC;YACpF,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,QAAQ,GAAG,WAAW;IACrE,MAAM,MAAM,GAAG,yBAAyB,EAAE,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC"}
|