@graphit/cli 0.1.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.
- package/dist/api/client.d.ts +16 -0
- package/dist/api/client.js +67 -0
- package/dist/api/client.js.map +1 -0
- package/dist/auth/credentials.d.ts +15 -0
- package/dist/auth/credentials.js +34 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/login.d.ts +2 -0
- package/dist/auth/login.js +229 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/token.d.ts +5 -0
- package/dist/auth/token.js +42 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +68 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/connector.d.ts +2 -0
- package/dist/commands/connector.js +97 -0
- package/dist/commands/connector.js.map +1 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.js +124 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/ds.d.ts +2 -0
- package/dist/commands/ds.js +53 -0
- package/dist/commands/ds.js.map +1 -0
- package/dist/commands/kb.d.ts +2 -0
- package/dist/commands/kb.js +259 -0
- package/dist/commands/kb.js.map +1 -0
- package/dist/commands/query.d.ts +2 -0
- package/dist/commands/query.js +61 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +173 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/output/format.d.ts +8 -0
- package/dist/output/format.js +30 -0
- package/dist/output/format.js.map +1 -0
- package/dist/output/json.d.ts +1 -0
- package/dist/output/json.js +4 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/table.d.ts +2 -0
- package/dist/output/table.js +17 -0
- package/dist/output/table.js.map +1 -0
- package/package.json +38 -0
- package/skill/SKILL.md +163 -0
- package/skill/cursor/graphit-chart-patterns.mdc +148 -0
- package/skill/cursor/graphit-chart-selection.mdc +89 -0
- package/skill/cursor/graphit-dashboard-planning.mdc +100 -0
- package/skill/cursor/graphit-domain-lenses.mdc +120 -0
- package/skill/cursor/graphit-kb-exploration.mdc +75 -0
- package/skill/cursor/graphit-sql-reference.mdc +127 -0
- package/skill/cursor/graphit-style.mdc +123 -0
- package/skill/graphit.mdc +135 -0
- package/skill/references/chart-patterns.md +142 -0
- package/skill/references/chart-selection.md +83 -0
- package/skill/references/dashboard-planning.md +94 -0
- package/skill/references/domain-lenses.md +114 -0
- package/skill/references/graphit-style.md +117 -0
- package/skill/references/kb-exploration.md +69 -0
- package/skill/references/sql-reference.md +121 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile, mkdir, copyFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, dirname } from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { readCredentials } from "../auth/credentials.js";
|
|
7
|
+
function resolveSkillDir() {
|
|
8
|
+
const fromDist = join(dirname(new URL(import.meta.url).pathname), "..", "..", "skill");
|
|
9
|
+
if (existsSync(join(fromDist, "SKILL.md")))
|
|
10
|
+
return fromDist;
|
|
11
|
+
return join(dirname(dirname(dirname(import.meta.url.replace("file://", "")))), "skill");
|
|
12
|
+
}
|
|
13
|
+
function detectEditors() {
|
|
14
|
+
const editors = [];
|
|
15
|
+
if (existsSync(".claude") || commandExists("claude"))
|
|
16
|
+
editors.push("claude-code");
|
|
17
|
+
if (existsSync(".cursor"))
|
|
18
|
+
editors.push("cursor");
|
|
19
|
+
if (existsSync(".codex") || commandExists("codex"))
|
|
20
|
+
editors.push("codex");
|
|
21
|
+
if (existsSync(".vscode"))
|
|
22
|
+
editors.push("vscode");
|
|
23
|
+
return editors;
|
|
24
|
+
}
|
|
25
|
+
function commandExists(cmd) {
|
|
26
|
+
try {
|
|
27
|
+
execFileSync("which", [cmd], { stdio: "ignore" });
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getInstallTargets(editors, project, skillMd, cursorMdc) {
|
|
35
|
+
const targets = [];
|
|
36
|
+
const home = homedir();
|
|
37
|
+
for (const editor of editors) {
|
|
38
|
+
switch (editor) {
|
|
39
|
+
case "claude-code": {
|
|
40
|
+
const base = project
|
|
41
|
+
? join(".claude", "skills", "graphit")
|
|
42
|
+
: join(home, ".claude", "skills", "graphit");
|
|
43
|
+
targets.push({
|
|
44
|
+
editor,
|
|
45
|
+
path: join(base, "SKILL.md"),
|
|
46
|
+
content: skillMd,
|
|
47
|
+
refsDir: join(base, "references"),
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "codex": {
|
|
52
|
+
const base = project
|
|
53
|
+
? join(".codex", "skills", "graphit")
|
|
54
|
+
: join(home, ".codex", "skills", "graphit");
|
|
55
|
+
targets.push({
|
|
56
|
+
editor,
|
|
57
|
+
path: join(base, "SKILL.md"),
|
|
58
|
+
content: skillMd,
|
|
59
|
+
refsDir: join(base, "references"),
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case "cursor":
|
|
64
|
+
targets.push({
|
|
65
|
+
editor,
|
|
66
|
+
path: project
|
|
67
|
+
? join(".cursor", "rules", "graphit.mdc")
|
|
68
|
+
: join(home, ".cursor", "rules", "graphit.mdc"),
|
|
69
|
+
content: cursorMdc,
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
case "vscode":
|
|
73
|
+
if (project) {
|
|
74
|
+
targets.push({ editor, path: "AGENTS.md", content: skillMd });
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return targets;
|
|
80
|
+
}
|
|
81
|
+
async function installReferences(sourceRefsDir, targetRefsDir, update) {
|
|
82
|
+
if (!existsSync(sourceRefsDir))
|
|
83
|
+
return [];
|
|
84
|
+
const files = readdirSync(sourceRefsDir).filter((f) => f.endsWith(".md"));
|
|
85
|
+
if (files.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
await mkdir(targetRefsDir, { recursive: true });
|
|
88
|
+
const written = [];
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const targetPath = join(targetRefsDir, file);
|
|
91
|
+
if (existsSync(targetPath) && !update)
|
|
92
|
+
continue;
|
|
93
|
+
if (existsSync(targetPath) && update) {
|
|
94
|
+
await copyFile(targetPath, `${targetPath}.bak`);
|
|
95
|
+
}
|
|
96
|
+
const content = await readFile(join(sourceRefsDir, file), "utf-8");
|
|
97
|
+
await writeFile(targetPath, content, "utf-8");
|
|
98
|
+
written.push(targetPath);
|
|
99
|
+
}
|
|
100
|
+
return written;
|
|
101
|
+
}
|
|
102
|
+
export function registerSetupCommand(program) {
|
|
103
|
+
program
|
|
104
|
+
.command("setup")
|
|
105
|
+
.description("Install Graphit skill for your AI coding assistant")
|
|
106
|
+
.option("--editor <name>", "Override editor detection (claude-code, cursor, codex, vscode)")
|
|
107
|
+
.option("--project", "Install at project level (in repo, committable)")
|
|
108
|
+
.option("--update", "Overwrite existing skill files")
|
|
109
|
+
.option("--dry-run", "Show what would be installed without writing")
|
|
110
|
+
.action(async function () {
|
|
111
|
+
const opts = this.opts();
|
|
112
|
+
const creds = await readCredentials();
|
|
113
|
+
if (!creds) {
|
|
114
|
+
console.log("Warning: Not logged in. Run `graphit auth login` first for full functionality.\n");
|
|
115
|
+
}
|
|
116
|
+
let editors;
|
|
117
|
+
if (opts.editor) {
|
|
118
|
+
editors = [opts.editor];
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
editors = detectEditors();
|
|
122
|
+
if (editors.length === 0) {
|
|
123
|
+
console.error(JSON.stringify({
|
|
124
|
+
error: "No supported editor detected. Use --editor to specify one.",
|
|
125
|
+
}));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const skillDir = resolveSkillDir();
|
|
130
|
+
const skillMd = await readFile(join(skillDir, "SKILL.md"), "utf-8");
|
|
131
|
+
const cursorMdc = await readFile(join(skillDir, "graphit.mdc"), "utf-8");
|
|
132
|
+
const sourceRefsDir = join(skillDir, "references");
|
|
133
|
+
const targets = getInstallTargets(editors, !!opts.project, skillMd, cursorMdc);
|
|
134
|
+
if (targets.length === 0) {
|
|
135
|
+
console.error(JSON.stringify({ error: "No install targets found." }));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
if (opts.dryRun) {
|
|
139
|
+
console.log("Dry run - would install:\n");
|
|
140
|
+
for (const t of targets) {
|
|
141
|
+
console.log(` ${t.editor}: ${t.path}`);
|
|
142
|
+
if (t.refsDir && existsSync(sourceRefsDir)) {
|
|
143
|
+
const refs = readdirSync(sourceRefsDir).filter((f) => f.endsWith(".md"));
|
|
144
|
+
refs.forEach((r) => console.log(` ${t.editor}: ${join(t.refsDir, r)}`));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const target of targets) {
|
|
150
|
+
if (existsSync(target.path) && !opts.update) {
|
|
151
|
+
console.log(`Skill file already exists at ${target.path}. Use --update to overwrite.`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (existsSync(target.path) && opts.update) {
|
|
155
|
+
await copyFile(target.path, `${target.path}.bak`);
|
|
156
|
+
}
|
|
157
|
+
await mkdir(dirname(target.path), { recursive: true });
|
|
158
|
+
await writeFile(target.path, target.content, "utf-8");
|
|
159
|
+
console.log(`Written: ${target.path}`);
|
|
160
|
+
if (target.refsDir) {
|
|
161
|
+
const refFiles = await installReferences(sourceRefsDir, target.refsDir, !!opts.update);
|
|
162
|
+
if (refFiles.length > 0) {
|
|
163
|
+
console.log(`Written: ${refFiles.length} reference files to ${target.refsDir}/`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const mode = opts.project ? "project" : "global";
|
|
168
|
+
const editorNames = editors.join(", ");
|
|
169
|
+
console.log(`\nGraphit skill installed for ${editorNames} (${mode}).`);
|
|
170
|
+
console.log('\nTry: "Explore the Graphit KB and build a revenue dashboard"');
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { registerAuthCommands } from "./commands/auth.js";
|
|
4
|
+
import { registerKBCommands } from "./commands/kb.js";
|
|
5
|
+
import { registerQueryCommands } from "./commands/query.js";
|
|
6
|
+
import { registerDSCommands } from "./commands/ds.js";
|
|
7
|
+
import { registerDashboardCommands } from "./commands/dashboard.js";
|
|
8
|
+
import { registerConnectorCommands } from "./commands/connector.js";
|
|
9
|
+
import { registerSetupCommand } from "./commands/setup.js";
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name("graphit")
|
|
13
|
+
.description("Graphit CLI - Build custom dashboards from any AI coding assistant")
|
|
14
|
+
.version("0.1.0")
|
|
15
|
+
.option("--output <format>", "Output format: json (default) or table", "json");
|
|
16
|
+
registerAuthCommands(program);
|
|
17
|
+
registerKBCommands(program);
|
|
18
|
+
registerQueryCommands(program);
|
|
19
|
+
registerDSCommands(program);
|
|
20
|
+
registerDashboardCommands(program);
|
|
21
|
+
registerConnectorCommands(program);
|
|
22
|
+
registerSetupCommand(program);
|
|
23
|
+
program.parse();
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,mBAAmB,EAAE,wCAAwC,EAAE,MAAM,CAAC,CAAC;AAEjF,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,yBAAyB,CAAC,OAAO,CAAC,CAAC;AACnC,yBAAyB,CAAC,OAAO,CAAC,CAAC;AACnC,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
export type OutputFormat = "json" | "table";
|
|
3
|
+
export declare function getOutputFormat(cmd: Command): OutputFormat;
|
|
4
|
+
export declare function output(cmd: Command, data: unknown, opts?: {
|
|
5
|
+
columns?: string[];
|
|
6
|
+
keyValue?: boolean;
|
|
7
|
+
}): void;
|
|
8
|
+
export declare function errorOutput(err: unknown): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { formatJson } from "./json.js";
|
|
2
|
+
import { formatTable, formatKeyValue } from "./table.js";
|
|
3
|
+
export function getOutputFormat(cmd) {
|
|
4
|
+
const root = cmd.parent ?? cmd;
|
|
5
|
+
const format = root.opts().output;
|
|
6
|
+
return format === "table" ? "table" : "json";
|
|
7
|
+
}
|
|
8
|
+
export function output(cmd, data, opts) {
|
|
9
|
+
const format = getOutputFormat(cmd);
|
|
10
|
+
if (format === "json") {
|
|
11
|
+
console.log(formatJson(data));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (opts?.keyValue && !Array.isArray(data)) {
|
|
15
|
+
console.log(formatKeyValue(data));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
19
|
+
console.log(formatTable(rows, opts?.columns));
|
|
20
|
+
}
|
|
21
|
+
export function errorOutput(err) {
|
|
22
|
+
const message = err instanceof Error
|
|
23
|
+
? err.message
|
|
24
|
+
: typeof err === "object" && err !== null && "detail" in err
|
|
25
|
+
? err.detail
|
|
26
|
+
: String(err);
|
|
27
|
+
console.error(JSON.stringify({ error: message }));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/output/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIzD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAgB,CAAC;IAC5C,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,GAAY,EACZ,IAAa,EACb,IAAiD;IAEjD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,IAAI,IAAI,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAA+B,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAiC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,OAAO,GACX,GAAG,YAAY,KAAK;QAClB,CAAC,CAAC,GAAG,CAAC,OAAO;QACb,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,IAAI,GAAG;YAC1D,CAAC,CAAE,GAA0B,CAAC,MAAM;YACpC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAEpB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatJson(data: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function formatTable(rows, columns) {
|
|
2
|
+
if (rows.length === 0)
|
|
3
|
+
return "(no results)";
|
|
4
|
+
const cols = columns || Object.keys(rows[0]);
|
|
5
|
+
const widths = cols.map((col) => Math.max(col.length, ...rows.map((r) => String(r[col] ?? "").length)));
|
|
6
|
+
const header = cols.map((col, i) => col.padEnd(widths[i])).join(" ");
|
|
7
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
8
|
+
const body = rows.map((row) => cols.map((col, i) => String(row[col] ?? "").padEnd(widths[i])).join(" "));
|
|
9
|
+
return [header, separator, ...body].join("\n");
|
|
10
|
+
}
|
|
11
|
+
export function formatKeyValue(data) {
|
|
12
|
+
const maxKeyLen = Math.max(...Object.keys(data).map((k) => k.length));
|
|
13
|
+
return Object.entries(data)
|
|
14
|
+
.map(([k, v]) => `${k.padEnd(maxKeyLen)} ${v}`)
|
|
15
|
+
.join("\n");
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/output/table.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CACzB,IAA+B,EAC/B,OAAkB;IAElB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAE7C,MAAM,IAAI,GAAG,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC9B,IAAI,CAAC,GAAG,CACN,GAAG,CAAC,MAAM,EACV,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAChD,CACF,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC1E,CAAC;IAEF,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA6B;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACxB,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;SAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphit/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Graphit CLI - Build custom dashboards from any AI coding assistant",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"graphit": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"lint": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"skill"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"graphit",
|
|
23
|
+
"bi",
|
|
24
|
+
"dashboard",
|
|
25
|
+
"cli",
|
|
26
|
+
"ai"
|
|
27
|
+
],
|
|
28
|
+
"license": "UNLICENSED",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"commander": "^13.0.0",
|
|
31
|
+
"open": "^10.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^22.0.0",
|
|
35
|
+
"tsx": "^4.0.0",
|
|
36
|
+
"typescript": "^5.7.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphit
|
|
3
|
+
description: >
|
|
4
|
+
Build HTML dashboards with Graphit. KB-aware queries, entity wrapping, cached data sources.
|
|
5
|
+
Triggers on: "dashboard", "graphit", "KB", "metric", "data source", "build a dashboard",
|
|
6
|
+
"explore the KB", "query data", "custom dashboard".
|
|
7
|
+
version: "0.4.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Graphit CLI
|
|
11
|
+
|
|
12
|
+
Build custom HTML dashboards from real data using the Graphit CLI.
|
|
13
|
+
|
|
14
|
+
## HARD CONSTRAINTS (violating these produces a broken dashboard)
|
|
15
|
+
|
|
16
|
+
### 1. ZERO external resources
|
|
17
|
+
The dashboard renders in a sandboxed iframe with a strict CSP. External requests are BLOCKED.
|
|
18
|
+
Your HTML must NEVER contain:
|
|
19
|
+
- `<script src="...">` - no Chart.js, D3.js, Alpine.js, ANY external JS
|
|
20
|
+
- `<link href="...">` - no Tailwind CDN, Google Fonts, ANY external CSS
|
|
21
|
+
- `<img src="https://...">` - no external images
|
|
22
|
+
|
|
23
|
+
If you use ANY `src=` or `href=` pointing to a URL, the dashboard will be blank.
|
|
24
|
+
Build charts with: CSS width% bars, inline SVG paths, or `<canvas>` with inline drawing code.
|
|
25
|
+
All CSS in `<style>`, all JS in `<script>`, all fonts from system stack.
|
|
26
|
+
|
|
27
|
+
### 2. ALWAYS query through data sources
|
|
28
|
+
NEVER query the warehouse directly when a cached data source covers the table. Data sources return in ~100ms. Warehouse queries take ~10s and cost Snowflake credits.
|
|
29
|
+
|
|
30
|
+
**Before writing ANY query:**
|
|
31
|
+
1. Run `graphit ds list` to see what data sources exist
|
|
32
|
+
2. If a DS covers your table, use `graphit query "SQL" --ds <id>` (DuckDB syntax)
|
|
33
|
+
3. Only use `graphit query "SQL" --warehouse --connection <id>` if NO data source exists and the user approves
|
|
34
|
+
|
|
35
|
+
**Wrong:** Jumping straight to `graphit query "SELECT SUM(cost) FROM marketing_ua" --warehouse` when a data source already caches that table.
|
|
36
|
+
**Right:** `graphit ds list` first, find the DS ID, then `graphit query "SELECT SUM(cost) FROM marketing_ua" --ds ds_abc123`.
|
|
37
|
+
|
|
38
|
+
### 3. EVERY element must have entity wrapping
|
|
39
|
+
Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL FOUR attributes:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div data-graphit-id="revenue-trend"
|
|
43
|
+
data-graphit-label="Revenue Trend"
|
|
44
|
+
data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
|
|
45
|
+
data-graphit-sql="SELECT region, SUM(revenue) FROM orders GROUP BY region">
|
|
46
|
+
<!-- chart/KPI/table content here -->
|
|
47
|
+
</div>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Attribute | Format | Example |
|
|
51
|
+
|-----------|--------|---------|
|
|
52
|
+
| `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
|
|
53
|
+
| `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
|
|
54
|
+
| `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"` |
|
|
55
|
+
| `data-graphit-sql` | SQL query (HTML-encode `<>&"`) | `"SELECT ..."` |
|
|
56
|
+
|
|
57
|
+
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly.
|
|
58
|
+
Missing any attribute = broken entity. Missing wrapping entirely = invisible to the platform.
|
|
59
|
+
|
|
60
|
+
**Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly. Users see the label in @ mention dropdowns and entity panels - if it doesn't match the title on screen, they can't find their chart.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Workflow
|
|
65
|
+
|
|
66
|
+
1. **Ask the user** what dashboard they want. Don't start querying until you know what they need.
|
|
67
|
+
2. **Explore KB** to understand available metrics, dimensions, tables, and tables.
|
|
68
|
+
3. **Find a data source** (`graphit ds list`) - prefer cached data sources (~100ms) over live warehouse (~10s).
|
|
69
|
+
4. **Query data** and embed results as inline JS variables.
|
|
70
|
+
5. **Build HTML** - all CSS and JS must be inline. Write to a local `.html` file.
|
|
71
|
+
6. **Save** with `graphit dashboard update-html <id> --file <path>`.
|
|
72
|
+
7. Give the user the dashboard URL so they can open it.
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `graphit kb list <type>` | List metrics, dimensions, tables, rules, domains, synonyms |
|
|
79
|
+
| `graphit kb get <type> <name>` | Full entity details by name |
|
|
80
|
+
| `graphit kb search <query>` | Search across all KB types |
|
|
81
|
+
| `graphit kb explore metric <name>` | Metric -> tables -> dimensions graph |
|
|
82
|
+
| `graphit ds list` | List cached data sources (use these for fast queries) |
|
|
83
|
+
| `graphit query "<sql>" --ds <id>` | Query cached data source (~100ms) |
|
|
84
|
+
| `graphit query "<sql>" --warehouse --connection <id>` | Query live Snowflake (~10s) |
|
|
85
|
+
| `graphit dashboard create --name "..."` | Create dashboard (returns ID) |
|
|
86
|
+
| `graphit dashboard get-html <id>` | Get current HTML content of a dashboard |
|
|
87
|
+
| `graphit dashboard update-html <id> --file <path>` | Upload HTML to dashboard |
|
|
88
|
+
| `graphit dashboard list` | List existing dashboards |
|
|
89
|
+
| `graphit metadata schemas --connection <id>` | List Snowflake schemas |
|
|
90
|
+
| `graphit connector list` | List active connections |
|
|
91
|
+
|
|
92
|
+
## Presenting Results to the User
|
|
93
|
+
|
|
94
|
+
The user CANNOT see raw CLI output clearly. You MUST format and present every result - never silently consume tool output and move on.
|
|
95
|
+
|
|
96
|
+
**After every query**, show the data:
|
|
97
|
+
```
|
|
98
|
+
Queried **MARKETING_UA** (ds_abc123, 6 rows):
|
|
99
|
+
|
|
100
|
+
| Channel | Spend | Installs | CPI |
|
|
101
|
+
|-------------|----------|----------|-------|
|
|
102
|
+
| Facebook | $42,100 | 12,400 | $3.40 |
|
|
103
|
+
| Google UAC | $38,500 | 9,800 | $3.93 |
|
|
104
|
+
| TikTok | $21,300 | 8,200 | $2.60 |
|
|
105
|
+
```
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**After KB exploration**, summarize what you found:
|
|
109
|
+
```
|
|
110
|
+
Found **12 metrics** and **8 dimensions** on table MARKETING_UA:
|
|
111
|
+
- **Metrics:** TOTAL_SPEND, CPI, ROAS_D7, ROAS_D30, INSTALLS, ...
|
|
112
|
+
- **Dimensions:** MEDIA_SOURCE, CAMPAIGN_NAME, COUNTRY, PLATFORM, ...
|
|
113
|
+
- **Rules:** EXCLUDE_ORGANIC (filters organic installs from paid metrics)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Formatting rules:**
|
|
117
|
+
- **Bold** metric names, table names, and key numbers
|
|
118
|
+
- Use **markdown tables** for any tabular data (query results, entity lists, comparisons)
|
|
119
|
+
- Format numbers: commas for thousands (`12,400`), `$` for currency, `%` for rates
|
|
120
|
+
- After KB or data source discovery, list what's available before asking what to build
|
|
121
|
+
- Show the SQL you ran (in a code block) so the user can validate the logic
|
|
122
|
+
- When a query returns nulls or zero rows, explain what you checked and what went wrong
|
|
123
|
+
- Narrate your progress between steps: "Found 3 data sources. Using **Marketing UA DS** (ds_abc123) which covers spend, installs, and ROAS columns."
|
|
124
|
+
|
|
125
|
+
**Never do these:**
|
|
126
|
+
- Run 3 queries silently then jump to building HTML
|
|
127
|
+
- Say "I found the data" without showing what the data looks like
|
|
128
|
+
- Present raw JSON output without formatting
|
|
129
|
+
- Skip showing KB exploration results before proposing a dashboard
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Chart Type Quick Reference
|
|
134
|
+
|
|
135
|
+
| Data shape | Chart | Inline technique |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| 1 temporal + 1 numeric | Line | SVG path |
|
|
138
|
+
| 1 categorical + 1 numeric | Bar | CSS width% divs |
|
|
139
|
+
| Single number | KPI card | Styled div |
|
|
140
|
+
| Part-whole (max 5) | Donut | Canvas arc |
|
|
141
|
+
| Matrix / cohort | Heatmap | CSS grid + opacity |
|
|
142
|
+
| Stages | Funnel | CSS width% bars, narrowing |
|
|
143
|
+
| Time sparkline | Sparkline | Inline SVG path |
|
|
144
|
+
| Detail / raw data | Table | HTML table + hover rows |
|
|
145
|
+
|
|
146
|
+
For more chart types (scatter, stacked bar, gauge, multi-series) see `references/chart-selection.md`.
|
|
147
|
+
For inline implementations of each, see `references/graphit-style.md` (Chart Patterns section).
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Reference Files
|
|
152
|
+
|
|
153
|
+
Detailed knowledge lives in `references/`. Consult the relevant file when you need depth beyond the quick references above.
|
|
154
|
+
|
|
155
|
+
| File | Consult when |
|
|
156
|
+
|------|-------------|
|
|
157
|
+
| `dashboard-planning.md` | Building a multi-chart dashboard. Covers framing the question, picking archetype, mandatory rules, metric contracts, anti-patterns. |
|
|
158
|
+
| `chart-selection.md` | Choosing chart types. Full dimension/measure defaults, perception ranking, cardinality guards, hard caps. |
|
|
159
|
+
| `kb-exploration.md` | Starting a build. KB-first discovery, metric vs dimension, naming conventions, reuse patterns, formula syntax. |
|
|
160
|
+
| `sql-reference.md` | Writing queries. DuckDB/Snowflake translation, formatting standards, gap-filling, JSON access, data source routing. |
|
|
161
|
+
| `domain-lenses.md` | Data matches a business domain. Marketing, finance, product/growth, ops, sales - signals, key metrics, must-have charts, anti-patterns. |
|
|
162
|
+
| `graphit-style.md` | Building the HTML. Design principles, typography scale, color system with usage rules, layout patterns (page structure, KPI cards, data tables). |
|
|
163
|
+
| `chart-patterns.md` | Implementing charts. Inline CSS/SVG/canvas code for: horizontal bar, vertical bar, stacked bar, line/area, multi-series line, sparkline, donut, heatmap grid, funnel, gauge. |
|