careervivid 1.1.13 → 1.3.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 +45 -1
- package/dist/api.d.ts +17 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +14 -2
- package/dist/commands/jobs.d.ts +3 -0
- package/dist/commands/jobs.d.ts.map +1 -0
- package/dist/commands/jobs.js +91 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.d.ts.map +1 -0
- package/dist/commands/portfolio.js +72 -0
- package/dist/commands/profile.d.ts +3 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +101 -0
- package/dist/commands/workspace.d.ts +3 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +39 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/index.js +10 -2
- package/dist/utils/gws-runner.d.ts +18 -0
- package/dist/utils/gws-runner.d.ts.map +1 -0
- package/dist/utils/gws-runner.js +80 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
- [Commands](#commands)
|
|
15
15
|
- [cv publish](#cv-publish)
|
|
16
16
|
- [cv whiteboard](#cv-whiteboard)
|
|
17
|
+
- [cv workspace](#cv-workspace)
|
|
18
|
+
- [cv profile](#cv-profile)
|
|
19
|
+
- [cv jobs](#cv-jobs)
|
|
17
20
|
- [cv auth](#cv-auth)
|
|
18
21
|
- [cv config](#cv-config)
|
|
19
22
|
- [Whiteboard Templates](#whiteboard-templates)
|
|
@@ -116,6 +119,46 @@ Print all available built-in Mermaid templates. (Also available as `cv whiteboar
|
|
|
116
119
|
cv list-templates
|
|
117
120
|
```
|
|
118
121
|
|
|
122
|
+
### `cv workspace` (or `cv gws`)
|
|
123
|
+
|
|
124
|
+
Manage your Google Workspace integration.
|
|
125
|
+
|
|
126
|
+
| Subcommand | Description |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `cv workspace check` | Verify GWS CLI installation and OAuth status |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### `cv profile`
|
|
133
|
+
|
|
134
|
+
Manage your CareerVivid profile.
|
|
135
|
+
|
|
136
|
+
| Subcommand | Description |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `cv profile export` | Export resume data to other formats (e.g. `gdoc`) |
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Export to Google Docs
|
|
142
|
+
cv profile export --format gdoc
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### `cv jobs`
|
|
148
|
+
|
|
149
|
+
Automate your job application tracking.
|
|
150
|
+
|
|
151
|
+
| Subcommand | Description |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `cv jobs sync-gmail` | Scan Gmail for applications and sync to a Google Sheet |
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Sync recent applications to Google Sheets
|
|
157
|
+
cv jobs sync-gmail
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
119
162
|
### `cv whiteboard`
|
|
120
163
|
|
|
121
164
|
Grouped commands for Mermaid architecture diagrams.
|
|
@@ -211,7 +254,8 @@ The CLI is designed to be **AI-agent-friendly**. Use `--json` for structured out
|
|
|
211
254
|
You are an expert technical writer and senior software architect.
|
|
212
255
|
Your secondary directive — after writing and fixing code — is to
|
|
213
256
|
proactively help the user document their work and build their
|
|
214
|
-
CareerVivid portfolio using the local `cv` CLI.
|
|
257
|
+
CareerVivid portfolio using the local `cv` CLI. The platform supports
|
|
258
|
+
the latest Gemini 2.5 and 3.0 models for high-quality content generation.
|
|
215
259
|
|
|
216
260
|
## 1. Triggers (When to Propose a Post)
|
|
217
261
|
Do not wait for the user to ask. Propose a CareerVivid post when:
|
package/dist/api.d.ts
CHANGED
|
@@ -51,5 +51,22 @@ export declare function pingAuth(): Promise<{
|
|
|
51
51
|
ok: boolean;
|
|
52
52
|
error?: string;
|
|
53
53
|
}>;
|
|
54
|
+
export declare function initPortfolio(title?: string, templateId?: string): Promise<{
|
|
55
|
+
success: boolean;
|
|
56
|
+
portfolioId: string;
|
|
57
|
+
url: string;
|
|
58
|
+
} | ApiError>;
|
|
59
|
+
export declare function updatePortfolioProjects(portfolioId: string, projects: any[], techStack?: string[]): Promise<{
|
|
60
|
+
success: boolean;
|
|
61
|
+
message: string;
|
|
62
|
+
} | ApiError>;
|
|
63
|
+
export declare function updatePortfolioHero(portfolioId: string, hero?: any, theme?: any, seoMetadata?: any): Promise<{
|
|
64
|
+
success: boolean;
|
|
65
|
+
message: string;
|
|
66
|
+
} | ApiError>;
|
|
67
|
+
export declare function uploadPortfolioAsset(image: string, path: string, mimeType: string): Promise<{
|
|
68
|
+
success: boolean;
|
|
69
|
+
downloadUrl: string;
|
|
70
|
+
} | ApiError>;
|
|
54
71
|
export declare function isApiError(v: unknown): v is ApiError;
|
|
55
72
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACrB,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAmED,wBAAsB,WAAW,CAC7B,OAAO,EAAE,cAAc,EACvB,MAAM,UAAQ,GACf,OAAO,CAAC,aAAa,GAAG,QAAQ,CAAC,CAgBnC;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAwC9E;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,QAAQ,CAEpD"}
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACrB,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAmED,wBAAsB,WAAW,CAC7B,OAAO,EAAE,cAAc,EACvB,MAAM,UAAQ,GACf,OAAO,CAAC,aAAa,GAAG,QAAQ,CAAC,CAgBnC;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAwC9E;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzE;AAED,wBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEnJ;AAED,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEnK;AAED,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEpK;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAEvJ;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,QAAQ,CAEpD"}
|
package/dist/api.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { getApiKey, getApiUrl } from "./config.js";
|
|
8
8
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
9
|
-
const CLI_VERSION = "1.
|
|
9
|
+
const CLI_VERSION = "1.3.0";
|
|
10
10
|
function requireApiKey() {
|
|
11
11
|
const key = getApiKey();
|
|
12
12
|
if (!key) {
|
|
@@ -71,7 +71,7 @@ export async function publishPost(payload, dryRun = false) {
|
|
|
71
71
|
message: "Dry run passed. No post was created.",
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
return apiRequest("POST", "", payload);
|
|
74
|
+
return apiRequest("POST", "publish", payload);
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Verify an API key against the /verifyAuth endpoint.
|
|
@@ -130,6 +130,18 @@ export async function pingAuth() {
|
|
|
130
130
|
}
|
|
131
131
|
return { ok: true };
|
|
132
132
|
}
|
|
133
|
+
export async function initPortfolio(title, templateId) {
|
|
134
|
+
return apiRequest("POST", "portfolio/init", { title, templateId });
|
|
135
|
+
}
|
|
136
|
+
export async function updatePortfolioProjects(portfolioId, projects, techStack) {
|
|
137
|
+
return apiRequest("PATCH", "portfolio/projects", { portfolioId, projects, techStack });
|
|
138
|
+
}
|
|
139
|
+
export async function updatePortfolioHero(portfolioId, hero, theme, seoMetadata) {
|
|
140
|
+
return apiRequest("PATCH", "portfolio/hero", { portfolioId, hero, theme, seoMetadata });
|
|
141
|
+
}
|
|
142
|
+
export async function uploadPortfolioAsset(image, path, mimeType) {
|
|
143
|
+
return apiRequest("POST", "portfolio/assets", { image, path, mimeType });
|
|
144
|
+
}
|
|
133
145
|
export function isApiError(v) {
|
|
134
146
|
return typeof v === "object" && v !== null && v.isError === true;
|
|
135
147
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/commands/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QA6GnD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import { checkGwsReady, runGwsCommand } from "../utils/gws-runner.js";
|
|
5
|
+
import { printError } from "../output.js";
|
|
6
|
+
export function registerJobsCommand(program) {
|
|
7
|
+
const jobsCmd = program
|
|
8
|
+
.command("jobs")
|
|
9
|
+
.description("Automate and track your job applications");
|
|
10
|
+
jobsCmd
|
|
11
|
+
.command("sync-gmail")
|
|
12
|
+
.description("Scan your Gmail for job applications and generate a tracking Sheet")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
const isJson = process.argv.includes("--json");
|
|
15
|
+
if (!isJson) {
|
|
16
|
+
console.log(`\n ${chalk.bold("Job Tracker: Syncing Gmail to Google Sheets")}\n`);
|
|
17
|
+
}
|
|
18
|
+
// 1. Verify GWS CLI is available
|
|
19
|
+
const isReady = await checkGwsReady();
|
|
20
|
+
if (!isReady) {
|
|
21
|
+
printError("Google Workspace CLI is not configured. Run 'cv workspace check'.", undefined, isJson);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// 2. Search Gmail for Application Emails
|
|
25
|
+
const gmailSpinner = ora("Scanning Gmail for recent applications...").start();
|
|
26
|
+
// Note: In a real app we would paginate, but for the demo we'll fetch the top 5
|
|
27
|
+
const query = encodeURIComponent("subject:application OR subject:applied OR subject:\"thank you for applying\"");
|
|
28
|
+
const listRes = await runGwsCommand(`gmail users messages list --params '{"userId": "me", "maxResults": 5, "q": "${query}"}'`);
|
|
29
|
+
if (!listRes.success) {
|
|
30
|
+
gmailSpinner.fail("Failed to read Gmail.");
|
|
31
|
+
printError(listRes.error || "Unknown error", undefined, isJson);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const messages = listRes.data?.messages || [];
|
|
35
|
+
if (messages.length === 0) {
|
|
36
|
+
gmailSpinner.info("No recent application emails found.");
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
// Fetch snippets for these messages (mocking AI extraction)
|
|
40
|
+
const extractedJobs = [];
|
|
41
|
+
for (const msg of messages) {
|
|
42
|
+
const msgRes = await runGwsCommand(`gmail users messages get --params '{"userId": "me", "id": "${msg.id}", "format": "metadata"}'`);
|
|
43
|
+
if (msgRes.success && msgRes.data) {
|
|
44
|
+
const snippet = msgRes.data.snippet || "";
|
|
45
|
+
// Mocking AI parse
|
|
46
|
+
extractedJobs.push({
|
|
47
|
+
company: snippet.split(" ")[0] || "Unknown Corp",
|
|
48
|
+
role: "Software Engineer",
|
|
49
|
+
date: new Date().toISOString().split('T')[0]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
gmailSpinner.succeed(`Found ${extractedJobs.length} applications via Gmail.`);
|
|
54
|
+
// 3. Create the Google Sheet Tracker
|
|
55
|
+
const sheetSpinner = ora("Creating Tracker inside Google Sheets...").start();
|
|
56
|
+
const createRes = await runGwsCommand(`sheets spreadsheets create --json '{"properties": {"title": "CareerVivid Job Tracker ${new Date().getFullYear()}"}}'`);
|
|
57
|
+
if (!createRes.success || !createRes.data?.spreadsheetId) {
|
|
58
|
+
sheetSpinner.fail("Failed to create Google Sheet.");
|
|
59
|
+
printError(createRes.error || "Unknown error", undefined, isJson);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const sheetId = createRes.data.spreadsheetId;
|
|
63
|
+
sheetSpinner.succeed(`Created new spreadsheet: ${sheetId}`);
|
|
64
|
+
// 4. Append Data to the Sheet
|
|
65
|
+
const appendSpinner = ora("Writing data to Sheet...").start();
|
|
66
|
+
const values = [
|
|
67
|
+
["Company", "Role", "Date Applied", "Status"], // Header
|
|
68
|
+
...extractedJobs.map(job => [job.company, job.role, job.date, "Applied"]) // Rows
|
|
69
|
+
];
|
|
70
|
+
const payload = JSON.stringify({ values }).replace(/'/g, "");
|
|
71
|
+
const updateRes = await runGwsCommand(`sheets spreadsheets values append \
|
|
72
|
+
--params '{"spreadsheetId": "${sheetId}", "range": "Sheet1!A1", "valueInputOption": "USER_ENTERED"}' \
|
|
73
|
+
--json '${payload}'`);
|
|
74
|
+
if (!updateRes.success) {
|
|
75
|
+
appendSpinner.fail("Failed to write to Google Sheet.");
|
|
76
|
+
printError(updateRes.error || "Unknown validation issue with Sheets API payload.", undefined, isJson);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
appendSpinner.succeed("Tracker updated successfully!");
|
|
80
|
+
// 5. Output Result
|
|
81
|
+
const sheetUrl = `https://docs.google.com/spreadsheets/d/${sheetId}/edit`;
|
|
82
|
+
if (isJson) {
|
|
83
|
+
console.log(JSON.stringify({ success: true, url: sheetUrl, spreadsheetId: sheetId, jobsFound: extractedJobs.length }));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(boxen(`${chalk.bold.green("✔ Sync Complete!")}\n\n` +
|
|
87
|
+
`Your applications have been synced to Google Sheets:\n` +
|
|
88
|
+
`${chalk.cyan.underline(sheetUrl)}`, { padding: 1, borderStyle: "round" }));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portfolio.d.ts","sourceRoot":"","sources":["../../src/commands/portfolio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,QA4ExD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as api from "../api.js";
|
|
3
|
+
import { printSuccess, printError } from "../output.js";
|
|
4
|
+
export function registerPortfolioCommand(program) {
|
|
5
|
+
const portfolioCmd = program
|
|
6
|
+
.command("portfolio")
|
|
7
|
+
.description("Manage your CareerVivid developer portfolio");
|
|
8
|
+
portfolioCmd
|
|
9
|
+
.command("init")
|
|
10
|
+
.description("Create a foundational portfolio site")
|
|
11
|
+
.option("-t, --title <title>", "Brand title for the portfolio")
|
|
12
|
+
.option("--template <name>", "Template ID (e.g. minimalist, developer)")
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
const isJson = process.argv.includes("--json");
|
|
15
|
+
if (!isJson)
|
|
16
|
+
console.log("Initializing your CareerVivid portfolio...");
|
|
17
|
+
const result = await api.initPortfolio(options.title, options.template);
|
|
18
|
+
if (api.isApiError(result)) {
|
|
19
|
+
printError(`Init failed: ${result.message}`, result.fields, isJson);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
printSuccess({ "URL": result.url, "ID": result.portfolioId }, isJson);
|
|
23
|
+
if (!isJson)
|
|
24
|
+
console.log(`\nYou can now use AI agents via MCP to sync projects to this ID.`);
|
|
25
|
+
});
|
|
26
|
+
portfolioCmd
|
|
27
|
+
.command("add-project")
|
|
28
|
+
.description("Sync project data to a portfolio")
|
|
29
|
+
.argument("<file>", "Path to a JSON file containing an array of project objects")
|
|
30
|
+
.requiredOption("--id <id>", "The portfolio ID to update")
|
|
31
|
+
.action(async (file, options) => {
|
|
32
|
+
const isJson = process.argv.includes("--json");
|
|
33
|
+
if (!fs.existsSync(file)) {
|
|
34
|
+
printError(`File not found: ${file}`, undefined, isJson);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
let data;
|
|
38
|
+
try {
|
|
39
|
+
data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
printError(`Error parsing JSON: ${err.message}`, undefined, isJson);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const projects = Array.isArray(data) ? data : [data];
|
|
46
|
+
if (!isJson)
|
|
47
|
+
console.log(`Syncing ${projects.length} project(s) to portfolio ${options.id}...`);
|
|
48
|
+
const result = await api.updatePortfolioProjects(options.id, projects);
|
|
49
|
+
if (api.isApiError(result)) {
|
|
50
|
+
printError(`Sync failed: ${result.message}`, result.fields, isJson);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
printSuccess({ "Message": "Projects successfully synced to portfolio!" }, isJson);
|
|
54
|
+
});
|
|
55
|
+
portfolioCmd
|
|
56
|
+
.command("preview")
|
|
57
|
+
.description("Get a preview URL for your active portfolio")
|
|
58
|
+
.requiredOption("--id <id>", "The portfolio ID")
|
|
59
|
+
.action((options) => {
|
|
60
|
+
const isJson = process.argv.includes("--json");
|
|
61
|
+
printSuccess({ "URL": `https://careervivid.app/portfolio/edit/${options.id}` }, isJson);
|
|
62
|
+
});
|
|
63
|
+
portfolioCmd
|
|
64
|
+
.command("publish")
|
|
65
|
+
.description("Mark your drafts as live")
|
|
66
|
+
.action(() => {
|
|
67
|
+
const isJson = process.argv.includes("--json");
|
|
68
|
+
if (!isJson)
|
|
69
|
+
console.log("Publishing via CLI is mocked for now. Real-time syncs apply immediately on the web.");
|
|
70
|
+
printSuccess({ "Message": "Portfolio synced to live!" }, isJson);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/commands/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,QAiHtD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import boxen from "boxen";
|
|
5
|
+
import { checkGwsReady, runGwsCommand } from "../utils/gws-runner.js";
|
|
6
|
+
import { printError } from "../output.js";
|
|
7
|
+
export function registerProfileCommand(program) {
|
|
8
|
+
const profileCmd = program
|
|
9
|
+
.command("profile")
|
|
10
|
+
.description("Manage your CareerVivid developer profile and resume");
|
|
11
|
+
profileCmd
|
|
12
|
+
.command("export")
|
|
13
|
+
.description("Export your resume data to external formats")
|
|
14
|
+
.argument("[file]", "Path to local resume.json (uses mock data if omitted)")
|
|
15
|
+
.requiredOption("--format <fmt>", "Format to export (e.g., 'gdoc')")
|
|
16
|
+
.action(async (file, options) => {
|
|
17
|
+
const isJson = process.argv.includes("--json");
|
|
18
|
+
if (options.format.toLowerCase() !== "gdoc") {
|
|
19
|
+
printError("Currently only '--format gdoc' is supported by this integration.", undefined, isJson);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (!isJson) {
|
|
23
|
+
console.log(`\n ${chalk.bold("Exporting CareerVivid Resume to Google Docs")}\n`);
|
|
24
|
+
}
|
|
25
|
+
// 1. Verify GWS CLI is available
|
|
26
|
+
const isReady = await checkGwsReady();
|
|
27
|
+
if (!isReady) {
|
|
28
|
+
printError("Google Workspace CLI is not configured. Run 'cv workspace check'.", undefined, isJson);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
// 2. Load Resume Data
|
|
32
|
+
let resumeData = {
|
|
33
|
+
name: "Alex Dev",
|
|
34
|
+
title: "Senior Full Stack Engineer",
|
|
35
|
+
summary: "Passionate developer building AI-first tools.",
|
|
36
|
+
experience: [
|
|
37
|
+
{ role: "Software Engineer", company: "Tech Corp", years: "2022 - Present" }
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
if (file && fs.existsSync(file)) {
|
|
41
|
+
try {
|
|
42
|
+
resumeData = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
printError(`Failed to parse resume file: ${err.message}`, undefined, isJson);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (file) {
|
|
50
|
+
printError(`File not found: ${file}`, undefined, isJson);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const title = `${resumeData.name.replace(/ /g, "_")}_Resume_Export`;
|
|
54
|
+
// 3. Create the Google Doc
|
|
55
|
+
const createSpinner = ora("Creating new Google Doc...").start();
|
|
56
|
+
const createRes = await runGwsCommand(`docs documents create --json '{"title": "${title}"}'`);
|
|
57
|
+
if (!createRes.success || !createRes.data?.documentId) {
|
|
58
|
+
createSpinner.fail("Failed to create Google Doc.");
|
|
59
|
+
printError(createRes.error || "Unknown error", undefined, isJson);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
createSpinner.succeed("Created base document.");
|
|
63
|
+
const docId = createRes.data.documentId;
|
|
64
|
+
// 4. Construct Content (Batch Update)
|
|
65
|
+
const contentSpinner = ora("Formatting resume content...").start();
|
|
66
|
+
// Text to insert (plain text for simplicity in this demo)
|
|
67
|
+
const textToInsert = `${resumeData.name}\n${resumeData.title}\n\nSUMMARY:\n${resumeData.summary}\n\nEXPERIENCE:\n` +
|
|
68
|
+
resumeData.experience.map(e => `- ${e.role} at ${e.company} (${e.years})`).join("\n");
|
|
69
|
+
// Google Docs API uses a specific JSON structure for insertions
|
|
70
|
+
const batchPayload = {
|
|
71
|
+
requests: [
|
|
72
|
+
{
|
|
73
|
+
insertText: {
|
|
74
|
+
location: { index: 1 },
|
|
75
|
+
text: textToInsert
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
// Needs precise escaping for bash if passing directly, or we save to tmp file and pass path.
|
|
81
|
+
// For simplicity, we'll strip single quotes from the payload if any exist.
|
|
82
|
+
const cleanPayload = JSON.stringify(batchPayload).replace(/'/g, "");
|
|
83
|
+
const updateRes = await runGwsCommand(`docs documents batchUpdate --params '{"documentId": "${docId}"}' --json '${cleanPayload}'`);
|
|
84
|
+
if (!updateRes.success) {
|
|
85
|
+
contentSpinner.fail("Failed to insert content.");
|
|
86
|
+
printError(updateRes.error || "Unknown error", undefined, isJson);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
contentSpinner.succeed("Resume content injected.");
|
|
90
|
+
// 5. Output Result
|
|
91
|
+
const docUrl = `https://docs.google.com/document/d/${docId}/edit`;
|
|
92
|
+
if (isJson) {
|
|
93
|
+
console.log(JSON.stringify({ success: true, url: docUrl, documentId: docId }));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log(boxen(`${chalk.bold.green("✔ Export Complete!")}\n\n` +
|
|
97
|
+
`Your resume has been successfully generated in Google Docs:\n` +
|
|
98
|
+
`${chalk.cyan.underline(docUrl)}`, { padding: 1, borderStyle: "round" }));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/commands/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8C/D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import boxen from "boxen";
|
|
3
|
+
import { checkGwsReady } from "../utils/gws-runner.js";
|
|
4
|
+
import { COLORS } from "../branding.js";
|
|
5
|
+
export function registerWorkspaceCommand(program) {
|
|
6
|
+
const workspace = program
|
|
7
|
+
.command("workspace")
|
|
8
|
+
.alias("gws")
|
|
9
|
+
.description("Manage Google Workspace integrations (Google Docs, Gmail, Sheets)");
|
|
10
|
+
workspace
|
|
11
|
+
.command("check")
|
|
12
|
+
.description("Verify that the Google Workspace CLI is installed and authenticated")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
console.log(`\n ${chalk.bold("CareerVivid Google Workspace Integration")}\n`);
|
|
15
|
+
const isReady = await checkGwsReady();
|
|
16
|
+
if (isReady) {
|
|
17
|
+
console.log(boxen(`${chalk.green("✔ Google Workspace integration is fully configured.")}\n\n` +
|
|
18
|
+
`You can now use CareerVivid commands that export to Google Docs,\n` +
|
|
19
|
+
`sync with Gmail, and manage Calendar invites.`, {
|
|
20
|
+
padding: 1,
|
|
21
|
+
margin: { top: 1, bottom: 1 },
|
|
22
|
+
borderStyle: "round",
|
|
23
|
+
borderColor: COLORS.success,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log(boxen(`${chalk.red("✖ Google Workspace integration is not ready.")}\n\n` +
|
|
28
|
+
`Please ensure you have installed \`gws\` and authenticated:\n\n` +
|
|
29
|
+
`${chalk.cyan("npm install -g @googleworkspace/cli")}\n` +
|
|
30
|
+
`${chalk.cyan("gws auth setup")}`, {
|
|
31
|
+
padding: 1,
|
|
32
|
+
margin: { top: 1, bottom: 1 },
|
|
33
|
+
borderStyle: "round",
|
|
34
|
+
borderColor: COLORS.error,
|
|
35
|
+
}));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* apiUrl — override for the publish endpoint (default: prod)
|
|
7
7
|
*/
|
|
8
8
|
export declare const CONFIG_FILE: string;
|
|
9
|
-
export declare const DEFAULT_API_URL = "https://careervivid.app/api
|
|
9
|
+
export declare const DEFAULT_API_URL = "https://careervivid.app/api";
|
|
10
10
|
export interface CareerVividConfig {
|
|
11
11
|
apiKey?: string;
|
|
12
12
|
apiUrl?: string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,eAAO,MAAM,WAAW,QAAyC,CAAC;AAElE,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,eAAO,MAAM,WAAW,QAAyC,CAAC;AAElE,eAAO,MAAM,eAAe,gCAAgC,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,iBAAiB,CAQ9C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAE1D;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIhF"}
|
package/dist/config.js
CHANGED
|
@@ -9,7 +9,7 @@ import { homedir } from "os";
|
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
11
11
|
export const CONFIG_FILE = join(homedir(), ".careervividrc.json");
|
|
12
|
-
export const DEFAULT_API_URL = "https://careervivid.app/api
|
|
12
|
+
export const DEFAULT_API_URL = "https://careervivid.app/api";
|
|
13
13
|
export function loadConfig() {
|
|
14
14
|
if (!existsSync(CONFIG_FILE))
|
|
15
15
|
return {};
|
package/dist/index.js
CHANGED
|
@@ -29,12 +29,16 @@ import { registerPublishCommand } from "./commands/publish.js";
|
|
|
29
29
|
import { registerConfigCommand } from "./commands/config.js";
|
|
30
30
|
import { registerUpdateCommand } from "./commands/update.js";
|
|
31
31
|
import { checkForUpdates } from "./updates.js";
|
|
32
|
-
import {
|
|
32
|
+
import { registerListTemplatesCommand, registerNewCommand, registerWhiteboardCommand } from "./commands/whiteboard.js";
|
|
33
|
+
import { registerPortfolioCommand } from "./commands/portfolio.js";
|
|
34
|
+
import { registerWorkspaceCommand } from "./commands/workspace.js";
|
|
35
|
+
import { registerProfileCommand } from "./commands/profile.js";
|
|
36
|
+
import { registerJobsCommand } from "./commands/jobs.js";
|
|
33
37
|
const program = new Command();
|
|
34
38
|
program
|
|
35
39
|
.name("cv")
|
|
36
40
|
.description("CareerVivid CLI — publish articles, diagrams, and portfolio updates from your terminal or AI agent")
|
|
37
|
-
.version("1.
|
|
41
|
+
.version("1.3.0", "-v, --version", "Print CLI version")
|
|
38
42
|
.addHelpText("before", getHelpHeader())
|
|
39
43
|
.helpOption("-h, --help", "Show help");
|
|
40
44
|
registerAuthCommand(program);
|
|
@@ -43,6 +47,10 @@ registerPublishCommand(program);
|
|
|
43
47
|
registerConfigCommand(program);
|
|
44
48
|
registerUpdateCommand(program);
|
|
45
49
|
registerWhiteboardCommand(program);
|
|
50
|
+
registerPortfolioCommand(program);
|
|
51
|
+
registerWorkspaceCommand(program);
|
|
52
|
+
registerProfileCommand(program);
|
|
53
|
+
registerJobsCommand(program);
|
|
46
54
|
// Shortcuts for whiteboard creation
|
|
47
55
|
registerNewCommand(program);
|
|
48
56
|
registerListTemplatesCommand(program);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type GwsResponse<T = any> = {
|
|
2
|
+
success: boolean;
|
|
3
|
+
data?: T;
|
|
4
|
+
error?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Executes a GWS CLI command, preferring a globally installed binary,
|
|
8
|
+
* falling back to npx if not found.
|
|
9
|
+
*
|
|
10
|
+
* @param command - The GWS command string (e.g., 'drive files list --params "..."')
|
|
11
|
+
* @returns Parsed JSON response from GWS
|
|
12
|
+
*/
|
|
13
|
+
export declare function runGwsCommand<T = any>(command: string): Promise<GwsResponse<T>>;
|
|
14
|
+
/**
|
|
15
|
+
* Interactive check to ensure GWS is installed and authenticated.
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkGwsReady(): Promise<boolean>;
|
|
18
|
+
//# sourceMappingURL=gws-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gws-runner.d.ts","sourceRoot":"","sources":["../../src/utils/gws-runner.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,IAAI;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAqCrF;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CA2BtD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
/**
|
|
6
|
+
* Executes a GWS CLI command, preferring a globally installed binary,
|
|
7
|
+
* falling back to npx if not found.
|
|
8
|
+
*
|
|
9
|
+
* @param command - The GWS command string (e.g., 'drive files list --params "..."')
|
|
10
|
+
* @returns Parsed JSON response from GWS
|
|
11
|
+
*/
|
|
12
|
+
export async function runGwsCommand(command) {
|
|
13
|
+
let baseCmd = 'gws';
|
|
14
|
+
// Check if gws is available globally
|
|
15
|
+
try {
|
|
16
|
+
await execAsync('command -v gws');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Fallback to npx if gws is not in PATH
|
|
20
|
+
baseCmd = 'npx --yes @googleworkspace/cli';
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const fullCmd = `${baseCmd} ${command}`;
|
|
24
|
+
const { stdout, stderr } = await execAsync(fullCmd);
|
|
25
|
+
// GWS outputs JSON on stdout
|
|
26
|
+
try {
|
|
27
|
+
const data = JSON.parse(stdout.trim());
|
|
28
|
+
return { success: true, data };
|
|
29
|
+
}
|
|
30
|
+
catch (parseError) {
|
|
31
|
+
// Sometimes there's non-JSON output before the JSON
|
|
32
|
+
// Try to extract JSON if possible, or just return raw
|
|
33
|
+
return { success: true, data: stdout.trim() };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// GWS errors usually contain stderr
|
|
38
|
+
let errorMsg = error.stderr || error.message;
|
|
39
|
+
try {
|
|
40
|
+
// Attempt to parse structured error from output if present
|
|
41
|
+
const parsedErr = JSON.parse(error.stdout || "{}");
|
|
42
|
+
if (parsedErr.error && parsedErr.error.message) {
|
|
43
|
+
errorMsg = parsedErr.error.message;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (_) { }
|
|
47
|
+
return { success: false, error: errorMsg };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Interactive check to ensure GWS is installed and authenticated.
|
|
52
|
+
*/
|
|
53
|
+
export async function checkGwsReady() {
|
|
54
|
+
const spinner = ora("Checking Google Workspace CLI (gws) connection...").start();
|
|
55
|
+
// Check if installed
|
|
56
|
+
try {
|
|
57
|
+
await execAsync('command -v gws');
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// If not installed globally, check if we can run it via npx
|
|
61
|
+
try {
|
|
62
|
+
await execAsync('npx --yes @googleworkspace/cli --version');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
spinner.fail("Google Workspace CLI not found.");
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Check auth by doing a simple safe call
|
|
70
|
+
// getting the user's gmail profile is a good token check
|
|
71
|
+
const result = await runGwsCommand("gmail users getProfile --params '{\"userId\": \"me\"}'");
|
|
72
|
+
if (result.success && result.data && result.data.emailAddress) {
|
|
73
|
+
spinner.succeed(`GWS CLI is ready! Authenticated as ${result.data.emailAddress}`);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
spinner.fail("GWS CLI is installed but not authenticated, or missing Gmail scopes.");
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "careervivid",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Official CLI for CareerVivid — publish articles, diagrams, and portfolio updates from your terminal or AI agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"commander": "^12.1.0",
|
|
27
27
|
"enquirer": "^2.4.1",
|
|
28
28
|
"gradient-string": "^3.0.0",
|
|
29
|
+
"mermaid": "^11.12.3",
|
|
29
30
|
"open": "^10.1.0",
|
|
30
31
|
"ora": "^8.1.0",
|
|
31
32
|
"semver": "^7.6.3",
|