@staff0rd/assist 0.208.0 → 0.209.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/README.md +1 -1
- package/claude/commands/sql.md +2 -2
- package/dist/index.js +473 -448
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.209.1",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -96,7 +96,7 @@ var package_default = {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
// src/commands/backlog/next.ts
|
|
99
|
-
import
|
|
99
|
+
import chalk9 from "chalk";
|
|
100
100
|
import enquirer2 from "enquirer";
|
|
101
101
|
|
|
102
102
|
// src/shared/exitOnCancel.ts
|
|
@@ -112,13 +112,276 @@ async function exitOnCancel(promise) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// src/commands/backlog/acquireLock.ts
|
|
115
|
-
import { existsSync as
|
|
116
|
-
import { join as
|
|
115
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
116
|
+
import { join as join6 } from "path";
|
|
117
117
|
|
|
118
118
|
// src/commands/backlog/shared.ts
|
|
119
|
-
import { existsSync as
|
|
120
|
-
import { join as
|
|
119
|
+
import { existsSync as existsSync5 } from "fs";
|
|
120
|
+
import { join as join5 } from "path";
|
|
121
|
+
import chalk2 from "chalk";
|
|
122
|
+
|
|
123
|
+
// src/shared/loadConfig.ts
|
|
124
|
+
import { existsSync as existsSync2, writeFileSync } from "fs";
|
|
125
|
+
import { homedir } from "os";
|
|
126
|
+
import { dirname, join } from "path";
|
|
121
127
|
import chalk from "chalk";
|
|
128
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
129
|
+
|
|
130
|
+
// src/shared/loadRawYaml.ts
|
|
131
|
+
import { existsSync, readFileSync } from "fs";
|
|
132
|
+
import { parse as parseYaml } from "yaml";
|
|
133
|
+
function loadRawYaml(path53) {
|
|
134
|
+
if (!existsSync(path53)) return {};
|
|
135
|
+
try {
|
|
136
|
+
const content = readFileSync(path53, "utf-8");
|
|
137
|
+
return parseYaml(content) || {};
|
|
138
|
+
} catch {
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/shared/mergeDenyRules.ts
|
|
144
|
+
function mergeDenyRules(globalDeny, projectDeny) {
|
|
145
|
+
if (!globalDeny && !projectDeny) return void 0;
|
|
146
|
+
if (!globalDeny) return projectDeny;
|
|
147
|
+
if (!projectDeny) return globalDeny;
|
|
148
|
+
const projectPatterns = new Set(projectDeny.map((r) => r.pattern));
|
|
149
|
+
const globalOnly = globalDeny.filter((r) => !projectPatterns.has(r.pattern));
|
|
150
|
+
return [...globalOnly, ...projectDeny];
|
|
151
|
+
}
|
|
152
|
+
function mergeRawConfigs(globalRaw, projectRaw) {
|
|
153
|
+
const deny = mergeDenyRules(
|
|
154
|
+
globalRaw.deny,
|
|
155
|
+
projectRaw.deny
|
|
156
|
+
);
|
|
157
|
+
const merged = { ...globalRaw, ...projectRaw };
|
|
158
|
+
if (deny !== void 0) {
|
|
159
|
+
merged.deny = deny;
|
|
160
|
+
}
|
|
161
|
+
return merged;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/shared/types.ts
|
|
165
|
+
import { z as z2 } from "zod";
|
|
166
|
+
|
|
167
|
+
// src/shared/runConfigSchema.ts
|
|
168
|
+
import { z } from "zod";
|
|
169
|
+
var runParamSchema = z.strictObject({
|
|
170
|
+
name: z.string(),
|
|
171
|
+
required: z.boolean().optional(),
|
|
172
|
+
default: z.string().optional(),
|
|
173
|
+
description: z.string().optional()
|
|
174
|
+
});
|
|
175
|
+
var runConfigSchema = z.strictObject({
|
|
176
|
+
name: z.string(),
|
|
177
|
+
command: z.string(),
|
|
178
|
+
args: z.array(z.string()).optional(),
|
|
179
|
+
params: z.array(runParamSchema).optional(),
|
|
180
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
181
|
+
filter: z.string().optional(),
|
|
182
|
+
pre: z.array(z.string()).optional(),
|
|
183
|
+
cwd: z.string().optional()
|
|
184
|
+
});
|
|
185
|
+
var runLinkSchema = z.strictObject({
|
|
186
|
+
link: z.string(),
|
|
187
|
+
prefix: z.string()
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// src/shared/types.ts
|
|
191
|
+
var transcriptConfigSchema = z2.strictObject({
|
|
192
|
+
vttDir: z2.string(),
|
|
193
|
+
transcriptsDir: z2.string(),
|
|
194
|
+
summaryDir: z2.string()
|
|
195
|
+
});
|
|
196
|
+
var DEFAULT_WAKE_WORDS = ["computer"];
|
|
197
|
+
var DEFAULT_MODELS_DIR = "~/.assist/voice/models";
|
|
198
|
+
var assistConfigSchema = z2.strictObject({
|
|
199
|
+
commit: z2.strictObject({
|
|
200
|
+
conventional: z2.boolean().default(false),
|
|
201
|
+
pull: z2.boolean().default(false),
|
|
202
|
+
push: z2.boolean().default(false)
|
|
203
|
+
}).default({ conventional: false, pull: false, push: false }),
|
|
204
|
+
devlog: z2.strictObject({
|
|
205
|
+
name: z2.string().optional(),
|
|
206
|
+
ignore: z2.array(z2.string()).optional(),
|
|
207
|
+
skip: z2.record(z2.string(), z2.array(z2.string())).optional()
|
|
208
|
+
}).optional(),
|
|
209
|
+
notify: z2.strictObject({
|
|
210
|
+
enabled: z2.boolean().default(true)
|
|
211
|
+
}).default({ enabled: true }),
|
|
212
|
+
complexity: z2.strictObject({
|
|
213
|
+
ignore: z2.array(z2.string()).default(["**/*test.ts*"])
|
|
214
|
+
}).default({ ignore: ["**/*test.ts*"] }),
|
|
215
|
+
hardcodedColors: z2.strictObject({
|
|
216
|
+
ignore: z2.array(z2.string()).default([])
|
|
217
|
+
}).optional(),
|
|
218
|
+
restructure: z2.strictObject({
|
|
219
|
+
ignore: z2.array(z2.string()).default([])
|
|
220
|
+
}).optional(),
|
|
221
|
+
jira: z2.strictObject({
|
|
222
|
+
acField: z2.string().default("customfield_11937")
|
|
223
|
+
}).optional(),
|
|
224
|
+
roam: z2.strictObject({
|
|
225
|
+
clientId: z2.string(),
|
|
226
|
+
clientSecret: z2.string(),
|
|
227
|
+
accessToken: z2.string().optional(),
|
|
228
|
+
refreshToken: z2.string().optional(),
|
|
229
|
+
tokenExpiresAt: z2.number().optional()
|
|
230
|
+
}).optional(),
|
|
231
|
+
run: z2.array(z2.union([runConfigSchema, runLinkSchema])).optional(),
|
|
232
|
+
transcript: transcriptConfigSchema.optional(),
|
|
233
|
+
cliReadVerbs: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
234
|
+
news: z2.strictObject({
|
|
235
|
+
feeds: z2.array(z2.string()).default([])
|
|
236
|
+
}).default({ feeds: [] }),
|
|
237
|
+
dotnet: z2.strictObject({
|
|
238
|
+
inspect: z2.strictObject({
|
|
239
|
+
suppress: z2.array(z2.string()).default([])
|
|
240
|
+
}).default({ suppress: [] })
|
|
241
|
+
}).optional(),
|
|
242
|
+
ravendb: z2.strictObject({
|
|
243
|
+
connections: z2.array(
|
|
244
|
+
z2.strictObject({
|
|
245
|
+
name: z2.string(),
|
|
246
|
+
url: z2.string(),
|
|
247
|
+
database: z2.string(),
|
|
248
|
+
apiKeyRef: z2.string()
|
|
249
|
+
})
|
|
250
|
+
).default([]),
|
|
251
|
+
defaultConnection: z2.string().optional()
|
|
252
|
+
}).optional(),
|
|
253
|
+
seq: z2.strictObject({
|
|
254
|
+
connections: z2.array(
|
|
255
|
+
z2.strictObject({
|
|
256
|
+
name: z2.string(),
|
|
257
|
+
url: z2.string(),
|
|
258
|
+
apiToken: z2.string()
|
|
259
|
+
})
|
|
260
|
+
).default([]),
|
|
261
|
+
defaultConnection: z2.string().optional()
|
|
262
|
+
}).optional(),
|
|
263
|
+
sql: z2.strictObject({
|
|
264
|
+
connections: z2.array(
|
|
265
|
+
z2.strictObject({
|
|
266
|
+
name: z2.string(),
|
|
267
|
+
server: z2.string(),
|
|
268
|
+
port: z2.number(),
|
|
269
|
+
user: z2.string(),
|
|
270
|
+
password: z2.string(),
|
|
271
|
+
database: z2.string()
|
|
272
|
+
})
|
|
273
|
+
).default([]),
|
|
274
|
+
defaultConnection: z2.string().optional()
|
|
275
|
+
}).optional(),
|
|
276
|
+
screenshot: z2.strictObject({
|
|
277
|
+
outputDir: z2.string().default("./screenshots")
|
|
278
|
+
}).default({ outputDir: "./screenshots" }),
|
|
279
|
+
backlog: z2.strictObject({
|
|
280
|
+
autoCommit: z2.boolean().default(false)
|
|
281
|
+
}).default({ autoCommit: false }),
|
|
282
|
+
mermaid: z2.strictObject({
|
|
283
|
+
krokiUrl: z2.string().default("https://kroki.io")
|
|
284
|
+
}).default({ krokiUrl: "https://kroki.io" }),
|
|
285
|
+
deny: z2.array(
|
|
286
|
+
z2.strictObject({
|
|
287
|
+
pattern: z2.string(),
|
|
288
|
+
message: z2.string()
|
|
289
|
+
})
|
|
290
|
+
).optional(),
|
|
291
|
+
sync: z2.strictObject({
|
|
292
|
+
autoConfirm: z2.boolean().default(false)
|
|
293
|
+
}).default({ autoConfirm: false }),
|
|
294
|
+
voice: z2.strictObject({
|
|
295
|
+
wakeWords: z2.array(z2.string()).default(DEFAULT_WAKE_WORDS),
|
|
296
|
+
mic: z2.string().optional(),
|
|
297
|
+
cwd: z2.string().optional(),
|
|
298
|
+
modelsDir: z2.string().default(DEFAULT_MODELS_DIR),
|
|
299
|
+
lockDir: z2.string().optional(),
|
|
300
|
+
submitWindows: z2.array(z2.string()).optional(),
|
|
301
|
+
models: z2.strictObject({
|
|
302
|
+
vad: z2.string().optional(),
|
|
303
|
+
smartTurn: z2.string().optional()
|
|
304
|
+
}).default({})
|
|
305
|
+
}).default({
|
|
306
|
+
wakeWords: DEFAULT_WAKE_WORDS,
|
|
307
|
+
modelsDir: DEFAULT_MODELS_DIR,
|
|
308
|
+
models: {}
|
|
309
|
+
})
|
|
310
|
+
});
|
|
311
|
+
function isRunLink(entry) {
|
|
312
|
+
return "link" in entry;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/shared/loadConfig.ts
|
|
316
|
+
function findConfigUp(startDir) {
|
|
317
|
+
let current = startDir;
|
|
318
|
+
while (current !== dirname(current)) {
|
|
319
|
+
const claudePath = join(current, ".claude", "assist.yml");
|
|
320
|
+
if (existsSync2(claudePath))
|
|
321
|
+
return { configPath: claudePath, rootDir: current };
|
|
322
|
+
const rootPath = join(current, "assist.yml");
|
|
323
|
+
if (existsSync2(rootPath)) return { configPath: rootPath, rootDir: current };
|
|
324
|
+
current = dirname(current);
|
|
325
|
+
}
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
function getConfigPath() {
|
|
329
|
+
const found = findConfigUp(process.cwd());
|
|
330
|
+
if (found) return found.configPath;
|
|
331
|
+
return join(process.cwd(), "assist.yml");
|
|
332
|
+
}
|
|
333
|
+
function getGlobalConfigPath() {
|
|
334
|
+
return join(homedir(), ".assist.yml");
|
|
335
|
+
}
|
|
336
|
+
function getConfigDir() {
|
|
337
|
+
return dirname(getConfigPath());
|
|
338
|
+
}
|
|
339
|
+
function getProjectRoot() {
|
|
340
|
+
const found = findConfigUp(process.cwd());
|
|
341
|
+
return found?.rootDir ?? process.cwd();
|
|
342
|
+
}
|
|
343
|
+
function loadConfig() {
|
|
344
|
+
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
345
|
+
const projectRaw = loadRawYaml(getConfigPath());
|
|
346
|
+
const merged = mergeRawConfigs(globalRaw, projectRaw);
|
|
347
|
+
return assistConfigSchema.parse(merged);
|
|
348
|
+
}
|
|
349
|
+
function loadConfigFrom(startDir) {
|
|
350
|
+
const found = findConfigUp(startDir);
|
|
351
|
+
const configPath = found?.configPath ?? join(startDir, "assist.yml");
|
|
352
|
+
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
353
|
+
const projectRaw = loadRawYaml(configPath);
|
|
354
|
+
const merged = mergeRawConfigs(globalRaw, projectRaw);
|
|
355
|
+
return {
|
|
356
|
+
config: assistConfigSchema.parse(merged),
|
|
357
|
+
configDir: dirname(configPath)
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function loadProjectConfig() {
|
|
361
|
+
return loadRawYaml(getConfigPath());
|
|
362
|
+
}
|
|
363
|
+
function loadGlobalConfigRaw() {
|
|
364
|
+
return loadRawYaml(getGlobalConfigPath());
|
|
365
|
+
}
|
|
366
|
+
function saveGlobalConfig(config) {
|
|
367
|
+
writeFileSync(getGlobalConfigPath(), stringifyYaml(config, { lineWidth: 0 }));
|
|
368
|
+
}
|
|
369
|
+
function saveConfig(config) {
|
|
370
|
+
const configPath = getConfigPath();
|
|
371
|
+
writeFileSync(configPath, stringifyYaml(config, { lineWidth: 0 }));
|
|
372
|
+
}
|
|
373
|
+
function getTranscriptConfig() {
|
|
374
|
+
const config = loadConfig();
|
|
375
|
+
if (!config.transcript) {
|
|
376
|
+
console.error(
|
|
377
|
+
chalk.red(
|
|
378
|
+
"Transcript directories not configured. Run 'assist transcript configure' first."
|
|
379
|
+
)
|
|
380
|
+
);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
return config.transcript;
|
|
384
|
+
}
|
|
122
385
|
|
|
123
386
|
// src/commands/backlog/deleteItemRelations.ts
|
|
124
387
|
function deleteItemRelations(db, itemId) {
|
|
@@ -139,8 +402,8 @@ function deleteItem(db, id) {
|
|
|
139
402
|
}
|
|
140
403
|
|
|
141
404
|
// src/commands/backlog/exportToJsonl.ts
|
|
142
|
-
import { statSync, writeFileSync } from "fs";
|
|
143
|
-
import { join } from "path";
|
|
405
|
+
import { statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
406
|
+
import { join as join2 } from "path";
|
|
144
407
|
|
|
145
408
|
// src/commands/backlog/loadComments.ts
|
|
146
409
|
function loadComments(db, itemId) {
|
|
@@ -210,13 +473,13 @@ function loadAllItems(db) {
|
|
|
210
473
|
|
|
211
474
|
// src/commands/backlog/exportToJsonl.ts
|
|
212
475
|
function getJsonlPath(dir) {
|
|
213
|
-
return
|
|
476
|
+
return join2(dir, ".assist", "backlog.jsonl");
|
|
214
477
|
}
|
|
215
478
|
function exportToJsonl(db, dir) {
|
|
216
479
|
const jsonlPath = getJsonlPath(dir);
|
|
217
480
|
const items = loadAllItems(db);
|
|
218
481
|
const lines = items.map((item) => JSON.stringify(item));
|
|
219
|
-
|
|
482
|
+
writeFileSync2(jsonlPath, lines.length > 0 ? `${lines.join("\n")}
|
|
220
483
|
` : "");
|
|
221
484
|
const mtimeMs = statSync(jsonlPath).mtimeMs;
|
|
222
485
|
db.prepare(
|
|
@@ -225,7 +488,7 @@ function exportToJsonl(db, dir) {
|
|
|
225
488
|
}
|
|
226
489
|
|
|
227
490
|
// src/commands/backlog/importFromJsonlIfNeeded.ts
|
|
228
|
-
import { readFileSync, statSync as statSync2 } from "fs";
|
|
491
|
+
import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
229
492
|
|
|
230
493
|
// src/commands/backlog/insertItemRelations.ts
|
|
231
494
|
function insertComments(db, item) {
|
|
@@ -332,43 +595,43 @@ function saveAllItems(db, items) {
|
|
|
332
595
|
}
|
|
333
596
|
|
|
334
597
|
// src/commands/backlog/types.ts
|
|
335
|
-
import { z } from "zod";
|
|
336
|
-
var backlogStatusSchema =
|
|
337
|
-
var backlogTypeSchema =
|
|
338
|
-
var planTaskSchema =
|
|
339
|
-
task:
|
|
598
|
+
import { z as z3 } from "zod";
|
|
599
|
+
var backlogStatusSchema = z3.enum(["todo", "in-progress", "done", "wontdo"]);
|
|
600
|
+
var backlogTypeSchema = z3.enum(["story", "bug"]);
|
|
601
|
+
var planTaskSchema = z3.object({
|
|
602
|
+
task: z3.string()
|
|
340
603
|
}).strip();
|
|
341
|
-
var planPhaseSchema =
|
|
342
|
-
name:
|
|
343
|
-
tasks:
|
|
344
|
-
manualChecks:
|
|
604
|
+
var planPhaseSchema = z3.strictObject({
|
|
605
|
+
name: z3.string(),
|
|
606
|
+
tasks: z3.array(planTaskSchema),
|
|
607
|
+
manualChecks: z3.array(z3.string()).optional()
|
|
345
608
|
});
|
|
346
|
-
var backlogCommentTypeSchema =
|
|
347
|
-
var backlogCommentSchema =
|
|
348
|
-
id:
|
|
349
|
-
text:
|
|
350
|
-
phase:
|
|
351
|
-
timestamp:
|
|
609
|
+
var backlogCommentTypeSchema = z3.enum(["comment", "summary"]);
|
|
610
|
+
var backlogCommentSchema = z3.strictObject({
|
|
611
|
+
id: z3.number().optional(),
|
|
612
|
+
text: z3.string(),
|
|
613
|
+
phase: z3.number().optional(),
|
|
614
|
+
timestamp: z3.string(),
|
|
352
615
|
type: backlogCommentTypeSchema
|
|
353
616
|
});
|
|
354
|
-
var backlogLinkTypeSchema =
|
|
355
|
-
var backlogLinkSchema =
|
|
617
|
+
var backlogLinkTypeSchema = z3.enum(["relates-to", "depends-on"]);
|
|
618
|
+
var backlogLinkSchema = z3.strictObject({
|
|
356
619
|
type: backlogLinkTypeSchema,
|
|
357
|
-
targetId:
|
|
620
|
+
targetId: z3.number()
|
|
358
621
|
});
|
|
359
|
-
var backlogItemSchema =
|
|
360
|
-
id:
|
|
622
|
+
var backlogItemSchema = z3.strictObject({
|
|
623
|
+
id: z3.number(),
|
|
361
624
|
type: backlogTypeSchema.default("story"),
|
|
362
|
-
name:
|
|
363
|
-
description:
|
|
364
|
-
acceptanceCriteria:
|
|
365
|
-
plan:
|
|
366
|
-
currentPhase:
|
|
625
|
+
name: z3.string(),
|
|
626
|
+
description: z3.string().optional(),
|
|
627
|
+
acceptanceCriteria: z3.array(z3.string()),
|
|
628
|
+
plan: z3.array(planPhaseSchema).optional(),
|
|
629
|
+
currentPhase: z3.number().optional(),
|
|
367
630
|
status: backlogStatusSchema,
|
|
368
|
-
comments:
|
|
369
|
-
links:
|
|
631
|
+
comments: z3.array(backlogCommentSchema).optional(),
|
|
632
|
+
links: z3.array(backlogLinkSchema).optional()
|
|
370
633
|
});
|
|
371
|
-
var backlogFileSchema =
|
|
634
|
+
var backlogFileSchema = z3.array(backlogItemSchema);
|
|
372
635
|
|
|
373
636
|
// src/commands/backlog/importFromJsonlIfNeeded.ts
|
|
374
637
|
function getLastImportMs(db) {
|
|
@@ -391,7 +654,7 @@ function importFromJsonlIfNeeded(db, dir) {
|
|
|
391
654
|
const fileMtimeMs = stat.mtimeMs;
|
|
392
655
|
const lastImportMs = getLastImportMs(db);
|
|
393
656
|
if (fileMtimeMs <= lastImportMs) return;
|
|
394
|
-
const content =
|
|
657
|
+
const content = readFileSync2(jsonlPath, "utf-8").trim();
|
|
395
658
|
if (content.length === 0) {
|
|
396
659
|
setLastImportMs(db, fileMtimeMs);
|
|
397
660
|
return;
|
|
@@ -405,14 +668,14 @@ function importFromJsonlIfNeeded(db, dir) {
|
|
|
405
668
|
}
|
|
406
669
|
|
|
407
670
|
// src/commands/backlog/migrateYamlIfNeeded.ts
|
|
408
|
-
import { existsSync, readFileSync as
|
|
409
|
-
import { parse as
|
|
671
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync } from "fs";
|
|
672
|
+
import { parse as parseYaml2 } from "yaml";
|
|
410
673
|
function migrateYamlIfNeeded(db, yamlPath) {
|
|
411
|
-
if (!
|
|
674
|
+
if (!existsSync3(yamlPath)) return false;
|
|
412
675
|
const existing = db.prepare("SELECT COUNT(*) as count FROM items").get();
|
|
413
676
|
if (existing.count > 0) return false;
|
|
414
|
-
const content =
|
|
415
|
-
const raw =
|
|
677
|
+
const content = readFileSync3(yamlPath, "utf-8");
|
|
678
|
+
const raw = parseYaml2(content) || [];
|
|
416
679
|
const items = backlogFileSchema.parse(raw);
|
|
417
680
|
if (items.length > 0) {
|
|
418
681
|
saveAllItems(db, items);
|
|
@@ -424,21 +687,21 @@ function migrateYamlIfNeeded(db, yamlPath) {
|
|
|
424
687
|
|
|
425
688
|
// src/commands/backlog/openDb.ts
|
|
426
689
|
import { mkdirSync } from "fs";
|
|
427
|
-
import { join as
|
|
690
|
+
import { join as join4 } from "path";
|
|
428
691
|
import Database from "better-sqlite3";
|
|
429
692
|
|
|
430
693
|
// src/commands/backlog/ensureGitignore.ts
|
|
431
|
-
import { existsSync as
|
|
432
|
-
import { join as
|
|
694
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
695
|
+
import { join as join3 } from "path";
|
|
433
696
|
var gitignoreEntries = [".assist-*", ".assist/*.db*"];
|
|
434
697
|
function ensureGitignore(dir) {
|
|
435
|
-
const gitignorePath =
|
|
436
|
-
const existing =
|
|
698
|
+
const gitignorePath = join3(dir, ".gitignore");
|
|
699
|
+
const existing = existsSync4(gitignorePath) ? readFileSync4(gitignorePath, "utf-8") : "";
|
|
437
700
|
const lines = existing.split(/\r?\n/);
|
|
438
701
|
const missing = gitignoreEntries.filter((entry) => !lines.includes(entry));
|
|
439
702
|
if (missing.length === 0) return;
|
|
440
703
|
const suffix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
441
|
-
|
|
704
|
+
writeFileSync3(gitignorePath, `${existing}${suffix}${missing.join("\n")}
|
|
442
705
|
`);
|
|
443
706
|
}
|
|
444
707
|
|
|
@@ -478,7 +741,7 @@ function migrateToOneBasedPhases(db) {
|
|
|
478
741
|
// src/commands/backlog/openDb.ts
|
|
479
742
|
var _db;
|
|
480
743
|
function getDbPath(dir) {
|
|
481
|
-
return
|
|
744
|
+
return join4(dir, ".assist", "backlog.db");
|
|
482
745
|
}
|
|
483
746
|
function initSchema(db) {
|
|
484
747
|
db.exec(`
|
|
@@ -535,7 +798,7 @@ function initSchema(db) {
|
|
|
535
798
|
function openDb(dir) {
|
|
536
799
|
if (_db) return _db;
|
|
537
800
|
const dbPath = getDbPath(dir);
|
|
538
|
-
mkdirSync(
|
|
801
|
+
mkdirSync(join4(dir, ".assist"), { recursive: true });
|
|
539
802
|
const db = new Database(dbPath);
|
|
540
803
|
db.pragma("journal_mode = WAL");
|
|
541
804
|
db.pragma("foreign_keys = ON");
|
|
@@ -584,14 +847,14 @@ function setBacklogDir(dir) {
|
|
|
584
847
|
_backlogDir = dir;
|
|
585
848
|
}
|
|
586
849
|
function getBacklogDir() {
|
|
587
|
-
return _backlogDir ??
|
|
850
|
+
return _backlogDir ?? getProjectRoot();
|
|
588
851
|
}
|
|
589
852
|
function getBacklogPath() {
|
|
590
|
-
return
|
|
853
|
+
return join5(getBacklogDir(), "assist.backlog.yml");
|
|
591
854
|
}
|
|
592
855
|
function backlogExists() {
|
|
593
856
|
const dir = getBacklogDir();
|
|
594
|
-
return
|
|
857
|
+
return existsSync5(join5(dir, ".assist", "backlog.db")) || existsSync5(join5(dir, ".assist", "backlog.jsonl")) || existsSync5(join5(dir, "assist.backlog.yml"));
|
|
595
858
|
}
|
|
596
859
|
function getDb() {
|
|
597
860
|
const dir = getBacklogDir();
|
|
@@ -624,7 +887,7 @@ function loadAndFindItem(id) {
|
|
|
624
887
|
const items = loadBacklog();
|
|
625
888
|
const item = findItem(items, Number.parseInt(id, 10));
|
|
626
889
|
if (!item) {
|
|
627
|
-
console.log(
|
|
890
|
+
console.log(chalk2.red(`Item #${id} not found.`));
|
|
628
891
|
return void 0;
|
|
629
892
|
}
|
|
630
893
|
return { items, item };
|
|
@@ -652,14 +915,10 @@ function removeItem(id) {
|
|
|
652
915
|
exportToJsonl(db, getBacklogDir());
|
|
653
916
|
return result.item.name;
|
|
654
917
|
}
|
|
655
|
-
function getNextId(items) {
|
|
656
|
-
if (items.length === 0) return 1;
|
|
657
|
-
return Math.max(...items.map((item) => item.id)) + 1;
|
|
658
|
-
}
|
|
659
918
|
|
|
660
919
|
// src/commands/backlog/acquireLock.ts
|
|
661
920
|
function getLockPath(itemId) {
|
|
662
|
-
return
|
|
921
|
+
return join6(getBacklogDir(), `.assist-lock-${itemId}.json`);
|
|
663
922
|
}
|
|
664
923
|
function isProcessAlive(pid) {
|
|
665
924
|
try {
|
|
@@ -671,9 +930,9 @@ function isProcessAlive(pid) {
|
|
|
671
930
|
}
|
|
672
931
|
function isLockedByOther(itemId) {
|
|
673
932
|
const lockPath = getLockPath(itemId);
|
|
674
|
-
if (!
|
|
933
|
+
if (!existsSync6(lockPath)) return false;
|
|
675
934
|
try {
|
|
676
|
-
const lock = JSON.parse(
|
|
935
|
+
const lock = JSON.parse(readFileSync5(lockPath, "utf-8"));
|
|
677
936
|
if (lock.pid === process.pid) return false;
|
|
678
937
|
return isProcessAlive(lock.pid);
|
|
679
938
|
} catch {
|
|
@@ -681,7 +940,7 @@ function isLockedByOther(itemId) {
|
|
|
681
940
|
}
|
|
682
941
|
}
|
|
683
942
|
function acquireLock(itemId) {
|
|
684
|
-
|
|
943
|
+
writeFileSync4(
|
|
685
944
|
getLockPath(itemId),
|
|
686
945
|
JSON.stringify({ pid: process.pid, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
687
946
|
);
|
|
@@ -695,30 +954,30 @@ function releaseLock(itemId) {
|
|
|
695
954
|
}
|
|
696
955
|
|
|
697
956
|
// src/commands/backlog/list/shared.ts
|
|
698
|
-
import
|
|
957
|
+
import chalk3 from "chalk";
|
|
699
958
|
function statusIcon(status2) {
|
|
700
959
|
switch (status2) {
|
|
701
960
|
case "todo":
|
|
702
|
-
return
|
|
961
|
+
return chalk3.dim("[ ]");
|
|
703
962
|
case "in-progress":
|
|
704
|
-
return
|
|
963
|
+
return chalk3.yellow("[~]");
|
|
705
964
|
case "done":
|
|
706
|
-
return
|
|
965
|
+
return chalk3.green("[x]");
|
|
707
966
|
case "wontdo":
|
|
708
|
-
return
|
|
967
|
+
return chalk3.dim("[-]");
|
|
709
968
|
}
|
|
710
969
|
}
|
|
711
970
|
function typeLabel(type) {
|
|
712
971
|
switch (type) {
|
|
713
972
|
case "bug":
|
|
714
|
-
return
|
|
973
|
+
return chalk3.magenta("Bug");
|
|
715
974
|
case "story":
|
|
716
|
-
return
|
|
975
|
+
return chalk3.cyan("Story");
|
|
717
976
|
}
|
|
718
977
|
}
|
|
719
978
|
function phaseLabel(item) {
|
|
720
979
|
if (!item.plan) return "";
|
|
721
|
-
return
|
|
980
|
+
return chalk3.dim(` (phase ${item.currentPhase ?? 1}/${item.plan.length})`);
|
|
722
981
|
}
|
|
723
982
|
function isBlocked(item, items) {
|
|
724
983
|
const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
|
|
@@ -730,15 +989,15 @@ function isBlocked(item, items) {
|
|
|
730
989
|
function dependencyLabel(item, items) {
|
|
731
990
|
const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
|
|
732
991
|
if (deps2.length === 0) return "";
|
|
733
|
-
if (isBlocked(item, items)) return
|
|
734
|
-
return
|
|
992
|
+
if (isBlocked(item, items)) return chalk3.red(" [blocked]");
|
|
993
|
+
return chalk3.dim(` [${deps2.length} dep${deps2.length > 1 ? "s" : ""}]`);
|
|
735
994
|
}
|
|
736
995
|
function printVerboseDetails(item) {
|
|
737
996
|
if (item.description) {
|
|
738
|
-
console.log(` ${
|
|
997
|
+
console.log(` ${chalk3.dim("Description:")} ${item.description}`);
|
|
739
998
|
}
|
|
740
999
|
if (item.acceptanceCriteria.length > 0) {
|
|
741
|
-
console.log(` ${
|
|
1000
|
+
console.log(` ${chalk3.dim("Acceptance criteria:")}`);
|
|
742
1001
|
for (const [i, criterion] of item.acceptanceCriteria.entries()) {
|
|
743
1002
|
console.log(` ${i + 1}. ${criterion}`);
|
|
744
1003
|
}
|
|
@@ -754,17 +1013,17 @@ function findResumable(items) {
|
|
|
754
1013
|
}
|
|
755
1014
|
|
|
756
1015
|
// src/commands/backlog/findUnblockedTodos.ts
|
|
757
|
-
import
|
|
1016
|
+
import chalk4 from "chalk";
|
|
758
1017
|
function findUnblockedTodos(items) {
|
|
759
1018
|
const todo = items.filter((i) => i.status === "todo");
|
|
760
1019
|
if (todo.length === 0) {
|
|
761
|
-
console.log(
|
|
1020
|
+
console.log(chalk4.green("All backlog items complete."));
|
|
762
1021
|
return void 0;
|
|
763
1022
|
}
|
|
764
1023
|
const unblocked = todo.filter((i) => !isBlocked(i, items));
|
|
765
1024
|
if (unblocked.length === 0) {
|
|
766
1025
|
console.log(
|
|
767
|
-
|
|
1026
|
+
chalk4.yellow("All remaining todo items are blocked by dependencies.")
|
|
768
1027
|
);
|
|
769
1028
|
return void 0;
|
|
770
1029
|
}
|
|
@@ -772,7 +1031,7 @@ function findUnblockedTodos(items) {
|
|
|
772
1031
|
}
|
|
773
1032
|
|
|
774
1033
|
// src/commands/backlog/run.ts
|
|
775
|
-
import
|
|
1034
|
+
import chalk8 from "chalk";
|
|
776
1035
|
|
|
777
1036
|
// src/commands/backlog/buildCommentLines.ts
|
|
778
1037
|
function buildCommentLines(comments2) {
|
|
@@ -891,11 +1150,11 @@ function buildReviewPhase() {
|
|
|
891
1150
|
}
|
|
892
1151
|
|
|
893
1152
|
// src/commands/backlog/executePhase.ts
|
|
894
|
-
import
|
|
1153
|
+
import chalk6 from "chalk";
|
|
895
1154
|
|
|
896
1155
|
// src/commands/backlog/resolvePhaseResult.ts
|
|
897
|
-
import { existsSync as
|
|
898
|
-
import
|
|
1156
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
1157
|
+
import chalk5 from "chalk";
|
|
899
1158
|
|
|
900
1159
|
// src/commands/backlog/handleIncompletePhase.ts
|
|
901
1160
|
import enquirer from "enquirer";
|
|
@@ -914,28 +1173,28 @@ async function handleIncompletePhase() {
|
|
|
914
1173
|
}
|
|
915
1174
|
|
|
916
1175
|
// src/commands/backlog/readSignal.ts
|
|
917
|
-
import { existsSync as
|
|
1176
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
918
1177
|
|
|
919
1178
|
// src/commands/backlog/writeSignal.ts
|
|
920
|
-
import { writeFileSync as
|
|
921
|
-
import { join as
|
|
1179
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
1180
|
+
import { join as join7 } from "path";
|
|
922
1181
|
function getSignalPath() {
|
|
923
1182
|
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
924
1183
|
const filename = sessionId ? `.assist-signal-${sessionId}.json` : ".assist-signal.json";
|
|
925
|
-
return
|
|
1184
|
+
return join7(getBacklogDir(), filename);
|
|
926
1185
|
}
|
|
927
1186
|
function writeSignal(event, data) {
|
|
928
1187
|
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
929
1188
|
const signal = { event, ...sessionId && { sessionId }, ...data };
|
|
930
|
-
|
|
1189
|
+
writeFileSync5(getSignalPath(), JSON.stringify(signal));
|
|
931
1190
|
}
|
|
932
1191
|
|
|
933
1192
|
// src/commands/backlog/readSignal.ts
|
|
934
1193
|
function readSignal() {
|
|
935
1194
|
const path53 = getSignalPath();
|
|
936
|
-
if (!
|
|
1195
|
+
if (!existsSync7(path53)) return void 0;
|
|
937
1196
|
try {
|
|
938
|
-
return JSON.parse(
|
|
1197
|
+
return JSON.parse(readFileSync6(path53, "utf-8"));
|
|
939
1198
|
} catch {
|
|
940
1199
|
return void 0;
|
|
941
1200
|
}
|
|
@@ -944,7 +1203,7 @@ function readSignal() {
|
|
|
944
1203
|
// src/commands/backlog/resolvePhaseResult.ts
|
|
945
1204
|
function cleanupSignal() {
|
|
946
1205
|
const statusPath = getSignalPath();
|
|
947
|
-
if (
|
|
1206
|
+
if (existsSync8(statusPath)) {
|
|
948
1207
|
unlinkSync2(statusPath);
|
|
949
1208
|
}
|
|
950
1209
|
}
|
|
@@ -954,7 +1213,7 @@ function isTerminalStatus(itemId) {
|
|
|
954
1213
|
return item?.status === "done" || item?.status === "wontdo";
|
|
955
1214
|
}
|
|
956
1215
|
async function resolvePhaseResult(phaseIndex, itemId) {
|
|
957
|
-
if (!
|
|
1216
|
+
if (!existsSync8(getSignalPath())) {
|
|
958
1217
|
if (isTerminalStatus(itemId)) return -1;
|
|
959
1218
|
const action = await handleIncompletePhase();
|
|
960
1219
|
if (action === "abort") return -1;
|
|
@@ -965,12 +1224,12 @@ async function resolvePhaseResult(phaseIndex, itemId) {
|
|
|
965
1224
|
if (signal?.event === "rewind") {
|
|
966
1225
|
const targetPhase = signal.targetPhase;
|
|
967
1226
|
const targetPhaseNumber = targetPhase + 1;
|
|
968
|
-
console.log(
|
|
1227
|
+
console.log(chalk5.yellow(`
|
|
969
1228
|
Rewinding to phase ${targetPhaseNumber}.`));
|
|
970
1229
|
return targetPhase;
|
|
971
1230
|
}
|
|
972
1231
|
const phaseNumber = phaseIndex + 1;
|
|
973
|
-
console.log(
|
|
1232
|
+
console.log(chalk5.green(`
|
|
974
1233
|
Phase ${phaseNumber} completed.`));
|
|
975
1234
|
return phaseIndex + 1;
|
|
976
1235
|
}
|
|
@@ -993,11 +1252,11 @@ function spawnClaude(prompt, options2 = {}) {
|
|
|
993
1252
|
}
|
|
994
1253
|
|
|
995
1254
|
// src/commands/backlog/watchForMarker.ts
|
|
996
|
-
import { existsSync as
|
|
1255
|
+
import { existsSync as existsSync9, unwatchFile, watchFile } from "fs";
|
|
997
1256
|
function watchForMarker(child) {
|
|
998
1257
|
const statusPath = getSignalPath();
|
|
999
1258
|
watchFile(statusPath, { interval: 1e3 }, () => {
|
|
1000
|
-
if (!
|
|
1259
|
+
if (!existsSync9(statusPath)) return;
|
|
1001
1260
|
const signal = readSignal();
|
|
1002
1261
|
if (signal) {
|
|
1003
1262
|
unwatchFile(statusPath);
|
|
@@ -1014,7 +1273,7 @@ async function executePhase(item, phaseIndex, phases, spawnOptions) {
|
|
|
1014
1273
|
const phase = phases[phaseIndex];
|
|
1015
1274
|
const phaseNumber = phaseIndex + 1;
|
|
1016
1275
|
console.log(
|
|
1017
|
-
|
|
1276
|
+
chalk6.bold(
|
|
1018
1277
|
`
|
|
1019
1278
|
--- Phase ${phaseNumber}/${phases.length}: ${phase.name} ---
|
|
1020
1279
|
`
|
|
@@ -1032,7 +1291,7 @@ async function executePhase(item, phaseIndex, phases, spawnOptions) {
|
|
|
1032
1291
|
}
|
|
1033
1292
|
|
|
1034
1293
|
// src/commands/backlog/prepareRun.ts
|
|
1035
|
-
import
|
|
1294
|
+
import chalk7 from "chalk";
|
|
1036
1295
|
|
|
1037
1296
|
// src/commands/backlog/resolvePlan.ts
|
|
1038
1297
|
function resolvePlan(item) {
|
|
@@ -1055,13 +1314,13 @@ function prepareRun(id) {
|
|
|
1055
1314
|
const plan2 = resolvePlan(item);
|
|
1056
1315
|
const startPhase = (item.currentPhase ?? 1) - 1;
|
|
1057
1316
|
if (item.status === "done") {
|
|
1058
|
-
console.log(
|
|
1317
|
+
console.log(chalk7.green(`Already done: #${id}: ${item.name}`));
|
|
1059
1318
|
return void 0;
|
|
1060
1319
|
}
|
|
1061
1320
|
if (startPhase > plan2.length) {
|
|
1062
1321
|
setStatus(id, "done");
|
|
1063
1322
|
console.log(
|
|
1064
|
-
|
|
1323
|
+
chalk7.green(`All phases already complete for #${id}: ${item.name}`)
|
|
1065
1324
|
);
|
|
1066
1325
|
return void 0;
|
|
1067
1326
|
}
|
|
@@ -1086,13 +1345,13 @@ async function run(id, spawnOptions) {
|
|
|
1086
1345
|
}
|
|
1087
1346
|
}
|
|
1088
1347
|
function logProgress(id, name, startPhase, total) {
|
|
1089
|
-
console.log(
|
|
1348
|
+
console.log(chalk8.bold(`Running plan for #${id}: ${name}`));
|
|
1090
1349
|
if (startPhase > 0) {
|
|
1091
1350
|
const phaseNumber = startPhase + 1;
|
|
1092
|
-
console.log(
|
|
1351
|
+
console.log(chalk8.dim(`Resuming from phase ${phaseNumber}/${total}
|
|
1093
1352
|
`));
|
|
1094
1353
|
} else {
|
|
1095
|
-
console.log(
|
|
1354
|
+
console.log(chalk8.dim(`${total} phase(s)
|
|
1096
1355
|
`));
|
|
1097
1356
|
}
|
|
1098
1357
|
}
|
|
@@ -1125,7 +1384,7 @@ async function runReview(item, plan2, spawnOptions) {
|
|
|
1125
1384
|
// src/commands/backlog/next.ts
|
|
1126
1385
|
function toChoice(item, items) {
|
|
1127
1386
|
const name = `${typeLabel(item.type)} #${item.id}: ${item.name}`;
|
|
1128
|
-
return isBlocked(item, items) ? { name, disabled:
|
|
1387
|
+
return isBlocked(item, items) ? { name, disabled: chalk9.red("[blocked]") } : { name };
|
|
1129
1388
|
}
|
|
1130
1389
|
async function selectItem(todo, items) {
|
|
1131
1390
|
const { selected } = await exitOnCancel(
|
|
@@ -1142,7 +1401,7 @@ async function pickItem(items, firstPick = false) {
|
|
|
1142
1401
|
const resumable = findResumable(items);
|
|
1143
1402
|
if (resumable) {
|
|
1144
1403
|
console.log(
|
|
1145
|
-
|
|
1404
|
+
chalk9.bold(
|
|
1146
1405
|
`Resuming in-progress item #${resumable.id}: ${resumable.name}`
|
|
1147
1406
|
)
|
|
1148
1407
|
);
|
|
@@ -1152,7 +1411,7 @@ async function pickItem(items, firstPick = false) {
|
|
|
1152
1411
|
if (!unblocked) return void 0;
|
|
1153
1412
|
if (firstPick && unblocked.length === 1) {
|
|
1154
1413
|
const item = unblocked[0];
|
|
1155
|
-
console.log(
|
|
1414
|
+
console.log(chalk9.bold(`Auto-selecting item #${item.id}: ${item.name}`));
|
|
1156
1415
|
return String(item.id);
|
|
1157
1416
|
}
|
|
1158
1417
|
const todo = items.filter((i) => i.status === "todo");
|
|
@@ -1170,7 +1429,7 @@ async function next(options2) {
|
|
|
1170
1429
|
}
|
|
1171
1430
|
|
|
1172
1431
|
// src/commands/backlog/phaseDone.ts
|
|
1173
|
-
import
|
|
1432
|
+
import chalk10 from "chalk";
|
|
1174
1433
|
|
|
1175
1434
|
// src/commands/backlog/addComment.ts
|
|
1176
1435
|
function addComment(item, text, phase) {
|
|
@@ -1205,7 +1464,7 @@ function phaseDone(id, phase, summary) {
|
|
|
1205
1464
|
});
|
|
1206
1465
|
const result = loadAndFindItem(id);
|
|
1207
1466
|
if (result?.item.status === "done") {
|
|
1208
|
-
console.log(
|
|
1467
|
+
console.log(chalk10.dim(`Item #${id} already done, skipping phase advance.`));
|
|
1209
1468
|
return;
|
|
1210
1469
|
}
|
|
1211
1470
|
if (result) {
|
|
@@ -1214,24 +1473,24 @@ function phaseDone(id, phase, summary) {
|
|
|
1214
1473
|
}
|
|
1215
1474
|
setCurrentPhase(id, phaseNumber + 1);
|
|
1216
1475
|
console.log(
|
|
1217
|
-
|
|
1476
|
+
chalk10.green(`Phase ${phaseNumber} of item #${id} marked as complete.`)
|
|
1218
1477
|
);
|
|
1219
1478
|
}
|
|
1220
1479
|
|
|
1221
1480
|
// src/commands/backlog/plan.ts
|
|
1222
|
-
import
|
|
1481
|
+
import chalk11 from "chalk";
|
|
1223
1482
|
function plan(id) {
|
|
1224
1483
|
const result = loadAndFindItem(id);
|
|
1225
1484
|
if (!result) return;
|
|
1226
1485
|
const { item } = result;
|
|
1227
1486
|
if (!item.plan || item.plan.length === 0) {
|
|
1228
|
-
console.log(
|
|
1487
|
+
console.log(chalk11.dim("No plan defined for this item."));
|
|
1229
1488
|
return;
|
|
1230
1489
|
}
|
|
1231
|
-
console.log(
|
|
1490
|
+
console.log(chalk11.bold(item.name));
|
|
1232
1491
|
console.log();
|
|
1233
1492
|
for (const [i, phase] of item.plan.entries()) {
|
|
1234
|
-
console.log(`${
|
|
1493
|
+
console.log(`${chalk11.bold(`Phase ${i + 1}:`)} ${phase.name}`);
|
|
1235
1494
|
for (const task of phase.tasks) {
|
|
1236
1495
|
console.log(` - ${task.task}`);
|
|
1237
1496
|
}
|
|
@@ -1240,35 +1499,35 @@ function plan(id) {
|
|
|
1240
1499
|
}
|
|
1241
1500
|
|
|
1242
1501
|
// src/commands/backlog/show/index.ts
|
|
1243
|
-
import
|
|
1502
|
+
import chalk15 from "chalk";
|
|
1244
1503
|
|
|
1245
1504
|
// src/commands/backlog/formatComment.ts
|
|
1246
|
-
import
|
|
1505
|
+
import chalk12 from "chalk";
|
|
1247
1506
|
function formatComment(entry) {
|
|
1248
|
-
const id = entry.id !== void 0 ?
|
|
1249
|
-
const tag = entry.type === "summary" ?
|
|
1250
|
-
const phase = entry.phase !== void 0 ?
|
|
1251
|
-
const time =
|
|
1507
|
+
const id = entry.id !== void 0 ? chalk12.dim(`#${entry.id} `) : "";
|
|
1508
|
+
const tag = entry.type === "summary" ? chalk12.magenta("[summary]") : chalk12.cyan("[comment]");
|
|
1509
|
+
const phase = entry.phase !== void 0 ? chalk12.dim(` (phase ${entry.phase})`) : "";
|
|
1510
|
+
const time = chalk12.dim(entry.timestamp);
|
|
1252
1511
|
return `${id}${tag}${phase} ${time}
|
|
1253
1512
|
${entry.text}`;
|
|
1254
1513
|
}
|
|
1255
1514
|
|
|
1256
1515
|
// src/commands/backlog/show/printLinks.ts
|
|
1257
|
-
import
|
|
1516
|
+
import chalk13 from "chalk";
|
|
1258
1517
|
function printLinks(item, items) {
|
|
1259
1518
|
const links = item.links ?? [];
|
|
1260
1519
|
if (links.length === 0) return;
|
|
1261
|
-
console.log(
|
|
1520
|
+
console.log(chalk13.bold("Links"));
|
|
1262
1521
|
for (const link3 of links) {
|
|
1263
1522
|
const target = items.find((i) => i.id === link3.targetId);
|
|
1264
|
-
const typeLabel2 = link3.type === "depends-on" ?
|
|
1523
|
+
const typeLabel2 = link3.type === "depends-on" ? chalk13.red("depends-on") : chalk13.blue("relates-to");
|
|
1265
1524
|
if (target) {
|
|
1266
1525
|
console.log(
|
|
1267
|
-
` ${typeLabel2} #${target.id} ${target.name} ${
|
|
1526
|
+
` ${typeLabel2} #${target.id} ${target.name} ${chalk13.dim(`(${target.status})`)}`
|
|
1268
1527
|
);
|
|
1269
1528
|
} else {
|
|
1270
1529
|
console.log(
|
|
1271
|
-
` ${typeLabel2} #${link3.targetId} ${
|
|
1530
|
+
` ${typeLabel2} #${link3.targetId} ${chalk13.dim("(not found)")}`
|
|
1272
1531
|
);
|
|
1273
1532
|
}
|
|
1274
1533
|
}
|
|
@@ -1276,15 +1535,15 @@ function printLinks(item, items) {
|
|
|
1276
1535
|
}
|
|
1277
1536
|
|
|
1278
1537
|
// src/commands/backlog/show/printPhaseTasks.ts
|
|
1279
|
-
import
|
|
1538
|
+
import chalk14 from "chalk";
|
|
1280
1539
|
function printPhaseTasks(phase) {
|
|
1281
1540
|
for (const task of phase.tasks) {
|
|
1282
1541
|
console.log(` - ${task.task}`);
|
|
1283
1542
|
}
|
|
1284
1543
|
if (phase.manualChecks && phase.manualChecks.length > 0) {
|
|
1285
|
-
console.log(` ${
|
|
1544
|
+
console.log(` ${chalk14.dim("Manual checks:")}`);
|
|
1286
1545
|
for (const check2 of phase.manualChecks) {
|
|
1287
|
-
console.log(` ${
|
|
1546
|
+
console.log(` ${chalk14.dim(`- ${check2}`)}`);
|
|
1288
1547
|
}
|
|
1289
1548
|
}
|
|
1290
1549
|
}
|
|
@@ -1292,7 +1551,7 @@ function printPhaseTasks(phase) {
|
|
|
1292
1551
|
// src/commands/backlog/show/index.ts
|
|
1293
1552
|
function printPlan(item) {
|
|
1294
1553
|
if (!item.plan || item.plan.length === 0) return;
|
|
1295
|
-
console.log(
|
|
1554
|
+
console.log(chalk15.bold("Plan"));
|
|
1296
1555
|
for (const [i, phase] of item.plan.entries()) {
|
|
1297
1556
|
const isCurrent = item.currentPhase === i + 1;
|
|
1298
1557
|
printPhase(phase, i, isCurrent);
|
|
@@ -1301,8 +1560,8 @@ function printPlan(item) {
|
|
|
1301
1560
|
}
|
|
1302
1561
|
function phaseHeader(index, name, isCurrent) {
|
|
1303
1562
|
const phaseNumber = index + 1;
|
|
1304
|
-
const marker = isCurrent ?
|
|
1305
|
-
const label2 = isCurrent ?
|
|
1563
|
+
const marker = isCurrent ? chalk15.green("\u25B6 ") : " ";
|
|
1564
|
+
const label2 = isCurrent ? chalk15.green.bold(`Phase ${phaseNumber}: ${name}`) : `${chalk15.bold(`Phase ${phaseNumber}:`)} ${name}`;
|
|
1306
1565
|
return `${marker}${label2}`;
|
|
1307
1566
|
}
|
|
1308
1567
|
function printPhase(phase, index, isCurrent) {
|
|
@@ -1310,15 +1569,15 @@ function printPhase(phase, index, isCurrent) {
|
|
|
1310
1569
|
printPhaseTasks(phase);
|
|
1311
1570
|
}
|
|
1312
1571
|
function printHeader(item) {
|
|
1313
|
-
console.log(
|
|
1572
|
+
console.log(chalk15.bold(`#${item.id} ${item.name}`));
|
|
1314
1573
|
console.log(
|
|
1315
|
-
`${
|
|
1574
|
+
`${chalk15.dim("Type:")} ${item.type} ${chalk15.dim("Status:")} ${item.status}`
|
|
1316
1575
|
);
|
|
1317
1576
|
console.log();
|
|
1318
1577
|
}
|
|
1319
1578
|
function printAcceptanceCriteria(criteria) {
|
|
1320
1579
|
if (criteria.length === 0) return;
|
|
1321
|
-
console.log(
|
|
1580
|
+
console.log(chalk15.bold("Acceptance Criteria"));
|
|
1322
1581
|
for (const [i, ac] of criteria.entries()) {
|
|
1323
1582
|
console.log(` ${i + 1}. ${ac}`);
|
|
1324
1583
|
}
|
|
@@ -1330,7 +1589,7 @@ function show(id) {
|
|
|
1330
1589
|
const { item, items } = result;
|
|
1331
1590
|
printHeader(item);
|
|
1332
1591
|
if (item.description) {
|
|
1333
|
-
console.log(
|
|
1592
|
+
console.log(chalk15.bold("Description"));
|
|
1334
1593
|
console.log(item.description);
|
|
1335
1594
|
console.log();
|
|
1336
1595
|
}
|
|
@@ -1342,7 +1601,7 @@ function show(id) {
|
|
|
1342
1601
|
function printComments(item) {
|
|
1343
1602
|
const entries = item.comments ?? [];
|
|
1344
1603
|
if (entries.length === 0) return;
|
|
1345
|
-
console.log(
|
|
1604
|
+
console.log(chalk15.bold("Comments"));
|
|
1346
1605
|
for (const entry of entries) {
|
|
1347
1606
|
console.log(` ${formatComment(entry)}`);
|
|
1348
1607
|
}
|
|
@@ -1356,7 +1615,7 @@ import { WebSocketServer } from "ws";
|
|
|
1356
1615
|
import {
|
|
1357
1616
|
createServer
|
|
1358
1617
|
} from "http";
|
|
1359
|
-
import
|
|
1618
|
+
import chalk16 from "chalk";
|
|
1360
1619
|
|
|
1361
1620
|
// src/shared/openBrowser.ts
|
|
1362
1621
|
import { exec } from "child_process";
|
|
@@ -1402,27 +1661,27 @@ function startWebServer(label2, port, handler, initialPath) {
|
|
|
1402
1661
|
handler(req, res, port);
|
|
1403
1662
|
});
|
|
1404
1663
|
server.listen(port, () => {
|
|
1405
|
-
console.log(
|
|
1406
|
-
console.log(
|
|
1664
|
+
console.log(chalk16.green(`${label2}: ${url}`));
|
|
1665
|
+
console.log(chalk16.dim("Press Ctrl+C to stop"));
|
|
1407
1666
|
openBrowser(url);
|
|
1408
1667
|
});
|
|
1409
1668
|
return server;
|
|
1410
1669
|
}
|
|
1411
1670
|
|
|
1412
1671
|
// src/commands/sessions/web/handleRequest.ts
|
|
1413
|
-
import { readFileSync as
|
|
1672
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
1414
1673
|
import { createRequire } from "module";
|
|
1415
1674
|
|
|
1416
1675
|
// src/shared/createBundleHandler.ts
|
|
1417
|
-
import { readFileSync as
|
|
1418
|
-
import { dirname, join as
|
|
1676
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
1677
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
1419
1678
|
import { fileURLToPath } from "url";
|
|
1420
1679
|
function createBundleHandler(importMetaUrl, bundlePath) {
|
|
1421
|
-
const dir =
|
|
1680
|
+
const dir = dirname2(fileURLToPath(importMetaUrl));
|
|
1422
1681
|
let cache;
|
|
1423
1682
|
return (_req, res) => {
|
|
1424
1683
|
if (!cache) {
|
|
1425
|
-
cache =
|
|
1684
|
+
cache = readFileSync7(join8(dir, bundlePath), "utf-8");
|
|
1426
1685
|
}
|
|
1427
1686
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
1428
1687
|
res.end(cache);
|
|
@@ -1507,6 +1766,12 @@ function validateRewind(item, phase) {
|
|
|
1507
1766
|
return void 0;
|
|
1508
1767
|
}
|
|
1509
1768
|
|
|
1769
|
+
// src/commands/backlog/getNextId.ts
|
|
1770
|
+
function getNextId(items) {
|
|
1771
|
+
if (items.length === 0) return 1;
|
|
1772
|
+
return Math.max(...items.map((item) => item.id)) + 1;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1510
1775
|
// src/commands/backlog/web/shared.ts
|
|
1511
1776
|
function listItems(req, res) {
|
|
1512
1777
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
@@ -1626,7 +1891,7 @@ function createCssHandler(packageEntry) {
|
|
|
1626
1891
|
return (_req, res) => {
|
|
1627
1892
|
if (!cache) {
|
|
1628
1893
|
const resolved = require2.resolve(packageEntry);
|
|
1629
|
-
cache =
|
|
1894
|
+
cache = readFileSync8(resolved, "utf-8");
|
|
1630
1895
|
}
|
|
1631
1896
|
res.writeHead(200, { "Content-Type": "text/css" });
|
|
1632
1897
|
res.end(cache);
|
|
@@ -1649,264 +1914,6 @@ var handleRequest = createFallbackHandler(
|
|
|
1649
1914
|
handleItemRoute
|
|
1650
1915
|
);
|
|
1651
1916
|
|
|
1652
|
-
// src/shared/loadConfig.ts
|
|
1653
|
-
import { existsSync as existsSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
1654
|
-
import { homedir } from "os";
|
|
1655
|
-
import { dirname as dirname2, join as join8 } from "path";
|
|
1656
|
-
import chalk16 from "chalk";
|
|
1657
|
-
import { stringify as stringifyYaml } from "yaml";
|
|
1658
|
-
|
|
1659
|
-
// src/shared/loadRawYaml.ts
|
|
1660
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
1661
|
-
import { parse as parseYaml2 } from "yaml";
|
|
1662
|
-
function loadRawYaml(path53) {
|
|
1663
|
-
if (!existsSync8(path53)) return {};
|
|
1664
|
-
try {
|
|
1665
|
-
const content = readFileSync8(path53, "utf-8");
|
|
1666
|
-
return parseYaml2(content) || {};
|
|
1667
|
-
} catch {
|
|
1668
|
-
return {};
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
// src/shared/mergeDenyRules.ts
|
|
1673
|
-
function mergeDenyRules(globalDeny, projectDeny) {
|
|
1674
|
-
if (!globalDeny && !projectDeny) return void 0;
|
|
1675
|
-
if (!globalDeny) return projectDeny;
|
|
1676
|
-
if (!projectDeny) return globalDeny;
|
|
1677
|
-
const projectPatterns = new Set(projectDeny.map((r) => r.pattern));
|
|
1678
|
-
const globalOnly = globalDeny.filter((r) => !projectPatterns.has(r.pattern));
|
|
1679
|
-
return [...globalOnly, ...projectDeny];
|
|
1680
|
-
}
|
|
1681
|
-
function mergeRawConfigs(globalRaw, projectRaw) {
|
|
1682
|
-
const deny = mergeDenyRules(
|
|
1683
|
-
globalRaw.deny,
|
|
1684
|
-
projectRaw.deny
|
|
1685
|
-
);
|
|
1686
|
-
const merged = { ...globalRaw, ...projectRaw };
|
|
1687
|
-
if (deny !== void 0) {
|
|
1688
|
-
merged.deny = deny;
|
|
1689
|
-
}
|
|
1690
|
-
return merged;
|
|
1691
|
-
}
|
|
1692
|
-
|
|
1693
|
-
// src/shared/types.ts
|
|
1694
|
-
import { z as z3 } from "zod";
|
|
1695
|
-
|
|
1696
|
-
// src/shared/runConfigSchema.ts
|
|
1697
|
-
import { z as z2 } from "zod";
|
|
1698
|
-
var runParamSchema = z2.strictObject({
|
|
1699
|
-
name: z2.string(),
|
|
1700
|
-
required: z2.boolean().optional(),
|
|
1701
|
-
default: z2.string().optional(),
|
|
1702
|
-
description: z2.string().optional()
|
|
1703
|
-
});
|
|
1704
|
-
var runConfigSchema = z2.strictObject({
|
|
1705
|
-
name: z2.string(),
|
|
1706
|
-
command: z2.string(),
|
|
1707
|
-
args: z2.array(z2.string()).optional(),
|
|
1708
|
-
params: z2.array(runParamSchema).optional(),
|
|
1709
|
-
env: z2.record(z2.string(), z2.string()).optional(),
|
|
1710
|
-
filter: z2.string().optional(),
|
|
1711
|
-
pre: z2.array(z2.string()).optional(),
|
|
1712
|
-
cwd: z2.string().optional()
|
|
1713
|
-
});
|
|
1714
|
-
var runLinkSchema = z2.strictObject({
|
|
1715
|
-
link: z2.string(),
|
|
1716
|
-
prefix: z2.string()
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
// src/shared/types.ts
|
|
1720
|
-
var transcriptConfigSchema = z3.strictObject({
|
|
1721
|
-
vttDir: z3.string(),
|
|
1722
|
-
transcriptsDir: z3.string(),
|
|
1723
|
-
summaryDir: z3.string()
|
|
1724
|
-
});
|
|
1725
|
-
var DEFAULT_WAKE_WORDS = ["computer"];
|
|
1726
|
-
var DEFAULT_MODELS_DIR = "~/.assist/voice/models";
|
|
1727
|
-
var assistConfigSchema = z3.strictObject({
|
|
1728
|
-
commit: z3.strictObject({
|
|
1729
|
-
conventional: z3.boolean().default(false),
|
|
1730
|
-
pull: z3.boolean().default(false),
|
|
1731
|
-
push: z3.boolean().default(false)
|
|
1732
|
-
}).default({ conventional: false, pull: false, push: false }),
|
|
1733
|
-
devlog: z3.strictObject({
|
|
1734
|
-
name: z3.string().optional(),
|
|
1735
|
-
ignore: z3.array(z3.string()).optional(),
|
|
1736
|
-
skip: z3.record(z3.string(), z3.array(z3.string())).optional()
|
|
1737
|
-
}).optional(),
|
|
1738
|
-
notify: z3.strictObject({
|
|
1739
|
-
enabled: z3.boolean().default(true)
|
|
1740
|
-
}).default({ enabled: true }),
|
|
1741
|
-
complexity: z3.strictObject({
|
|
1742
|
-
ignore: z3.array(z3.string()).default(["**/*test.ts*"])
|
|
1743
|
-
}).default({ ignore: ["**/*test.ts*"] }),
|
|
1744
|
-
hardcodedColors: z3.strictObject({
|
|
1745
|
-
ignore: z3.array(z3.string()).default([])
|
|
1746
|
-
}).optional(),
|
|
1747
|
-
restructure: z3.strictObject({
|
|
1748
|
-
ignore: z3.array(z3.string()).default([])
|
|
1749
|
-
}).optional(),
|
|
1750
|
-
jira: z3.strictObject({
|
|
1751
|
-
acField: z3.string().default("customfield_11937")
|
|
1752
|
-
}).optional(),
|
|
1753
|
-
roam: z3.strictObject({
|
|
1754
|
-
clientId: z3.string(),
|
|
1755
|
-
clientSecret: z3.string(),
|
|
1756
|
-
accessToken: z3.string().optional(),
|
|
1757
|
-
refreshToken: z3.string().optional(),
|
|
1758
|
-
tokenExpiresAt: z3.number().optional()
|
|
1759
|
-
}).optional(),
|
|
1760
|
-
run: z3.array(z3.union([runConfigSchema, runLinkSchema])).optional(),
|
|
1761
|
-
transcript: transcriptConfigSchema.optional(),
|
|
1762
|
-
cliReadVerbs: z3.record(z3.string(), z3.array(z3.string())).optional(),
|
|
1763
|
-
news: z3.strictObject({
|
|
1764
|
-
feeds: z3.array(z3.string()).default([])
|
|
1765
|
-
}).default({ feeds: [] }),
|
|
1766
|
-
dotnet: z3.strictObject({
|
|
1767
|
-
inspect: z3.strictObject({
|
|
1768
|
-
suppress: z3.array(z3.string()).default([])
|
|
1769
|
-
}).default({ suppress: [] })
|
|
1770
|
-
}).optional(),
|
|
1771
|
-
ravendb: z3.strictObject({
|
|
1772
|
-
connections: z3.array(
|
|
1773
|
-
z3.strictObject({
|
|
1774
|
-
name: z3.string(),
|
|
1775
|
-
url: z3.string(),
|
|
1776
|
-
database: z3.string(),
|
|
1777
|
-
apiKeyRef: z3.string()
|
|
1778
|
-
})
|
|
1779
|
-
).default([]),
|
|
1780
|
-
defaultConnection: z3.string().optional()
|
|
1781
|
-
}).optional(),
|
|
1782
|
-
seq: z3.strictObject({
|
|
1783
|
-
connections: z3.array(
|
|
1784
|
-
z3.strictObject({
|
|
1785
|
-
name: z3.string(),
|
|
1786
|
-
url: z3.string(),
|
|
1787
|
-
apiToken: z3.string()
|
|
1788
|
-
})
|
|
1789
|
-
).default([]),
|
|
1790
|
-
defaultConnection: z3.string().optional()
|
|
1791
|
-
}).optional(),
|
|
1792
|
-
sql: z3.strictObject({
|
|
1793
|
-
connections: z3.array(
|
|
1794
|
-
z3.strictObject({
|
|
1795
|
-
name: z3.string(),
|
|
1796
|
-
server: z3.string(),
|
|
1797
|
-
port: z3.number(),
|
|
1798
|
-
user: z3.string(),
|
|
1799
|
-
password: z3.string(),
|
|
1800
|
-
database: z3.string()
|
|
1801
|
-
})
|
|
1802
|
-
).default([]),
|
|
1803
|
-
defaultConnection: z3.string().optional()
|
|
1804
|
-
}).optional(),
|
|
1805
|
-
screenshot: z3.strictObject({
|
|
1806
|
-
outputDir: z3.string().default("./screenshots")
|
|
1807
|
-
}).default({ outputDir: "./screenshots" }),
|
|
1808
|
-
backlog: z3.strictObject({
|
|
1809
|
-
autoCommit: z3.boolean().default(false)
|
|
1810
|
-
}).default({ autoCommit: false }),
|
|
1811
|
-
mermaid: z3.strictObject({
|
|
1812
|
-
krokiUrl: z3.string().default("https://kroki.io")
|
|
1813
|
-
}).default({ krokiUrl: "https://kroki.io" }),
|
|
1814
|
-
deny: z3.array(
|
|
1815
|
-
z3.strictObject({
|
|
1816
|
-
pattern: z3.string(),
|
|
1817
|
-
message: z3.string()
|
|
1818
|
-
})
|
|
1819
|
-
).optional(),
|
|
1820
|
-
sync: z3.strictObject({
|
|
1821
|
-
autoConfirm: z3.boolean().default(false)
|
|
1822
|
-
}).default({ autoConfirm: false }),
|
|
1823
|
-
voice: z3.strictObject({
|
|
1824
|
-
wakeWords: z3.array(z3.string()).default(DEFAULT_WAKE_WORDS),
|
|
1825
|
-
mic: z3.string().optional(),
|
|
1826
|
-
cwd: z3.string().optional(),
|
|
1827
|
-
modelsDir: z3.string().default(DEFAULT_MODELS_DIR),
|
|
1828
|
-
lockDir: z3.string().optional(),
|
|
1829
|
-
submitWindows: z3.array(z3.string()).optional(),
|
|
1830
|
-
models: z3.strictObject({
|
|
1831
|
-
vad: z3.string().optional(),
|
|
1832
|
-
smartTurn: z3.string().optional()
|
|
1833
|
-
}).default({})
|
|
1834
|
-
}).default({
|
|
1835
|
-
wakeWords: DEFAULT_WAKE_WORDS,
|
|
1836
|
-
modelsDir: DEFAULT_MODELS_DIR,
|
|
1837
|
-
models: {}
|
|
1838
|
-
})
|
|
1839
|
-
});
|
|
1840
|
-
function isRunLink(entry) {
|
|
1841
|
-
return "link" in entry;
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
// src/shared/loadConfig.ts
|
|
1845
|
-
function findConfigUp(startDir) {
|
|
1846
|
-
let current = startDir;
|
|
1847
|
-
while (current !== dirname2(current)) {
|
|
1848
|
-
const claudePath = join8(current, ".claude", "assist.yml");
|
|
1849
|
-
if (existsSync9(claudePath)) return claudePath;
|
|
1850
|
-
const rootPath = join8(current, "assist.yml");
|
|
1851
|
-
if (existsSync9(rootPath)) return rootPath;
|
|
1852
|
-
current = dirname2(current);
|
|
1853
|
-
}
|
|
1854
|
-
return null;
|
|
1855
|
-
}
|
|
1856
|
-
function getConfigPath() {
|
|
1857
|
-
const found = findConfigUp(process.cwd());
|
|
1858
|
-
if (found) return found;
|
|
1859
|
-
return join8(process.cwd(), "assist.yml");
|
|
1860
|
-
}
|
|
1861
|
-
function getGlobalConfigPath() {
|
|
1862
|
-
return join8(homedir(), ".assist.yml");
|
|
1863
|
-
}
|
|
1864
|
-
function getConfigDir() {
|
|
1865
|
-
return dirname2(getConfigPath());
|
|
1866
|
-
}
|
|
1867
|
-
function loadConfig() {
|
|
1868
|
-
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
1869
|
-
const projectRaw = loadRawYaml(getConfigPath());
|
|
1870
|
-
const merged = mergeRawConfigs(globalRaw, projectRaw);
|
|
1871
|
-
return assistConfigSchema.parse(merged);
|
|
1872
|
-
}
|
|
1873
|
-
function loadConfigFrom(startDir) {
|
|
1874
|
-
const found = findConfigUp(startDir);
|
|
1875
|
-
const configPath = found ?? join8(startDir, "assist.yml");
|
|
1876
|
-
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
1877
|
-
const projectRaw = loadRawYaml(configPath);
|
|
1878
|
-
const merged = mergeRawConfigs(globalRaw, projectRaw);
|
|
1879
|
-
return {
|
|
1880
|
-
config: assistConfigSchema.parse(merged),
|
|
1881
|
-
configDir: dirname2(configPath)
|
|
1882
|
-
};
|
|
1883
|
-
}
|
|
1884
|
-
function loadProjectConfig() {
|
|
1885
|
-
return loadRawYaml(getConfigPath());
|
|
1886
|
-
}
|
|
1887
|
-
function loadGlobalConfigRaw() {
|
|
1888
|
-
return loadRawYaml(getGlobalConfigPath());
|
|
1889
|
-
}
|
|
1890
|
-
function saveGlobalConfig(config) {
|
|
1891
|
-
writeFileSync5(getGlobalConfigPath(), stringifyYaml(config, { lineWidth: 0 }));
|
|
1892
|
-
}
|
|
1893
|
-
function saveConfig(config) {
|
|
1894
|
-
const configPath = getConfigPath();
|
|
1895
|
-
writeFileSync5(configPath, stringifyYaml(config, { lineWidth: 0 }));
|
|
1896
|
-
}
|
|
1897
|
-
function getTranscriptConfig() {
|
|
1898
|
-
const config = loadConfig();
|
|
1899
|
-
if (!config.transcript) {
|
|
1900
|
-
console.error(
|
|
1901
|
-
chalk16.red(
|
|
1902
|
-
"Transcript directories not configured. Run 'assist transcript configure' first."
|
|
1903
|
-
)
|
|
1904
|
-
);
|
|
1905
|
-
process.exit(1);
|
|
1906
|
-
}
|
|
1907
|
-
return config.transcript;
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
1917
|
// src/shared/resolveRunConfigs.ts
|
|
1911
1918
|
import { dirname as dirname3, relative, resolve as resolve3 } from "path";
|
|
1912
1919
|
|
|
@@ -11901,11 +11908,6 @@ async function sqlColumns(table, connectionName) {
|
|
|
11901
11908
|
}
|
|
11902
11909
|
|
|
11903
11910
|
// src/commands/sql/sqlMutate.ts
|
|
11904
|
-
async function sqlMutate(_query, _connectionName) {
|
|
11905
|
-
throw new Error("assist sql mutate is not yet implemented");
|
|
11906
|
-
}
|
|
11907
|
-
|
|
11908
|
-
// src/commands/sql/sqlQuery.ts
|
|
11909
11911
|
import chalk137 from "chalk";
|
|
11910
11912
|
|
|
11911
11913
|
// src/commands/sql/isMutation.ts
|
|
@@ -11936,11 +11938,32 @@ function isMutation(sql2) {
|
|
|
11936
11938
|
return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
|
|
11937
11939
|
}
|
|
11938
11940
|
|
|
11941
|
+
// src/commands/sql/sqlMutate.ts
|
|
11942
|
+
async function sqlMutate(query, connectionName) {
|
|
11943
|
+
if (!isMutation(query)) {
|
|
11944
|
+
console.error(
|
|
11945
|
+
chalk137.red(
|
|
11946
|
+
"assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
|
|
11947
|
+
)
|
|
11948
|
+
);
|
|
11949
|
+
process.exit(1);
|
|
11950
|
+
}
|
|
11951
|
+
const conn = resolveConnection3(connectionName);
|
|
11952
|
+
const pool = await sqlConnect(conn);
|
|
11953
|
+
try {
|
|
11954
|
+
const result = await pool.request().query(query);
|
|
11955
|
+
console.log(chalk137.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
|
|
11956
|
+
} finally {
|
|
11957
|
+
await pool.close();
|
|
11958
|
+
}
|
|
11959
|
+
}
|
|
11960
|
+
|
|
11939
11961
|
// src/commands/sql/sqlQuery.ts
|
|
11962
|
+
import chalk138 from "chalk";
|
|
11940
11963
|
async function sqlQuery(query, connectionName) {
|
|
11941
11964
|
if (isMutation(query)) {
|
|
11942
11965
|
console.error(
|
|
11943
|
-
|
|
11966
|
+
chalk138.red(
|
|
11944
11967
|
"assist sql query refuses mutating statements. Use `assist sql mutate` instead."
|
|
11945
11968
|
)
|
|
11946
11969
|
);
|
|
@@ -11955,7 +11978,7 @@ async function sqlQuery(query, connectionName) {
|
|
|
11955
11978
|
printTable(rows);
|
|
11956
11979
|
} else {
|
|
11957
11980
|
console.log(
|
|
11958
|
-
|
|
11981
|
+
chalk138.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
|
|
11959
11982
|
);
|
|
11960
11983
|
}
|
|
11961
11984
|
} finally {
|
|
@@ -12001,7 +12024,9 @@ function registerSql(program2) {
|
|
|
12001
12024
|
cmd.command("query <sql> [connection]").description("Execute a read-only SQL query (rejects mutating statements)").action(
|
|
12002
12025
|
(query, connection) => sqlQuery(query, connection)
|
|
12003
12026
|
);
|
|
12004
|
-
cmd.command("mutate <sql> [connection]").description(
|
|
12027
|
+
cmd.command("mutate <sql> [connection]").description(
|
|
12028
|
+
"Execute a mutating SQL statement (rejects non-mutating statements)"
|
|
12029
|
+
).action(
|
|
12005
12030
|
(query, connection) => sqlMutate(query, connection)
|
|
12006
12031
|
);
|
|
12007
12032
|
cmd.command("tables [connection]").description("List tables in the connected database").action((connection) => sqlTables(connection));
|
|
@@ -12533,14 +12558,14 @@ import {
|
|
|
12533
12558
|
import { dirname as dirname22, join as join38 } from "path";
|
|
12534
12559
|
|
|
12535
12560
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
12536
|
-
import
|
|
12561
|
+
import chalk139 from "chalk";
|
|
12537
12562
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
12538
12563
|
function validateStagedContent(filename, content) {
|
|
12539
12564
|
const firstLine = content.split("\n")[0];
|
|
12540
12565
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
12541
12566
|
if (!match) {
|
|
12542
12567
|
console.error(
|
|
12543
|
-
|
|
12568
|
+
chalk139.red(
|
|
12544
12569
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
12545
12570
|
)
|
|
12546
12571
|
);
|
|
@@ -12549,7 +12574,7 @@ function validateStagedContent(filename, content) {
|
|
|
12549
12574
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
12550
12575
|
if (!contentAfterLink) {
|
|
12551
12576
|
console.error(
|
|
12552
|
-
|
|
12577
|
+
chalk139.red(
|
|
12553
12578
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
12554
12579
|
)
|
|
12555
12580
|
);
|
|
@@ -12945,7 +12970,7 @@ function registerVoice(program2) {
|
|
|
12945
12970
|
|
|
12946
12971
|
// src/commands/roam/auth.ts
|
|
12947
12972
|
import { randomBytes } from "crypto";
|
|
12948
|
-
import
|
|
12973
|
+
import chalk140 from "chalk";
|
|
12949
12974
|
|
|
12950
12975
|
// src/lib/openBrowser.ts
|
|
12951
12976
|
import { execSync as execSync37 } from "child_process";
|
|
@@ -13120,13 +13145,13 @@ async function auth() {
|
|
|
13120
13145
|
saveGlobalConfig(config);
|
|
13121
13146
|
const state = randomBytes(16).toString("hex");
|
|
13122
13147
|
console.log(
|
|
13123
|
-
|
|
13148
|
+
chalk140.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
13124
13149
|
);
|
|
13125
|
-
console.log(
|
|
13126
|
-
console.log(
|
|
13127
|
-
console.log(
|
|
13150
|
+
console.log(chalk140.white("http://localhost:14523/callback\n"));
|
|
13151
|
+
console.log(chalk140.blue("Opening browser for authorization..."));
|
|
13152
|
+
console.log(chalk140.dim("Waiting for authorization callback..."));
|
|
13128
13153
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
13129
|
-
console.log(
|
|
13154
|
+
console.log(chalk140.dim("Exchanging code for tokens..."));
|
|
13130
13155
|
const tokens = await exchangeToken({
|
|
13131
13156
|
code,
|
|
13132
13157
|
clientId,
|
|
@@ -13142,7 +13167,7 @@ async function auth() {
|
|
|
13142
13167
|
};
|
|
13143
13168
|
saveGlobalConfig(config);
|
|
13144
13169
|
console.log(
|
|
13145
|
-
|
|
13170
|
+
chalk140.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
13146
13171
|
);
|
|
13147
13172
|
}
|
|
13148
13173
|
|
|
@@ -13555,7 +13580,7 @@ import { execSync as execSync39 } from "child_process";
|
|
|
13555
13580
|
import { existsSync as existsSync43, mkdirSync as mkdirSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync30 } from "fs";
|
|
13556
13581
|
import { tmpdir as tmpdir7 } from "os";
|
|
13557
13582
|
import { join as join49, resolve as resolve13 } from "path";
|
|
13558
|
-
import
|
|
13583
|
+
import chalk141 from "chalk";
|
|
13559
13584
|
|
|
13560
13585
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
13561
13586
|
var captureWindowPs1 = `
|
|
@@ -13706,20 +13731,20 @@ function screenshot(processName) {
|
|
|
13706
13731
|
const config = loadConfig();
|
|
13707
13732
|
const outputDir = resolve13(config.screenshot.outputDir);
|
|
13708
13733
|
const outputPath = buildOutputPath(outputDir, processName);
|
|
13709
|
-
console.log(
|
|
13734
|
+
console.log(chalk141.gray(`Capturing window for process "${processName}" ...`));
|
|
13710
13735
|
try {
|
|
13711
13736
|
runPowerShellScript(processName, outputPath);
|
|
13712
|
-
console.log(
|
|
13737
|
+
console.log(chalk141.green(`Screenshot saved: ${outputPath}`));
|
|
13713
13738
|
} catch (error) {
|
|
13714
13739
|
const msg = error instanceof Error ? error.message : String(error);
|
|
13715
|
-
console.error(
|
|
13740
|
+
console.error(chalk141.red(`Failed to capture screenshot: ${msg}`));
|
|
13716
13741
|
process.exit(1);
|
|
13717
13742
|
}
|
|
13718
13743
|
}
|
|
13719
13744
|
|
|
13720
13745
|
// src/commands/sessions/summarise/index.ts
|
|
13721
13746
|
import * as fs27 from "fs";
|
|
13722
|
-
import
|
|
13747
|
+
import chalk142 from "chalk";
|
|
13723
13748
|
|
|
13724
13749
|
// src/commands/sessions/summarise/shared.ts
|
|
13725
13750
|
import * as fs25 from "fs";
|
|
@@ -13859,22 +13884,22 @@ ${firstMessage}`);
|
|
|
13859
13884
|
async function summarise3(options2) {
|
|
13860
13885
|
const files = await discoverSessionJsonlPaths();
|
|
13861
13886
|
if (files.length === 0) {
|
|
13862
|
-
console.log(
|
|
13887
|
+
console.log(chalk142.yellow("No sessions found."));
|
|
13863
13888
|
return;
|
|
13864
13889
|
}
|
|
13865
13890
|
const toProcess = selectCandidates(files, options2);
|
|
13866
13891
|
if (toProcess.length === 0) {
|
|
13867
|
-
console.log(
|
|
13892
|
+
console.log(chalk142.green("All sessions already summarised."));
|
|
13868
13893
|
return;
|
|
13869
13894
|
}
|
|
13870
13895
|
console.log(
|
|
13871
|
-
|
|
13896
|
+
chalk142.cyan(
|
|
13872
13897
|
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
13873
13898
|
)
|
|
13874
13899
|
);
|
|
13875
13900
|
const { succeeded, failed } = processSessions(toProcess);
|
|
13876
13901
|
console.log(
|
|
13877
|
-
|
|
13902
|
+
chalk142.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk142.yellow(`, ${failed} skipped`) : "")
|
|
13878
13903
|
);
|
|
13879
13904
|
}
|
|
13880
13905
|
function selectCandidates(files, options2) {
|
|
@@ -13894,16 +13919,16 @@ function processSessions(files) {
|
|
|
13894
13919
|
let failed = 0;
|
|
13895
13920
|
for (let i = 0; i < files.length; i++) {
|
|
13896
13921
|
const file = files[i];
|
|
13897
|
-
process.stdout.write(
|
|
13922
|
+
process.stdout.write(chalk142.dim(` [${i + 1}/${files.length}] `));
|
|
13898
13923
|
const summary = summariseSession(file);
|
|
13899
13924
|
if (summary) {
|
|
13900
13925
|
writeSummary(file, summary);
|
|
13901
13926
|
succeeded++;
|
|
13902
|
-
process.stdout.write(`${
|
|
13927
|
+
process.stdout.write(`${chalk142.green("\u2713")} ${summary}
|
|
13903
13928
|
`);
|
|
13904
13929
|
} else {
|
|
13905
13930
|
failed++;
|
|
13906
|
-
process.stdout.write(` ${
|
|
13931
|
+
process.stdout.write(` ${chalk142.yellow("skip")}
|
|
13907
13932
|
`);
|
|
13908
13933
|
}
|
|
13909
13934
|
}
|
|
@@ -13918,10 +13943,10 @@ function registerSessions(program2) {
|
|
|
13918
13943
|
}
|
|
13919
13944
|
|
|
13920
13945
|
// src/commands/statusLine.ts
|
|
13921
|
-
import
|
|
13946
|
+
import chalk144 from "chalk";
|
|
13922
13947
|
|
|
13923
13948
|
// src/commands/buildLimitsSegment.ts
|
|
13924
|
-
import
|
|
13949
|
+
import chalk143 from "chalk";
|
|
13925
13950
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
13926
13951
|
var SEVEN_DAY_SECONDS = 7 * 86400;
|
|
13927
13952
|
function formatTimeLeft(resetsAt) {
|
|
@@ -13944,10 +13969,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
|
|
|
13944
13969
|
function colorizeRateLimit(pct, resetsAt, windowSeconds) {
|
|
13945
13970
|
const label2 = `${Math.round(pct)}%`;
|
|
13946
13971
|
const projected = projectUsage(pct, resetsAt, windowSeconds);
|
|
13947
|
-
if (projected == null) return
|
|
13948
|
-
if (projected > 100) return
|
|
13949
|
-
if (projected > 75) return
|
|
13950
|
-
return
|
|
13972
|
+
if (projected == null) return chalk143.green(label2);
|
|
13973
|
+
if (projected > 100) return chalk143.red(label2);
|
|
13974
|
+
if (projected > 75) return chalk143.yellow(label2);
|
|
13975
|
+
return chalk143.green(label2);
|
|
13951
13976
|
}
|
|
13952
13977
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
|
|
13953
13978
|
const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
|
|
@@ -13973,14 +13998,14 @@ function buildLimitsSegment(rateLimits) {
|
|
|
13973
13998
|
}
|
|
13974
13999
|
|
|
13975
14000
|
// src/commands/statusLine.ts
|
|
13976
|
-
|
|
14001
|
+
chalk144.level = 3;
|
|
13977
14002
|
function formatNumber(num) {
|
|
13978
14003
|
return num.toLocaleString("en-US");
|
|
13979
14004
|
}
|
|
13980
14005
|
function colorizePercent(pct) {
|
|
13981
14006
|
const label2 = `${Math.round(pct)}%`;
|
|
13982
|
-
if (pct > 80) return
|
|
13983
|
-
if (pct > 40) return
|
|
14007
|
+
if (pct > 80) return chalk144.red(label2);
|
|
14008
|
+
if (pct > 40) return chalk144.yellow(label2);
|
|
13984
14009
|
return label2;
|
|
13985
14010
|
}
|
|
13986
14011
|
async function statusLine() {
|
|
@@ -14003,7 +14028,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
|
|
|
14003
14028
|
// src/commands/sync/syncClaudeMd.ts
|
|
14004
14029
|
import * as fs28 from "fs";
|
|
14005
14030
|
import * as path49 from "path";
|
|
14006
|
-
import
|
|
14031
|
+
import chalk145 from "chalk";
|
|
14007
14032
|
async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
14008
14033
|
const source = path49.join(claudeDir, "CLAUDE.md");
|
|
14009
14034
|
const target = path49.join(targetBase, "CLAUDE.md");
|
|
@@ -14012,12 +14037,12 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
14012
14037
|
const targetContent = fs28.readFileSync(target, "utf-8");
|
|
14013
14038
|
if (sourceContent !== targetContent) {
|
|
14014
14039
|
console.log(
|
|
14015
|
-
|
|
14040
|
+
chalk145.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
14016
14041
|
);
|
|
14017
14042
|
console.log();
|
|
14018
14043
|
printDiff(targetContent, sourceContent);
|
|
14019
14044
|
const confirm = options2?.yes || await promptConfirm(
|
|
14020
|
-
|
|
14045
|
+
chalk145.red("Overwrite existing CLAUDE.md?"),
|
|
14021
14046
|
false
|
|
14022
14047
|
);
|
|
14023
14048
|
if (!confirm) {
|
|
@@ -14033,7 +14058,7 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
14033
14058
|
// src/commands/sync/syncSettings.ts
|
|
14034
14059
|
import * as fs29 from "fs";
|
|
14035
14060
|
import * as path50 from "path";
|
|
14036
|
-
import
|
|
14061
|
+
import chalk146 from "chalk";
|
|
14037
14062
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
14038
14063
|
const source = path50.join(claudeDir, "settings.json");
|
|
14039
14064
|
const target = path50.join(targetBase, "settings.json");
|
|
@@ -14049,14 +14074,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
14049
14074
|
if (mergedContent !== normalizedTarget) {
|
|
14050
14075
|
if (!options2?.yes) {
|
|
14051
14076
|
console.log(
|
|
14052
|
-
|
|
14077
|
+
chalk146.yellow(
|
|
14053
14078
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
14054
14079
|
)
|
|
14055
14080
|
);
|
|
14056
14081
|
console.log();
|
|
14057
14082
|
printDiff(targetContent, mergedContent);
|
|
14058
14083
|
const confirm = await promptConfirm(
|
|
14059
|
-
|
|
14084
|
+
chalk146.red("Overwrite existing settings.json?"),
|
|
14060
14085
|
false
|
|
14061
14086
|
);
|
|
14062
14087
|
if (!confirm) {
|