@overscore/cli 0.11.0 → 0.11.1
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
|
@@ -268,27 +268,20 @@ async function deploy() {
|
|
|
268
268
|
const baseUrl = apiUrl.replace(/\/api$/, "");
|
|
269
269
|
// Upload knowledge base (project-level, shared across all artifacts)
|
|
270
270
|
await uploadKnowledgeBase(process.cwd(), baseUrl, projectSlug, apiKey);
|
|
271
|
-
// Sync
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (storedHash && hubHash && storedHash !== hubHash) {
|
|
286
|
-
console.log(" ⚠ Company context was updated in the Hub since you pulled.");
|
|
287
|
-
console.log(` Review at: ${baseUrl}/projects/${projectSlug}/context`);
|
|
288
|
-
console.log(" Your local changes were NOT uploaded to avoid overwriting.");
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
const putRes = await fetch(`${baseUrl}/api/projects/${projectSlug}/context`, {
|
|
271
|
+
// Sync dashboard context — skip stub files (they have no real content)
|
|
272
|
+
const dashCtxPath = path.resolve(process.cwd(), ".claude/rules/this-dashboard.md");
|
|
273
|
+
const legacyDashCtxPath = path.resolve(process.cwd(), ".claude/rules/dashboard-context.md");
|
|
274
|
+
const activeDashCtxPath = fs.existsSync(dashCtxPath)
|
|
275
|
+
? dashCtxPath
|
|
276
|
+
: fs.existsSync(legacyDashCtxPath)
|
|
277
|
+
? legacyDashCtxPath
|
|
278
|
+
: null;
|
|
279
|
+
if (activeDashCtxPath) {
|
|
280
|
+
const content = fs.readFileSync(activeDashCtxPath, "utf-8").trim();
|
|
281
|
+
const isStub = !content || content.startsWith("<!--") || content === `# ${dashboardSlug}`;
|
|
282
|
+
if (!isStub) {
|
|
283
|
+
try {
|
|
284
|
+
const res = await fetch(`${baseUrl}/api/dashboards/${dashboardSlug}/context`, {
|
|
292
285
|
method: "PUT",
|
|
293
286
|
headers: {
|
|
294
287
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -296,40 +289,17 @@ async function deploy() {
|
|
|
296
289
|
},
|
|
297
290
|
body: JSON.stringify({ context_md: content }),
|
|
298
291
|
});
|
|
299
|
-
if (
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
if (putData.context_md_hash) {
|
|
303
|
-
fs.mkdirSync(path.dirname(hashPath), { recursive: true });
|
|
304
|
-
fs.writeFileSync(hashPath, putData.context_md_hash);
|
|
305
|
-
}
|
|
306
|
-
console.log(" Company context synced.");
|
|
292
|
+
if (res.ok) {
|
|
293
|
+
const lines = content.split("\n").length;
|
|
294
|
+
console.log(` Dashboard context synced (${lines} lines).`);
|
|
307
295
|
}
|
|
308
296
|
}
|
|
297
|
+
catch {
|
|
298
|
+
// Non-fatal
|
|
299
|
+
}
|
|
309
300
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
// Sync dashboard context (no conflict detection needed — per-dashboard)
|
|
315
|
-
const dashCtxPath = path.resolve(process.cwd(), ".claude/rules/this-dashboard.md");
|
|
316
|
-
const legacyDashCtxPath = path.resolve(process.cwd(), ".claude/rules/dashboard-context.md");
|
|
317
|
-
const activeDashCtxPath = fs.existsSync(dashCtxPath) ? dashCtxPath : fs.existsSync(legacyDashCtxPath) ? legacyDashCtxPath : null;
|
|
318
|
-
if (activeDashCtxPath) {
|
|
319
|
-
const content = fs.readFileSync(activeDashCtxPath, "utf-8");
|
|
320
|
-
try {
|
|
321
|
-
await fetch(`${baseUrl}/api/dashboards/${dashboardSlug}/context`, {
|
|
322
|
-
method: "PUT",
|
|
323
|
-
headers: {
|
|
324
|
-
Authorization: `Bearer ${apiKey}`,
|
|
325
|
-
"Content-Type": "application/json",
|
|
326
|
-
},
|
|
327
|
-
body: JSON.stringify({ context_md: content }),
|
|
328
|
-
});
|
|
329
|
-
console.log(" Dashboard context synced.");
|
|
330
|
-
}
|
|
331
|
-
catch {
|
|
332
|
-
// Non-fatal
|
|
301
|
+
else {
|
|
302
|
+
console.log(" Dashboard context: stub, not synced — run save-knowledge to populate it.");
|
|
333
303
|
}
|
|
334
304
|
}
|
|
335
305
|
// Pull latest platform rules so the local project stays up to date
|
|
@@ -736,51 +706,85 @@ VITE_OVERSCORE_DASHBOARD_SLUG=${dashboardSlug}
|
|
|
736
706
|
VITE_OVERSCORE_API_URL=https://overscore.dev/api
|
|
737
707
|
`;
|
|
738
708
|
fs.writeFileSync(path.join(targetDir, ".env"), envContent, { mode: 0o600 });
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
709
|
+
const rulesDir = path.join(targetDir, ".claude", "rules");
|
|
710
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
711
|
+
// Sync project knowledge base (preferred) — or fall back to legacy company-context.md
|
|
712
|
+
const hasKnowledge = await syncKnowledgeBase(targetDir, baseUrl, projectSlug, apiKey);
|
|
713
|
+
if (!hasKnowledge) {
|
|
714
|
+
try {
|
|
715
|
+
const ctxRes = await fetch(`${baseUrl}/api/projects/${projectSlug}/context`, {
|
|
716
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
717
|
+
});
|
|
718
|
+
if (ctxRes.ok) {
|
|
719
|
+
const ctxData = (await ctxRes.json());
|
|
720
|
+
if (ctxData.context_md) {
|
|
721
|
+
fs.writeFileSync(path.join(rulesDir, "company-context.md"), ctxData.context_md);
|
|
722
|
+
if (ctxData.context_md_hash) {
|
|
723
|
+
fs.writeFileSync(path.join(rulesDir, ".context-hash"), ctxData.context_md_hash);
|
|
724
|
+
}
|
|
753
725
|
}
|
|
754
|
-
console.log(" Updated company context from Hub.");
|
|
755
726
|
}
|
|
756
727
|
}
|
|
728
|
+
catch {
|
|
729
|
+
// Non-fatal
|
|
730
|
+
}
|
|
757
731
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
// Pull dashboard context from Hub (may be newer than the source zip)
|
|
732
|
+
// Pull dashboard context from Hub
|
|
733
|
+
let dashboardContextLoaded = false;
|
|
762
734
|
try {
|
|
763
735
|
const dashCtxRes = await fetch(`${baseUrl}/api/dashboards/${dashboardSlug}/context`, {
|
|
764
736
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
765
737
|
});
|
|
766
738
|
if (dashCtxRes.ok) {
|
|
767
739
|
const dashCtxData = (await dashCtxRes.json());
|
|
768
|
-
if (dashCtxData.context_md) {
|
|
769
|
-
const rulesDir = path.join(targetDir, ".claude", "rules");
|
|
770
|
-
fs.mkdirSync(rulesDir, { recursive: true });
|
|
740
|
+
if (dashCtxData.context_md && dashCtxData.context_md.trim()) {
|
|
771
741
|
fs.writeFileSync(path.join(rulesDir, "this-dashboard.md"), dashCtxData.context_md);
|
|
772
|
-
|
|
742
|
+
dashboardContextLoaded = true;
|
|
773
743
|
}
|
|
774
744
|
}
|
|
775
745
|
}
|
|
776
746
|
catch {
|
|
777
747
|
// Non-fatal
|
|
778
748
|
}
|
|
779
|
-
//
|
|
749
|
+
// Create a stub this-dashboard.md if none came from the Hub so Claude knows to fill it in
|
|
750
|
+
if (!dashboardContextLoaded) {
|
|
751
|
+
const stubPath = path.join(rulesDir, "this-dashboard.md");
|
|
752
|
+
if (!fs.existsSync(stubPath) || fs.readFileSync(stubPath, "utf-8").trim() === "") {
|
|
753
|
+
fs.writeFileSync(stubPath, `# ${slug}\n\n<!-- Claude: fill this in as you build. What does this dashboard answer? Who is the audience? Which queries power which charts? Any non-obvious filter logic or design decisions? -->\n`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
// Pull latest platform rules (includes save-knowledge skill)
|
|
780
757
|
await syncPlatformRules(targetDir, baseUrl, apiKey);
|
|
758
|
+
// Print context summary so the developer knows what Claude has access to
|
|
759
|
+
const knowledgeDir = path.join(targetDir, ".claude", "knowledge");
|
|
760
|
+
const knowledgeFiles = fs.existsSync(knowledgeDir)
|
|
761
|
+
? fs.readdirSync(knowledgeDir).filter((f) => f.endsWith(".md") && f !== "INDEX.md")
|
|
762
|
+
: [];
|
|
763
|
+
const dashCtxPath = path.join(rulesDir, "this-dashboard.md");
|
|
764
|
+
const dashCtxContent = fs.existsSync(dashCtxPath) ? fs.readFileSync(dashCtxPath, "utf-8").trim() : "";
|
|
765
|
+
const dashCtxIsStub = dashCtxContent.startsWith("<!--") || dashCtxContent === `# ${slug}`;
|
|
766
|
+
const sqlStylePath = path.join(rulesDir, "sql-style.md");
|
|
781
767
|
console.log(`
|
|
782
768
|
Pulled v${version}${commitMsg ? ` — "${commitMsg}"` : ""}
|
|
783
769
|
|
|
770
|
+
Context loaded:`);
|
|
771
|
+
if (knowledgeFiles.length > 0) {
|
|
772
|
+
console.log(` Knowledge base: ${knowledgeFiles.length} file${knowledgeFiles.length !== 1 ? "s" : ""} (${knowledgeFiles.join(", ")})`);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
console.log(` Knowledge base: empty — Claude will build it as you work`);
|
|
776
|
+
}
|
|
777
|
+
if (dashCtxIsStub || !dashCtxContent) {
|
|
778
|
+
console.log(` Dashboard context: none yet — stub created, Claude will fill it in`);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
const lines = dashCtxContent.split("\n").length;
|
|
782
|
+
console.log(` Dashboard context: this-dashboard.md (${lines} lines)`);
|
|
783
|
+
}
|
|
784
|
+
if (fs.existsSync(sqlStylePath)) {
|
|
785
|
+
console.log(` SQL style: project override active`);
|
|
786
|
+
}
|
|
787
|
+
console.log(`
|
|
784
788
|
Next steps:
|
|
785
789
|
|
|
786
790
|
cd ${slug}
|
package/package.json
CHANGED
|
@@ -5,26 +5,32 @@ description: Reviews data insights discovered during dashboard or analysis work
|
|
|
5
5
|
|
|
6
6
|
# Save Knowledge
|
|
7
7
|
|
|
8
|
-
Run
|
|
9
|
-
|
|
10
|
-
## When to invoke
|
|
11
|
-
- Before `npx @overscore/cli deploy`
|
|
12
|
-
- When the user says "save what you learned" or similar
|
|
8
|
+
Run before deploying a dashboard, before publishing an analysis, or whenever you discover something non-obvious about the data.
|
|
13
9
|
|
|
14
10
|
## Process
|
|
15
11
|
|
|
16
|
-
1. Review the
|
|
12
|
+
1. Review the code you've written and queries you've run this session.
|
|
13
|
+
|
|
14
|
+
2. **Read the existing files before proposing anything.** Read `.claude/knowledge/INDEX.md`, then read the specific knowledge files relevant to what you learned. Read `.claude/rules/this-dashboard.md` (or `analysis-log.md` for analyses) in full. You need to know what's already there before you can know what's new.
|
|
15
|
+
|
|
16
|
+
3. For each insight from this session, decide what action to take:
|
|
17
|
+
|
|
18
|
+
| Situation | Action |
|
|
19
|
+
|---|---|
|
|
20
|
+
| New topic, no existing file covers it | Create a new `.claude/knowledge/{topic}.md`; add entry to INDEX.md |
|
|
21
|
+
| New fact that belongs in an existing knowledge file | Append a new line or section to that file — do not rewrite what's there |
|
|
22
|
+
| Fact that refines or corrects something already documented | Edit that specific line/section only — do not duplicate it |
|
|
23
|
+
| Already documented accurately | Skip entirely |
|
|
24
|
+
| Dashboard/analysis-specific insight not yet captured | Append to `this-dashboard.md` or `analysis-log.md` |
|
|
25
|
+
| Insight that updates something already there | Edit that specific part only |
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
**Never overwrite a file wholesale. Never duplicate a fact that's already accurate. Only write what's genuinely new or changed.**
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
- **Project knowledge** (helps ALL future artifacts) → propose addition to `.claude/knowledge/`
|
|
22
|
-
- **Dashboard-specific** (design decision, query note) → append to `.claude/rules/this-dashboard.md`
|
|
23
|
-
- **Already known** → skip
|
|
29
|
+
4. Present proposed changes to the user. Be specific — show the exact text you'd add or the exact edit you'd make. "I should note something about X" is not acceptable — show the line.
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
5. After the user approves, write the files. If you created a new knowledge file, update INDEX.md.
|
|
26
32
|
|
|
27
|
-
## What qualifies as project knowledge vs
|
|
33
|
+
## What qualifies as project knowledge vs artifact-specific
|
|
28
34
|
- "dates are UTC-7 no DST" → project knowledge (helps all artifacts)
|
|
29
35
|
- "affiliate_id = 0 is unattributed" → project knowledge
|
|
30
36
|
- "Revenue chart excludes refunds per Finance definition" → dashboard-specific
|
|
@@ -5,11 +5,7 @@ description: Reviews data insights discovered during analysis work and proposes
|
|
|
5
5
|
|
|
6
6
|
# Save Knowledge
|
|
7
7
|
|
|
8
|
-
Run
|
|
9
|
-
|
|
10
|
-
## When to invoke
|
|
11
|
-
- Before `analysis publish` (review what you learned first)
|
|
12
|
-
- When the user says "save what you learned" or similar
|
|
8
|
+
Run before publishing an analysis or whenever you discover something non-obvious about the data.
|
|
13
9
|
|
|
14
10
|
## Process
|
|
15
11
|
|
|
@@ -19,14 +15,21 @@ Run this skill before publishing an analysis or when explicitly asked.
|
|
|
19
15
|
- Metric definitions or business rules
|
|
20
16
|
- Data quality issues or known gaps
|
|
21
17
|
|
|
22
|
-
2. Read `.claude/knowledge/INDEX.md
|
|
18
|
+
2. **Read the existing knowledge files before proposing anything.** Read `.claude/knowledge/INDEX.md`, then read the specific files relevant to what you discovered. You need to know what's already there before you can know what's new.
|
|
19
|
+
|
|
20
|
+
3. For each insight, decide what action to take:
|
|
21
|
+
|
|
22
|
+
| Situation | Action |
|
|
23
|
+
|---|---|
|
|
24
|
+
| New topic, no existing file covers it | Create a new `.claude/knowledge/{topic}.md`; add entry to INDEX.md |
|
|
25
|
+
| New fact that belongs in an existing knowledge file | Append a new line or section to that file — do not rewrite what's there |
|
|
26
|
+
| Fact that refines or corrects something already documented | Edit that specific line/section only — do not duplicate it |
|
|
27
|
+
| Already documented accurately | Skip entirely |
|
|
28
|
+
| Analysis-specific (only relevant to this investigation) | Leave in analysis-log.md — don't promote to project knowledge |
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
- **Project knowledge** (helps ALL future artifacts) → propose a new file or addition to `.claude/knowledge/`
|
|
26
|
-
- **Already known** (in an existing knowledge file) → skip
|
|
27
|
-
- **Analysis-specific** (only relevant to this investigation) → leave in analysis-log.md
|
|
30
|
+
**Never overwrite a file wholesale. Never duplicate a fact that's already accurate. Only write what's genuinely new or changed.**
|
|
28
31
|
|
|
29
|
-
4. Present proposed
|
|
32
|
+
4. Present proposed changes to the user. Show the exact text you'd add or the exact edit you'd make — not a summary of what you intend to write.
|
|
30
33
|
|
|
31
34
|
5. After approval, write the files and update INDEX.md.
|
|
32
35
|
|
|
@@ -34,5 +37,5 @@ Run this skill before publishing an analysis or when explicitly asked.
|
|
|
34
37
|
- "unique_page_views is already COUNT(DISTINCT domain_userid)" → YES
|
|
35
38
|
- "date_az is America/Phoenix UTC-7, no DST" → YES
|
|
36
39
|
- "affiliate_id = 0 is unattributed, usually exclude" → YES
|
|
37
|
-
- "Q1 churn spiked because of the pricing change" → NO (analysis conclusion, not data fact)
|
|
40
|
+
- "Q1 churn spiked because of the pricing change" → NO (analysis conclusion, not a data fact)
|
|
38
41
|
- "I used a 90-day window for this analysis" → NO (analysis-specific decision)
|