@overscore/cli 0.11.7 → 0.11.10

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/index.js CHANGED
@@ -1171,6 +1171,9 @@ async function syncHubManagedRules(targetDir, baseUrl, projectSlug, apiKey) {
1171
1171
  catch {
1172
1172
  // Non-fatal
1173
1173
  }
1174
+ // Platform rules (skills, capabilities) — same sync that dashboard pull/deploy gets.
1175
+ await syncPlatformRules(targetDir, baseUrl, apiKey);
1176
+ syncClaudeMdImports(targetDir);
1174
1177
  }
1175
1178
  /**
1176
1179
  * Sync the project knowledge base from the Hub into .claude/knowledge/.
@@ -1269,6 +1272,41 @@ async function uploadKnowledgeBase(targetDir, baseUrl, projectSlug, apiKey) {
1269
1272
  * User-owned files (company-context.md, this-dashboard.md, knowledge/)
1270
1273
  * are NOT touched — those sync via separate mechanisms.
1271
1274
  */
1275
+ const DASHBOARD_IMPORTS = [
1276
+ "@.claude/rules/this-dashboard.md",
1277
+ "@.claude/knowledge/INDEX.md",
1278
+ "@.claude/platform-capabilities.md",
1279
+ ];
1280
+ const ANALYSIS_IMPORTS = [
1281
+ "@this-analysis.md",
1282
+ "@.claude/knowledge/INDEX.md",
1283
+ "@.claude/platform-capabilities.md",
1284
+ ];
1285
+ /**
1286
+ * Ensures the project's CLAUDE.md has the standard @import lines at the top.
1287
+ * Detects dashboard vs analysis by file presence. Injects only what's missing —
1288
+ * never overwrites existing content. Returns true if CLAUDE.md was modified.
1289
+ */
1290
+ function syncClaudeMdImports(targetDir) {
1291
+ const claudeMdPath = path.join(targetDir, ".claude", "CLAUDE.md");
1292
+ if (!fs.existsSync(claudeMdPath))
1293
+ return false;
1294
+ const isAnalysis = fs.existsSync(path.join(targetDir, "analysis-log.md"));
1295
+ const requiredImports = isAnalysis ? ANALYSIS_IMPORTS : DASHBOARD_IMPORTS;
1296
+ let content = fs.readFileSync(claudeMdPath, "utf-8");
1297
+ const missing = requiredImports.filter((imp) => !content.includes(imp));
1298
+ if (missing.length === 0)
1299
+ return false;
1300
+ // Inject missing imports right after the first # heading line
1301
+ const lines = content.split("\n");
1302
+ const headingIdx = lines.findIndex((l) => l.startsWith("# "));
1303
+ if (headingIdx === -1)
1304
+ return false;
1305
+ lines.splice(headingIdx + 1, 0, "", ...missing);
1306
+ const updated = lines.join("\n").replace(/\n{3,}/g, "\n\n");
1307
+ fs.writeFileSync(claudeMdPath, updated);
1308
+ return true;
1309
+ }
1272
1310
  async function syncPlatformRules(targetDir, baseUrl, apiKey) {
1273
1311
  try {
1274
1312
  const res = await fetch(`${baseUrl}/api/projects/platform-rules`, {
@@ -1286,14 +1324,85 @@ async function syncPlatformRules(targetDir, baseUrl, apiKey) {
1286
1324
  fs.writeFileSync(fullPath, content);
1287
1325
  count++;
1288
1326
  }
1289
- if (count > 0) {
1290
- console.log(` Platform rules synced (${count} files).`);
1327
+ const claudeUpdated = syncClaudeMdImports(targetDir);
1328
+ const total = count + (claudeUpdated ? 1 : 0);
1329
+ if (total > 0) {
1330
+ console.log(` Platform rules synced (${count} files${claudeUpdated ? " + CLAUDE.md updated" : ""}).`);
1291
1331
  }
1292
1332
  }
1293
1333
  catch {
1294
1334
  // Non-fatal — existing rules stay in place
1295
1335
  }
1296
1336
  }
1337
+ /**
1338
+ * Explicit on-demand platform rules sync. Reports what changed so Claude
1339
+ * can tell the user what's new. Called by `npx @overscore/cli platform update`.
1340
+ */
1341
+ async function platformUpdate() {
1342
+ const { apiUrl, apiKey } = await loadEnv();
1343
+ const baseUrl = apiUrl.replace(/\/api$/, "");
1344
+ console.log("\n Syncing platform rules from Hub...\n");
1345
+ const capPath = path.resolve(process.cwd(), ".claude", "platform-capabilities.md");
1346
+ const prevContent = fs.existsSync(capPath) ? fs.readFileSync(capPath, "utf-8") : null;
1347
+ const prevVersion = prevContent?.match(/^version:\s*(.+)$/m)?.[1]?.trim() ?? null;
1348
+ const res = await fetch(`${baseUrl}/api/projects/platform-rules`, {
1349
+ headers: { Authorization: `Bearer ${apiKey}` },
1350
+ });
1351
+ if (!res.ok) {
1352
+ console.error(`\n Error: Could not reach Hub (HTTP ${res.status})\n`);
1353
+ process.exit(1);
1354
+ }
1355
+ const data = (await res.json());
1356
+ if (!data.files) {
1357
+ console.error("\n Error: No files in platform-rules response.\n");
1358
+ process.exit(1);
1359
+ }
1360
+ const updated = [];
1361
+ for (const [relPath, content] of Object.entries(data.files)) {
1362
+ const fullPath = path.join(process.cwd(), ".claude", relPath);
1363
+ const existing = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, "utf-8") : null;
1364
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
1365
+ fs.writeFileSync(fullPath, content);
1366
+ if (existing !== content)
1367
+ updated.push(relPath);
1368
+ }
1369
+ const claudeUpdated = syncClaudeMdImports(process.cwd());
1370
+ if (claudeUpdated)
1371
+ updated.push(".claude/CLAUDE.md (@imports updated)");
1372
+ if (updated.length === 0) {
1373
+ console.log(" Already up to date — no changes.\n");
1374
+ return;
1375
+ }
1376
+ console.log(` Updated ${updated.length} file${updated.length === 1 ? "" : "s"}:\n`);
1377
+ for (const f of updated)
1378
+ console.log(` ${f}`);
1379
+ if (updated.includes("platform-capabilities.md")) {
1380
+ const newContent = data.files["platform-capabilities.md"];
1381
+ const newVersion = newContent.match(/^version:\s*(.+)$/m)?.[1]?.trim() ?? "unknown";
1382
+ if (prevVersion && prevVersion !== newVersion) {
1383
+ console.log(`\n Capabilities updated: ${prevVersion} → ${newVersion}`);
1384
+ const changelog = extractChangelogSince(newContent, prevVersion);
1385
+ if (changelog)
1386
+ console.log(`\n What's new:\n\n${changelog}`);
1387
+ }
1388
+ else if (!prevVersion) {
1389
+ console.log(`\n Platform capabilities synced (v${newVersion}).`);
1390
+ console.log(` Re-read .claude/platform-capabilities.md in your Claude session to see available commands.`);
1391
+ }
1392
+ }
1393
+ console.log();
1394
+ }
1395
+ function extractChangelogSince(content, sinceVersion) {
1396
+ const match = content.match(/## Changelog\n([\s\S]*?)(?=\n## |\s*$)/);
1397
+ if (!match)
1398
+ return null;
1399
+ const blocks = match[1].split(/(?=### )/);
1400
+ const newer = blocks.filter((b) => {
1401
+ const v = b.match(/### (\d{4}-\d{2}-\d{2})/)?.[1];
1402
+ return v && v > sinceVersion;
1403
+ });
1404
+ return newer.length ? newer.join("").trim() : null;
1405
+ }
1297
1406
  /**
1298
1407
  * Recursively copies a template directory to a target directory, substituting
1299
1408
  * `{{KEY}}` placeholders in text files. Mirrors directory structure.
@@ -1616,6 +1725,9 @@ async function analysisPublish() {
1616
1725
  console.log(`\n URL: ${data.url}\n`);
1617
1726
  // Upload knowledge base so insights from this analysis persist
1618
1727
  await uploadKnowledgeBase(process.cwd(), baseUrl, meta.project_slug, cfg.apiKey);
1728
+ // Sync platform rules so skills and capabilities stay current
1729
+ await syncPlatformRules(process.cwd(), baseUrl, cfg.apiKey);
1730
+ syncClaudeMdImports(process.cwd());
1619
1731
  }
1620
1732
  /**
1621
1733
  * Ensure analysis.json has a valid id by fetching from the Hub.
@@ -2281,6 +2393,22 @@ else if (command === "analysis") {
2281
2393
  `);
2282
2394
  }
2283
2395
  }
2396
+ else if (command === "platform") {
2397
+ if (subcommand === "update") {
2398
+ platformUpdate();
2399
+ }
2400
+ else {
2401
+ console.log(`
2402
+ overscore platform — Manage platform rules and capabilities
2403
+
2404
+ Commands:
2405
+ update Sync latest platform rules, skills, and capabilities from Hub
2406
+
2407
+ Example:
2408
+ npx @overscore/cli platform update
2409
+ `);
2410
+ }
2411
+ }
2284
2412
  else if (command === "query") {
2285
2413
  if (subcommand === "list") {
2286
2414
  queryList();
@@ -2339,6 +2467,7 @@ else {
2339
2467
  pull <slug> Pull source code from the hub
2340
2468
  versions List deploy history
2341
2469
  query <subcommand> Manage dashboard queries
2470
+ platform <subcommand> Sync platform rules and capabilities
2342
2471
  analysis <subcommand> Manage analyses (frozen investigations)
2343
2472
  install-skills Install the Overscore skill library into ~/.claude/skills/
2344
2473
 
@@ -2350,6 +2479,7 @@ else {
2350
2479
  npx @overscore/cli pull <slug>
2351
2480
  npx @overscore/cli pull <slug> --version 3
2352
2481
  npx @overscore/cli versions
2482
+ npx @overscore/cli platform update
2353
2483
  npx @overscore/cli query list
2354
2484
  npx @overscore/cli query add <name> "<sql>"
2355
2485
  npx @overscore/cli query update <name> "<sql>"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overscore/cli",
3
- "version": "0.11.7",
3
+ "version": "0.11.10",
4
4
  "description": "CLI for deploying Overscore dashboards and publishing analyses",
5
5
  "bin": {
6
6
  "overscore": "dist/index.js"
@@ -2,6 +2,7 @@
2
2
 
3
3
  @this-analysis.md
4
4
  @.claude/knowledge/INDEX.md
5
+ @.claude/platform-capabilities.md
5
6
 
6
7
  This is an **Overscore Analysis** — a one-off, frozen investigation that produces a single self-contained `report.html` you can share in Slack and reopen later to refresh.
7
8