@staff0rd/assist 0.66.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/commands/backlog/web/bundle.js +121 -0
- package/dist/index.js +405 -202
- package/package.json +9 -2
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.67.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -28,7 +28,8 @@ var package_default = {
|
|
|
28
28
|
"verify:knip": "knip --no-progress --treat-config-hints-as-errors",
|
|
29
29
|
"verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
|
|
30
30
|
"verify:maintainability": "assist complexity maintainability ./src --threshold 70",
|
|
31
|
-
"verify:custom-lint": "assist lint"
|
|
31
|
+
"verify:custom-lint": "assist lint",
|
|
32
|
+
"verify:react-build": 'esbuild src/commands/backlog/web/ui/App.tsx --bundle --minify --format=iife --target=es2020 --outfile=dist/commands/backlog/web/bundle.js --jsx=automatic --jsx-import-source=react "--define:process.env.NODE_ENV=\\"production\\""'
|
|
32
33
|
},
|
|
33
34
|
keywords: [],
|
|
34
35
|
author: "",
|
|
@@ -58,9 +59,15 @@ var package_default = {
|
|
|
58
59
|
"@semantic-release/git": "^10.0.1",
|
|
59
60
|
"@types/node": "^24.10.1",
|
|
60
61
|
"@types/node-notifier": "^8.0.5",
|
|
62
|
+
"@types/react": "^19.2.14",
|
|
63
|
+
"@types/react-dom": "^19.2.3",
|
|
61
64
|
"@types/semver": "^7.7.1",
|
|
65
|
+
esbuild: "^0.27.3",
|
|
62
66
|
jscpd: "^4.0.5",
|
|
63
67
|
knip: "^5.71.0",
|
|
68
|
+
marked: "^15.0.12",
|
|
69
|
+
react: "^19.2.4",
|
|
70
|
+
"react-dom": "^19.2.4",
|
|
64
71
|
"semantic-release": "^25.0.2",
|
|
65
72
|
tsup: "^8.5.1"
|
|
66
73
|
}
|
|
@@ -147,9 +154,11 @@ function loadConfig() {
|
|
|
147
154
|
const merged = { ...globalRaw, ...projectRaw };
|
|
148
155
|
return assistConfigSchema.parse(merged);
|
|
149
156
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
function loadProjectConfig() {
|
|
158
|
+
return loadRawConfig(getConfigPath());
|
|
159
|
+
}
|
|
160
|
+
function loadGlobalConfigRaw() {
|
|
161
|
+
return loadRawConfig(getGlobalConfigPath());
|
|
153
162
|
}
|
|
154
163
|
function saveGlobalConfig(config) {
|
|
155
164
|
writeFileSync(getGlobalConfigPath(), stringifyYaml(config, { lineWidth: 0 }));
|
|
@@ -291,14 +300,10 @@ function exitValidationFailed(issues, key) {
|
|
|
291
300
|
function validateConfig(updated, key) {
|
|
292
301
|
const result = assistConfigSchema.safeParse(updated);
|
|
293
302
|
if (!result.success) return exitValidationFailed(result.error.issues, key);
|
|
294
|
-
return
|
|
303
|
+
return updated;
|
|
295
304
|
}
|
|
296
305
|
function applyConfigSet(key, coerced) {
|
|
297
|
-
const updated = setNestedValue(
|
|
298
|
-
loadConfig(),
|
|
299
|
-
key,
|
|
300
|
-
coerced
|
|
301
|
-
);
|
|
306
|
+
const updated = setNestedValue(loadProjectConfig(), key, coerced);
|
|
302
307
|
saveConfig(validateConfig(updated, key));
|
|
303
308
|
}
|
|
304
309
|
function configSet(key, value) {
|
|
@@ -2082,6 +2087,202 @@ async function start(id) {
|
|
|
2082
2087
|
}
|
|
2083
2088
|
}
|
|
2084
2089
|
|
|
2090
|
+
// src/commands/backlog/web/index.ts
|
|
2091
|
+
import { createServer } from "http";
|
|
2092
|
+
import chalk28 from "chalk";
|
|
2093
|
+
|
|
2094
|
+
// src/commands/backlog/web/handleRequest.ts
|
|
2095
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
2096
|
+
import { dirname as dirname11, join as join10 } from "path";
|
|
2097
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2098
|
+
|
|
2099
|
+
// src/commands/backlog/web/getHtml.ts
|
|
2100
|
+
function getHtml() {
|
|
2101
|
+
return `<!DOCTYPE html>
|
|
2102
|
+
<html lang="en">
|
|
2103
|
+
<head>
|
|
2104
|
+
<meta charset="UTF-8">
|
|
2105
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2106
|
+
<title>Backlog</title>
|
|
2107
|
+
<style>
|
|
2108
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
2109
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f5f5f5; color: #333; line-height: 1.5; }
|
|
2110
|
+
.container { max-width: 800px; margin: 0 auto; padding: 24px 16px; }
|
|
2111
|
+
header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
|
|
2112
|
+
header h1 { font-size: 1.5rem; }
|
|
2113
|
+
button { cursor: pointer; border: none; border-radius: 6px; padding: 8px 16px; font-size: 0.875rem; font-weight: 500; }
|
|
2114
|
+
.btn-primary { background: #2563eb; color: #fff; }
|
|
2115
|
+
.btn-primary:hover { background: #1d4ed8; }
|
|
2116
|
+
.btn-secondary { background: #e5e7eb; color: #333; }
|
|
2117
|
+
.btn-secondary:hover { background: #d1d5db; }
|
|
2118
|
+
.btn-danger { background: #ef4444; color: #fff; }
|
|
2119
|
+
.btn-danger:hover { background: #dc2626; }
|
|
2120
|
+
.card { background: #fff; border-radius: 8px; padding: 16px; margin-bottom: 8px; cursor: pointer; border: 1px solid #e5e7eb; transition: box-shadow 0.15s; display: flex; align-items: center; gap: 12px; }
|
|
2121
|
+
.card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
|
2122
|
+
.status-icon { font-size: 1.1rem; flex-shrink: 0; }
|
|
2123
|
+
.status-todo { color: #9ca3af; }
|
|
2124
|
+
.status-in-progress { color: #f59e0b; }
|
|
2125
|
+
.status-done { color: #22c55e; }
|
|
2126
|
+
.card-id { color: #9ca3af; font-size: 0.85rem; flex-shrink: 0; }
|
|
2127
|
+
.card-name { font-weight: 500; }
|
|
2128
|
+
.detail { background: #fff; border-radius: 8px; padding: 24px; border: 1px solid #e5e7eb; }
|
|
2129
|
+
.detail h2 { margin-bottom: 4px; }
|
|
2130
|
+
.detail-id { color: #9ca3af; font-size: 0.9rem; margin-bottom: 16px; }
|
|
2131
|
+
.detail-section { margin-bottom: 16px; }
|
|
2132
|
+
.detail-section h3 { font-size: 0.85rem; text-transform: uppercase; color: #6b7280; margin-bottom: 8px; letter-spacing: 0.05em; }
|
|
2133
|
+
.detail-section .markdown { line-height: 1.7; }
|
|
2134
|
+
.detail-section .markdown p { margin-bottom: 0.5em; }
|
|
2135
|
+
.detail-section .markdown pre { background: #f3f4f6; padding: 12px; border-radius: 6px; overflow-x: auto; }
|
|
2136
|
+
.detail-section .markdown code { background: #f3f4f6; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
|
|
2137
|
+
.detail-section .markdown pre code { background: none; padding: 0; }
|
|
2138
|
+
.ac-list { list-style: none; }
|
|
2139
|
+
.ac-list li { padding: 4px 0; }
|
|
2140
|
+
.ac-list li::before { content: "\\2022"; color: #6b7280; margin-right: 8px; }
|
|
2141
|
+
.back-link { display: inline-block; margin-bottom: 16px; color: #2563eb; text-decoration: none; font-size: 0.9rem; }
|
|
2142
|
+
.back-link:hover { text-decoration: underline; }
|
|
2143
|
+
.form { background: #fff; border-radius: 8px; padding: 24px; border: 1px solid #e5e7eb; }
|
|
2144
|
+
.form h2 { margin-bottom: 16px; }
|
|
2145
|
+
.field { margin-bottom: 16px; }
|
|
2146
|
+
.field label { display: block; font-weight: 500; margin-bottom: 4px; font-size: 0.9rem; }
|
|
2147
|
+
.field input, .field textarea { width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 0.9rem; font-family: inherit; }
|
|
2148
|
+
.field textarea { min-height: 120px; resize: vertical; }
|
|
2149
|
+
.ac-inputs { display: flex; flex-direction: column; gap: 8px; }
|
|
2150
|
+
.ac-row { display: flex; gap: 8px; }
|
|
2151
|
+
.ac-row input { flex: 1; }
|
|
2152
|
+
.form-actions { display: flex; gap: 8px; margin-top: 16px; }
|
|
2153
|
+
.preview-toggle { font-size: 0.8rem; color: #2563eb; cursor: pointer; margin-left: 8px; }
|
|
2154
|
+
.preview-box { border: 1px solid #e5e7eb; border-radius: 6px; padding: 12px; margin-top: 8px; min-height: 60px; background: #fafafa; }
|
|
2155
|
+
.empty { text-align: center; color: #9ca3af; padding: 48px 16px; }
|
|
2156
|
+
.status-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.8rem; font-weight: 500; }
|
|
2157
|
+
.badge-todo { background: #f3f4f6; color: #6b7280; }
|
|
2158
|
+
.badge-in-progress { background: #fef3c7; color: #92400e; }
|
|
2159
|
+
.badge-done { background: #d1fae5; color: #065f46; }
|
|
2160
|
+
</style>
|
|
2161
|
+
</head>
|
|
2162
|
+
<body>
|
|
2163
|
+
<div class="container" id="app"></div>
|
|
2164
|
+
<script src="/bundle.js"></script>
|
|
2165
|
+
</body>
|
|
2166
|
+
</html>`;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// src/commands/backlog/web/shared.ts
|
|
2170
|
+
function respondJson(res, status, data) {
|
|
2171
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
2172
|
+
res.end(JSON.stringify(data));
|
|
2173
|
+
}
|
|
2174
|
+
function readBody(req) {
|
|
2175
|
+
return new Promise((resolve3, reject) => {
|
|
2176
|
+
let body = "";
|
|
2177
|
+
req.on("data", (chunk) => {
|
|
2178
|
+
body += chunk.toString();
|
|
2179
|
+
});
|
|
2180
|
+
req.on("end", () => resolve3(body));
|
|
2181
|
+
req.on("error", reject);
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
function listItems(_req, res) {
|
|
2185
|
+
respondJson(res, 200, loadBacklog());
|
|
2186
|
+
}
|
|
2187
|
+
function getItemById(res, id) {
|
|
2188
|
+
const items = loadBacklog();
|
|
2189
|
+
const item = items.find((i) => i.id === id);
|
|
2190
|
+
if (!item) {
|
|
2191
|
+
respondJson(res, 404, { error: "Not found" });
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
respondJson(res, 200, item);
|
|
2195
|
+
}
|
|
2196
|
+
async function createItem(req, res) {
|
|
2197
|
+
const body = JSON.parse(await readBody(req));
|
|
2198
|
+
const items = loadBacklog();
|
|
2199
|
+
const id = getNextId(items);
|
|
2200
|
+
const newItem = {
|
|
2201
|
+
id,
|
|
2202
|
+
name: body.name,
|
|
2203
|
+
description: body.description,
|
|
2204
|
+
acceptanceCriteria: body.acceptanceCriteria ?? [],
|
|
2205
|
+
status: "todo"
|
|
2206
|
+
};
|
|
2207
|
+
items.push(newItem);
|
|
2208
|
+
saveBacklog(items);
|
|
2209
|
+
respondJson(res, 201, newItem);
|
|
2210
|
+
}
|
|
2211
|
+
async function updateItem(req, res, id) {
|
|
2212
|
+
const body = JSON.parse(await readBody(req));
|
|
2213
|
+
const items = loadBacklog();
|
|
2214
|
+
const item = items.find((i) => i.id === id);
|
|
2215
|
+
if (!item) {
|
|
2216
|
+
respondJson(res, 404, { error: "Not found" });
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
item.name = body.name;
|
|
2220
|
+
item.description = body.description;
|
|
2221
|
+
item.acceptanceCriteria = body.acceptanceCriteria ?? [];
|
|
2222
|
+
saveBacklog(items);
|
|
2223
|
+
respondJson(res, 200, item);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// src/commands/backlog/web/handleRequest.ts
|
|
2227
|
+
var __dirname4 = dirname11(fileURLToPath3(import.meta.url));
|
|
2228
|
+
var bundleCache;
|
|
2229
|
+
function serveBundle(_req, res) {
|
|
2230
|
+
if (!bundleCache) {
|
|
2231
|
+
bundleCache = readFileSync11(
|
|
2232
|
+
join10(__dirname4, "commands/backlog/web/bundle.js"),
|
|
2233
|
+
"utf-8"
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
2237
|
+
res.end(bundleCache);
|
|
2238
|
+
}
|
|
2239
|
+
function serveHtml(_req, res) {
|
|
2240
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
2241
|
+
res.end(getHtml());
|
|
2242
|
+
}
|
|
2243
|
+
var routes = {
|
|
2244
|
+
"GET /": serveHtml,
|
|
2245
|
+
"GET /bundle.js": serveBundle,
|
|
2246
|
+
"GET /api/items": listItems,
|
|
2247
|
+
"POST /api/items": createItem
|
|
2248
|
+
};
|
|
2249
|
+
async function handleRequest(req, res, port) {
|
|
2250
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
2251
|
+
const method = req.method ?? "GET";
|
|
2252
|
+
const key = `${method} ${url.pathname}`;
|
|
2253
|
+
const handler = routes[key];
|
|
2254
|
+
if (handler) {
|
|
2255
|
+
await handler(req, res);
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
const itemMatch = url.pathname.match(/^\/api\/items\/(\d+)$/);
|
|
2259
|
+
if (itemMatch) {
|
|
2260
|
+
const id = Number.parseInt(itemMatch[1], 10);
|
|
2261
|
+
if (method === "GET") {
|
|
2262
|
+
getItemById(res, id);
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
if (method === "PUT") {
|
|
2266
|
+
await updateItem(req, res, id);
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
res.writeHead(404);
|
|
2271
|
+
res.end();
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/commands/backlog/web/index.ts
|
|
2275
|
+
async function web(options2) {
|
|
2276
|
+
const port = Number.parseInt(options2.port, 10);
|
|
2277
|
+
const server = createServer((req, res) => {
|
|
2278
|
+
handleRequest(req, res, port);
|
|
2279
|
+
});
|
|
2280
|
+
server.listen(port, () => {
|
|
2281
|
+
console.log(chalk28.green(`Backlog web view: http://localhost:${port}`));
|
|
2282
|
+
console.log(chalk28.dim("Press Ctrl+C to stop"));
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2085
2286
|
// src/commands/registerBacklog.ts
|
|
2086
2287
|
function registerBacklog(program2) {
|
|
2087
2288
|
const backlogCommand = program2.command("backlog").description("Manage a backlog of work items").action(list);
|
|
@@ -2090,18 +2291,19 @@ function registerBacklog(program2) {
|
|
|
2090
2291
|
backlogCommand.command("add").description("Add a new backlog item").action(add);
|
|
2091
2292
|
backlogCommand.command("start <id>").description("Set a backlog item to in-progress").action(start);
|
|
2092
2293
|
backlogCommand.command("done <id>").description("Set a backlog item to done").action(done);
|
|
2294
|
+
backlogCommand.command("web").description("Start a web view of the backlog").option("-p, --port <number>", "Port to listen on", "3000").action(web);
|
|
2093
2295
|
}
|
|
2094
2296
|
|
|
2095
2297
|
// src/commands/complexity/analyze.ts
|
|
2096
|
-
import
|
|
2298
|
+
import chalk34 from "chalk";
|
|
2097
2299
|
|
|
2098
2300
|
// src/commands/complexity/cyclomatic.ts
|
|
2099
|
-
import
|
|
2301
|
+
import chalk30 from "chalk";
|
|
2100
2302
|
|
|
2101
2303
|
// src/commands/complexity/shared/index.ts
|
|
2102
2304
|
import fs11 from "fs";
|
|
2103
2305
|
import path16 from "path";
|
|
2104
|
-
import
|
|
2306
|
+
import chalk29 from "chalk";
|
|
2105
2307
|
import ts5 from "typescript";
|
|
2106
2308
|
|
|
2107
2309
|
// src/commands/complexity/findSourceFiles.ts
|
|
@@ -2347,7 +2549,7 @@ function createSourceFromFile(filePath) {
|
|
|
2347
2549
|
function withSourceFiles(pattern2, callback) {
|
|
2348
2550
|
const files = findSourceFiles2(pattern2);
|
|
2349
2551
|
if (files.length === 0) {
|
|
2350
|
-
console.log(
|
|
2552
|
+
console.log(chalk29.yellow("No files found matching pattern"));
|
|
2351
2553
|
return void 0;
|
|
2352
2554
|
}
|
|
2353
2555
|
return callback(files);
|
|
@@ -2380,11 +2582,11 @@ async function cyclomatic(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2380
2582
|
results.sort((a, b) => b.complexity - a.complexity);
|
|
2381
2583
|
for (const { file, name, complexity } of results) {
|
|
2382
2584
|
const exceedsThreshold = options2.threshold !== void 0 && complexity > options2.threshold;
|
|
2383
|
-
const color = exceedsThreshold ?
|
|
2384
|
-
console.log(`${color(`${file}:${name}`)} \u2192 ${
|
|
2585
|
+
const color = exceedsThreshold ? chalk30.red : chalk30.white;
|
|
2586
|
+
console.log(`${color(`${file}:${name}`)} \u2192 ${chalk30.cyan(complexity)}`);
|
|
2385
2587
|
}
|
|
2386
2588
|
console.log(
|
|
2387
|
-
|
|
2589
|
+
chalk30.dim(
|
|
2388
2590
|
`
|
|
2389
2591
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
2390
2592
|
)
|
|
@@ -2396,7 +2598,7 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
2396
2598
|
}
|
|
2397
2599
|
|
|
2398
2600
|
// src/commands/complexity/halstead.ts
|
|
2399
|
-
import
|
|
2601
|
+
import chalk31 from "chalk";
|
|
2400
2602
|
async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
2401
2603
|
withSourceFiles(pattern2, (files) => {
|
|
2402
2604
|
const results = [];
|
|
@@ -2411,13 +2613,13 @@ async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2411
2613
|
results.sort((a, b) => b.metrics.effort - a.metrics.effort);
|
|
2412
2614
|
for (const { file, name, metrics } of results) {
|
|
2413
2615
|
const exceedsThreshold = options2.threshold !== void 0 && metrics.volume > options2.threshold;
|
|
2414
|
-
const color = exceedsThreshold ?
|
|
2616
|
+
const color = exceedsThreshold ? chalk31.red : chalk31.white;
|
|
2415
2617
|
console.log(
|
|
2416
|
-
`${color(`${file}:${name}`)} \u2192 volume: ${
|
|
2618
|
+
`${color(`${file}:${name}`)} \u2192 volume: ${chalk31.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk31.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk31.magenta(metrics.effort.toFixed(1))}`
|
|
2417
2619
|
);
|
|
2418
2620
|
}
|
|
2419
2621
|
console.log(
|
|
2420
|
-
|
|
2622
|
+
chalk31.dim(
|
|
2421
2623
|
`
|
|
2422
2624
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
2423
2625
|
)
|
|
@@ -2432,28 +2634,28 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
2432
2634
|
import fs12 from "fs";
|
|
2433
2635
|
|
|
2434
2636
|
// src/commands/complexity/maintainability/displayMaintainabilityResults.ts
|
|
2435
|
-
import
|
|
2637
|
+
import chalk32 from "chalk";
|
|
2436
2638
|
function displayMaintainabilityResults(results, threshold) {
|
|
2437
2639
|
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
2438
2640
|
if (threshold !== void 0 && filtered.length === 0) {
|
|
2439
|
-
console.log(
|
|
2641
|
+
console.log(chalk32.green("All files pass maintainability threshold"));
|
|
2440
2642
|
} else {
|
|
2441
2643
|
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
2442
|
-
const color = threshold !== void 0 ?
|
|
2644
|
+
const color = threshold !== void 0 ? chalk32.red : chalk32.white;
|
|
2443
2645
|
console.log(
|
|
2444
|
-
`${color(file)} \u2192 avg: ${
|
|
2646
|
+
`${color(file)} \u2192 avg: ${chalk32.cyan(avgMaintainability.toFixed(1))}, min: ${chalk32.yellow(minMaintainability.toFixed(1))}`
|
|
2445
2647
|
);
|
|
2446
2648
|
}
|
|
2447
2649
|
}
|
|
2448
|
-
console.log(
|
|
2650
|
+
console.log(chalk32.dim(`
|
|
2449
2651
|
Analyzed ${results.length} files`));
|
|
2450
2652
|
if (filtered.length > 0 && threshold !== void 0) {
|
|
2451
2653
|
console.error(
|
|
2452
|
-
|
|
2654
|
+
chalk32.red(
|
|
2453
2655
|
`
|
|
2454
2656
|
Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code.
|
|
2455
2657
|
|
|
2456
|
-
\u26A0\uFE0F ${
|
|
2658
|
+
\u26A0\uFE0F ${chalk32.bold("Diagnose and fix one file at a time")} \u2014 do not investigate or fix multiple files in parallel. Run 'assist complexity <file>' to see all metrics. For larger files, start by extracting responsibilities into smaller files.`
|
|
2457
2659
|
)
|
|
2458
2660
|
);
|
|
2459
2661
|
process.exit(1);
|
|
@@ -2510,7 +2712,7 @@ async function maintainability(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2510
2712
|
|
|
2511
2713
|
// src/commands/complexity/sloc.ts
|
|
2512
2714
|
import fs13 from "fs";
|
|
2513
|
-
import
|
|
2715
|
+
import chalk33 from "chalk";
|
|
2514
2716
|
async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
2515
2717
|
withSourceFiles(pattern2, (files) => {
|
|
2516
2718
|
const results = [];
|
|
@@ -2526,12 +2728,12 @@ async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
2526
2728
|
results.sort((a, b) => b.lines - a.lines);
|
|
2527
2729
|
for (const { file, lines } of results) {
|
|
2528
2730
|
const exceedsThreshold = options2.threshold !== void 0 && lines > options2.threshold;
|
|
2529
|
-
const color = exceedsThreshold ?
|
|
2530
|
-
console.log(`${color(file)} \u2192 ${
|
|
2731
|
+
const color = exceedsThreshold ? chalk33.red : chalk33.white;
|
|
2732
|
+
console.log(`${color(file)} \u2192 ${chalk33.cyan(lines)} lines`);
|
|
2531
2733
|
}
|
|
2532
2734
|
const total = results.reduce((sum, r) => sum + r.lines, 0);
|
|
2533
2735
|
console.log(
|
|
2534
|
-
|
|
2736
|
+
chalk33.dim(`
|
|
2535
2737
|
Total: ${total} lines across ${files.length} files`)
|
|
2536
2738
|
);
|
|
2537
2739
|
if (hasViolation) {
|
|
@@ -2545,21 +2747,21 @@ async function analyze(pattern2) {
|
|
|
2545
2747
|
const searchPattern = pattern2.includes("*") || pattern2.includes("/") ? pattern2 : `**/${pattern2}`;
|
|
2546
2748
|
const files = findSourceFiles2(searchPattern);
|
|
2547
2749
|
if (files.length === 0) {
|
|
2548
|
-
console.log(
|
|
2750
|
+
console.log(chalk34.yellow("No files found matching pattern"));
|
|
2549
2751
|
return;
|
|
2550
2752
|
}
|
|
2551
2753
|
if (files.length === 1) {
|
|
2552
2754
|
const file = files[0];
|
|
2553
|
-
console.log(
|
|
2755
|
+
console.log(chalk34.bold.underline("SLOC"));
|
|
2554
2756
|
await sloc(file);
|
|
2555
2757
|
console.log();
|
|
2556
|
-
console.log(
|
|
2758
|
+
console.log(chalk34.bold.underline("Cyclomatic Complexity"));
|
|
2557
2759
|
await cyclomatic(file);
|
|
2558
2760
|
console.log();
|
|
2559
|
-
console.log(
|
|
2761
|
+
console.log(chalk34.bold.underline("Halstead Metrics"));
|
|
2560
2762
|
await halstead(file);
|
|
2561
2763
|
console.log();
|
|
2562
|
-
console.log(
|
|
2764
|
+
console.log(chalk34.bold.underline("Maintainability Index"));
|
|
2563
2765
|
await maintainability(file);
|
|
2564
2766
|
return;
|
|
2565
2767
|
}
|
|
@@ -2586,8 +2788,8 @@ function registerComplexity(program2) {
|
|
|
2586
2788
|
}
|
|
2587
2789
|
|
|
2588
2790
|
// src/commands/deploy/redirect.ts
|
|
2589
|
-
import { existsSync as existsSync15, readFileSync as
|
|
2590
|
-
import
|
|
2791
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
2792
|
+
import chalk35 from "chalk";
|
|
2591
2793
|
var TRAILING_SLASH_SCRIPT = ` <script>
|
|
2592
2794
|
if (!window.location.pathname.endsWith('/')) {
|
|
2593
2795
|
window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
|
|
@@ -2596,22 +2798,22 @@ var TRAILING_SLASH_SCRIPT = ` <script>
|
|
|
2596
2798
|
function redirect() {
|
|
2597
2799
|
const indexPath = "index.html";
|
|
2598
2800
|
if (!existsSync15(indexPath)) {
|
|
2599
|
-
console.log(
|
|
2801
|
+
console.log(chalk35.yellow("No index.html found"));
|
|
2600
2802
|
return;
|
|
2601
2803
|
}
|
|
2602
|
-
const content =
|
|
2804
|
+
const content = readFileSync12(indexPath, "utf-8");
|
|
2603
2805
|
if (content.includes("window.location.pathname.endsWith('/')")) {
|
|
2604
|
-
console.log(
|
|
2806
|
+
console.log(chalk35.dim("Trailing slash script already present"));
|
|
2605
2807
|
return;
|
|
2606
2808
|
}
|
|
2607
2809
|
const headCloseIndex = content.indexOf("</head>");
|
|
2608
2810
|
if (headCloseIndex === -1) {
|
|
2609
|
-
console.log(
|
|
2811
|
+
console.log(chalk35.red("Could not find </head> tag in index.html"));
|
|
2610
2812
|
return;
|
|
2611
2813
|
}
|
|
2612
2814
|
const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
|
|
2613
2815
|
writeFileSync12(indexPath, newContent);
|
|
2614
|
-
console.log(
|
|
2816
|
+
console.log(chalk35.green("Added trailing slash redirect to index.html"));
|
|
2615
2817
|
}
|
|
2616
2818
|
|
|
2617
2819
|
// src/commands/registerDeploy.ts
|
|
@@ -2627,19 +2829,19 @@ import { basename as basename3 } from "path";
|
|
|
2627
2829
|
|
|
2628
2830
|
// src/commands/devlog/shared.ts
|
|
2629
2831
|
import { execSync as execSync11 } from "child_process";
|
|
2630
|
-
import
|
|
2832
|
+
import chalk36 from "chalk";
|
|
2631
2833
|
|
|
2632
2834
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
2633
|
-
import { readdirSync, readFileSync as
|
|
2835
|
+
import { readdirSync, readFileSync as readFileSync13 } from "fs";
|
|
2634
2836
|
import { homedir as homedir2 } from "os";
|
|
2635
|
-
import { join as
|
|
2636
|
-
var DEVLOG_DIR =
|
|
2837
|
+
import { join as join11 } from "path";
|
|
2838
|
+
var DEVLOG_DIR = join11(homedir2(), "git/blog/src/content/devlog");
|
|
2637
2839
|
function loadDevlogEntries(repoName) {
|
|
2638
2840
|
const entries = /* @__PURE__ */ new Map();
|
|
2639
2841
|
try {
|
|
2640
2842
|
const files = readdirSync(DEVLOG_DIR).filter((f) => f.endsWith(".md"));
|
|
2641
2843
|
for (const file of files) {
|
|
2642
|
-
const content =
|
|
2844
|
+
const content = readFileSync13(join11(DEVLOG_DIR, file), "utf-8");
|
|
2643
2845
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2644
2846
|
if (frontmatterMatch) {
|
|
2645
2847
|
const frontmatter = frontmatterMatch[1];
|
|
@@ -2688,13 +2890,13 @@ function shouldIgnoreCommit(files, ignorePaths) {
|
|
|
2688
2890
|
}
|
|
2689
2891
|
function printCommitsWithFiles(commits, ignore2, verbose) {
|
|
2690
2892
|
for (const commit2 of commits) {
|
|
2691
|
-
console.log(` ${
|
|
2893
|
+
console.log(` ${chalk36.yellow(commit2.hash)} ${commit2.message}`);
|
|
2692
2894
|
if (verbose) {
|
|
2693
2895
|
const visibleFiles = commit2.files.filter(
|
|
2694
2896
|
(file) => !ignore2.some((p) => file.startsWith(p))
|
|
2695
2897
|
);
|
|
2696
2898
|
for (const file of visibleFiles) {
|
|
2697
|
-
console.log(` ${
|
|
2899
|
+
console.log(` ${chalk36.dim(file)}`);
|
|
2698
2900
|
}
|
|
2699
2901
|
}
|
|
2700
2902
|
}
|
|
@@ -2719,15 +2921,15 @@ function parseGitLogCommits(output, ignore2, afterDate) {
|
|
|
2719
2921
|
}
|
|
2720
2922
|
|
|
2721
2923
|
// src/commands/devlog/list/printDateHeader.ts
|
|
2722
|
-
import
|
|
2924
|
+
import chalk37 from "chalk";
|
|
2723
2925
|
function printDateHeader(date, isSkipped, entries) {
|
|
2724
2926
|
if (isSkipped) {
|
|
2725
|
-
console.log(`${
|
|
2927
|
+
console.log(`${chalk37.bold.blue(date)} ${chalk37.dim("skipped")}`);
|
|
2726
2928
|
} else if (entries && entries.length > 0) {
|
|
2727
|
-
const entryInfo = entries.map((e) => `${
|
|
2728
|
-
console.log(`${
|
|
2929
|
+
const entryInfo = entries.map((e) => `${chalk37.green(e.version)} ${e.title}`).join(" | ");
|
|
2930
|
+
console.log(`${chalk37.bold.blue(date)} ${entryInfo}`);
|
|
2729
2931
|
} else {
|
|
2730
|
-
console.log(`${
|
|
2932
|
+
console.log(`${chalk37.bold.blue(date)} ${chalk37.red("\u26A0 devlog missing")}`);
|
|
2731
2933
|
}
|
|
2732
2934
|
}
|
|
2733
2935
|
|
|
@@ -2830,24 +3032,24 @@ function bumpVersion(version2, type) {
|
|
|
2830
3032
|
|
|
2831
3033
|
// src/commands/devlog/next/displayNextEntry/index.ts
|
|
2832
3034
|
import { execSync as execSync14 } from "child_process";
|
|
2833
|
-
import
|
|
3035
|
+
import chalk39 from "chalk";
|
|
2834
3036
|
|
|
2835
3037
|
// src/commands/devlog/next/displayNextEntry/displayVersion.ts
|
|
2836
|
-
import
|
|
3038
|
+
import chalk38 from "chalk";
|
|
2837
3039
|
function displayVersion(conventional, firstHash, patchVersion, minorVersion) {
|
|
2838
3040
|
if (conventional && firstHash) {
|
|
2839
3041
|
const version2 = getVersionAtCommit(firstHash);
|
|
2840
3042
|
if (version2) {
|
|
2841
|
-
console.log(`${
|
|
3043
|
+
console.log(`${chalk38.bold("version:")} ${stripToMinor(version2)}`);
|
|
2842
3044
|
} else {
|
|
2843
|
-
console.log(`${
|
|
3045
|
+
console.log(`${chalk38.bold("version:")} ${chalk38.red("unknown")}`);
|
|
2844
3046
|
}
|
|
2845
3047
|
} else if (patchVersion && minorVersion) {
|
|
2846
3048
|
console.log(
|
|
2847
|
-
`${
|
|
3049
|
+
`${chalk38.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
2848
3050
|
);
|
|
2849
3051
|
} else {
|
|
2850
|
-
console.log(`${
|
|
3052
|
+
console.log(`${chalk38.bold("version:")} v0.1 (initial)`);
|
|
2851
3053
|
}
|
|
2852
3054
|
}
|
|
2853
3055
|
|
|
@@ -2894,16 +3096,16 @@ function noCommitsMessage(hasLastInfo) {
|
|
|
2894
3096
|
return hasLastInfo ? "No commits after last versioned entry" : "No commits found";
|
|
2895
3097
|
}
|
|
2896
3098
|
function logName(repoName) {
|
|
2897
|
-
console.log(`${
|
|
3099
|
+
console.log(`${chalk39.bold("name:")} ${repoName}`);
|
|
2898
3100
|
}
|
|
2899
3101
|
function displayNextEntry(ctx, targetDate, commits) {
|
|
2900
3102
|
logName(ctx.repoName);
|
|
2901
3103
|
printVersionInfo(ctx.config, ctx.lastInfo, commits[0]?.hash);
|
|
2902
|
-
console.log(
|
|
3104
|
+
console.log(chalk39.bold.blue(targetDate));
|
|
2903
3105
|
printCommitsWithFiles(commits, ctx.ignore, ctx.verbose);
|
|
2904
3106
|
}
|
|
2905
3107
|
function logNoCommits(lastInfo) {
|
|
2906
|
-
console.log(
|
|
3108
|
+
console.log(chalk39.dim(noCommitsMessage(!!lastInfo)));
|
|
2907
3109
|
}
|
|
2908
3110
|
|
|
2909
3111
|
// src/commands/devlog/next/index.ts
|
|
@@ -2938,42 +3140,40 @@ function next(options2) {
|
|
|
2938
3140
|
}
|
|
2939
3141
|
|
|
2940
3142
|
// src/commands/devlog/skip.ts
|
|
2941
|
-
import
|
|
3143
|
+
import chalk40 from "chalk";
|
|
2942
3144
|
function skip(date) {
|
|
2943
3145
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
2944
|
-
console.log(
|
|
3146
|
+
console.log(chalk40.red("Invalid date format. Use YYYY-MM-DD"));
|
|
2945
3147
|
process.exit(1);
|
|
2946
3148
|
}
|
|
2947
|
-
const config =
|
|
2948
|
-
const
|
|
3149
|
+
const config = loadProjectConfig();
|
|
3150
|
+
const devlog = config.devlog ?? {};
|
|
3151
|
+
const skip2 = devlog.skip ?? {};
|
|
3152
|
+
const skipDays = skip2.days ?? [];
|
|
2949
3153
|
if (skipDays.includes(date)) {
|
|
2950
|
-
console.log(
|
|
3154
|
+
console.log(chalk40.yellow(`${date} is already in skip list`));
|
|
2951
3155
|
return;
|
|
2952
3156
|
}
|
|
2953
3157
|
skipDays.push(date);
|
|
2954
3158
|
skipDays.sort();
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
...config.devlog?.skip,
|
|
2959
|
-
days: skipDays
|
|
2960
|
-
}
|
|
2961
|
-
};
|
|
3159
|
+
skip2.days = skipDays;
|
|
3160
|
+
devlog.skip = skip2;
|
|
3161
|
+
config.devlog = devlog;
|
|
2962
3162
|
saveConfig(config);
|
|
2963
|
-
console.log(
|
|
3163
|
+
console.log(chalk40.green(`Added ${date} to skip list`));
|
|
2964
3164
|
}
|
|
2965
3165
|
|
|
2966
3166
|
// src/commands/devlog/version.ts
|
|
2967
|
-
import
|
|
3167
|
+
import chalk41 from "chalk";
|
|
2968
3168
|
function version() {
|
|
2969
3169
|
const config = loadConfig();
|
|
2970
3170
|
const name = getRepoName();
|
|
2971
3171
|
const lastInfo = getLastVersionInfo(name, config);
|
|
2972
3172
|
const lastVersion = lastInfo?.version ?? null;
|
|
2973
3173
|
const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
|
|
2974
|
-
console.log(`${
|
|
2975
|
-
console.log(`${
|
|
2976
|
-
console.log(`${
|
|
3174
|
+
console.log(`${chalk41.bold("name:")} ${name}`);
|
|
3175
|
+
console.log(`${chalk41.bold("last:")} ${lastVersion ?? chalk41.dim("none")}`);
|
|
3176
|
+
console.log(`${chalk41.bold("next:")} ${nextVersion ?? chalk41.dim("none")}`);
|
|
2977
3177
|
}
|
|
2978
3178
|
|
|
2979
3179
|
// src/commands/registerDevlog.ts
|
|
@@ -2996,21 +3196,21 @@ import { execSync as execSync17 } from "child_process";
|
|
|
2996
3196
|
import { execSync as execSync16 } from "child_process";
|
|
2997
3197
|
import { unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "fs";
|
|
2998
3198
|
import { tmpdir as tmpdir2 } from "os";
|
|
2999
|
-
import { join as
|
|
3199
|
+
import { join as join13 } from "path";
|
|
3000
3200
|
|
|
3001
3201
|
// src/commands/prs/loadCommentsCache.ts
|
|
3002
|
-
import { existsSync as existsSync16, readFileSync as
|
|
3003
|
-
import { join as
|
|
3202
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14, unlinkSync as unlinkSync3 } from "fs";
|
|
3203
|
+
import { join as join12 } from "path";
|
|
3004
3204
|
import { parse } from "yaml";
|
|
3005
3205
|
function getCachePath(prNumber) {
|
|
3006
|
-
return
|
|
3206
|
+
return join12(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
3007
3207
|
}
|
|
3008
3208
|
function loadCommentsCache(prNumber) {
|
|
3009
3209
|
const cachePath = getCachePath(prNumber);
|
|
3010
3210
|
if (!existsSync16(cachePath)) {
|
|
3011
3211
|
return null;
|
|
3012
3212
|
}
|
|
3013
|
-
const content =
|
|
3213
|
+
const content = readFileSync14(cachePath, "utf-8");
|
|
3014
3214
|
return parse(content);
|
|
3015
3215
|
}
|
|
3016
3216
|
function deleteCommentsCache(prNumber) {
|
|
@@ -3066,7 +3266,7 @@ function replyToComment(org, repo, prNumber, commentId, message) {
|
|
|
3066
3266
|
}
|
|
3067
3267
|
function resolveThread(threadId) {
|
|
3068
3268
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
3069
|
-
const queryFile =
|
|
3269
|
+
const queryFile = join13(tmpdir2(), `gh-mutation-${Date.now()}.graphql`);
|
|
3070
3270
|
writeFileSync13(queryFile, mutation);
|
|
3071
3271
|
try {
|
|
3072
3272
|
execSync16(
|
|
@@ -3148,7 +3348,7 @@ function fixed(commentId, sha) {
|
|
|
3148
3348
|
|
|
3149
3349
|
// src/commands/prs/listComments/index.ts
|
|
3150
3350
|
import { existsSync as existsSync17, mkdirSync as mkdirSync4, writeFileSync as writeFileSync15 } from "fs";
|
|
3151
|
-
import { join as
|
|
3351
|
+
import { join as join15 } from "path";
|
|
3152
3352
|
import { stringify } from "yaml";
|
|
3153
3353
|
|
|
3154
3354
|
// src/lib/isClaudeCode.ts
|
|
@@ -3160,10 +3360,10 @@ function isClaudeCode2() {
|
|
|
3160
3360
|
import { execSync as execSync18 } from "child_process";
|
|
3161
3361
|
import { unlinkSync as unlinkSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
3162
3362
|
import { tmpdir as tmpdir3 } from "os";
|
|
3163
|
-
import { join as
|
|
3363
|
+
import { join as join14 } from "path";
|
|
3164
3364
|
var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
|
|
3165
3365
|
function fetchThreadIds(org, repo, prNumber) {
|
|
3166
|
-
const queryFile =
|
|
3366
|
+
const queryFile = join14(tmpdir3(), `gh-query-${Date.now()}.graphql`);
|
|
3167
3367
|
writeFileSync14(queryFile, THREAD_QUERY);
|
|
3168
3368
|
try {
|
|
3169
3369
|
const result = execSync18(
|
|
@@ -3230,20 +3430,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
3230
3430
|
}
|
|
3231
3431
|
|
|
3232
3432
|
// src/commands/prs/listComments/formatForHuman.ts
|
|
3233
|
-
import
|
|
3433
|
+
import chalk42 from "chalk";
|
|
3234
3434
|
function formatForHuman(comment) {
|
|
3235
3435
|
if (comment.type === "review") {
|
|
3236
|
-
const stateColor = comment.state === "APPROVED" ?
|
|
3436
|
+
const stateColor = comment.state === "APPROVED" ? chalk42.green : comment.state === "CHANGES_REQUESTED" ? chalk42.red : chalk42.yellow;
|
|
3237
3437
|
return [
|
|
3238
|
-
`${
|
|
3438
|
+
`${chalk42.cyan("Review")} by ${chalk42.bold(comment.user)} ${stateColor(`[${comment.state}]`)}`,
|
|
3239
3439
|
comment.body,
|
|
3240
3440
|
""
|
|
3241
3441
|
].join("\n");
|
|
3242
3442
|
}
|
|
3243
3443
|
const location = comment.line ? `:${comment.line}` : "";
|
|
3244
3444
|
return [
|
|
3245
|
-
`${
|
|
3246
|
-
|
|
3445
|
+
`${chalk42.cyan("Line comment")} by ${chalk42.bold(comment.user)} on ${chalk42.dim(`${comment.path}${location}`)}`,
|
|
3446
|
+
chalk42.dim(comment.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
3247
3447
|
comment.body,
|
|
3248
3448
|
""
|
|
3249
3449
|
].join("\n");
|
|
@@ -3266,7 +3466,7 @@ function printComments(comments) {
|
|
|
3266
3466
|
}
|
|
3267
3467
|
}
|
|
3268
3468
|
function writeCommentsCache(prNumber, comments) {
|
|
3269
|
-
const assistDir =
|
|
3469
|
+
const assistDir = join15(process.cwd(), ".assist");
|
|
3270
3470
|
if (!existsSync17(assistDir)) {
|
|
3271
3471
|
mkdirSync4(assistDir, { recursive: true });
|
|
3272
3472
|
}
|
|
@@ -3275,7 +3475,7 @@ function writeCommentsCache(prNumber, comments) {
|
|
|
3275
3475
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3276
3476
|
comments
|
|
3277
3477
|
};
|
|
3278
|
-
const cachePath =
|
|
3478
|
+
const cachePath = join15(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
3279
3479
|
writeFileSync15(cachePath, stringify(cacheData));
|
|
3280
3480
|
}
|
|
3281
3481
|
function handleKnownErrors(error) {
|
|
@@ -3322,13 +3522,13 @@ import { execSync as execSync20 } from "child_process";
|
|
|
3322
3522
|
import enquirer5 from "enquirer";
|
|
3323
3523
|
|
|
3324
3524
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
3325
|
-
import
|
|
3525
|
+
import chalk43 from "chalk";
|
|
3326
3526
|
var STATUS_MAP = {
|
|
3327
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
3328
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
3527
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk43.magenta("merged"), date: pr.mergedAt } : null,
|
|
3528
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk43.red("closed"), date: pr.closedAt } : null
|
|
3329
3529
|
};
|
|
3330
3530
|
function defaultStatus(pr) {
|
|
3331
|
-
return { label:
|
|
3531
|
+
return { label: chalk43.green("opened"), date: pr.createdAt };
|
|
3332
3532
|
}
|
|
3333
3533
|
function getStatus(pr) {
|
|
3334
3534
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -3337,11 +3537,11 @@ function formatDate(dateStr) {
|
|
|
3337
3537
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
3338
3538
|
}
|
|
3339
3539
|
function formatPrHeader(pr, status) {
|
|
3340
|
-
return `${
|
|
3540
|
+
return `${chalk43.cyan(`#${pr.number}`)} ${pr.title} ${chalk43.dim(`(${pr.author.login},`)} ${status.label} ${chalk43.dim(`${formatDate(status.date)})`)}`;
|
|
3341
3541
|
}
|
|
3342
3542
|
function logPrDetails(pr) {
|
|
3343
3543
|
console.log(
|
|
3344
|
-
|
|
3544
|
+
chalk43.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
3345
3545
|
);
|
|
3346
3546
|
console.log();
|
|
3347
3547
|
}
|
|
@@ -3508,7 +3708,7 @@ import { spawn as spawn3 } from "child_process";
|
|
|
3508
3708
|
import * as path17 from "path";
|
|
3509
3709
|
|
|
3510
3710
|
// src/commands/refactor/logViolations.ts
|
|
3511
|
-
import
|
|
3711
|
+
import chalk44 from "chalk";
|
|
3512
3712
|
var DEFAULT_MAX_LINES = 100;
|
|
3513
3713
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
3514
3714
|
if (violations.length === 0) {
|
|
@@ -3517,43 +3717,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
3517
3717
|
}
|
|
3518
3718
|
return;
|
|
3519
3719
|
}
|
|
3520
|
-
console.error(
|
|
3720
|
+
console.error(chalk44.red(`
|
|
3521
3721
|
Refactor check failed:
|
|
3522
3722
|
`));
|
|
3523
|
-
console.error(
|
|
3723
|
+
console.error(chalk44.red(` The following files exceed ${maxLines} lines:
|
|
3524
3724
|
`));
|
|
3525
3725
|
for (const violation of violations) {
|
|
3526
|
-
console.error(
|
|
3726
|
+
console.error(chalk44.red(` ${violation.file} (${violation.lines} lines)`));
|
|
3527
3727
|
}
|
|
3528
3728
|
console.error(
|
|
3529
|
-
|
|
3729
|
+
chalk44.yellow(
|
|
3530
3730
|
`
|
|
3531
3731
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
3532
3732
|
way to refactor it, ignore it with:
|
|
3533
3733
|
`
|
|
3534
3734
|
)
|
|
3535
3735
|
);
|
|
3536
|
-
console.error(
|
|
3736
|
+
console.error(chalk44.gray(` assist refactor ignore <file>
|
|
3537
3737
|
`));
|
|
3538
3738
|
if (process.env.CLAUDECODE) {
|
|
3539
|
-
console.error(
|
|
3739
|
+
console.error(chalk44.cyan(`
|
|
3540
3740
|
## Extracting Code to New Files
|
|
3541
3741
|
`));
|
|
3542
3742
|
console.error(
|
|
3543
|
-
|
|
3743
|
+
chalk44.cyan(
|
|
3544
3744
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
3545
3745
|
`
|
|
3546
3746
|
)
|
|
3547
3747
|
);
|
|
3548
3748
|
console.error(
|
|
3549
|
-
|
|
3749
|
+
chalk44.cyan(
|
|
3550
3750
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
3551
3751
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
3552
3752
|
`
|
|
3553
3753
|
)
|
|
3554
3754
|
);
|
|
3555
3755
|
console.error(
|
|
3556
|
-
|
|
3756
|
+
chalk44.cyan(
|
|
3557
3757
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
3558
3758
|
domains, move it to a common/shared folder.
|
|
3559
3759
|
`
|
|
@@ -3709,11 +3909,11 @@ async function check(pattern2, options2) {
|
|
|
3709
3909
|
|
|
3710
3910
|
// src/commands/refactor/ignore.ts
|
|
3711
3911
|
import fs16 from "fs";
|
|
3712
|
-
import
|
|
3912
|
+
import chalk45 from "chalk";
|
|
3713
3913
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
3714
3914
|
function ignore(file) {
|
|
3715
3915
|
if (!fs16.existsSync(file)) {
|
|
3716
|
-
console.error(
|
|
3916
|
+
console.error(chalk45.red(`Error: File does not exist: ${file}`));
|
|
3717
3917
|
process.exit(1);
|
|
3718
3918
|
}
|
|
3719
3919
|
const content = fs16.readFileSync(file, "utf-8");
|
|
@@ -3729,7 +3929,7 @@ function ignore(file) {
|
|
|
3729
3929
|
fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
3730
3930
|
}
|
|
3731
3931
|
console.log(
|
|
3732
|
-
|
|
3932
|
+
chalk45.green(
|
|
3733
3933
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
3734
3934
|
)
|
|
3735
3935
|
);
|
|
@@ -3737,7 +3937,7 @@ function ignore(file) {
|
|
|
3737
3937
|
|
|
3738
3938
|
// src/commands/refactor/restructure/index.ts
|
|
3739
3939
|
import path26 from "path";
|
|
3740
|
-
import
|
|
3940
|
+
import chalk48 from "chalk";
|
|
3741
3941
|
|
|
3742
3942
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
3743
3943
|
import path18 from "path";
|
|
@@ -3980,50 +4180,50 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
3980
4180
|
|
|
3981
4181
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
3982
4182
|
import path22 from "path";
|
|
3983
|
-
import
|
|
4183
|
+
import chalk46 from "chalk";
|
|
3984
4184
|
function relPath(filePath) {
|
|
3985
4185
|
return path22.relative(process.cwd(), filePath);
|
|
3986
4186
|
}
|
|
3987
4187
|
function displayMoves(plan) {
|
|
3988
4188
|
if (plan.moves.length === 0) return;
|
|
3989
|
-
console.log(
|
|
4189
|
+
console.log(chalk46.bold("\nFile moves:"));
|
|
3990
4190
|
for (const move of plan.moves) {
|
|
3991
4191
|
console.log(
|
|
3992
|
-
` ${
|
|
4192
|
+
` ${chalk46.red(relPath(move.from))} \u2192 ${chalk46.green(relPath(move.to))}`
|
|
3993
4193
|
);
|
|
3994
|
-
console.log(
|
|
4194
|
+
console.log(chalk46.dim(` ${move.reason}`));
|
|
3995
4195
|
}
|
|
3996
4196
|
}
|
|
3997
4197
|
function displayRewrites(rewrites) {
|
|
3998
4198
|
if (rewrites.length === 0) return;
|
|
3999
4199
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
4000
|
-
console.log(
|
|
4200
|
+
console.log(chalk46.bold(`
|
|
4001
4201
|
Import rewrites (${affectedFiles.size} files):`));
|
|
4002
4202
|
for (const file of affectedFiles) {
|
|
4003
|
-
console.log(` ${
|
|
4203
|
+
console.log(` ${chalk46.cyan(relPath(file))}:`);
|
|
4004
4204
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
4005
4205
|
(r) => r.file === file
|
|
4006
4206
|
)) {
|
|
4007
4207
|
console.log(
|
|
4008
|
-
` ${
|
|
4208
|
+
` ${chalk46.red(`"${oldSpecifier}"`)} \u2192 ${chalk46.green(`"${newSpecifier}"`)}`
|
|
4009
4209
|
);
|
|
4010
4210
|
}
|
|
4011
4211
|
}
|
|
4012
4212
|
}
|
|
4013
4213
|
function displayPlan(plan) {
|
|
4014
4214
|
if (plan.warnings.length > 0) {
|
|
4015
|
-
console.log(
|
|
4016
|
-
for (const w of plan.warnings) console.log(
|
|
4215
|
+
console.log(chalk46.yellow("\nWarnings:"));
|
|
4216
|
+
for (const w of plan.warnings) console.log(chalk46.yellow(` ${w}`));
|
|
4017
4217
|
}
|
|
4018
4218
|
if (plan.newDirectories.length > 0) {
|
|
4019
|
-
console.log(
|
|
4219
|
+
console.log(chalk46.bold("\nNew directories:"));
|
|
4020
4220
|
for (const dir of plan.newDirectories)
|
|
4021
|
-
console.log(
|
|
4221
|
+
console.log(chalk46.green(` ${dir}/`));
|
|
4022
4222
|
}
|
|
4023
4223
|
displayMoves(plan);
|
|
4024
4224
|
displayRewrites(plan.rewrites);
|
|
4025
4225
|
console.log(
|
|
4026
|
-
|
|
4226
|
+
chalk46.dim(
|
|
4027
4227
|
`
|
|
4028
4228
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
4029
4229
|
)
|
|
@@ -4033,18 +4233,18 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
4033
4233
|
// src/commands/refactor/restructure/executePlan.ts
|
|
4034
4234
|
import fs18 from "fs";
|
|
4035
4235
|
import path23 from "path";
|
|
4036
|
-
import
|
|
4236
|
+
import chalk47 from "chalk";
|
|
4037
4237
|
function executePlan(plan) {
|
|
4038
4238
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
4039
4239
|
for (const [file, content] of updatedContents) {
|
|
4040
4240
|
fs18.writeFileSync(file, content, "utf-8");
|
|
4041
4241
|
console.log(
|
|
4042
|
-
|
|
4242
|
+
chalk47.cyan(` Rewrote imports in ${path23.relative(process.cwd(), file)}`)
|
|
4043
4243
|
);
|
|
4044
4244
|
}
|
|
4045
4245
|
for (const dir of plan.newDirectories) {
|
|
4046
4246
|
fs18.mkdirSync(dir, { recursive: true });
|
|
4047
|
-
console.log(
|
|
4247
|
+
console.log(chalk47.green(` Created ${path23.relative(process.cwd(), dir)}/`));
|
|
4048
4248
|
}
|
|
4049
4249
|
for (const move of plan.moves) {
|
|
4050
4250
|
const targetDir = path23.dirname(move.to);
|
|
@@ -4053,7 +4253,7 @@ function executePlan(plan) {
|
|
|
4053
4253
|
}
|
|
4054
4254
|
fs18.renameSync(move.from, move.to);
|
|
4055
4255
|
console.log(
|
|
4056
|
-
|
|
4256
|
+
chalk47.white(
|
|
4057
4257
|
` Moved ${path23.relative(process.cwd(), move.from)} \u2192 ${path23.relative(process.cwd(), move.to)}`
|
|
4058
4258
|
)
|
|
4059
4259
|
);
|
|
@@ -4068,7 +4268,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
4068
4268
|
if (entries.length === 0) {
|
|
4069
4269
|
fs18.rmdirSync(dir);
|
|
4070
4270
|
console.log(
|
|
4071
|
-
|
|
4271
|
+
chalk47.dim(
|
|
4072
4272
|
` Removed empty directory ${path23.relative(process.cwd(), dir)}`
|
|
4073
4273
|
)
|
|
4074
4274
|
);
|
|
@@ -4199,22 +4399,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
4199
4399
|
const targetPattern = pattern2 ?? "src";
|
|
4200
4400
|
const files = findSourceFiles2(targetPattern);
|
|
4201
4401
|
if (files.length === 0) {
|
|
4202
|
-
console.log(
|
|
4402
|
+
console.log(chalk48.yellow("No files found matching pattern"));
|
|
4203
4403
|
return;
|
|
4204
4404
|
}
|
|
4205
4405
|
const tsConfigPath = path26.resolve("tsconfig.json");
|
|
4206
4406
|
const plan = buildPlan(files, tsConfigPath);
|
|
4207
4407
|
if (plan.moves.length === 0) {
|
|
4208
|
-
console.log(
|
|
4408
|
+
console.log(chalk48.green("No restructuring needed"));
|
|
4209
4409
|
return;
|
|
4210
4410
|
}
|
|
4211
4411
|
displayPlan(plan);
|
|
4212
4412
|
if (options2.apply) {
|
|
4213
|
-
console.log(
|
|
4413
|
+
console.log(chalk48.bold("\nApplying changes..."));
|
|
4214
4414
|
executePlan(plan);
|
|
4215
|
-
console.log(
|
|
4415
|
+
console.log(chalk48.green("\nRestructuring complete"));
|
|
4216
4416
|
} else {
|
|
4217
|
-
console.log(
|
|
4417
|
+
console.log(chalk48.dim("\nDry run. Use --apply to execute."));
|
|
4218
4418
|
}
|
|
4219
4419
|
}
|
|
4220
4420
|
|
|
@@ -4238,7 +4438,7 @@ function registerRefactor(program2) {
|
|
|
4238
4438
|
|
|
4239
4439
|
// src/commands/transcript/shared.ts
|
|
4240
4440
|
import { existsSync as existsSync18, readdirSync as readdirSync2, statSync } from "fs";
|
|
4241
|
-
import { basename as basename4, join as
|
|
4441
|
+
import { basename as basename4, join as join16, relative } from "path";
|
|
4242
4442
|
import * as readline2 from "readline";
|
|
4243
4443
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
4244
4444
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -4256,7 +4456,7 @@ function collectFiles(dir, extension) {
|
|
|
4256
4456
|
if (!existsSync18(dir)) return [];
|
|
4257
4457
|
const results = [];
|
|
4258
4458
|
for (const entry of readdirSync2(dir)) {
|
|
4259
|
-
const fullPath =
|
|
4459
|
+
const fullPath = join16(dir, entry);
|
|
4260
4460
|
if (statSync(fullPath).isDirectory()) {
|
|
4261
4461
|
results.push(...collectFiles(fullPath, extension));
|
|
4262
4462
|
} else if (entry.endsWith(extension)) {
|
|
@@ -4333,7 +4533,7 @@ function validateDirectories(transcript) {
|
|
|
4333
4533
|
}
|
|
4334
4534
|
async function configure() {
|
|
4335
4535
|
const rl = createReadlineInterface();
|
|
4336
|
-
const config =
|
|
4536
|
+
const config = loadProjectConfig();
|
|
4337
4537
|
const existing = config.transcript;
|
|
4338
4538
|
console.log("Configure transcript directories\n");
|
|
4339
4539
|
if (existing) printExisting(existing);
|
|
@@ -4353,11 +4553,11 @@ async function configure() {
|
|
|
4353
4553
|
import { existsSync as existsSync20 } from "fs";
|
|
4354
4554
|
|
|
4355
4555
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
4356
|
-
import { dirname as
|
|
4556
|
+
import { dirname as dirname13, join as join18 } from "path";
|
|
4357
4557
|
|
|
4358
4558
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
4359
4559
|
import { renameSync } from "fs";
|
|
4360
|
-
import { join as
|
|
4560
|
+
import { join as join17 } from "path";
|
|
4361
4561
|
async function resolveDate(rl, choice) {
|
|
4362
4562
|
if (choice === "1") return getDatePrefix(0);
|
|
4363
4563
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -4372,7 +4572,7 @@ async function resolveDate(rl, choice) {
|
|
|
4372
4572
|
}
|
|
4373
4573
|
function renameWithPrefix(vttDir, vttFile, prefix) {
|
|
4374
4574
|
const newFilename = `${prefix}.${vttFile}`;
|
|
4375
|
-
renameSync(
|
|
4575
|
+
renameSync(join17(vttDir, vttFile), join17(vttDir, newFilename));
|
|
4376
4576
|
console.log(`Renamed to: ${newFilename}`);
|
|
4377
4577
|
return newFilename;
|
|
4378
4578
|
}
|
|
@@ -4403,15 +4603,15 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
4403
4603
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
4404
4604
|
const vttFile = vttFiles[i];
|
|
4405
4605
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
4406
|
-
const vttFileDir =
|
|
4606
|
+
const vttFileDir = dirname13(vttFile.absolutePath);
|
|
4407
4607
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
4408
4608
|
if (newFilename) {
|
|
4409
|
-
const newRelativePath =
|
|
4410
|
-
|
|
4609
|
+
const newRelativePath = join18(
|
|
4610
|
+
dirname13(vttFile.relativePath),
|
|
4411
4611
|
newFilename
|
|
4412
4612
|
);
|
|
4413
4613
|
vttFiles[i] = {
|
|
4414
|
-
absolutePath:
|
|
4614
|
+
absolutePath: join18(vttFileDir, newFilename),
|
|
4415
4615
|
relativePath: newRelativePath,
|
|
4416
4616
|
filename: newFilename
|
|
4417
4617
|
};
|
|
@@ -4424,8 +4624,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
4424
4624
|
}
|
|
4425
4625
|
|
|
4426
4626
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
4427
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as
|
|
4428
|
-
import { basename as basename5, dirname as
|
|
4627
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync15, writeFileSync as writeFileSync16 } from "fs";
|
|
4628
|
+
import { basename as basename5, dirname as dirname14, join as join19 } from "path";
|
|
4429
4629
|
|
|
4430
4630
|
// src/commands/transcript/cleanText.ts
|
|
4431
4631
|
function cleanText(text) {
|
|
@@ -4635,17 +4835,17 @@ function toMdFilename(vttFilename) {
|
|
|
4635
4835
|
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
4636
4836
|
}
|
|
4637
4837
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
4638
|
-
return relativeDir === "." ? transcriptsDir :
|
|
4838
|
+
return relativeDir === "." ? transcriptsDir : join19(transcriptsDir, relativeDir);
|
|
4639
4839
|
}
|
|
4640
4840
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
4641
4841
|
const mdFile = toMdFilename(vttFile.filename);
|
|
4642
|
-
const relativeDir =
|
|
4842
|
+
const relativeDir = dirname14(vttFile.relativePath);
|
|
4643
4843
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
4644
|
-
const outputPath =
|
|
4844
|
+
const outputPath = join19(outputDir, mdFile);
|
|
4645
4845
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
4646
4846
|
}
|
|
4647
4847
|
function logSkipped(relativeDir, mdFile) {
|
|
4648
|
-
console.log(`Skipping (already exists): ${
|
|
4848
|
+
console.log(`Skipping (already exists): ${join19(relativeDir, mdFile)}`);
|
|
4649
4849
|
return "skipped";
|
|
4650
4850
|
}
|
|
4651
4851
|
function ensureDirectory(dir, label2) {
|
|
@@ -4672,7 +4872,7 @@ function logReduction(cueCount, messageCount) {
|
|
|
4672
4872
|
}
|
|
4673
4873
|
function readAndParseCues(inputPath) {
|
|
4674
4874
|
console.log(`Reading: ${inputPath}`);
|
|
4675
|
-
return processCues(
|
|
4875
|
+
return processCues(readFileSync15(inputPath, "utf-8"));
|
|
4676
4876
|
}
|
|
4677
4877
|
function writeFormatted(outputPath, content) {
|
|
4678
4878
|
writeFileSync16(outputPath, content, "utf-8");
|
|
@@ -4744,27 +4944,27 @@ async function format() {
|
|
|
4744
4944
|
|
|
4745
4945
|
// src/commands/transcript/summarise/index.ts
|
|
4746
4946
|
import { existsSync as existsSync22 } from "fs";
|
|
4747
|
-
import { basename as basename6, dirname as
|
|
4947
|
+
import { basename as basename6, dirname as dirname16, join as join21, relative as relative2 } from "path";
|
|
4748
4948
|
|
|
4749
4949
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
4750
4950
|
import {
|
|
4751
4951
|
existsSync as existsSync21,
|
|
4752
4952
|
mkdirSync as mkdirSync6,
|
|
4753
|
-
readFileSync as
|
|
4953
|
+
readFileSync as readFileSync16,
|
|
4754
4954
|
renameSync as renameSync2,
|
|
4755
4955
|
rmSync
|
|
4756
4956
|
} from "fs";
|
|
4757
|
-
import { dirname as
|
|
4957
|
+
import { dirname as dirname15, join as join20 } from "path";
|
|
4758
4958
|
|
|
4759
4959
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
4760
|
-
import
|
|
4960
|
+
import chalk49 from "chalk";
|
|
4761
4961
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
4762
4962
|
function validateStagedContent(filename, content) {
|
|
4763
4963
|
const firstLine = content.split("\n")[0];
|
|
4764
4964
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
4765
4965
|
if (!match) {
|
|
4766
4966
|
console.error(
|
|
4767
|
-
|
|
4967
|
+
chalk49.red(
|
|
4768
4968
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
4769
4969
|
)
|
|
4770
4970
|
);
|
|
@@ -4773,7 +4973,7 @@ function validateStagedContent(filename, content) {
|
|
|
4773
4973
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
4774
4974
|
if (!contentAfterLink) {
|
|
4775
4975
|
console.error(
|
|
4776
|
-
|
|
4976
|
+
chalk49.red(
|
|
4777
4977
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
4778
4978
|
)
|
|
4779
4979
|
);
|
|
@@ -4783,7 +4983,7 @@ function validateStagedContent(filename, content) {
|
|
|
4783
4983
|
}
|
|
4784
4984
|
|
|
4785
4985
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
4786
|
-
var STAGING_DIR =
|
|
4986
|
+
var STAGING_DIR = join20(process.cwd(), ".assist", "transcript");
|
|
4787
4987
|
function processStagedFile() {
|
|
4788
4988
|
if (!existsSync21(STAGING_DIR)) {
|
|
4789
4989
|
return false;
|
|
@@ -4794,7 +4994,7 @@ function processStagedFile() {
|
|
|
4794
4994
|
}
|
|
4795
4995
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
4796
4996
|
const stagedFile = stagedFiles[0];
|
|
4797
|
-
const content =
|
|
4997
|
+
const content = readFileSync16(stagedFile.absolutePath, "utf-8");
|
|
4798
4998
|
validateStagedContent(stagedFile.filename, content);
|
|
4799
4999
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
4800
5000
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -4807,8 +5007,8 @@ function processStagedFile() {
|
|
|
4807
5007
|
);
|
|
4808
5008
|
process.exit(1);
|
|
4809
5009
|
}
|
|
4810
|
-
const destPath =
|
|
4811
|
-
const destDir =
|
|
5010
|
+
const destPath = join20(summaryDir, matchingTranscript.relativePath);
|
|
5011
|
+
const destDir = dirname15(destPath);
|
|
4812
5012
|
if (!existsSync21(destDir)) {
|
|
4813
5013
|
mkdirSync6(destDir, { recursive: true });
|
|
4814
5014
|
}
|
|
@@ -4822,8 +5022,8 @@ function processStagedFile() {
|
|
|
4822
5022
|
|
|
4823
5023
|
// src/commands/transcript/summarise/index.ts
|
|
4824
5024
|
function buildRelativeKey(relativePath, baseName) {
|
|
4825
|
-
const relDir =
|
|
4826
|
-
return relDir === "." ? baseName :
|
|
5025
|
+
const relDir = dirname16(relativePath);
|
|
5026
|
+
return relDir === "." ? baseName : join21(relDir, baseName);
|
|
4827
5027
|
}
|
|
4828
5028
|
function buildSummaryIndex(summaryDir) {
|
|
4829
5029
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -4857,8 +5057,8 @@ function summarise() {
|
|
|
4857
5057
|
}
|
|
4858
5058
|
const next2 = missing[0];
|
|
4859
5059
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
4860
|
-
const outputPath =
|
|
4861
|
-
const summaryFileDir =
|
|
5060
|
+
const outputPath = join21(STAGING_DIR, outputFilename);
|
|
5061
|
+
const summaryFileDir = join21(summaryDir, dirname16(next2.relativePath));
|
|
4862
5062
|
const relativeTranscriptPath = encodeURI(
|
|
4863
5063
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
4864
5064
|
);
|
|
@@ -4891,7 +5091,7 @@ function registerVerify(program2) {
|
|
|
4891
5091
|
|
|
4892
5092
|
// src/commands/roam/auth.ts
|
|
4893
5093
|
import { randomBytes } from "crypto";
|
|
4894
|
-
import
|
|
5094
|
+
import chalk50 from "chalk";
|
|
4895
5095
|
|
|
4896
5096
|
// src/lib/openBrowser.ts
|
|
4897
5097
|
import { execSync as execSync23 } from "child_process";
|
|
@@ -4938,7 +5138,7 @@ ${url}`);
|
|
|
4938
5138
|
}
|
|
4939
5139
|
|
|
4940
5140
|
// src/commands/roam/waitForCallback.ts
|
|
4941
|
-
import { createServer } from "http";
|
|
5141
|
+
import { createServer as createServer2 } from "http";
|
|
4942
5142
|
function respondHtml(res, status, title) {
|
|
4943
5143
|
res.writeHead(status, { "Content-Type": "text/html" });
|
|
4944
5144
|
res.end(
|
|
@@ -4961,7 +5161,7 @@ function waitForCallback(port, expectedState) {
|
|
|
4961
5161
|
server.close();
|
|
4962
5162
|
reject(new Error("Authorization timed out after 120 seconds"));
|
|
4963
5163
|
}, 12e4);
|
|
4964
|
-
const server =
|
|
5164
|
+
const server = createServer2((req, res) => {
|
|
4965
5165
|
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
4966
5166
|
if (url.pathname !== "/callback") {
|
|
4967
5167
|
res.writeHead(404);
|
|
@@ -5057,19 +5257,22 @@ async function promptCredentials(existing) {
|
|
|
5057
5257
|
|
|
5058
5258
|
// src/commands/roam/auth.ts
|
|
5059
5259
|
async function auth() {
|
|
5060
|
-
const config =
|
|
5061
|
-
const { clientId, clientSecret } = await promptCredentials(
|
|
5062
|
-
|
|
5260
|
+
const config = loadGlobalConfigRaw();
|
|
5261
|
+
const { clientId, clientSecret } = await promptCredentials(
|
|
5262
|
+
config.roam
|
|
5263
|
+
);
|
|
5264
|
+
const existingRoam = config.roam ?? {};
|
|
5265
|
+
config.roam = { ...existingRoam, clientId, clientSecret };
|
|
5063
5266
|
saveGlobalConfig(config);
|
|
5064
5267
|
const state = randomBytes(16).toString("hex");
|
|
5065
5268
|
console.log(
|
|
5066
|
-
|
|
5269
|
+
chalk50.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
5067
5270
|
);
|
|
5068
|
-
console.log(
|
|
5069
|
-
console.log(
|
|
5070
|
-
console.log(
|
|
5271
|
+
console.log(chalk50.white("http://localhost:14523/callback\n"));
|
|
5272
|
+
console.log(chalk50.blue("Opening browser for authorization..."));
|
|
5273
|
+
console.log(chalk50.dim("Waiting for authorization callback..."));
|
|
5071
5274
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
5072
|
-
console.log(
|
|
5275
|
+
console.log(chalk50.dim("Exchanging code for tokens..."));
|
|
5073
5276
|
const tokens = await exchangeToken({
|
|
5074
5277
|
code,
|
|
5075
5278
|
clientId,
|
|
@@ -5085,7 +5288,7 @@ async function auth() {
|
|
|
5085
5288
|
};
|
|
5086
5289
|
saveGlobalConfig(config);
|
|
5087
5290
|
console.log(
|
|
5088
|
-
|
|
5291
|
+
chalk50.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
5089
5292
|
);
|
|
5090
5293
|
}
|
|
5091
5294
|
|
|
@@ -5100,7 +5303,7 @@ import { spawn as spawn4 } from "child_process";
|
|
|
5100
5303
|
|
|
5101
5304
|
// src/commands/run/add.ts
|
|
5102
5305
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync17 } from "fs";
|
|
5103
|
-
import { join as
|
|
5306
|
+
import { join as join22 } from "path";
|
|
5104
5307
|
function findAddIndex() {
|
|
5105
5308
|
const addIndex = process.argv.indexOf("add");
|
|
5106
5309
|
if (addIndex === -1 || addIndex + 2 >= process.argv.length) return -1;
|
|
@@ -5143,7 +5346,7 @@ function requireParsedArgs() {
|
|
|
5143
5346
|
return parsed;
|
|
5144
5347
|
}
|
|
5145
5348
|
function getOrInitRunList() {
|
|
5146
|
-
const config =
|
|
5349
|
+
const config = loadProjectConfig();
|
|
5147
5350
|
if (!config.run) config.run = [];
|
|
5148
5351
|
return { config, runList: config.run };
|
|
5149
5352
|
}
|
|
@@ -5154,7 +5357,7 @@ function saveNewRunConfig(name, command, args) {
|
|
|
5154
5357
|
saveConfig(config);
|
|
5155
5358
|
}
|
|
5156
5359
|
function createCommandFile(name) {
|
|
5157
|
-
const dir =
|
|
5360
|
+
const dir = join22(".claude", "commands");
|
|
5158
5361
|
mkdirSync7(dir, { recursive: true });
|
|
5159
5362
|
const content = `---
|
|
5160
5363
|
description: Run ${name}
|
|
@@ -5162,7 +5365,7 @@ description: Run ${name}
|
|
|
5162
5365
|
|
|
5163
5366
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
5164
5367
|
`;
|
|
5165
|
-
const filePath =
|
|
5368
|
+
const filePath = join22(dir, `${name}.md`);
|
|
5166
5369
|
writeFileSync17(filePath, content);
|
|
5167
5370
|
console.log(`Created command file: ${filePath}`);
|
|
5168
5371
|
}
|
|
@@ -5243,12 +5446,12 @@ async function statusLine() {
|
|
|
5243
5446
|
import * as fs23 from "fs";
|
|
5244
5447
|
import * as os from "os";
|
|
5245
5448
|
import * as path29 from "path";
|
|
5246
|
-
import { fileURLToPath as
|
|
5449
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5247
5450
|
|
|
5248
5451
|
// src/commands/sync/syncClaudeMd.ts
|
|
5249
5452
|
import * as fs21 from "fs";
|
|
5250
5453
|
import * as path27 from "path";
|
|
5251
|
-
import
|
|
5454
|
+
import chalk51 from "chalk";
|
|
5252
5455
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
5253
5456
|
const source = path27.join(claudeDir, "CLAUDE.md");
|
|
5254
5457
|
const target = path27.join(targetBase, "CLAUDE.md");
|
|
@@ -5257,12 +5460,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
5257
5460
|
const targetContent = fs21.readFileSync(target, "utf-8");
|
|
5258
5461
|
if (sourceContent !== targetContent) {
|
|
5259
5462
|
console.log(
|
|
5260
|
-
|
|
5463
|
+
chalk51.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
5261
5464
|
);
|
|
5262
5465
|
console.log();
|
|
5263
5466
|
printDiff(targetContent, sourceContent);
|
|
5264
5467
|
const confirm = await promptConfirm(
|
|
5265
|
-
|
|
5468
|
+
chalk51.red("Overwrite existing CLAUDE.md?"),
|
|
5266
5469
|
false
|
|
5267
5470
|
);
|
|
5268
5471
|
if (!confirm) {
|
|
@@ -5278,7 +5481,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
5278
5481
|
// src/commands/sync/syncSettings.ts
|
|
5279
5482
|
import * as fs22 from "fs";
|
|
5280
5483
|
import * as path28 from "path";
|
|
5281
|
-
import
|
|
5484
|
+
import chalk52 from "chalk";
|
|
5282
5485
|
async function syncSettings(claudeDir, targetBase) {
|
|
5283
5486
|
const source = path28.join(claudeDir, "settings.json");
|
|
5284
5487
|
const target = path28.join(targetBase, "settings.json");
|
|
@@ -5289,12 +5492,12 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
5289
5492
|
const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
|
|
5290
5493
|
if (normalizedSource !== normalizedTarget) {
|
|
5291
5494
|
console.log(
|
|
5292
|
-
|
|
5495
|
+
chalk52.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
|
|
5293
5496
|
);
|
|
5294
5497
|
console.log();
|
|
5295
5498
|
printDiff(targetContent, sourceContent);
|
|
5296
5499
|
const confirm = await promptConfirm(
|
|
5297
|
-
|
|
5500
|
+
chalk52.red("Overwrite existing settings.json?"),
|
|
5298
5501
|
false
|
|
5299
5502
|
);
|
|
5300
5503
|
if (!confirm) {
|
|
@@ -5308,10 +5511,10 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
5308
5511
|
}
|
|
5309
5512
|
|
|
5310
5513
|
// src/commands/sync.ts
|
|
5311
|
-
var __filename2 =
|
|
5312
|
-
var
|
|
5514
|
+
var __filename2 = fileURLToPath4(import.meta.url);
|
|
5515
|
+
var __dirname5 = path29.dirname(__filename2);
|
|
5313
5516
|
async function sync() {
|
|
5314
|
-
const claudeDir = path29.join(
|
|
5517
|
+
const claudeDir = path29.join(__dirname5, "..", "claude");
|
|
5315
5518
|
const targetBase = path29.join(os.homedir(), ".claude");
|
|
5316
5519
|
syncCommands(claudeDir, targetBase);
|
|
5317
5520
|
await syncSettings(claudeDir, targetBase);
|
|
@@ -5332,11 +5535,11 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
5332
5535
|
// src/commands/update.ts
|
|
5333
5536
|
import { execSync as execSync24 } from "child_process";
|
|
5334
5537
|
import * as path30 from "path";
|
|
5335
|
-
import { fileURLToPath as
|
|
5336
|
-
var __filename3 =
|
|
5337
|
-
var
|
|
5538
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5539
|
+
var __filename3 = fileURLToPath5(import.meta.url);
|
|
5540
|
+
var __dirname6 = path30.dirname(__filename3);
|
|
5338
5541
|
function getInstallDir() {
|
|
5339
|
-
return path30.resolve(
|
|
5542
|
+
return path30.resolve(__dirname6, "..");
|
|
5340
5543
|
}
|
|
5341
5544
|
function isGitRepo(dir) {
|
|
5342
5545
|
try {
|