codedeck 0.6.0 → 0.7.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 (140) hide show
  1. package/README.md +50 -35
  2. package/dist/cli/commands/claims.js +3 -2
  3. package/dist/cli/commands/claims.js.map +1 -1
  4. package/dist/cli/commands/doctor.js +8 -40
  5. package/dist/cli/commands/doctor.js.map +1 -1
  6. package/dist/cli/commands/logs.js +2 -2
  7. package/dist/cli/commands/logs.js.map +1 -1
  8. package/dist/cli/commands/open.js +102 -11
  9. package/dist/cli/commands/open.js.map +1 -1
  10. package/dist/cli/commands/ps.js +6 -6
  11. package/dist/cli/commands/ps.js.map +1 -1
  12. package/dist/cli/commands/review.js +36 -58
  13. package/dist/cli/commands/review.js.map +1 -1
  14. package/dist/cli/commands/run.js +27 -27
  15. package/dist/cli/commands/run.js.map +1 -1
  16. package/dist/cli/commands/setup.js +96 -314
  17. package/dist/cli/commands/setup.js.map +1 -1
  18. package/dist/cli/commands/show.js +2 -1
  19. package/dist/cli/commands/show.js.map +1 -1
  20. package/dist/cli/commands/ui.js +61 -0
  21. package/dist/cli/commands/ui.js.map +1 -0
  22. package/dist/cli/commands/usage-backfill.js +149 -0
  23. package/dist/cli/commands/usage-backfill.js.map +1 -0
  24. package/dist/cli/commands/usage.js +113 -45
  25. package/dist/cli/commands/usage.js.map +1 -1
  26. package/dist/cli/commands/wait.js +3 -2
  27. package/dist/cli/commands/wait.js.map +1 -1
  28. package/dist/cli/index.js +55 -39
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/web-launch.js +126 -0
  31. package/dist/cli/web-launch.js.map +1 -0
  32. package/dist/config/config.js +0 -70
  33. package/dist/config/config.js.map +1 -1
  34. package/dist/config/paths.js +1 -0
  35. package/dist/config/paths.js.map +1 -1
  36. package/dist/config/setup.js +248 -0
  37. package/dist/config/setup.js.map +1 -0
  38. package/dist/config/web-host.js +31 -0
  39. package/dist/config/web-host.js.map +1 -0
  40. package/dist/config/web-port.js +24 -0
  41. package/dist/config/web-port.js.map +1 -0
  42. package/dist/core/claude-transcript.js +226 -0
  43. package/dist/core/claude-transcript.js.map +1 -0
  44. package/dist/core/errors.js +17 -0
  45. package/dist/core/errors.js.map +1 -1
  46. package/dist/core/models.js +4 -1
  47. package/dist/core/models.js.map +1 -1
  48. package/dist/core/pricing.js +87 -55
  49. package/dist/core/pricing.js.map +1 -1
  50. package/dist/core/roles.js +6 -5
  51. package/dist/core/roles.js.map +1 -1
  52. package/dist/core/run-skills.js +114 -0
  53. package/dist/core/run-skills.js.map +1 -0
  54. package/dist/core/run-usage.js +56 -10
  55. package/dist/core/run-usage.js.map +1 -1
  56. package/dist/core/session.js +6 -16
  57. package/dist/core/session.js.map +1 -1
  58. package/dist/core/usage-query.js +41 -0
  59. package/dist/core/usage-query.js.map +1 -0
  60. package/dist/core/usage-source.js +17 -0
  61. package/dist/core/usage-source.js.map +1 -0
  62. package/dist/daemon/build-id.js +30 -0
  63. package/dist/daemon/build-id.js.map +1 -0
  64. package/dist/daemon/daemon.js +1257 -700
  65. package/dist/daemon/daemon.js.map +1 -1
  66. package/dist/daemon/instance-lock.js +48 -0
  67. package/dist/daemon/instance-lock.js.map +1 -0
  68. package/dist/daemon/ipc.js +4 -3
  69. package/dist/daemon/ipc.js.map +1 -1
  70. package/dist/daemon/web-supervisor.js +251 -0
  71. package/dist/daemon/web-supervisor.js.map +1 -0
  72. package/dist/drivers/claude/driver.js +1 -0
  73. package/dist/drivers/claude/driver.js.map +1 -1
  74. package/dist/drivers/codex/driver.js +14 -3
  75. package/dist/drivers/codex/driver.js.map +1 -1
  76. package/dist/drivers/codex/host-skills.js +57 -0
  77. package/dist/drivers/codex/host-skills.js.map +1 -0
  78. package/dist/drivers/opencode/driver.js +6 -2
  79. package/dist/drivers/opencode/driver.js.map +1 -1
  80. package/dist/drivers/session-driver.js +7 -1
  81. package/dist/drivers/session-driver.js.map +1 -1
  82. package/dist/drivers/session-runtime.js +2 -0
  83. package/dist/drivers/session-runtime.js.map +1 -1
  84. package/dist/drivers/terminal.js +15 -0
  85. package/dist/drivers/terminal.js.map +1 -1
  86. package/dist/git/diff.js +29 -4
  87. package/dist/git/diff.js.map +1 -1
  88. package/dist/open/contract.js.map +1 -1
  89. package/dist/open/launchers/claude.js +78 -2
  90. package/dist/open/launchers/claude.js.map +1 -1
  91. package/dist/open/link-watcher.js +44 -0
  92. package/dist/open/link-watcher.js.map +1 -0
  93. package/dist/open/pty.js +330 -5
  94. package/dist/open/pty.js.map +1 -1
  95. package/dist/open/runtime.js +15 -12
  96. package/dist/open/runtime.js.map +1 -1
  97. package/dist/plugin/agents/general.md +33 -6
  98. package/dist/plugin/hooks/pane-ticker.ts +102 -0
  99. package/dist/plugin/hooks/register.tsx +39 -18
  100. package/dist/plugin/hooks/session-id.sh +1 -1
  101. package/dist/plugin/mods/agents/pane.ts +285 -156
  102. package/dist/plugin/mods/agents/types.ts +23 -3
  103. package/dist/plugin/statusline.sh +57 -32
  104. package/dist/store/database.js +49 -1
  105. package/dist/store/database.js.map +1 -1
  106. package/dist/store/native-links.js +80 -0
  107. package/dist/store/native-links.js.map +1 -0
  108. package/dist/store/sessions.js +166 -51
  109. package/dist/store/sessions.js.map +1 -1
  110. package/dist/store/usage-ledger.js +153 -0
  111. package/dist/store/usage-ledger.js.map +1 -0
  112. package/dist/utils/process.js +2 -16
  113. package/dist/utils/process.js.map +1 -1
  114. package/dist/web/brand.js +59 -0
  115. package/dist/web/brand.js.map +1 -0
  116. package/dist/web/child.js +67 -0
  117. package/dist/web/child.js.map +1 -0
  118. package/dist/web/home-page.js +48 -0
  119. package/dist/web/home-page.js.map +1 -0
  120. package/dist/web/review-page.js +12 -5
  121. package/dist/web/review-page.js.map +1 -1
  122. package/dist/web/security.js +170 -0
  123. package/dist/web/security.js.map +1 -0
  124. package/dist/web/server.js +198 -0
  125. package/dist/web/server.js.map +1 -0
  126. package/dist/web/setup-icons.js +37 -0
  127. package/dist/web/setup-icons.js.map +1 -0
  128. package/dist/web/setup-page.js +1795 -0
  129. package/dist/web/setup-page.js.map +1 -0
  130. package/dist/web/setup-routes.js +475 -0
  131. package/dist/web/setup-routes.js.map +1 -0
  132. package/dist/web/usage-page.js +609 -0
  133. package/dist/web/usage-page.js.map +1 -0
  134. package/dist/web/usage-routes.js +88 -0
  135. package/dist/web/usage-routes.js.map +1 -0
  136. package/dist/web/web-token.js +56 -0
  137. package/dist/web/web-token.js.map +1 -0
  138. package/package.json +1 -1
  139. package/dist/plugin/prompts/_partials/commit.md +0 -145
  140. package/dist/plugin/prompts/_partials/pr-writer.md +0 -186
@@ -65,26 +65,6 @@ const role =
65
65
  tail(payload.agent?.name) ??
66
66
  tail(payload.session_name);
67
67
 
68
- const taskName = (() => {
69
- const sessionFile = text(process.env.CODEDECK_SESSION_FILE);
70
- const sessionId = payload.session_id;
71
- if (
72
- !sessionFile ||
73
- typeof sessionId !== "string" ||
74
- !/^[0-9a-fA-F-]{8,}$/.test(sessionId)
75
- ) return undefined;
76
-
77
- try {
78
- const value = readFileSync(`${sessionFile}.${sessionId}.name`, "utf8")
79
- .replace(/\s+/g, " ")
80
- .trim()
81
- .slice(0, 30);
82
- return value || undefined;
83
- } catch {
84
- return undefined;
85
- }
86
- })();
87
-
88
68
  const cwd = firstText(payload.workspace?.current_dir, payload.cwd) ?? process.cwd();
89
69
  const project = text(basename(cwd)) ?? (cwd === "/" ? "/" : undefined);
90
70
 
@@ -132,9 +112,8 @@ const compactTokens = (value) => {
132
112
 
133
113
  /**
134
114
  * Since Claude Code 2.1.132, these fields describe the latest context window,
135
- * not cumulative session totals. The aggregate token field therefore stays
136
- * worker-only instead of adding a repeated local window to the run total.
137
- * Degraded mode may show this current local snapshot when it is available.
115
+ * not cumulative session totals. Use the local snapshot only without an active
116
+ * run summary, which already combines worker and orchestrator token totals.
138
117
  */
139
118
  const localTokens = () => {
140
119
  const context = payload.context_window;
@@ -177,12 +156,23 @@ const localCost = () => {
177
156
  return typeof total === "number" && Number.isFinite(total) ? total : undefined;
178
157
  };
179
158
 
159
+ const runId = text(process.env.CODEDECK_RUN_ID);
160
+
180
161
  const getRunUsage = () => {
181
- const runId = text(process.env.CODEDECK_RUN_ID);
182
162
  if (!runId) return undefined;
183
163
 
164
+ const args = ["usage", runId, "--json"];
165
+ const sessionId = payload.session_id;
166
+ const validSessionId =
167
+ typeof sessionId === "string" && /^[0-9a-fA-F-]{8,}$/.test(sessionId);
168
+ const observeCost = nonNegativeNumber(payload.cost?.total_cost_usd);
169
+ if (validSessionId && observeCost !== undefined) args.push("--observe", `${sessionId}=${observeCost}`);
170
+ if (validSessionId && typeof payload.transcript_path === "string" && payload.transcript_path.length > 0) {
171
+ args.push("--transcript", `${sessionId}=${payload.transcript_path}`);
172
+ }
173
+
184
174
  try {
185
- const output = execFileSync("codedeck", ["usage", runId, "--json"], {
175
+ const output = execFileSync("codedeck", args, {
186
176
  encoding: "utf8",
187
177
  stdio: ["ignore", "pipe", "ignore"],
188
178
  timeout: 1000,
@@ -215,20 +205,52 @@ const getRunUsage = () => {
215
205
  }
216
206
  };
217
207
 
208
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
209
+
210
+ const getOrchestratorUsage = (usage) => {
211
+ const value = usage.orchestrator;
212
+ if (
213
+ !isObject(value) ||
214
+ nonNegativeNumber(value.costUsd) === undefined ||
215
+ typeof value.costComplete !== "boolean" ||
216
+ nonNegativeNumber(value.inputTokens) === undefined ||
217
+ nonNegativeNumber(value.outputTokens) === undefined ||
218
+ nonNegativeNumber(value.cachedTokens) === undefined ||
219
+ !Array.isArray(value.sources)
220
+ ) return undefined;
221
+ if (!value.sources.every((source) =>
222
+ isObject(source) &&
223
+ typeof source.nativeId === "string" &&
224
+ nonNegativeNumber(source.costUsd) !== undefined
225
+ )) return undefined;
226
+ return value;
227
+ };
228
+
218
229
  const local = localCost();
219
230
  const runUsage = getRunUsage();
220
- const workerTokens = runUsage
221
- ? runUsage.inputTokens + runUsage.outputTokens + runUsage.cachedTokens
231
+ const orchestratorUsage = runUsage ? getOrchestratorUsage(runUsage) : undefined;
232
+ const workerTokens = runUsage ? nonNegativeNumber(runUsage.totalTokens) : undefined;
233
+ const orchestratorTokens = runUsage && isObject(runUsage.orchestrator)
234
+ ? nonNegativeNumber(runUsage.orchestrator.totalTokens)
235
+ : undefined;
236
+ const runTokens = workerTokens !== undefined && orchestratorTokens !== undefined
237
+ ? nonNegativeNumber(workerTokens + orchestratorTokens)
222
238
  : undefined;
223
239
  const tokenField = () => {
224
- const total = workerTokens ?? (runUsage ? undefined : localTokens());
240
+ const total = runId ? runTokens : localTokens();
225
241
  return total === undefined ? undefined : paint(MUTED, compactTokens(total) + " tok");
226
242
  };
227
243
 
228
244
  const runField = () => {
229
245
  if (!runUsage) return undefined;
230
- const total = (local ?? 0) + runUsage.costUsd;
231
- const incomplete = !runUsage.costComplete;
246
+ const sourceCost = orchestratorUsage
247
+ ? orchestratorUsage.sources.reduce(
248
+ (sum, source) => source.nativeId === payload.session_id ? sum : sum + source.costUsd,
249
+ 0,
250
+ )
251
+ : 0;
252
+ const total = (local ?? 0) + runUsage.costUsd + sourceCost;
253
+ const incomplete = !runUsage.costComplete || Boolean(orchestratorUsage && !orchestratorUsage.costComplete);
232
254
  if (!incomplete && total < COST_DISPLAY_THRESHOLD) return undefined;
233
255
  return paint(MUTED, "run ") + costAmount(total, incomplete);
234
256
  };
@@ -238,8 +260,12 @@ const localField = () => {
238
260
  return costAmount(local);
239
261
  };
240
262
 
263
+ const collapsedProjectBranch = project && branch &&
264
+ (branch === project || branch === `worktree-${project}`);
241
265
  const projectBranch = project && branch
242
- ? paint(TEXT, clean(project)) + paint(MUTED, "/") + paint(BLOOD, clean(branch))
266
+ ? collapsedProjectBranch
267
+ ? paint(BLOOD, clean(branch))
268
+ : paint(TEXT, clean(project)) + paint(MUTED, "/") + paint(BLOOD, clean(branch))
243
269
  : project
244
270
  ? paint(TEXT, clean(project))
245
271
  : branch
@@ -247,7 +273,6 @@ const projectBranch = project && branch
247
273
  : undefined;
248
274
 
249
275
  const fields = [
250
- taskName && paint(EMBER, clean(taskName)),
251
276
  role && paint(EMBER, clean(role)),
252
277
  projectBranch,
253
278
  contextField(),
@@ -65,7 +65,9 @@ export class Database {
65
65
  fast INTEGER NOT NULL DEFAULT 0,
66
66
  sandbox TEXT,
67
67
  dangerously_bypass_approvals_and_sandbox INTEGER,
68
- origin TEXT
68
+ origin TEXT,
69
+ parent_id TEXT,
70
+ role TEXT
69
71
  );
70
72
 
71
73
  CREATE TABLE IF NOT EXISTS events (
@@ -89,9 +91,53 @@ export class Database {
89
91
  active INTEGER NOT NULL DEFAULT 1
90
92
  );
91
93
 
94
+ CREATE TABLE IF NOT EXISTS usage_sources (
95
+ source_key TEXT PRIMARY KEY,
96
+ cost REAL,
97
+ input_tokens INTEGER,
98
+ output_tokens INTEGER,
99
+ cached_tokens INTEGER,
100
+ updated_at TEXT NOT NULL
101
+ );
102
+
103
+ CREATE TABLE IF NOT EXISTS usage_attributions (
104
+ session_id TEXT NOT NULL,
105
+ source_key TEXT NOT NULL,
106
+ cost REAL,
107
+ input_tokens INTEGER NOT NULL DEFAULT 0,
108
+ output_tokens INTEGER NOT NULL DEFAULT 0,
109
+ cached_tokens INTEGER NOT NULL DEFAULT 0,
110
+ PRIMARY KEY (session_id, source_key),
111
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
112
+ FOREIGN KEY (source_key) REFERENCES usage_sources(source_key) ON DELETE CASCADE
113
+ );
114
+
115
+ CREATE TABLE IF NOT EXISTS session_native_links (
116
+ session_id TEXT NOT NULL,
117
+ native_id TEXT NOT NULL,
118
+ linked_at TEXT NOT NULL,
119
+ reconciled_at TEXT,
120
+ state TEXT,
121
+ PRIMARY KEY (session_id, native_id),
122
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
123
+ );
124
+
125
+ CREATE TABLE IF NOT EXISTS usage_legacy (
126
+ native_id TEXT PRIMARY KEY,
127
+ ended_at TEXT NOT NULL,
128
+ cwd TEXT,
129
+ repository TEXT,
130
+ model TEXT,
131
+ cost REAL,
132
+ input_tokens INTEGER NOT NULL DEFAULT 0,
133
+ output_tokens INTEGER NOT NULL DEFAULT 0,
134
+ cached_tokens INTEGER NOT NULL DEFAULT 0
135
+ );
136
+
92
137
  CREATE INDEX IF NOT EXISTS idx_events_session_seq ON events(session_id, sequence);
93
138
  CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
94
139
  CREATE INDEX IF NOT EXISTS idx_sessions_created ON sessions(created_at DESC);
140
+ CREATE INDEX IF NOT EXISTS idx_usage_attributions_source_key ON usage_attributions(source_key);
95
141
  CREATE UNIQUE INDEX IF NOT EXISTS idx_claims_active_session_path
96
142
  ON claims(session_id, path_glob) WHERE active = 1;
97
143
  `);
@@ -115,6 +161,8 @@ export class Database {
115
161
  ["origin", "TEXT"],
116
162
  ["pending_message", "TEXT"],
117
163
  ["pending_at", "TEXT"],
164
+ ["parent_id", "TEXT"],
165
+ ["role", "TEXT"],
118
166
  ];
119
167
  for (const [name, type] of additions) {
120
168
  if (!existing.has(name))
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/store/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/store/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { SessionStore } from "./sessions.js";
2
+ function toNativeLink(row) {
3
+ return {
4
+ sessionId: row.session_id,
5
+ nativeId: row.native_id,
6
+ linkedAt: row.linked_at,
7
+ reconciledAt: row.reconciled_at,
8
+ state: row.state,
9
+ };
10
+ }
11
+ export class NativeLinkStore {
12
+ db;
13
+ constructor(db) {
14
+ this.db = db;
15
+ }
16
+ link(sessionId, nativeId) {
17
+ const previous = this.db.prepare(`
18
+ SELECT native_id FROM session_native_links
19
+ WHERE session_id = ? AND native_id <> ? AND reconciled_at IS NULL
20
+ ORDER BY linked_at, native_id
21
+ `).all(sessionId, nativeId).map((row) => row.native_id);
22
+ const result = this.db.prepare(`
23
+ INSERT INTO session_native_links (session_id, native_id, linked_at)
24
+ VALUES (?, ?, ?)
25
+ ON CONFLICT(session_id, native_id) DO NOTHING
26
+ `).run(sessionId, nativeId, new Date().toISOString());
27
+ return { created: Number(result.changes) > 0, previous };
28
+ }
29
+ unreconciled(sessionId) {
30
+ const rows = this.db.prepare(`
31
+ SELECT session_id, native_id, linked_at, reconciled_at, state
32
+ FROM session_native_links
33
+ WHERE session_id = ? AND reconciled_at IS NULL
34
+ ORDER BY linked_at, native_id
35
+ `).all(sessionId);
36
+ return rows.map(toNativeLink);
37
+ }
38
+ linksFor(sessionIds) {
39
+ if (sessionIds.length === 0)
40
+ return [];
41
+ const placeholders = sessionIds.map(() => "?").join(", ");
42
+ const rows = this.db.prepare(`
43
+ SELECT session_id, native_id, linked_at, reconciled_at, state
44
+ FROM session_native_links
45
+ WHERE session_id IN (${placeholders})
46
+ ORDER BY session_id, linked_at, native_id
47
+ `).all(...sessionIds);
48
+ return rows.map(toNativeLink);
49
+ }
50
+ markReconciled(sessionId, nativeId, state) {
51
+ this.db.prepare(`
52
+ UPDATE session_native_links SET reconciled_at = ?, state = ?
53
+ WHERE session_id = ? AND native_id = ?
54
+ `).run(new Date().toISOString(), state, sessionId, nativeId);
55
+ }
56
+ staleOpenRows(isDead) {
57
+ const candidates = this.db.prepare(`
58
+ SELECT DISTINCT sessions.id, sessions.status
59
+ FROM sessions
60
+ INNER JOIN session_native_links
61
+ ON session_native_links.session_id = sessions.id
62
+ WHERE sessions.origin = 'open'
63
+ AND session_native_links.reconciled_at IS NULL
64
+ AND sessions.status IN ('interrupted', 'working')
65
+ ORDER BY sessions.created_at, sessions.id
66
+ `).all();
67
+ const sessions = new SessionStore(this.db);
68
+ const stale = [];
69
+ for (const candidate of candidates) {
70
+ const session = sessions.get(candidate.id);
71
+ if (!session)
72
+ continue;
73
+ if (candidate.status === "interrupted" || (candidate.status === "working" && isDead(session))) {
74
+ stale.push(session);
75
+ }
76
+ }
77
+ return stale;
78
+ }
79
+ }
80
+ //# sourceMappingURL=native-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-links.js","sourceRoot":"","sources":["../../src/store/native-links.ts"],"names":[],"mappings":"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"}