@overscore/cli 0.11.10 → 0.11.12

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.
Files changed (2) hide show
  1. package/dist/index.js +54 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1262,16 +1262,6 @@ async function uploadKnowledgeBase(targetDir, baseUrl, projectSlug, apiKey) {
1262
1262
  // Non-fatal
1263
1263
  }
1264
1264
  }
1265
- /**
1266
- * Fetch the latest platform-owned Claude Code rules from the Hub and write
1267
- * them into the project's .claude/ directory. This keeps rules like
1268
- * data-fetching.md, query-performance.md, and skills up to date even in
1269
- * projects that were scaffolded months ago.
1270
- *
1271
- * Called from `deploy` (before uploading) and `pull` (after extracting).
1272
- * User-owned files (company-context.md, this-dashboard.md, knowledge/)
1273
- * are NOT touched — those sync via separate mechanisms.
1274
- */
1275
1265
  const DASHBOARD_IMPORTS = [
1276
1266
  "@.claude/rules/this-dashboard.md",
1277
1267
  "@.claude/knowledge/INDEX.md",
@@ -1284,16 +1274,19 @@ const ANALYSIS_IMPORTS = [
1284
1274
  ];
1285
1275
  /**
1286
1276
  * 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.
1277
+ * Detects dashboard vs analysis by reading the CLAUDE.md heading. Injects only
1278
+ * what's missing — never overwrites existing content. Returns true if modified.
1289
1279
  */
1290
1280
  function syncClaudeMdImports(targetDir) {
1291
1281
  const claudeMdPath = path.join(targetDir, ".claude", "CLAUDE.md");
1292
1282
  if (!fs.existsSync(claudeMdPath))
1293
1283
  return false;
1294
- const isAnalysis = fs.existsSync(path.join(targetDir, "analysis-log.md"));
1284
+ const content = fs.readFileSync(claudeMdPath, "utf-8");
1285
+ const firstHeading = content.match(/^#\s+Overscore\s+(Dashboard|Analysis)/im);
1286
+ if (!firstHeading)
1287
+ return false;
1288
+ const isAnalysis = firstHeading[1].toLowerCase() === "analysis";
1295
1289
  const requiredImports = isAnalysis ? ANALYSIS_IMPORTS : DASHBOARD_IMPORTS;
1296
- let content = fs.readFileSync(claudeMdPath, "utf-8");
1297
1290
  const missing = requiredImports.filter((imp) => !content.includes(imp));
1298
1291
  if (missing.length === 0)
1299
1292
  return false;
@@ -1307,6 +1300,12 @@ function syncClaudeMdImports(targetDir) {
1307
1300
  fs.writeFileSync(claudeMdPath, updated);
1308
1301
  return true;
1309
1302
  }
1303
+ /**
1304
+ * Fetch the latest platform-owned Claude Code rules from the Hub and write
1305
+ * them into the project's .claude/ directory. Called from `deploy` (before
1306
+ * uploading), `pull` (after extracting), and `syncHubManagedRules` (analysis
1307
+ * paths). User-owned files (knowledge/, this-dashboard.md) are NOT touched.
1308
+ */
1310
1309
  async function syncPlatformRules(targetDir, baseUrl, apiKey) {
1311
1310
  try {
1312
1311
  const res = await fetch(`${baseUrl}/api/projects/platform-rules`, {
@@ -1337,11 +1336,15 @@ async function syncPlatformRules(targetDir, baseUrl, apiKey) {
1337
1336
  /**
1338
1337
  * Explicit on-demand platform rules sync. Reports what changed so Claude
1339
1338
  * can tell the user what's new. Called by `npx @overscore/cli platform update`.
1339
+ *
1340
+ * With `--check`: read-only mode. Reports what *would* change without writing.
1340
1341
  */
1341
- async function platformUpdate() {
1342
+ async function platformUpdate(checkOnly = false) {
1342
1343
  const { apiUrl, apiKey } = await loadEnv();
1343
1344
  const baseUrl = apiUrl.replace(/\/api$/, "");
1344
- console.log("\n Syncing platform rules from Hub...\n");
1345
+ console.log(checkOnly
1346
+ ? "\n Checking platform rules (no changes will be written)...\n"
1347
+ : "\n Syncing platform rules from Hub...\n");
1345
1348
  const capPath = path.resolve(process.cwd(), ".claude", "platform-capabilities.md");
1346
1349
  const prevContent = fs.existsSync(capPath) ? fs.readFileSync(capPath, "utf-8") : null;
1347
1350
  const prevVersion = prevContent?.match(/^version:\s*(.+)$/m)?.[1]?.trim() ?? null;
@@ -1361,19 +1364,41 @@ async function platformUpdate() {
1361
1364
  for (const [relPath, content] of Object.entries(data.files)) {
1362
1365
  const fullPath = path.join(process.cwd(), ".claude", relPath);
1363
1366
  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
+ if (existing !== content) {
1367
1368
  updated.push(relPath);
1369
+ if (!checkOnly) {
1370
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
1371
+ fs.writeFileSync(fullPath, content);
1372
+ }
1373
+ }
1374
+ }
1375
+ // syncClaudeMdImports only makes sense if we're actually writing files.
1376
+ // In check mode, compute whether imports would be added by reading current state.
1377
+ let claudeWouldChange = false;
1378
+ if (checkOnly) {
1379
+ const claudeMdPath = path.join(process.cwd(), ".claude", "CLAUDE.md");
1380
+ if (fs.existsSync(claudeMdPath)) {
1381
+ const cmContent = fs.readFileSync(claudeMdPath, "utf-8");
1382
+ const heading = cmContent.match(/^#\s+Overscore\s+(Dashboard|Analysis)/im);
1383
+ if (heading) {
1384
+ const required = heading[1].toLowerCase() === "analysis" ? ANALYSIS_IMPORTS : DASHBOARD_IMPORTS;
1385
+ claudeWouldChange = required.some((imp) => !cmContent.includes(imp));
1386
+ }
1387
+ }
1388
+ if (claudeWouldChange)
1389
+ updated.push(".claude/CLAUDE.md (@imports would be added)");
1390
+ }
1391
+ else {
1392
+ const claudeUpdated = syncClaudeMdImports(process.cwd());
1393
+ if (claudeUpdated)
1394
+ updated.push(".claude/CLAUDE.md (@imports updated)");
1368
1395
  }
1369
- const claudeUpdated = syncClaudeMdImports(process.cwd());
1370
- if (claudeUpdated)
1371
- updated.push(".claude/CLAUDE.md (@imports updated)");
1372
1396
  if (updated.length === 0) {
1373
1397
  console.log(" Already up to date — no changes.\n");
1374
1398
  return;
1375
1399
  }
1376
- console.log(` Updated ${updated.length} file${updated.length === 1 ? "" : "s"}:\n`);
1400
+ const verb = checkOnly ? "Would update" : "Updated";
1401
+ console.log(` ${verb} ${updated.length} file${updated.length === 1 ? "" : "s"}:\n`);
1377
1402
  for (const f of updated)
1378
1403
  console.log(` ${f}`);
1379
1404
  if (updated.includes("platform-capabilities.md")) {
@@ -2395,17 +2420,20 @@ else if (command === "analysis") {
2395
2420
  }
2396
2421
  else if (command === "platform") {
2397
2422
  if (subcommand === "update") {
2398
- platformUpdate();
2423
+ const checkOnly = process.argv.includes("--check");
2424
+ platformUpdate(checkOnly);
2399
2425
  }
2400
2426
  else {
2401
2427
  console.log(`
2402
2428
  overscore platform — Manage platform rules and capabilities
2403
2429
 
2404
2430
  Commands:
2405
- update Sync latest platform rules, skills, and capabilities from Hub
2431
+ update Sync latest platform rules, skills, and capabilities from Hub
2432
+ update --check Show what would change without writing anything
2406
2433
 
2407
- Example:
2434
+ Examples:
2408
2435
  npx @overscore/cli platform update
2436
+ npx @overscore/cli platform update --check
2409
2437
  `);
2410
2438
  }
2411
2439
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overscore/cli",
3
- "version": "0.11.10",
3
+ "version": "0.11.12",
4
4
  "description": "CLI for deploying Overscore dashboards and publishing analyses",
5
5
  "bin": {
6
6
  "overscore": "dist/index.js"