@overscore/cli 0.8.0 → 0.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.
Files changed (2) hide show
  1. package/dist/index.js +108 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -287,6 +287,8 @@ async function deploy() {
287
287
  // Non-fatal
288
288
  }
289
289
  }
290
+ // Pull latest platform rules so the local project stays up to date
291
+ await syncPlatformRules(process.cwd(), baseUrl, apiKey);
290
292
  }
291
293
  async function queryList() {
292
294
  const { dashboardSlug } = loadEnv();
@@ -543,6 +545,26 @@ VITE_OVERSCORE_API_URL=https://overscore.dev/api
543
545
  catch {
544
546
  // Non-fatal — the version from the source zip is fine
545
547
  }
548
+ // Pull dashboard context from Hub (may be newer than the source zip)
549
+ try {
550
+ const dashCtxRes = await fetch(`${baseUrl}/api/dashboards/${dashboardSlug}/context`, {
551
+ headers: { Authorization: `Bearer ${apiKey}` },
552
+ });
553
+ if (dashCtxRes.ok) {
554
+ const dashCtxData = (await dashCtxRes.json());
555
+ if (dashCtxData.context_md) {
556
+ const rulesDir = path.join(targetDir, ".claude", "rules");
557
+ fs.mkdirSync(rulesDir, { recursive: true });
558
+ fs.writeFileSync(path.join(rulesDir, "dashboard-context.md"), dashCtxData.context_md);
559
+ console.log(" Updated dashboard context from Hub.");
560
+ }
561
+ }
562
+ }
563
+ catch {
564
+ // Non-fatal
565
+ }
566
+ // Pull latest platform rules
567
+ await syncPlatformRules(targetDir, baseUrl, apiKey);
546
568
  console.log(`
547
569
  Pulled v${version}${commitMsg ? ` — "${commitMsg}"` : ""}
548
570
 
@@ -711,6 +733,84 @@ async function analysisApiRequest(cfg, method, urlPath, body) {
711
733
  }
712
734
  return data;
713
735
  }
736
+ /**
737
+ * Fetch hub-managed rule files (company-context, sql-style) for a project
738
+ * and write them into the target analysis folder, overwriting whatever's
739
+ * there. Best-effort and non-fatal — if the hub is unreachable, the existing
740
+ * files (template defaults or bundle contents) stay in place.
741
+ *
742
+ * Called from both `analysis new` (after the template walker) and `analysis
743
+ * pull` (after the bundle extraction) so hub overrides always reflect the
744
+ * latest state regardless of how the analysis was created.
745
+ */
746
+ async function syncHubManagedRules(targetDir, baseUrl, projectSlug, apiKey) {
747
+ const authHeader = { Authorization: `Bearer ${apiKey}` };
748
+ const rulesDir = path.join(targetDir, ".claude", "rules");
749
+ fs.mkdirSync(rulesDir, { recursive: true });
750
+ // Company context — null means "no company context set yet"; leave any
751
+ // existing file alone in that case.
752
+ try {
753
+ const ctxRes = await fetch(`${baseUrl}/api/projects/${projectSlug}/context`, { headers: authHeader });
754
+ if (ctxRes.ok) {
755
+ const ctxData = (await ctxRes.json());
756
+ if (ctxData.context_md) {
757
+ fs.writeFileSync(path.join(rulesDir, "company-context.md"), ctxData.context_md);
758
+ }
759
+ }
760
+ }
761
+ catch {
762
+ // Non-fatal
763
+ }
764
+ // SQL style override — null means "use the CLI default"; leave the
765
+ // template/bundle file alone in that case.
766
+ try {
767
+ const sqlRes = await fetch(`${baseUrl}/api/projects/${projectSlug}/sql-style`, { headers: authHeader });
768
+ if (sqlRes.ok) {
769
+ const sqlData = (await sqlRes.json());
770
+ if (sqlData.sql_style_md) {
771
+ fs.writeFileSync(path.join(rulesDir, "sql-style.md"), sqlData.sql_style_md);
772
+ }
773
+ }
774
+ }
775
+ catch {
776
+ // Non-fatal
777
+ }
778
+ }
779
+ /**
780
+ * Fetch the latest platform-owned Claude Code rules from the Hub and write
781
+ * them into the project's .claude/ directory. This keeps rules like
782
+ * data-fetching.md, query-performance.md, and skills up to date even in
783
+ * projects that were scaffolded months ago.
784
+ *
785
+ * Called from `deploy` (before uploading) and `pull` (after extracting).
786
+ * User-owned files (company-context.md, dashboard-context.md) are NOT
787
+ * touched — those sync via separate mechanisms.
788
+ */
789
+ async function syncPlatformRules(targetDir, baseUrl, apiKey) {
790
+ try {
791
+ const res = await fetch(`${baseUrl}/api/platform-rules`, {
792
+ headers: { Authorization: `Bearer ${apiKey}` },
793
+ });
794
+ if (!res.ok)
795
+ return;
796
+ const data = (await res.json());
797
+ if (!data.files)
798
+ return;
799
+ let count = 0;
800
+ for (const [relPath, content] of Object.entries(data.files)) {
801
+ const fullPath = path.join(targetDir, ".claude", relPath);
802
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
803
+ fs.writeFileSync(fullPath, content);
804
+ count++;
805
+ }
806
+ if (count > 0) {
807
+ console.log(` Platform rules synced (${count} files).`);
808
+ }
809
+ }
810
+ catch {
811
+ // Non-fatal — existing rules stay in place
812
+ }
813
+ }
714
814
  /**
715
815
  * Recursively copies a template directory to a target directory, substituting
716
816
  * `{{KEY}}` placeholders in text files. Mirrors directory structure.
@@ -793,38 +893,10 @@ async function analysisNew(title) {
793
893
  PROJECT_SLUG: data.project_slug,
794
894
  DATE: today,
795
895
  });
796
- // Pull project's company-context.md and any project-level rule overrides
797
- // (best-effort, non-fatal — the template defaults are still in place if
798
- // the network is down or the hub is unreachable).
896
+ // Pull hub-managed rules (company context + sql style override). Best-effort
897
+ // and non-fatal — template defaults stay in place if the hub is unreachable.
799
898
  const baseUrl = cfg.apiUrl.replace(/\/api$/, "");
800
- const authHeader = { Authorization: `Bearer ${cfg.apiKey}` };
801
- // Company context
802
- try {
803
- const ctxRes = await fetch(`${baseUrl}/api/projects/${data.project_slug}/context`, { headers: authHeader });
804
- if (ctxRes.ok) {
805
- const ctxData = (await ctxRes.json());
806
- if (ctxData.context_md) {
807
- fs.writeFileSync(path.join(targetDir, ".claude", "rules", "company-context.md"), ctxData.context_md);
808
- }
809
- }
810
- }
811
- catch {
812
- // Non-fatal
813
- }
814
- // SQL style override — if the project has a custom version, replace the
815
- // template default. If null/missing, the template default stays in place.
816
- try {
817
- const sqlRes = await fetch(`${baseUrl}/api/projects/${data.project_slug}/sql-style`, { headers: authHeader });
818
- if (sqlRes.ok) {
819
- const sqlData = (await sqlRes.json());
820
- if (sqlData.sql_style_md) {
821
- fs.writeFileSync(path.join(targetDir, ".claude", "rules", "sql-style.md"), sqlData.sql_style_md);
822
- }
823
- }
824
- }
825
- catch {
826
- // Non-fatal
827
- }
899
+ await syncHubManagedRules(targetDir, baseUrl, data.project_slug, cfg.apiKey);
828
900
  console.log(` Analysis created: ${data.title}
829
901
  Slug: ${data.slug}
830
902
  Project: ${data.project_slug}
@@ -965,6 +1037,11 @@ async function analysisPull(slug) {
965
1037
  // Non-fatal
966
1038
  }
967
1039
  }
1040
+ // Refresh hub-managed rule files after restoring from the bundle. The
1041
+ // bundle contains whatever was set at publish time; this overwrites them
1042
+ // with the latest hub state so company context and SQL style stay in
1043
+ // sync across pulls. Best-effort and non-fatal.
1044
+ await syncHubManagedRules(targetDir, baseUrl, projectSlug, cfg.apiKey);
968
1045
  console.log(`
969
1046
  Pulled "${analysisTitle}"
970
1047
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overscore/cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "CLI for deploying Overscore dashboards and publishing analyses",
5
5
  "bin": {
6
6
  "overscore": "dist/index.js"