@planckspace/cli 0.0.2 → 0.1.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.
Files changed (76) hide show
  1. package/README.md +109 -0
  2. package/dist/commands/init.d.ts +2 -0
  3. package/dist/commands/init.d.ts.map +1 -0
  4. package/dist/commands/init.js +12 -0
  5. package/dist/commands/init.js.map +1 -0
  6. package/dist/commands/login.d.ts +2 -0
  7. package/dist/commands/login.d.ts.map +1 -0
  8. package/dist/commands/login.js +45 -0
  9. package/dist/commands/login.js.map +1 -0
  10. package/dist/commands/logout.d.ts +2 -0
  11. package/dist/commands/logout.d.ts.map +1 -0
  12. package/dist/commands/logout.js +7 -0
  13. package/dist/commands/logout.js.map +1 -0
  14. package/dist/commands/scan.d.ts +5 -0
  15. package/dist/commands/scan.d.ts.map +1 -0
  16. package/dist/commands/scan.js +51 -0
  17. package/dist/commands/scan.js.map +1 -0
  18. package/dist/commands/status.d.ts +2 -0
  19. package/dist/commands/status.d.ts.map +1 -0
  20. package/dist/commands/status.js +31 -0
  21. package/dist/commands/status.js.map +1 -0
  22. package/dist/commands/sync.d.ts +15 -0
  23. package/dist/commands/sync.d.ts.map +1 -0
  24. package/dist/commands/sync.js +77 -0
  25. package/dist/commands/sync.js.map +1 -0
  26. package/dist/config.d.ts +6 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +40 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/correlate.d.ts +21 -0
  31. package/dist/correlate.d.ts.map +1 -0
  32. package/dist/correlate.js +204 -0
  33. package/dist/correlate.js.map +1 -0
  34. package/dist/db/store.d.ts +46 -0
  35. package/dist/db/store.d.ts.map +1 -0
  36. package/dist/db/store.js +210 -0
  37. package/dist/db/store.js.map +1 -0
  38. package/dist/env.d.ts +6 -0
  39. package/dist/env.d.ts.map +1 -0
  40. package/dist/env.js +7 -0
  41. package/dist/env.js.map +1 -0
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +97 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/scrapers/claudeCode.d.ts +4 -0
  47. package/dist/scrapers/claudeCode.d.ts.map +1 -0
  48. package/dist/scrapers/claudeCode.js +211 -0
  49. package/dist/scrapers/claudeCode.js.map +1 -0
  50. package/dist/scrapers/cursor.d.ts +9 -0
  51. package/dist/scrapers/cursor.d.ts.map +1 -0
  52. package/dist/scrapers/cursor.js +280 -0
  53. package/dist/scrapers/cursor.js.map +1 -0
  54. package/dist/scrapers/repo.d.ts +12 -0
  55. package/dist/scrapers/repo.d.ts.map +1 -0
  56. package/dist/scrapers/repo.js +40 -0
  57. package/dist/scrapers/repo.js.map +1 -0
  58. package/dist/scrapers/types.d.ts +36 -0
  59. package/dist/scrapers/types.d.ts.map +1 -0
  60. package/dist/scrapers/types.js +5 -0
  61. package/dist/scrapers/types.js.map +1 -0
  62. package/dist/scrapers/windsurf.d.ts +9 -0
  63. package/dist/scrapers/windsurf.d.ts.map +1 -0
  64. package/dist/scrapers/windsurf.js +255 -0
  65. package/dist/scrapers/windsurf.js.map +1 -0
  66. package/dist/sync/payload.d.ts +14 -0
  67. package/dist/sync/payload.d.ts.map +1 -0
  68. package/dist/sync/payload.js +36 -0
  69. package/dist/sync/payload.js.map +1 -0
  70. package/dist/sync/syncEngine.d.ts +16 -0
  71. package/dist/sync/syncEngine.d.ts.map +1 -0
  72. package/dist/sync/syncEngine.js +76 -0
  73. package/dist/sync/syncEngine.js.map +1 -0
  74. package/install.sh +126 -0
  75. package/package.json +39 -7
  76. package/index.js +0 -1
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @planckspace/cli
2
+
3
+ PlanckSpace CLI — sync AI token usage from Claude Code, Cursor, and Windsurf to your team ledger.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ curl -fsSL https://planckspace.dev/install | sh
9
+ ```
10
+
11
+ With an invite token:
12
+
13
+ ```sh
14
+ curl -fsSL https://planckspace.dev/install | sh -s -- --token pk_live_xxx
15
+ ```
16
+
17
+ Or install manually:
18
+
19
+ ```sh
20
+ npm install -g @planckspace/cli
21
+ ```
22
+
23
+ See [docs/INSTALL.md](docs/INSTALL.md) for manual steps and troubleshooting.
24
+
25
+ ## Commands
26
+
27
+ | Command | Description |
28
+ |---|---|
29
+ | `planck init` | Initialize `~/.planckspace/` (local DB + config) |
30
+ | `planck login <token>` | Connect to a PlanckSpace workspace |
31
+ | `planck scan` | Ingest local AI session history |
32
+ | `planck sync` | Push unsynced sessions to the team dashboard |
33
+ | `planck sync --watch` | Keep syncing on an interval (default 60 s) |
34
+ | `planck status` | Show connection + session stats |
35
+ | `planck logout` | Disconnect (local data retained) |
36
+
37
+ ### Non-interactive login (CI / install scripts)
38
+
39
+ ```sh
40
+ planck login --token pk_live_xxx
41
+ # or
42
+ PLANCKSPACE_TOKEN=pk_live_xxx planck login
43
+ ```
44
+
45
+ ### Backfill + sync in one step
46
+
47
+ ```sh
48
+ planck scan --sync
49
+ ```
50
+
51
+ ## What gets synced
52
+
53
+ - Session metadata: tool, model, token counts, cost, duration, repo name, outcome
54
+ - Git attribution: `git config user.email` from each repo (maps sessions to team members)
55
+ - **Never synced**: prompt text, code content, file contents, `turnsJson`
56
+
57
+ See [docs/COVERAGE.md](docs/COVERAGE.md) for per-tool capture details.
58
+
59
+ ## Supported editors
60
+
61
+ | Editor | Sessions | Tokens / cost | Notes |
62
+ |---|---|---|---|
63
+ | Claude Code | Yes | Yes | Full |
64
+ | Cursor | Yes | No | Cursor meters server-side; token fields are 0 |
65
+ | Windsurf | Yes (beta) | Provisional | Unverified schema — may vary by version |
66
+
67
+ ## Requirements
68
+
69
+ - Node.js 20+
70
+ - macOS or Linux (Windows: use WSL)
71
+
72
+ ## Publishing
73
+
74
+ Build and publish to npm:
75
+
76
+ ```sh
77
+ npm run release
78
+ # equivalent to: npm run build && npm publish
79
+ ```
80
+
81
+ `prepublishOnly` runs `tsc` automatically before every `npm publish`.
82
+
83
+ To log in to npm first:
84
+
85
+ ```sh
86
+ npm login
87
+ npm run release
88
+ ```
89
+
90
+ The package publishes as `@planckspace/cli` (public scoped package).
91
+
92
+ ## Development
93
+
94
+ ```sh
95
+ npm install
96
+ npm run dev -- scan # run any command via tsx (no build needed)
97
+ npm test # vitest
98
+ npm run build # compile TypeScript → dist/
99
+ ```
100
+
101
+ Override the API endpoint during development:
102
+
103
+ ```sh
104
+ PLANCKSPACE_API_URL=http://localhost:4000 npm run dev -- sync
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,2 @@
1
+ export declare function runInit(): void;
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAGA,wBAAgB,OAAO,IAAI,IAAI,CAS9B"}
@@ -0,0 +1,12 @@
1
+ import fs from "fs";
2
+ import { PLANCKSPACE_DIR, DB_PATH, initDb } from "../db/store.js";
3
+ export function runInit() {
4
+ fs.mkdirSync(PLANCKSPACE_DIR, { recursive: true, mode: 0o700 });
5
+ initDb(DB_PATH);
6
+ console.log("PlanckSpace initialized.");
7
+ console.log(` Directory : ${PLANCKSPACE_DIR}`);
8
+ console.log(` Database : ${DB_PATH} (WAL mode)`);
9
+ console.log("");
10
+ console.log("Next step: run `planck login` to connect your workspace.");
11
+ }
12
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,UAAU,OAAO;IACrB,EAAE,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,OAAO,CAAC,CAAC;IAEhB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,iBAAiB,eAAe,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,iBAAiB,OAAO,aAAa,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runLogin(tokenArg?: string): Promise<void>;
2
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAOA,wBAAsB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiD/D"}
@@ -0,0 +1,45 @@
1
+ import { readConfig, writeConfig, resolveApiUrl } from "../config.js";
2
+ export async function runLogin(tokenArg) {
3
+ const token = tokenArg ?? process.env.PLANCKSPACE_TOKEN ?? null;
4
+ if (!token) {
5
+ console.error("No token provided.\n" +
6
+ " planck login <token>\n" +
7
+ " planck login --token pk_live_...\n" +
8
+ " PLANCKSPACE_TOKEN=pk_live_... planck login");
9
+ process.exit(1);
10
+ }
11
+ const config = readConfig();
12
+ const apiUrl = resolveApiUrl();
13
+ let workspaceId = null;
14
+ let workspaceName = null;
15
+ try {
16
+ const res = await fetch(`${apiUrl}/api/me`, {
17
+ headers: { Authorization: `Bearer ${token}` },
18
+ });
19
+ if (res.ok) {
20
+ const data = (await res.json());
21
+ workspaceId = data.workspaceId ?? null;
22
+ workspaceName = data.workspaceName ?? null;
23
+ }
24
+ else if (res.status === 401) {
25
+ console.error("Invalid token — check your PlanckSpace token and try again.");
26
+ process.exit(1);
27
+ }
28
+ else {
29
+ console.error(`API returned ${res.status}; token stored, workspace will resolve on first sync.`);
30
+ }
31
+ }
32
+ catch {
33
+ console.error("Could not reach API; token stored, workspace will resolve on first sync.");
34
+ }
35
+ // Persist the apiUrl we actually used (may come from PLANCKSPACE_API_URL),
36
+ // so later commands stay pointed at the same backend without the env var.
37
+ writeConfig({ ...config, apiUrl, token, workspaceId, workspaceName });
38
+ if (workspaceName) {
39
+ console.log(`Connected to ${workspaceName}.`);
40
+ }
41
+ else {
42
+ console.log("Token saved. Run `planck sync` to complete workspace setup.");
43
+ }
44
+ }
45
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAOtE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAiB;IAC9C,MAAM,KAAK,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAEhE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CACX,sBAAsB;YACpB,0BAA0B;YAC1B,sCAAsC;YACtC,8CAA8C,CACjD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,aAAa,GAAkB,IAAI,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,SAAS,EAAE;YAC1C,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SAC9C,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAe,CAAC;YAC9C,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YACvC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;QAC7C,CAAC;aAAM,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;YAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CACX,gBAAgB,GAAG,CAAC,MAAM,uDAAuD,CAClF,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC5F,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC;IAEtE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,gBAAgB,aAAa,GAAG,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runLogout(): void;
2
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,IAAI,IAAI,CAIhC"}
@@ -0,0 +1,7 @@
1
+ import { readConfig, writeConfig } from "../config.js";
2
+ export function runLogout() {
3
+ const config = readConfig();
4
+ writeConfig({ ...config, token: null, workspaceId: null, workspaceName: null });
5
+ console.log("Logged out. Local session data retained.");
6
+ }
7
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type ScanOptions = {
2
+ sync?: boolean;
3
+ };
4
+ export declare function runScan(options?: ScanOptions): Promise<void>;
5
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7C,wBAAsB,OAAO,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDtE"}
@@ -0,0 +1,51 @@
1
+ import { scanClaudeCodeLogs } from "../scrapers/claudeCode.js";
2
+ import { scanCursorLogs } from "../scrapers/cursor.js";
3
+ import { scanWindsurfLogs } from "../scrapers/windsurf.js";
4
+ import { initDb, upsertSessions, setMeta } from "../db/store.js";
5
+ import { correlateSessions } from "../correlate.js";
6
+ import { getSyncDeps, syncAndReport } from "./sync.js";
7
+ export async function runScan(options = {}) {
8
+ initDb();
9
+ // Each scraper fails closed (returns []) when its tool isn't installed, so we can
10
+ // run them unconditionally and merge. Cursor & Windsurf are beta (see COVERAGE.md).
11
+ console.log("Scanning Claude Code, Cursor, and Windsurf logs ...");
12
+ const [claudeSessions, cursorSessions, windsurfSessions] = await Promise.all([
13
+ scanClaudeCodeLogs(),
14
+ Promise.resolve().then(() => scanCursorLogs()),
15
+ Promise.resolve().then(() => scanWindsurfLogs()),
16
+ ]);
17
+ const sessions = [
18
+ ...claudeSessions,
19
+ ...cursorSessions,
20
+ ...windsurfSessions,
21
+ ];
22
+ if (sessions.length === 0) {
23
+ console.log("No sessions found.");
24
+ }
25
+ else {
26
+ const { added, updated } = upsertSessions(sessions);
27
+ setMeta("last_scan_at", new Date().toISOString());
28
+ const totalCost = sessions.reduce((sum, s) => sum + s.costUsd, 0);
29
+ const byTool = (t) => sessions.filter((s) => s.tool === t).length;
30
+ console.log(`Scanned ${sessions.length} session(s) ` +
31
+ `(claude_code ${byTool("claude_code")}, cursor ${byTool("cursor")} [beta], ` +
32
+ `windsurf ${byTool("windsurf")} [beta]): ` +
33
+ `${added} added, ${updated} updated. ` +
34
+ `Total cost tracked: $${totalCost.toFixed(4)}`);
35
+ }
36
+ // Correlate against git every run — outcomes drift as branches merge or age out.
37
+ // Runs unconditionally (even with no new sessions) so prior verdicts stay current.
38
+ const { updated: correlated } = await correlateSessions();
39
+ if (correlated > 0) {
40
+ console.log(`Correlated git outcomes for ${correlated} session(s).`);
41
+ }
42
+ if (options.sync) {
43
+ const deps = await getSyncDeps();
44
+ if (!deps) {
45
+ console.log("Not connected — skipping sync. Run `planck login <token>` to enable.");
46
+ return;
47
+ }
48
+ await syncAndReport(deps);
49
+ }
50
+ }
51
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAKvD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAuB,EAAE;IACrD,MAAM,EAAE,CAAC;IAET,kFAAkF;IAClF,oFAAoF;IACpF,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IACnE,MAAM,CAAC,cAAc,EAAE,cAAc,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC3E,kBAAkB,EAAE;QACpB,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC;QAC9C,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAC;KACjD,CAAC,CAAC;IACH,MAAM,QAAQ,GAAoB;QAChC,GAAG,cAAc;QACjB,GAAG,cAAc;QACjB,GAAG,gBAAgB;KACpB,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACpC,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACpD,OAAO,CAAC,cAAc,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAElD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,CAAC,CAAwB,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAEzF,OAAO,CAAC,GAAG,CACT,WAAW,QAAQ,CAAC,MAAM,cAAc;YACxC,gBAAgB,MAAM,CAAC,aAAa,CAAC,YAAY,MAAM,CAAC,QAAQ,CAAC,WAAW;YAC5E,YAAY,MAAM,CAAC,UAAU,CAAC,YAAY;YAC1C,GAAG,KAAK,WAAW,OAAO,YAAY;YACtC,wBAAwB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAC/C,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,mFAAmF;IACnF,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,+BAA+B,UAAU,cAAc,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;YACpF,OAAO;QACT,CAAC;QACD,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runStatus(): void;
2
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,IAAI,IAAI,CA8BhC"}
@@ -0,0 +1,31 @@
1
+ import { readConfig } from "../config.js";
2
+ import { initDb, getSessions, getUnsyncedSessions, getMeta } from "../db/store.js";
3
+ export function runStatus() {
4
+ initDb();
5
+ const config = readConfig();
6
+ const workspace = config.workspaceName ?? "not connected";
7
+ const sessions = getSessions();
8
+ const unsynced = getUnsyncedSessions();
9
+ const lastScan = getMeta("last_scan_at");
10
+ const totalCost = sessions.reduce((sum, r) => sum + r.costUsd, 0);
11
+ const syncedCount = sessions.length - unsynced.length;
12
+ // Per-tool breakdown. Cursor & Windsurf are beta: Cursor does not persist token
13
+ // usage locally (so cost reads $0), and Windsurf parsing is unverified — see
14
+ // docs/COVERAGE.md for exactly what each tool captures.
15
+ const BETA_TOOLS = new Set(["cursor", "windsurf"]);
16
+ const counts = new Map();
17
+ for (const r of sessions)
18
+ counts.set(r.tool, (counts.get(r.tool) ?? 0) + 1);
19
+ console.log("PlanckSpace Status");
20
+ console.log("─────────────────────────────────────");
21
+ console.log(`Workspace : ${workspace}`);
22
+ console.log(`Sessions : ${sessions.length}`);
23
+ for (const tool of [...counts.keys()].sort()) {
24
+ const label = BETA_TOOLS.has(tool) ? `${tool} (beta)` : tool;
25
+ console.log(` ${label.padEnd(18)}: ${counts.get(tool)}`);
26
+ }
27
+ console.log(`Last scan : ${lastScan ? new Date(lastScan).toLocaleString() : "never"}`);
28
+ console.log(`Cost : $${totalCost.toFixed(4)}`);
29
+ console.log(`Sync : ${syncedCount} synced / ${unsynced.length} pending`);
30
+ }
31
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEnF,MAAM,UAAU,SAAS;IACvB,MAAM,EAAE,CAAC;IAET,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,IAAI,eAAe,CAAC;IAC1D,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAEzC,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAEtD,gFAAgF;IAChF,6EAA6E;IAC7E,wDAAwD;IACxD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAE5E,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,iBAAiB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,aAAa,QAAQ,CAAC,MAAM,UAAU,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type SyncDeps } from "../sync/syncEngine.js";
2
+ export type SyncOptions = {
3
+ watch?: boolean;
4
+ /** Watch interval in seconds (default 60). */
5
+ interval?: number;
6
+ };
7
+ /** Run one sync pass and print the standard summary line. */
8
+ export declare function syncAndReport(deps: SyncDeps): Promise<void>;
9
+ /**
10
+ * Build sync deps for callers that have already confirmed connection
11
+ * (e.g. `scan --sync`). Returns null if not connected.
12
+ */
13
+ export declare function getSyncDeps(): Promise<SyncDeps | null>;
14
+ export declare function runSync(options?: SyncOptions): Promise<void>;
15
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AA+CF,6DAA6D;AAC7D,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAE5D;AAED,wBAAsB,OAAO,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE"}
@@ -0,0 +1,77 @@
1
+ import { initDb } from "../db/store.js";
2
+ import { readConfig, writeConfig, resolveApiUrl } from "../config.js";
3
+ import { syncOnce } from "../sync/syncEngine.js";
4
+ /**
5
+ * Resolve the connection details sync needs, or null if not connected.
6
+ * Honors login's "workspace resolves on first sync" promise by hitting /api/me
7
+ * when the workspaceId isn't cached yet.
8
+ */
9
+ async function resolveSyncDeps() {
10
+ const config = readConfig();
11
+ if (!config.token)
12
+ return null;
13
+ const apiUrl = resolveApiUrl();
14
+ let workspaceId = config.workspaceId;
15
+ if (!workspaceId) {
16
+ try {
17
+ const res = await fetch(`${apiUrl}/api/me`, {
18
+ headers: { Authorization: `Bearer ${config.token}` },
19
+ });
20
+ if (res.ok) {
21
+ const data = (await res.json());
22
+ workspaceId = data.workspaceId ?? null;
23
+ if (workspaceId) {
24
+ writeConfig({
25
+ ...config,
26
+ workspaceId,
27
+ workspaceName: data.workspaceName ?? config.workspaceName,
28
+ });
29
+ }
30
+ }
31
+ }
32
+ catch {
33
+ // Offline or API down — fall through; the server derives the workspace
34
+ // from the token anyway and ignores the payload's workspaceId field.
35
+ }
36
+ }
37
+ return {
38
+ apiUrl,
39
+ token: config.token,
40
+ // Backend schema requires a non-empty workspaceId but always overrides it
41
+ // with the token's workspace, so a placeholder is safe when unresolved.
42
+ workspaceId: workspaceId ?? "unknown",
43
+ };
44
+ }
45
+ /** Run one sync pass and print the standard summary line. */
46
+ export async function syncAndReport(deps) {
47
+ const { synced, failed } = await syncOnce(deps);
48
+ console.log(`synced ${synced}, failed ${failed}`);
49
+ }
50
+ /**
51
+ * Build sync deps for callers that have already confirmed connection
52
+ * (e.g. `scan --sync`). Returns null if not connected.
53
+ */
54
+ export async function getSyncDeps() {
55
+ return resolveSyncDeps();
56
+ }
57
+ export async function runSync(options = {}) {
58
+ initDb();
59
+ const deps = await resolveSyncDeps();
60
+ if (!deps) {
61
+ console.error("Not connected. Run `planck login <token>` first.");
62
+ process.exit(1);
63
+ }
64
+ if (options.watch) {
65
+ const intervalSec = options.interval && options.interval > 0 ? options.interval : 60;
66
+ console.log(`Watching — syncing every ${intervalSec}s. Press Ctrl+C to stop.`);
67
+ // Simple loop for MVP. Each pass re-reads unsynced rows, so newly scraped
68
+ // sessions get picked up without restarting.
69
+ // eslint-disable-next-line no-constant-condition
70
+ while (true) {
71
+ await syncAndReport(deps);
72
+ await new Promise((resolve) => setTimeout(resolve, intervalSec * 1000));
73
+ }
74
+ }
75
+ await syncAndReport(deps);
76
+ }
77
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { PlanckConfig } from "@planckspace/core";
2
+ export declare function readConfig(): PlanckConfig;
3
+ export declare function writeConfig(config: PlanckConfig): void;
4
+ /** apiUrl precedence: PLANCKSPACE_API_URL env → config file → hardcoded default */
5
+ export declare function resolveApiUrl(): string;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAYtD,wBAAgB,UAAU,IAAI,YAAY,CAczC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAOtD;AAED,mFAAmF;AACnF,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
package/dist/config.js ADDED
@@ -0,0 +1,40 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ const CONFIG_PATH = path.join(os.homedir(), ".planckspace", "config.json");
5
+ const DEFAULTS = {
6
+ apiUrl: "https://api.planckspace.dev",
7
+ token: null,
8
+ workspaceId: null,
9
+ workspaceName: null,
10
+ pausedWorkspaces: [],
11
+ };
12
+ export function readConfig() {
13
+ try {
14
+ const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
15
+ const parsed = JSON.parse(raw);
16
+ return {
17
+ apiUrl: parsed.apiUrl ?? DEFAULTS.apiUrl,
18
+ token: parsed.token ?? null,
19
+ workspaceId: parsed.workspaceId ?? null,
20
+ workspaceName: parsed.workspaceName ?? null,
21
+ pausedWorkspaces: parsed.pausedWorkspaces ?? [],
22
+ };
23
+ }
24
+ catch {
25
+ return { ...DEFAULTS };
26
+ }
27
+ }
28
+ export function writeConfig(config) {
29
+ const dir = path.dirname(CONFIG_PATH);
30
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
31
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), {
32
+ mode: 0o600,
33
+ encoding: "utf-8",
34
+ });
35
+ }
36
+ /** apiUrl precedence: PLANCKSPACE_API_URL env → config file → hardcoded default */
37
+ export function resolveApiUrl() {
38
+ return process.env.PLANCKSPACE_API_URL ?? readConfig().apiUrl;
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,aAAa,CAAC,CAAC;AAE3E,MAAM,QAAQ,GAAiB;IAC7B,MAAM,EAAE,6BAA6B;IACrC,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,IAAI;IACnB,gBAAgB,EAAE,EAAE;CACrB,CAAC;AAEF,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;QACxD,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM;YACxC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;YAC3B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;YACvC,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,IAAI;YAC3C,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE;SAChD,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC7D,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE,OAAO;KAClB,CAAC,CAAC;AACL,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,UAAU,EAAE,CAAC,MAAM,CAAC;AAChE,CAAC"}
@@ -0,0 +1,21 @@
1
+ /** A session-branch commit that reaches the default branch within this many days = shipped. */
2
+ export declare const SHIPPED_MERGE_WINDOW_DAYS = 7;
3
+ /** A session that ended this long ago with no commits by its author = abandoned. */
4
+ export declare const ABANDONED_AFTER_HOURS = 24;
5
+ /** While a session is still this fresh and undetermined, we hold off and call it unknown. */
6
+ export declare const UNKNOWN_WINDOW_DAYS = 7;
7
+ /** Branch names treated as the repo's default/trunk, in priority order. */
8
+ export declare const DEFAULT_BRANCH_CANDIDATES: readonly ["main", "master"];
9
+ export type Outcome = "shipped" | "partial" | "abandoned" | "errored" | "unknown";
10
+ /**
11
+ * Correlate every stored session that has a repoName + workingDir against its
12
+ * git repo, recording the git author email and a recomputed outcome.
13
+ *
14
+ * Safe to re-run: outcomes are derived fresh each pass, and any repo that's
15
+ * gone missing, isn't a repo anymore, or otherwise misbehaves is skipped
16
+ * without aborting the rest. `now` is injectable for testing.
17
+ */
18
+ export declare function correlateSessions(now?: number): Promise<{
19
+ updated: number;
20
+ }>;
21
+ //# sourceMappingURL=correlate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlate.d.ts","sourceRoot":"","sources":["../src/correlate.ts"],"names":[],"mappings":"AAaA,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,2EAA2E;AAC3E,eAAO,MAAM,yBAAyB,6BAA8B,CAAC;AAKrE,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAsKlF;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA6B9F"}