careervivid 1.8.0 → 1.9.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/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.8.0";
9
+ const CLI_VERSION = "1.9.0";
10
10
  function requireApiKey() {
11
11
  const key = getApiKey();
12
12
  if (!key) {
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4MpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6J7D"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuOpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6J7D"}
@@ -55,6 +55,7 @@ async function publishSingleFile(filePath, content, opts, jsonMode) {
55
55
  const dryRun = !!opts.dryRun;
56
56
  // ── Parse Frontmatter ───────────────────────────────────────────
57
57
  let postId = undefined;
58
+ let frontmatterTags = [];
58
59
  let cleanContent = content;
59
60
  // Robust frontmatter extraction (multiline, flexible spacing)
60
61
  // Matches --- start, any non-greedy content, and --- end with optional whitespace/newlines
@@ -64,10 +65,28 @@ async function publishSingleFile(filePath, content, opts, jsonMode) {
64
65
  // Match postId with potential leading/trailing whitespace or quotes
65
66
  const idMatch = fm.match(/postId:\s*["']?([a-zA-Z0-9_-]+)["']?/i);
66
67
  postId = idMatch?.[1];
68
+ // Match tags: [tag1, tag2] or tags: tag1, tag2
69
+ const tagsMatch = fm.match(/tags:\s*(?:\[(.*?)\]|(.*))/i);
70
+ if (tagsMatch) {
71
+ const tagsStr = tagsMatch[1] || tagsMatch[2];
72
+ frontmatterTags = tagsStr
73
+ ?.split(",")
74
+ .map(t => t.trim().replace(/^["']|["']$/g, ""))
75
+ .filter(Boolean) || [];
76
+ }
67
77
  // Remove the frontmatter block AND any trailing whitespace/newlines
68
78
  const afterFm = content.slice(frontmatterMatch[0].length);
69
79
  cleanContent = afterFm.replace(/^\s+/, "");
70
80
  }
81
+ // Best Practice: Also look for "Tags: #tag1, #tag2" at the bottom of the article
82
+ const footerTagsMatch = cleanContent.match(/Tags:\s*(.*)$/im);
83
+ let footerTags = [];
84
+ if (footerTagsMatch) {
85
+ footerTags = footerTagsMatch[1]
86
+ .split(/[,\s]+/)
87
+ .map(t => t.trim().replace(/^#/, ""))
88
+ .filter(Boolean);
89
+ }
71
90
  // Fail if onlyUpdate is set but no postId is found
72
91
  if (opts.onlyUpdate && !postId) {
73
92
  if (!jsonMode) {
@@ -101,15 +120,19 @@ async function publishSingleFile(filePath, content, opts, jsonMode) {
101
120
  title = answers.title.trim();
102
121
  }
103
122
  }
104
- const tags = opts.tags
105
- ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean)
106
- : [];
123
+ const tags = [
124
+ ...frontmatterTags,
125
+ ...footerTags,
126
+ ...(opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : [])
127
+ ];
128
+ // De-duplicate and limit to 5 tags
129
+ const finalTags = [...new Set(tags)].slice(0, 5);
107
130
  const payload = {
108
131
  type,
109
132
  dataFormat: format,
110
133
  title,
111
134
  content: cleanContent,
112
- tags,
135
+ tags: finalTags,
113
136
  ...(opts.cover ? { coverImage: opts.cover } : {}),
114
137
  ...(opts.official ? { isOfficialPost: true } : {}),
115
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "careervivid",
3
- "version": "1.8.0",
3
+ "version": "1.9.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": {