@overscore/cli 0.13.13 → 0.13.15
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
|
@@ -1608,6 +1608,37 @@ function decidePlatformWrite(fullPath, serverContent, serverHash, lastSyncedHash
|
|
|
1608
1608
|
}
|
|
1609
1609
|
return { action: "skip", reason: "user-modified" };
|
|
1610
1610
|
}
|
|
1611
|
+
// The Hub platform-rules channel currently serves dashboard content (React
|
|
1612
|
+
// rules, deploy-dashboard/start-dashboard skills, and dashboard flavors of
|
|
1613
|
+
// skills the analysis template ships its own versions of). Syncing it into
|
|
1614
|
+
// an analysis misleads Claude and marks the analysis's own skills as
|
|
1615
|
+
// "user-modified" — so analyses only receive the artifact-neutral files.
|
|
1616
|
+
const ANALYSIS_PLATFORM_ALLOW = [
|
|
1617
|
+
"platform-capabilities.md",
|
|
1618
|
+
"skills/update-platform/",
|
|
1619
|
+
];
|
|
1620
|
+
function isAnalysisDir(targetDir) {
|
|
1621
|
+
if (fs.existsSync(path.join(targetDir, ".overscore", "analysis.json"))) {
|
|
1622
|
+
return true;
|
|
1623
|
+
}
|
|
1624
|
+
// Pulled bundles don't include .overscore/ — fall back to the CLAUDE.md
|
|
1625
|
+
// heading, the same detection syncClaudeMdImports uses.
|
|
1626
|
+
const claudeMdPath = path.join(targetDir, ".claude", "CLAUDE.md");
|
|
1627
|
+
if (!fs.existsSync(claudeMdPath))
|
|
1628
|
+
return false;
|
|
1629
|
+
return /^#\s+Overscore\s+Analysis/im.test(fs.readFileSync(claudeMdPath, "utf-8"));
|
|
1630
|
+
}
|
|
1631
|
+
function filterPlatformFilesForArtifact(targetDir, files) {
|
|
1632
|
+
if (!isAnalysisDir(targetDir))
|
|
1633
|
+
return files;
|
|
1634
|
+
const kept = {};
|
|
1635
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
1636
|
+
if (ANALYSIS_PLATFORM_ALLOW.some((p) => relPath === p || relPath.startsWith(p))) {
|
|
1637
|
+
kept[relPath] = content;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return kept;
|
|
1641
|
+
}
|
|
1611
1642
|
/**
|
|
1612
1643
|
* Fetch the latest platform-owned Claude Code rules from the Hub and write
|
|
1613
1644
|
* them into the project's .claude/ directory. Called from `deploy` (before
|
|
@@ -1615,6 +1646,7 @@ function decidePlatformWrite(fullPath, serverContent, serverHash, lastSyncedHash
|
|
|
1615
1646
|
* paths). User-owned files (knowledge/, this-dashboard.md) are NOT touched.
|
|
1616
1647
|
*
|
|
1617
1648
|
* Preserves user edits via .claude/.platform-meta.json hash tracking.
|
|
1649
|
+
* Analyses only receive the artifact-neutral subset (see ANALYSIS_PLATFORM_ALLOW).
|
|
1618
1650
|
*/
|
|
1619
1651
|
async function syncPlatformRules(targetDir, baseUrl, apiKey) {
|
|
1620
1652
|
try {
|
|
@@ -1626,10 +1658,11 @@ async function syncPlatformRules(targetDir, baseUrl, apiKey) {
|
|
|
1626
1658
|
const data = (await res.json());
|
|
1627
1659
|
if (!data.files)
|
|
1628
1660
|
return;
|
|
1661
|
+
const files = filterPlatformFilesForArtifact(targetDir, data.files);
|
|
1629
1662
|
const meta = readPlatformMeta(targetDir);
|
|
1630
1663
|
const written = [];
|
|
1631
1664
|
const preserved = [];
|
|
1632
|
-
for (const [relPath, content] of Object.entries(
|
|
1665
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
1633
1666
|
const fullPath = path.join(targetDir, ".claude", relPath);
|
|
1634
1667
|
const serverHash = data.hashes?.[relPath] ?? md5(content);
|
|
1635
1668
|
const lastSyncedHash = meta.last_synced[relPath] ?? null;
|
|
@@ -1695,10 +1728,11 @@ async function platformUpdate(checkOnly = false, force = false) {
|
|
|
1695
1728
|
console.error("\n Error: No files in platform-rules response.\n");
|
|
1696
1729
|
process.exit(1);
|
|
1697
1730
|
}
|
|
1731
|
+
const files = filterPlatformFilesForArtifact(process.cwd(), data.files);
|
|
1698
1732
|
const meta = readPlatformMeta(process.cwd());
|
|
1699
1733
|
const updated = [];
|
|
1700
1734
|
const preserved = [];
|
|
1701
|
-
for (const [relPath, content] of Object.entries(
|
|
1735
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
1702
1736
|
const fullPath = path.join(process.cwd(), ".claude", relPath);
|
|
1703
1737
|
const serverHash = data.hashes?.[relPath] ?? md5(content);
|
|
1704
1738
|
const lastSyncedHash = meta.last_synced[relPath] ?? null;
|
|
@@ -1901,6 +1935,27 @@ async function analysisNew(title) {
|
|
|
1901
1935
|
https://${data.project_slug}.${HUB_HOST}/analysis/${data.slug}
|
|
1902
1936
|
`);
|
|
1903
1937
|
}
|
|
1938
|
+
async function dashboardCreate(name, slugArg, description) {
|
|
1939
|
+
if (!name || name.startsWith("--")) {
|
|
1940
|
+
console.error('\n Usage: npx @overscore/cli dashboard create "<name>" [--slug <slug>] [--description <text>]\n');
|
|
1941
|
+
process.exit(1);
|
|
1942
|
+
}
|
|
1943
|
+
// Reuses the project-level config/request helpers (resolve project + key, then POST).
|
|
1944
|
+
const cfg = await loadAnalysisConfig();
|
|
1945
|
+
console.log(`\n Creating dashboard: "${name}"\n`);
|
|
1946
|
+
const data = (await analysisApiRequest(cfg, "POST", `/api/projects/${cfg.projectSlug}/dashboards`, { name, slug: slugArg, description }));
|
|
1947
|
+
console.log(` Dashboard created: ${data.name}
|
|
1948
|
+
Slug: ${data.slug}
|
|
1949
|
+
Project: ${data.project_slug}
|
|
1950
|
+
|
|
1951
|
+
Next step — scaffold the local folder for it:
|
|
1952
|
+
|
|
1953
|
+
npm create overscore@latest -- --project ${data.project_slug} --dashboard ${data.slug}
|
|
1954
|
+
|
|
1955
|
+
then build it and \`npx @overscore/cli deploy\`. It will be live at:
|
|
1956
|
+
https://${data.project_slug}.${HUB_HOST}/${data.slug}/
|
|
1957
|
+
`);
|
|
1958
|
+
}
|
|
1904
1959
|
function loadAnalysisMetadata() {
|
|
1905
1960
|
const metaPath = path.resolve(process.cwd(), ".overscore", "analysis.json");
|
|
1906
1961
|
if (!fs.existsSync(metaPath)) {
|
|
@@ -1916,6 +1971,9 @@ const ANALYSIS_PUBLISH_BASELINE_EXCLUDE = new Set([
|
|
|
1916
1971
|
".git",
|
|
1917
1972
|
".DS_Store",
|
|
1918
1973
|
".overscore",
|
|
1974
|
+
// Raw query-result cache (real warehouse rows) — never publishable, even
|
|
1975
|
+
// if .overscoreignore is edited. Mirrors the dashboard deploy SOURCE_EXCLUDE.
|
|
1976
|
+
"data",
|
|
1919
1977
|
]);
|
|
1920
1978
|
function isBaselineExcluded(name) {
|
|
1921
1979
|
if (ANALYSIS_PUBLISH_BASELINE_EXCLUDE.has(name))
|
|
@@ -2915,6 +2973,25 @@ else if (command === "analysis") {
|
|
|
2915
2973
|
`);
|
|
2916
2974
|
}
|
|
2917
2975
|
}
|
|
2976
|
+
else if (command === "dashboard") {
|
|
2977
|
+
if (subcommand === "create") {
|
|
2978
|
+
const slugIdx = process.argv.indexOf("--slug");
|
|
2979
|
+
const descIdx = process.argv.indexOf("--description");
|
|
2980
|
+
dashboardCreate(process.argv[4], slugIdx !== -1 ? process.argv[slugIdx + 1] : undefined, descIdx !== -1 ? process.argv[descIdx + 1] : undefined);
|
|
2981
|
+
}
|
|
2982
|
+
else {
|
|
2983
|
+
console.log(`
|
|
2984
|
+
overscore dashboard — Manage dashboards
|
|
2985
|
+
|
|
2986
|
+
Commands:
|
|
2987
|
+
create "<name>" [--slug <slug>] [--description <text>]
|
|
2988
|
+
Create a new (empty) dashboard in your project
|
|
2989
|
+
|
|
2990
|
+
Examples:
|
|
2991
|
+
npx @overscore/cli dashboard create "Sales Pipeline (metrics)" --slug sales-pipeline-metrics
|
|
2992
|
+
`);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2918
2995
|
else if (command === "platform") {
|
|
2919
2996
|
if (subcommand === "update") {
|
|
2920
2997
|
const checkOnly = process.argv.includes("--check");
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ paths:
|
|
|
11
11
|
|
|
12
12
|
- **Inline all CSS.** No `<link rel="stylesheet">`, no Tailwind CDN, no Google Fonts (or use a `data:` URI / system stack). Use a single `<style>` block in the `<head>`.
|
|
13
13
|
- **Bake the data into the HTML.** No `fetch()`, no `XMLHttpRequest`, no API calls. The numbers must be visible to anyone who opens the file with no logins, no tokens, no broken external dependencies.
|
|
14
|
-
- **Charts are inline.** Inline SVG is the safest option. Embedded base64 PNGs are OK.
|
|
14
|
+
- **Charts are inline.** Inline SVG is the safest option. Embedded base64 PNGs are OK. **No CDN or external `<script src>` — the published report is served under a CSP that blocks all external scripts**, so a CDN-loaded chart library works in local preview but silently breaks after publish. If you must use a JS chart library, paste its source into an inline `<script>` block — but inline SVG is preferred because it survives indefinitely.
|
|
15
15
|
- **No `useQuery`, no `@overscore/client`, no React.** This is a static HTML file, not a React app.
|
|
16
16
|
|
|
17
17
|
## Aesthetic direction
|