@workscribe/cli 2.0.0 → 2.0.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/dist/index.js +569 -195
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1374,6 +1374,34 @@ var init_migrate = __esm({
|
|
|
1374
1374
|
}
|
|
1375
1375
|
});
|
|
1376
1376
|
|
|
1377
|
+
// src/db/session-lookup.ts
|
|
1378
|
+
function findRecentSessionsForRepo(db, lookup, limit) {
|
|
1379
|
+
return db.prepare(RECENT_SESSIONS_FOR_REPO).all({
|
|
1380
|
+
repo_name: lookup.repoName,
|
|
1381
|
+
other_repo_name: lookup.otherRepoName,
|
|
1382
|
+
roots: JSON.stringify(lookup.roots),
|
|
1383
|
+
limit
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
var RECENT_SESSIONS_FOR_REPO;
|
|
1387
|
+
var init_session_lookup = __esm({
|
|
1388
|
+
"src/db/session-lookup.ts"() {
|
|
1389
|
+
"use strict";
|
|
1390
|
+
RECENT_SESSIONS_FOR_REPO = `
|
|
1391
|
+
SELECT s.* FROM sessions s
|
|
1392
|
+
WHERE s.repo_name = $repo_name
|
|
1393
|
+
OR (s.repo_name = $other_repo_name AND EXISTS (
|
|
1394
|
+
SELECT 1 FROM session_events se
|
|
1395
|
+
INNER JOIN events e ON e.id = se.event_id
|
|
1396
|
+
INNER JOIN json_each($roots) root
|
|
1397
|
+
WHERE se.session_id = s.id
|
|
1398
|
+
AND (e.cwd = root.value OR substr(e.cwd, 1, length(root.value) + 1) = root.value || '/')
|
|
1399
|
+
))
|
|
1400
|
+
ORDER BY s.started_at DESC
|
|
1401
|
+
LIMIT $limit`;
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1377
1405
|
// src/db/index.ts
|
|
1378
1406
|
function getDb() {
|
|
1379
1407
|
if (_db) return _db;
|
|
@@ -1404,9 +1432,8 @@ function insertEvent(params) {
|
|
|
1404
1432
|
`);
|
|
1405
1433
|
stmt.run(params);
|
|
1406
1434
|
}
|
|
1407
|
-
function
|
|
1408
|
-
|
|
1409
|
-
return db.prepare("SELECT * FROM sessions WHERE repo_name = $repo_name ORDER BY started_at DESC LIMIT $limit").all({ repo_name: repoName, limit });
|
|
1435
|
+
function getRecentSessionsForRepo(lookup, limit) {
|
|
1436
|
+
return findRecentSessionsForRepo(getDb(), lookup, limit);
|
|
1410
1437
|
}
|
|
1411
1438
|
function getLastSessionTouchingFile(filePath) {
|
|
1412
1439
|
const db = getDb();
|
|
@@ -1572,6 +1599,7 @@ var init_db = __esm({
|
|
|
1572
1599
|
init_paths();
|
|
1573
1600
|
init_migrations();
|
|
1574
1601
|
init_migrate();
|
|
1602
|
+
init_session_lookup();
|
|
1575
1603
|
_db = null;
|
|
1576
1604
|
}
|
|
1577
1605
|
});
|
|
@@ -1627,7 +1655,7 @@ var init_redact = __esm({
|
|
|
1627
1655
|
}
|
|
1628
1656
|
});
|
|
1629
1657
|
|
|
1630
|
-
// src/capture/
|
|
1658
|
+
// src/capture/repo-name.ts
|
|
1631
1659
|
function parseOwnerAndRepo(url) {
|
|
1632
1660
|
const normalized = url.trim().replace(/^[^/]+:(?!\/\/)/, (match) => match.replace(":", "/"));
|
|
1633
1661
|
const segments = normalized.split("/").filter(Boolean);
|
|
@@ -1636,16 +1664,24 @@ function parseOwnerAndRepo(url) {
|
|
|
1636
1664
|
const owner = segments[segments.length - 2];
|
|
1637
1665
|
return owner && repo ? { owner, repo } : null;
|
|
1638
1666
|
}
|
|
1639
|
-
function
|
|
1667
|
+
function formatRepoName(remote, qualify) {
|
|
1668
|
+
return qualify ? `${remote.owner}/${remote.repo}` : remote.repo;
|
|
1669
|
+
}
|
|
1670
|
+
var init_repo_name = __esm({
|
|
1671
|
+
"src/capture/repo-name.ts"() {
|
|
1672
|
+
"use strict";
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
// src/capture/git.ts
|
|
1677
|
+
function remoteOf(root) {
|
|
1640
1678
|
try {
|
|
1641
1679
|
const url = (0, import_child_process.execSync)("git remote get-url origin", {
|
|
1642
1680
|
cwd: root,
|
|
1643
1681
|
timeout: 2e3,
|
|
1644
1682
|
stdio: "pipe"
|
|
1645
1683
|
}).toString().trim();
|
|
1646
|
-
|
|
1647
|
-
if (!parsed) return null;
|
|
1648
|
-
return qualifyWithOwner ? `${parsed.owner}/${parsed.repo}` : parsed.repo;
|
|
1684
|
+
return parseOwnerAndRepo(url);
|
|
1649
1685
|
} catch {
|
|
1650
1686
|
return null;
|
|
1651
1687
|
}
|
|
@@ -1660,35 +1696,46 @@ function nameFromPackageJson(root) {
|
|
|
1660
1696
|
return null;
|
|
1661
1697
|
}
|
|
1662
1698
|
}
|
|
1663
|
-
function
|
|
1699
|
+
function currentBranch(cwd) {
|
|
1700
|
+
try {
|
|
1701
|
+
return (0, import_child_process.execSync)("git branch --show-current", {
|
|
1702
|
+
cwd,
|
|
1703
|
+
timeout: 2e3,
|
|
1704
|
+
stdio: "pipe"
|
|
1705
|
+
}).toString().trim() || null;
|
|
1706
|
+
} catch {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
function nameRepo(root, nameOverride, qualifyRepoName) {
|
|
1711
|
+
if (nameOverride !== null) return { repoName: nameOverride, remote: null };
|
|
1712
|
+
const remote = remoteOf(root);
|
|
1713
|
+
if (remote) return { repoName: formatRepoName(remote, qualifyRepoName), remote };
|
|
1714
|
+
return { repoName: nameFromPackageJson(root) ?? import_path6.default.basename(root), remote: null };
|
|
1715
|
+
}
|
|
1716
|
+
function getRepoContext(cwd, config) {
|
|
1717
|
+
let root;
|
|
1664
1718
|
try {
|
|
1665
|
-
|
|
1719
|
+
root = (0, import_child_process.execSync)("git rev-parse --show-toplevel", {
|
|
1666
1720
|
cwd,
|
|
1667
1721
|
timeout: 2e3,
|
|
1668
1722
|
stdio: "pipe"
|
|
1669
1723
|
}).toString().trim();
|
|
1670
|
-
const repoName = nameOverride ?? nameFromRemote(root, qualifyRepoName) ?? nameFromPackageJson(root) ?? import_path6.default.basename(root);
|
|
1671
|
-
let branch = null;
|
|
1672
|
-
try {
|
|
1673
|
-
branch = (0, import_child_process.execSync)("git branch --show-current", {
|
|
1674
|
-
cwd,
|
|
1675
|
-
timeout: 2e3,
|
|
1676
|
-
stdio: "pipe"
|
|
1677
|
-
}).toString().trim() || null;
|
|
1678
|
-
} catch {
|
|
1679
|
-
}
|
|
1680
|
-
return { repoName, branch };
|
|
1681
1724
|
} catch {
|
|
1682
|
-
return
|
|
1725
|
+
return NOT_A_REPO;
|
|
1683
1726
|
}
|
|
1727
|
+
const naming = nameRepo(root, config.projects?.[cwd] ?? null, config.git.qualifyRepoName);
|
|
1728
|
+
return { ...naming, branch: currentBranch(cwd), root };
|
|
1684
1729
|
}
|
|
1685
|
-
var import_child_process, import_path6, import_fs8;
|
|
1730
|
+
var import_child_process, import_path6, import_fs8, NOT_A_REPO;
|
|
1686
1731
|
var init_git = __esm({
|
|
1687
1732
|
"src/capture/git.ts"() {
|
|
1688
1733
|
"use strict";
|
|
1689
1734
|
import_child_process = require("child_process");
|
|
1690
1735
|
import_path6 = __toESM(require("path"));
|
|
1691
1736
|
import_fs8 = __toESM(require("fs"));
|
|
1737
|
+
init_repo_name();
|
|
1738
|
+
NOT_A_REPO = { repoName: null, branch: null, root: null, remote: null };
|
|
1692
1739
|
}
|
|
1693
1740
|
});
|
|
1694
1741
|
|
|
@@ -1818,8 +1865,7 @@ function captureCommand(opts) {
|
|
|
1818
1865
|
}
|
|
1819
1866
|
}
|
|
1820
1867
|
const command2 = opts.command ?? opts.file ?? opts.type;
|
|
1821
|
-
const
|
|
1822
|
-
const { repoName: repoName2, branch: branch2 } = getGitContext(cwd, nameOverride2, config.git.qualifyRepoName);
|
|
1868
|
+
const { repoName: repoName2, branch: branch2 } = getRepoContext(cwd, config);
|
|
1823
1869
|
insertEvent({
|
|
1824
1870
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1825
1871
|
command: command2,
|
|
@@ -1840,8 +1886,7 @@ function captureCommand(opts) {
|
|
|
1840
1886
|
const last = getLastEvent();
|
|
1841
1887
|
if (isDuplicate(command, cwd, last ?? void 0)) return;
|
|
1842
1888
|
const safeCommand = redact(command, config.redact.extra);
|
|
1843
|
-
const
|
|
1844
|
-
const { repoName, branch } = getGitContext(cwd, nameOverride);
|
|
1889
|
+
const { repoName, branch } = getRepoContext(cwd, config);
|
|
1845
1890
|
const { category, metadata } = normalizeCommand(safeCommand);
|
|
1846
1891
|
if (category === "ai_session" && config.capture.aiTools === false) return;
|
|
1847
1892
|
if (category === "other") {
|
|
@@ -2105,10 +2150,112 @@ var init_completion_scripts = __esm({
|
|
|
2105
2150
|
}
|
|
2106
2151
|
});
|
|
2107
2152
|
|
|
2153
|
+
// src/utils/backup.ts
|
|
2154
|
+
function backUpFile(file) {
|
|
2155
|
+
const base = `${file}.workscribe-backup-${Date.now()}`;
|
|
2156
|
+
for (let attempt = 1; attempt <= MAX_BACKUP_ATTEMPTS; attempt++) {
|
|
2157
|
+
const backup = attempt === 1 ? base : `${base}-${attempt}`;
|
|
2158
|
+
try {
|
|
2159
|
+
import_fs12.default.copyFileSync(file, backup, import_fs12.default.constants.COPYFILE_EXCL);
|
|
2160
|
+
return backup;
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
if (error.code !== "EEXIST") throw error;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
throw new Error(`Couldn't back up ${file}: too many backups named ${base}`);
|
|
2166
|
+
}
|
|
2167
|
+
var import_fs12, MAX_BACKUP_ATTEMPTS;
|
|
2168
|
+
var init_backup = __esm({
|
|
2169
|
+
"src/utils/backup.ts"() {
|
|
2170
|
+
"use strict";
|
|
2171
|
+
import_fs12 = __toESM(require("fs"));
|
|
2172
|
+
MAX_BACKUP_ATTEMPTS = 100;
|
|
2173
|
+
}
|
|
2174
|
+
});
|
|
2175
|
+
|
|
2176
|
+
// src/capture/completions-block.ts
|
|
2177
|
+
function completionsBlock(shellName, file) {
|
|
2178
|
+
const guardedSource = shellName === "zsh" ? `if [ -f "${file}" ]; then ${ZSH_COMPINIT}; source "${file}"; fi` : `[ -f "${file}" ] && source "${file}"`;
|
|
2179
|
+
return `${COMPLETIONS_MARKER}
|
|
2180
|
+
${guardedSource}`;
|
|
2181
|
+
}
|
|
2182
|
+
function splitLines(content) {
|
|
2183
|
+
return { lines: content.split("\n"), lineEnding: content.includes("\r\n") ? "\r" : "" };
|
|
2184
|
+
}
|
|
2185
|
+
function bare(line) {
|
|
2186
|
+
return (line ?? "").replace(/\r$/, "");
|
|
2187
|
+
}
|
|
2188
|
+
function blockLengthAfterMarker(lines, markerIndex) {
|
|
2189
|
+
const next = bare(lines[markerIndex + 1]);
|
|
2190
|
+
if (GUARDED_ZSH.test(next) || GUARDED_BASH.test(next) || UNGUARDED_SOURCE.test(next)) return 1;
|
|
2191
|
+
if (next === ZSH_COMPINIT && UNGUARDED_SOURCE.test(bare(lines[markerIndex + 2]))) return 2;
|
|
2192
|
+
return 0;
|
|
2193
|
+
}
|
|
2194
|
+
function findBlocks(lines) {
|
|
2195
|
+
const blocks = [];
|
|
2196
|
+
for (let index = 0; index < lines.length; index++) {
|
|
2197
|
+
if (bare(lines[index]) !== COMPLETIONS_MARKER) continue;
|
|
2198
|
+
const length = blockLengthAfterMarker(lines, index);
|
|
2199
|
+
if (length === 0) continue;
|
|
2200
|
+
blocks.push({ first: index, last: index + length });
|
|
2201
|
+
index += length;
|
|
2202
|
+
}
|
|
2203
|
+
return blocks;
|
|
2204
|
+
}
|
|
2205
|
+
function blockText(lines, block) {
|
|
2206
|
+
return lines.slice(block.first, block.last + 1).map(bare).join("\n");
|
|
2207
|
+
}
|
|
2208
|
+
function hasCompletionsBlock(content) {
|
|
2209
|
+
return findBlocks(splitLines(content).lines).length > 0;
|
|
2210
|
+
}
|
|
2211
|
+
function isCompletionsBlockOutdated(content, currentBlock) {
|
|
2212
|
+
const { lines } = splitLines(content);
|
|
2213
|
+
const blocks = findBlocks(lines);
|
|
2214
|
+
if (blocks.length === 0) return false;
|
|
2215
|
+
return blocks.length > 1 || blockText(lines, blocks[0]) !== currentBlock;
|
|
2216
|
+
}
|
|
2217
|
+
function rebuild(content, replacement) {
|
|
2218
|
+
const { lines, lineEnding } = splitLines(content);
|
|
2219
|
+
const blocks = findBlocks(lines);
|
|
2220
|
+
if (blocks.length === 0) return content;
|
|
2221
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
2222
|
+
blocks.forEach((block, blockIndex) => {
|
|
2223
|
+
const isReplaced = blockIndex === 0 && replacement !== null;
|
|
2224
|
+
for (let index = block.first; index <= block.last; index++) dropped.add(index);
|
|
2225
|
+
if (!isReplaced && block.first > 0 && bare(lines[block.first - 1]) === "") dropped.add(block.first - 1);
|
|
2226
|
+
});
|
|
2227
|
+
const replacementLines = replacement === null ? [] : replacement.split("\n").map((line) => line + lineEnding);
|
|
2228
|
+
return lines.flatMap((line, index) => {
|
|
2229
|
+
if (replacement !== null && index === blocks[0].first) return replacementLines;
|
|
2230
|
+
return dropped.has(index) ? [] : [line];
|
|
2231
|
+
}).join("\n");
|
|
2232
|
+
}
|
|
2233
|
+
function replaceCompletionsBlock(content, newBlock) {
|
|
2234
|
+
return rebuild(content, newBlock);
|
|
2235
|
+
}
|
|
2236
|
+
function removeCompletionsBlock(content) {
|
|
2237
|
+
return rebuild(content, null);
|
|
2238
|
+
}
|
|
2239
|
+
var COMPLETIONS_MARKER, ZSH_COMPINIT, OWN_SCRIPT, GUARDED_ZSH, GUARDED_BASH, UNGUARDED_SOURCE;
|
|
2240
|
+
var init_completions_block = __esm({
|
|
2241
|
+
"src/capture/completions-block.ts"() {
|
|
2242
|
+
"use strict";
|
|
2243
|
+
COMPLETIONS_MARKER = "# Workscribe completions";
|
|
2244
|
+
ZSH_COMPINIT = "autoload -Uz compinit && compinit";
|
|
2245
|
+
OWN_SCRIPT = '"[^"]*completion\\.(?:zsh|bash)"';
|
|
2246
|
+
GUARDED_ZSH = new RegExp(`^if \\[ -f ${OWN_SCRIPT} \\]; then ${ZSH_COMPINIT}; source ${OWN_SCRIPT}; fi$`);
|
|
2247
|
+
GUARDED_BASH = new RegExp(`^\\[ -f ${OWN_SCRIPT} \\] && source ${OWN_SCRIPT}$`);
|
|
2248
|
+
UNGUARDED_SOURCE = new RegExp(`^source ${OWN_SCRIPT}$`);
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2108
2252
|
// src/capture/shell-hook.ts
|
|
2253
|
+
function backUp(rcFile) {
|
|
2254
|
+
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backUpFile(rcFile))}`);
|
|
2255
|
+
}
|
|
2109
2256
|
function getInstalledHookBlock(rcFile) {
|
|
2110
|
-
if (!
|
|
2111
|
-
const content =
|
|
2257
|
+
if (!import_fs13.default.existsSync(rcFile)) return null;
|
|
2258
|
+
const content = import_fs13.default.readFileSync(rcFile, "utf-8");
|
|
2112
2259
|
const startIdx = content.indexOf(HOOK_START);
|
|
2113
2260
|
const endIdx = content.indexOf(HOOK_END);
|
|
2114
2261
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
@@ -2123,34 +2270,28 @@ function isHookOutdated(rcFile, hookContent) {
|
|
|
2123
2270
|
return installed.trim() !== hookContent.trim();
|
|
2124
2271
|
}
|
|
2125
2272
|
function installHook(rcFile, hookContent) {
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2129
|
-
import_fs12.default.appendFileSync(rcFile, `
|
|
2273
|
+
backUp(rcFile);
|
|
2274
|
+
import_fs13.default.appendFileSync(rcFile, `
|
|
2130
2275
|
${hookContent}
|
|
2131
2276
|
`);
|
|
2132
2277
|
}
|
|
2133
2278
|
function updateHook(rcFile, hookContent) {
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2137
|
-
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
2279
|
+
backUp(rcFile);
|
|
2280
|
+
const content = import_fs13.default.readFileSync(rcFile, "utf-8");
|
|
2138
2281
|
const startIdx = content.indexOf(HOOK_START);
|
|
2139
2282
|
const endIdx = content.indexOf(HOOK_END) + HOOK_END.length;
|
|
2140
|
-
|
|
2283
|
+
import_fs13.default.writeFileSync(rcFile, content.slice(0, startIdx) + hookContent + content.slice(endIdx));
|
|
2141
2284
|
}
|
|
2142
2285
|
function removeHook(rcFile) {
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2146
|
-
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
2286
|
+
backUp(rcFile);
|
|
2287
|
+
const content = import_fs13.default.readFileSync(rcFile, "utf-8");
|
|
2147
2288
|
const startIdx = content.indexOf(HOOK_START);
|
|
2148
2289
|
const endIdx = content.indexOf(HOOK_END) + HOOK_END.length;
|
|
2149
2290
|
const preambleIdx = content.lastIndexOf(HOOK_PREAMBLE, startIdx);
|
|
2150
2291
|
const blockStart = preambleIdx !== -1 ? preambleIdx : startIdx;
|
|
2151
2292
|
const before = content.slice(0, blockStart).replace(/\n+$/, "\n");
|
|
2152
2293
|
const after = content.slice(endIdx).replace(/^\n/, "");
|
|
2153
|
-
|
|
2294
|
+
import_fs13.default.writeFileSync(rcFile, before + after);
|
|
2154
2295
|
}
|
|
2155
2296
|
function resolveWorkscribeBinary() {
|
|
2156
2297
|
try {
|
|
@@ -2167,53 +2308,86 @@ function injectBinaryPath(hookContent, binaryPath, nodePath) {
|
|
|
2167
2308
|
}
|
|
2168
2309
|
function isCompletionsInstalled(shell) {
|
|
2169
2310
|
if (shell.name === "fish") {
|
|
2170
|
-
return
|
|
2311
|
+
return import_fs13.default.existsSync(import_path9.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish"));
|
|
2171
2312
|
}
|
|
2172
2313
|
try {
|
|
2173
|
-
return
|
|
2314
|
+
return import_fs13.default.readFileSync(shell.rcFile, "utf-8").includes(COMPLETIONS_MARKER);
|
|
2174
2315
|
} catch {
|
|
2175
2316
|
return false;
|
|
2176
2317
|
}
|
|
2177
2318
|
}
|
|
2178
|
-
function
|
|
2319
|
+
function completionScriptFile(shellName) {
|
|
2320
|
+
return import_path9.default.join(WORKSCRIBE_DIR, `completion.${shellName}`);
|
|
2321
|
+
}
|
|
2322
|
+
function readRcFile(rcFile) {
|
|
2323
|
+
try {
|
|
2324
|
+
return import_fs13.default.readFileSync(rcFile, "utf-8");
|
|
2325
|
+
} catch {
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
function writeCompletionScript(shell, commands) {
|
|
2179
2330
|
if (shell.name === "fish") {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
import_fs12.default.writeFileSync(import_path9.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish"), fishCompletion(commands), { mode: 420 });
|
|
2184
|
-
} else {
|
|
2185
|
-
const script = shell.name === "zsh" ? zshCompletion(commands) : bashCompletion(commands);
|
|
2186
|
-
const cacheFile = import_path9.default.join(WORKSCRIBE_DIR, `completion.${shell.name}`);
|
|
2187
|
-
import_fs12.default.writeFileSync(cacheFile, script, { mode: 420 });
|
|
2188
|
-
if (!isCompletionsInstalled(shell)) {
|
|
2189
|
-
const sourceLine = shell.name === "zsh" ? `
|
|
2190
|
-
${COMPLETIONS_MARKER}
|
|
2191
|
-
autoload -Uz compinit && compinit
|
|
2192
|
-
source "${cacheFile}"
|
|
2193
|
-
` : `
|
|
2194
|
-
${COMPLETIONS_MARKER}
|
|
2195
|
-
source "${cacheFile}"
|
|
2196
|
-
`;
|
|
2197
|
-
import_fs12.default.appendFileSync(shell.rcFile, sourceLine);
|
|
2198
|
-
}
|
|
2331
|
+
import_fs13.default.mkdirSync(FISH_COMPLETIONS_DIR, { recursive: true });
|
|
2332
|
+
import_fs13.default.writeFileSync(FISH_COMPLETIONS_FILE, fishCompletion(commands), { mode: 420 });
|
|
2333
|
+
return;
|
|
2199
2334
|
}
|
|
2335
|
+
const script = shell.name === "zsh" ? zshCompletion(commands) : bashCompletion(commands);
|
|
2336
|
+
import_fs13.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true });
|
|
2337
|
+
import_fs13.default.writeFileSync(completionScriptFile(shell.name), script, { mode: 420 });
|
|
2338
|
+
}
|
|
2339
|
+
function installCompletions(shell, commands) {
|
|
2340
|
+
writeCompletionScript(shell, commands);
|
|
2341
|
+
if (shell.name !== "fish" && !isCompletionsInstalled(shell)) {
|
|
2342
|
+
import_fs13.default.appendFileSync(shell.rcFile, `
|
|
2343
|
+
${completionsBlock(shell.name, completionScriptFile(shell.name))}
|
|
2344
|
+
`);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
function isCompletionsOutdated(shell) {
|
|
2348
|
+
if (shell.name === "fish") return false;
|
|
2349
|
+
const content = readRcFile(shell.rcFile);
|
|
2350
|
+
return content !== null && isCompletionsBlockOutdated(content, completionsBlock(shell.name, completionScriptFile(shell.name)));
|
|
2351
|
+
}
|
|
2352
|
+
function updateCompletions(shell) {
|
|
2353
|
+
const content = readRcFile(shell.rcFile);
|
|
2354
|
+
if (content === null || !hasCompletionsBlock(content)) return;
|
|
2355
|
+
backUp(shell.rcFile);
|
|
2356
|
+
import_fs13.default.writeFileSync(
|
|
2357
|
+
shell.rcFile,
|
|
2358
|
+
replaceCompletionsBlock(content, completionsBlock(shell.name, completionScriptFile(shell.name)))
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
function removeCompletions(rcFile) {
|
|
2362
|
+
const content = readRcFile(rcFile);
|
|
2363
|
+
if (content === null || !hasCompletionsBlock(content)) return false;
|
|
2364
|
+
backUp(rcFile);
|
|
2365
|
+
import_fs13.default.writeFileSync(rcFile, removeCompletionsBlock(content));
|
|
2366
|
+
return true;
|
|
2367
|
+
}
|
|
2368
|
+
function removeFishCompletions() {
|
|
2369
|
+
if (!import_fs13.default.existsSync(FISH_COMPLETIONS_FILE)) return false;
|
|
2370
|
+
import_fs13.default.rmSync(FISH_COMPLETIONS_FILE, { force: true });
|
|
2371
|
+
return true;
|
|
2200
2372
|
}
|
|
2201
|
-
var
|
|
2373
|
+
var import_fs13, import_path9, import_os3, import_child_process3, FISH_COMPLETIONS_DIR, HOOK_START, HOOK_END, HOOK_PREAMBLE, FISH_COMPLETIONS_FILE;
|
|
2202
2374
|
var init_shell_hook = __esm({
|
|
2203
2375
|
"src/capture/shell-hook.ts"() {
|
|
2204
2376
|
"use strict";
|
|
2205
|
-
|
|
2377
|
+
import_fs13 = __toESM(require("fs"));
|
|
2206
2378
|
import_path9 = __toESM(require("path"));
|
|
2207
2379
|
import_os3 = __toESM(require("os"));
|
|
2208
2380
|
import_child_process3 = require("child_process");
|
|
2209
2381
|
init_logger();
|
|
2210
2382
|
init_completion_scripts();
|
|
2211
2383
|
init_paths();
|
|
2384
|
+
init_backup();
|
|
2385
|
+
init_completions_block();
|
|
2212
2386
|
FISH_COMPLETIONS_DIR = import_path9.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
2213
2387
|
HOOK_START = "# workscribe:hook:start";
|
|
2214
2388
|
HOOK_END = "# workscribe:hook:end";
|
|
2215
2389
|
HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
2216
|
-
|
|
2390
|
+
FISH_COMPLETIONS_FILE = import_path9.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish");
|
|
2217
2391
|
}
|
|
2218
2392
|
});
|
|
2219
2393
|
|
|
@@ -2234,11 +2408,43 @@ var init_utils = __esm({
|
|
|
2234
2408
|
}
|
|
2235
2409
|
});
|
|
2236
2410
|
|
|
2411
|
+
// src/capture/hooks/claude-code-command.ts
|
|
2412
|
+
function buildHookCommand(options) {
|
|
2413
|
+
if (!options.binaryPath) return `${BARE_GUARD}workscribe ${options.args}${GUARD_SUFFIX}`;
|
|
2414
|
+
const { nodePath, binaryPath, args } = options;
|
|
2415
|
+
return `test -x "${nodePath}" && test -x "${binaryPath}" && "${nodePath}" "${binaryPath}" ${args}${GUARD_SUFFIX}`;
|
|
2416
|
+
}
|
|
2417
|
+
function withoutLeadingGuards(command) {
|
|
2418
|
+
return command.trimStart().replace(LEADING_PATH_GUARDS, "");
|
|
2419
|
+
}
|
|
2420
|
+
function isPinnedCommand(command) {
|
|
2421
|
+
const invocation = withoutLeadingGuards(command);
|
|
2422
|
+
return invocation.startsWith('"') || invocation.startsWith("/");
|
|
2423
|
+
}
|
|
2424
|
+
function isGuardedCommand(command) {
|
|
2425
|
+
const trimmed = command.trim();
|
|
2426
|
+
const hasLeadingGuard = LEADING_PATH_GUARDS.test(trimmed) || trimmed.startsWith(BARE_GUARD);
|
|
2427
|
+
return hasLeadingGuard && trimmed.endsWith(GUARD_SUFFIX);
|
|
2428
|
+
}
|
|
2429
|
+
function pinnedNodePath(command) {
|
|
2430
|
+
return withoutLeadingGuards(command).match(/^"([^"]+)"/)?.[1] ?? null;
|
|
2431
|
+
}
|
|
2432
|
+
var HOOK_MARKER, GUARD_SUFFIX, LEADING_PATH_GUARDS, BARE_GUARD;
|
|
2433
|
+
var init_claude_code_command = __esm({
|
|
2434
|
+
"src/capture/hooks/claude-code-command.ts"() {
|
|
2435
|
+
"use strict";
|
|
2436
|
+
HOOK_MARKER = "_hook --source claude-code";
|
|
2437
|
+
GUARD_SUFFIX = " || true";
|
|
2438
|
+
LEADING_PATH_GUARDS = /^(?:test -x "[^"]*" && )+/;
|
|
2439
|
+
BARE_GUARD = "command -v workscribe >/dev/null 2>&1 && ";
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2237
2443
|
// src/capture/hooks/claude-code.ts
|
|
2238
2444
|
function readSettings() {
|
|
2239
|
-
if (!
|
|
2445
|
+
if (!import_fs14.default.existsSync(CLAUDE_SETTINGS_PATH)) return {};
|
|
2240
2446
|
try {
|
|
2241
|
-
return JSON.parse(
|
|
2447
|
+
return JSON.parse(import_fs14.default.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
2242
2448
|
} catch {
|
|
2243
2449
|
return {};
|
|
2244
2450
|
}
|
|
@@ -2264,10 +2470,6 @@ function findWorkscribeHookCommands(settings) {
|
|
|
2264
2470
|
userPromptSubmit: commandsForEvent(hooks, "UserPromptSubmit")
|
|
2265
2471
|
};
|
|
2266
2472
|
}
|
|
2267
|
-
function isPinnedCommand(command) {
|
|
2268
|
-
const trimmed = command.trimStart();
|
|
2269
|
-
return trimmed.startsWith('"') || trimmed.startsWith("/");
|
|
2270
|
-
}
|
|
2271
2473
|
function withoutWorkscribeEntries(entries) {
|
|
2272
2474
|
return entries.map((entry) => {
|
|
2273
2475
|
const record = entry;
|
|
@@ -2279,11 +2481,29 @@ function withoutWorkscribeEntries(entries) {
|
|
|
2279
2481
|
return { ...record, hooks: filteredHooks };
|
|
2280
2482
|
}).filter((entry) => entry.hooks.length > 0);
|
|
2281
2483
|
}
|
|
2282
|
-
function
|
|
2484
|
+
function isExecutable(filePath) {
|
|
2485
|
+
try {
|
|
2486
|
+
import_fs14.default.accessSync(filePath, import_fs14.default.constants.X_OK);
|
|
2487
|
+
return true;
|
|
2488
|
+
} catch {
|
|
2489
|
+
return false;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
function allWorkscribeCommands() {
|
|
2283
2493
|
const { postToolUse, userPromptSubmit } = findWorkscribeHookCommands(readSettings());
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2494
|
+
return [...postToolUse, ...userPromptSubmit];
|
|
2495
|
+
}
|
|
2496
|
+
function checkHookHealth() {
|
|
2497
|
+
const allCommands = allWorkscribeCommands();
|
|
2498
|
+
if (allCommands.length === 0) {
|
|
2499
|
+
return { installed: false, pinned: true, guarded: true, nodeMissing: false, confirmedBroken: null };
|
|
2500
|
+
}
|
|
2501
|
+
const guarded = allCommands.every(isGuardedCommand);
|
|
2502
|
+
const nodePaths = allCommands.map(pinnedNodePath).filter((nodePath) => nodePath !== null);
|
|
2503
|
+
const nodeMissing = nodePaths.some((nodePath) => !isExecutable(nodePath));
|
|
2504
|
+
if (allCommands.every(isPinnedCommand)) {
|
|
2505
|
+
return { installed: true, pinned: true, guarded, nodeMissing, confirmedBroken: null };
|
|
2506
|
+
}
|
|
2287
2507
|
let confirmedBroken;
|
|
2288
2508
|
try {
|
|
2289
2509
|
(0, import_child_process5.execFileSync)("workscribe", ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
@@ -2291,48 +2511,76 @@ function checkHookHealth() {
|
|
|
2291
2511
|
} catch {
|
|
2292
2512
|
confirmedBroken = true;
|
|
2293
2513
|
}
|
|
2294
|
-
return { installed: true, pinned: false, confirmedBroken };
|
|
2514
|
+
return { installed: true, pinned: false, guarded, nodeMissing, confirmedBroken };
|
|
2515
|
+
}
|
|
2516
|
+
function readSettingsStrictly() {
|
|
2517
|
+
if (!import_fs14.default.existsSync(CLAUDE_SETTINGS_PATH)) return "missing";
|
|
2518
|
+
try {
|
|
2519
|
+
const parsed = JSON.parse(import_fs14.default.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
2520
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : "unreadable";
|
|
2521
|
+
} catch {
|
|
2522
|
+
return "unreadable";
|
|
2523
|
+
}
|
|
2295
2524
|
}
|
|
2296
|
-
|
|
2525
|
+
function hooksWithoutWorkscribe(hooks) {
|
|
2526
|
+
let removedAny = false;
|
|
2527
|
+
const remaining = {};
|
|
2528
|
+
for (const [eventName, entries] of Object.entries(hooks)) {
|
|
2529
|
+
if (!Array.isArray(entries)) {
|
|
2530
|
+
remaining[eventName] = entries;
|
|
2531
|
+
continue;
|
|
2532
|
+
}
|
|
2533
|
+
const kept = withoutWorkscribeEntries(entries);
|
|
2534
|
+
if (commandsForEvent(hooks, eventName).length > 0) removedAny = true;
|
|
2535
|
+
if (kept.length > 0) remaining[eventName] = kept;
|
|
2536
|
+
}
|
|
2537
|
+
return removedAny ? remaining : null;
|
|
2538
|
+
}
|
|
2539
|
+
function isHookCurrent() {
|
|
2540
|
+
const { postToolUse, userPromptSubmit } = findWorkscribeHookCommands(readSettings());
|
|
2541
|
+
if (postToolUse.length === 0 || userPromptSubmit.length === 0) return false;
|
|
2542
|
+
return [...postToolUse, ...userPromptSubmit].every((command) => {
|
|
2543
|
+
const nodePath = pinnedNodePath(command);
|
|
2544
|
+
return isPinnedCommand(command) && isGuardedCommand(command) && nodePath !== null && isExecutable(nodePath);
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
var import_fs14, import_path10, import_os4, import_child_process5, CLAUDE_SETTINGS_PATH, claudeCodeIntegration;
|
|
2297
2548
|
var init_claude_code = __esm({
|
|
2298
2549
|
"src/capture/hooks/claude-code.ts"() {
|
|
2299
2550
|
"use strict";
|
|
2300
|
-
|
|
2551
|
+
import_fs14 = __toESM(require("fs"));
|
|
2301
2552
|
import_path10 = __toESM(require("path"));
|
|
2302
2553
|
import_os4 = __toESM(require("os"));
|
|
2303
2554
|
import_child_process5 = require("child_process");
|
|
2304
2555
|
init_utils();
|
|
2305
2556
|
init_shell_hook();
|
|
2557
|
+
init_backup();
|
|
2558
|
+
init_claude_code_command();
|
|
2306
2559
|
CLAUDE_SETTINGS_PATH = import_path10.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2307
|
-
HOOK_MARKER = "_hook --source claude-code";
|
|
2308
2560
|
claudeCodeIntegration = {
|
|
2309
2561
|
displayName: "Claude Code",
|
|
2310
2562
|
isInstalled() {
|
|
2311
2563
|
return isCommandInstalled("claude");
|
|
2312
2564
|
},
|
|
2313
|
-
// "Installed" means a *
|
|
2314
|
-
//
|
|
2315
|
-
// existing prompt-and-install flow naturally offer to repair a stale,
|
|
2316
|
-
// unpinned hook from before this fix, with no new UI code needed there.
|
|
2565
|
+
// "Installed" means a *current* hook is present, not just any workscribe hook — so `workscribe init` offers to
|
|
2566
|
+
// update a stale one (unpinned, unguarded, or pinned to a Node that's gone) through its outdated-hook prompt
|
|
2317
2567
|
isHookInstalled() {
|
|
2318
|
-
|
|
2319
|
-
if (postToolUse.length === 0 || userPromptSubmit.length === 0) return false;
|
|
2320
|
-
return [...postToolUse, ...userPromptSubmit].every(isPinnedCommand);
|
|
2568
|
+
return isHookCurrent();
|
|
2321
2569
|
},
|
|
2322
2570
|
install() {
|
|
2323
2571
|
const settings = readSettings();
|
|
2324
2572
|
const existingHooks = settings.hooks ?? {};
|
|
2325
2573
|
const existingPostToolUse = withoutWorkscribeEntries(existingHooks.PostToolUse ?? []);
|
|
2326
2574
|
const existingUserPromptSubmit = withoutWorkscribeEntries(existingHooks.UserPromptSubmit ?? []);
|
|
2327
|
-
const
|
|
2328
|
-
const
|
|
2575
|
+
const binaryPath = resolveWorkscribeBinary();
|
|
2576
|
+
const command = (args) => buildHookCommand({ nodePath: process.execPath, binaryPath, args });
|
|
2329
2577
|
settings.hooks = {
|
|
2330
2578
|
...existingHooks,
|
|
2331
2579
|
PostToolUse: [
|
|
2332
2580
|
...existingPostToolUse,
|
|
2333
2581
|
{
|
|
2334
2582
|
matcher: "Bash",
|
|
2335
|
-
hooks: [{ type: "command", command:
|
|
2583
|
+
hooks: [{ type: "command", command: command(HOOK_MARKER), async: true }]
|
|
2336
2584
|
}
|
|
2337
2585
|
],
|
|
2338
2586
|
UserPromptSubmit: [
|
|
@@ -2341,14 +2589,33 @@ var init_claude_code = __esm({
|
|
|
2341
2589
|
hooks: [
|
|
2342
2590
|
{
|
|
2343
2591
|
type: "command",
|
|
2344
|
-
command: `${
|
|
2592
|
+
command: command(`${HOOK_MARKER} --event UserPromptSubmit`)
|
|
2345
2593
|
}
|
|
2346
2594
|
]
|
|
2347
2595
|
}
|
|
2348
2596
|
]
|
|
2349
2597
|
};
|
|
2350
|
-
|
|
2351
|
-
|
|
2598
|
+
import_fs14.default.mkdirSync(import_path10.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2599
|
+
import_fs14.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2600
|
+
},
|
|
2601
|
+
// Some workscribe hook is present, but not all of them are current (pinned and guarded) — including a
|
|
2602
|
+
// half-installed one where only PostToolUse or only UserPromptSubmit exists
|
|
2603
|
+
isHookOutdated() {
|
|
2604
|
+
return allWorkscribeCommands().length > 0 && !isHookCurrent();
|
|
2605
|
+
},
|
|
2606
|
+
uninstall() {
|
|
2607
|
+
const settings = readSettingsStrictly();
|
|
2608
|
+
if (settings === "missing") return "not-installed";
|
|
2609
|
+
if (settings === "unreadable") return "unreadable";
|
|
2610
|
+
const hooks = settings.hooks;
|
|
2611
|
+
if (hooks === null || typeof hooks !== "object" || Array.isArray(hooks)) return "not-installed";
|
|
2612
|
+
const remainingHooks = hooksWithoutWorkscribe(hooks);
|
|
2613
|
+
if (!remainingHooks) return "not-installed";
|
|
2614
|
+
const updated = { ...settings, hooks: remainingHooks };
|
|
2615
|
+
if (Object.keys(remainingHooks).length === 0) delete updated.hooks;
|
|
2616
|
+
backUpFile(CLAUDE_SETTINGS_PATH);
|
|
2617
|
+
import_fs14.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(updated, null, 2));
|
|
2618
|
+
return "removed";
|
|
2352
2619
|
}
|
|
2353
2620
|
};
|
|
2354
2621
|
}
|
|
@@ -2469,10 +2736,10 @@ function defaultDetectionEnvironment() {
|
|
|
2469
2736
|
homeDir: import_os5.default.homedir(),
|
|
2470
2737
|
// `which` spawns a process, so each command is looked up at most once per run
|
|
2471
2738
|
isCommandInstalled: memoize(isCommandInstalled),
|
|
2472
|
-
pathExists: (absolutePath) =>
|
|
2739
|
+
pathExists: (absolutePath) => import_fs15.default.existsSync(absolutePath),
|
|
2473
2740
|
listDirectory: (absolutePath) => {
|
|
2474
2741
|
try {
|
|
2475
|
-
return
|
|
2742
|
+
return import_fs15.default.readdirSync(absolutePath);
|
|
2476
2743
|
} catch {
|
|
2477
2744
|
return [];
|
|
2478
2745
|
}
|
|
@@ -2493,11 +2760,11 @@ function isAgentPresent(agent, environment) {
|
|
|
2493
2760
|
function detectAgents(environment = defaultDetectionEnvironment()) {
|
|
2494
2761
|
return SKILL_AGENTS.filter((agent) => isAgentPresent(agent, environment));
|
|
2495
2762
|
}
|
|
2496
|
-
var
|
|
2763
|
+
var import_fs15, import_os5, import_path11;
|
|
2497
2764
|
var init_detect = __esm({
|
|
2498
2765
|
"src/skills/detect.ts"() {
|
|
2499
2766
|
"use strict";
|
|
2500
|
-
|
|
2767
|
+
import_fs15 = __toESM(require("fs"));
|
|
2501
2768
|
import_os5 = __toESM(require("os"));
|
|
2502
2769
|
import_path11 = __toESM(require("path"));
|
|
2503
2770
|
init_utils();
|
|
@@ -2548,7 +2815,7 @@ function isSkillMarker(value) {
|
|
|
2548
2815
|
}
|
|
2549
2816
|
function readMarker(skillDir) {
|
|
2550
2817
|
try {
|
|
2551
|
-
const parsed = JSON.parse(
|
|
2818
|
+
const parsed = JSON.parse(import_fs16.default.readFileSync(import_path12.default.join(skillDir, MARKER_FILE), "utf-8"));
|
|
2552
2819
|
return isSkillMarker(parsed) ? parsed : null;
|
|
2553
2820
|
} catch {
|
|
2554
2821
|
return null;
|
|
@@ -2557,62 +2824,62 @@ function readMarker(skillDir) {
|
|
|
2557
2824
|
function readInstallState(skillsFolder, currentVersion) {
|
|
2558
2825
|
const skillDir = import_path12.default.join(skillsFolder, SKILL_NAME);
|
|
2559
2826
|
return describeInstallState({
|
|
2560
|
-
folderExists:
|
|
2827
|
+
folderExists: import_fs16.default.existsSync(skillDir),
|
|
2561
2828
|
marker: readMarker(skillDir),
|
|
2562
2829
|
currentVersion
|
|
2563
2830
|
});
|
|
2564
2831
|
}
|
|
2565
2832
|
function copyDirectory(from, to) {
|
|
2566
|
-
|
|
2567
|
-
for (const entry of
|
|
2833
|
+
import_fs16.default.mkdirSync(to, { recursive: true });
|
|
2834
|
+
for (const entry of import_fs16.default.readdirSync(from, { withFileTypes: true })) {
|
|
2568
2835
|
const source = import_path12.default.join(from, entry.name);
|
|
2569
2836
|
const destination = import_path12.default.join(to, entry.name);
|
|
2570
2837
|
if (entry.isDirectory()) copyDirectory(source, destination);
|
|
2571
|
-
else
|
|
2838
|
+
else import_fs16.default.copyFileSync(source, destination);
|
|
2572
2839
|
}
|
|
2573
2840
|
}
|
|
2574
2841
|
function swapInto(skillDir, preparedDir) {
|
|
2575
2842
|
const previousDir = import_path12.default.join(import_path12.default.dirname(skillDir), `.${SKILL_NAME}.old-${process.pid}`);
|
|
2576
|
-
|
|
2577
|
-
const hadPrevious =
|
|
2578
|
-
if (hadPrevious)
|
|
2843
|
+
import_fs16.default.rmSync(previousDir, { recursive: true, force: true });
|
|
2844
|
+
const hadPrevious = import_fs16.default.existsSync(skillDir);
|
|
2845
|
+
if (hadPrevious) import_fs16.default.renameSync(skillDir, previousDir);
|
|
2579
2846
|
try {
|
|
2580
|
-
|
|
2847
|
+
import_fs16.default.renameSync(preparedDir, skillDir);
|
|
2581
2848
|
} catch (error) {
|
|
2582
|
-
if (hadPrevious)
|
|
2849
|
+
if (hadPrevious) import_fs16.default.renameSync(previousDir, skillDir);
|
|
2583
2850
|
throw error;
|
|
2584
2851
|
}
|
|
2585
|
-
if (hadPrevious)
|
|
2852
|
+
if (hadPrevious) import_fs16.default.rmSync(previousDir, { recursive: true, force: true });
|
|
2586
2853
|
}
|
|
2587
2854
|
function installSkill(options) {
|
|
2588
2855
|
const state = readInstallState(options.skillsFolder, options.version);
|
|
2589
2856
|
if (state.kind === "installed") return "up-to-date";
|
|
2590
2857
|
if (state.kind === "not-ours" && !options.force) return "skipped-not-ours";
|
|
2591
|
-
|
|
2858
|
+
import_fs16.default.mkdirSync(options.skillsFolder, { recursive: true });
|
|
2592
2859
|
const preparedDir = import_path12.default.join(options.skillsFolder, `.${SKILL_NAME}.tmp-${process.pid}`);
|
|
2593
|
-
|
|
2860
|
+
import_fs16.default.rmSync(preparedDir, { recursive: true, force: true });
|
|
2594
2861
|
try {
|
|
2595
2862
|
copyDirectory(options.sourceDir, preparedDir);
|
|
2596
2863
|
const marker = { version: options.version, installedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2597
|
-
|
|
2864
|
+
import_fs16.default.writeFileSync(import_path12.default.join(preparedDir, MARKER_FILE), JSON.stringify(marker, null, 2));
|
|
2598
2865
|
swapInto(import_path12.default.join(options.skillsFolder, SKILL_NAME), preparedDir);
|
|
2599
2866
|
} finally {
|
|
2600
|
-
|
|
2867
|
+
import_fs16.default.rmSync(preparedDir, { recursive: true, force: true });
|
|
2601
2868
|
}
|
|
2602
2869
|
return state.kind === "not-installed" ? "installed" : "updated";
|
|
2603
2870
|
}
|
|
2604
2871
|
function uninstallSkill(skillsFolder) {
|
|
2605
2872
|
const skillDir = import_path12.default.join(skillsFolder, SKILL_NAME);
|
|
2606
|
-
if (!
|
|
2873
|
+
if (!import_fs16.default.existsSync(skillDir)) return "not-installed";
|
|
2607
2874
|
if (!readMarker(skillDir)) return "skipped-not-ours";
|
|
2608
|
-
|
|
2875
|
+
import_fs16.default.rmSync(skillDir, { recursive: true, force: true });
|
|
2609
2876
|
return "removed";
|
|
2610
2877
|
}
|
|
2611
|
-
var
|
|
2878
|
+
var import_fs16, import_path12;
|
|
2612
2879
|
var init_installer = __esm({
|
|
2613
2880
|
"src/skills/installer.ts"() {
|
|
2614
2881
|
"use strict";
|
|
2615
|
-
|
|
2882
|
+
import_fs16 = __toESM(require("fs"));
|
|
2616
2883
|
import_path12 = __toESM(require("path"));
|
|
2617
2884
|
init_agents();
|
|
2618
2885
|
init_state();
|
|
@@ -2628,7 +2895,7 @@ function packagedSkillCandidates(fromDirectory) {
|
|
|
2628
2895
|
}
|
|
2629
2896
|
function resolvePackagedSkillDir(fromDirectory = __dirname) {
|
|
2630
2897
|
const found = packagedSkillCandidates(fromDirectory).find(
|
|
2631
|
-
(candidate) =>
|
|
2898
|
+
(candidate) => import_fs17.default.existsSync(import_path13.default.join(candidate, SKILL_ENTRY_FILE))
|
|
2632
2899
|
);
|
|
2633
2900
|
if (!found) {
|
|
2634
2901
|
throw new Error(
|
|
@@ -2637,11 +2904,11 @@ function resolvePackagedSkillDir(fromDirectory = __dirname) {
|
|
|
2637
2904
|
}
|
|
2638
2905
|
return found;
|
|
2639
2906
|
}
|
|
2640
|
-
var
|
|
2907
|
+
var import_fs17, import_path13, SKILL_ENTRY_FILE;
|
|
2641
2908
|
var init_package_path = __esm({
|
|
2642
2909
|
"src/skills/package-path.ts"() {
|
|
2643
2910
|
"use strict";
|
|
2644
|
-
|
|
2911
|
+
import_fs17 = __toESM(require("fs"));
|
|
2645
2912
|
import_path13 = __toESM(require("path"));
|
|
2646
2913
|
init_agents();
|
|
2647
2914
|
SKILL_ENTRY_FILE = "SKILL.md";
|
|
@@ -2739,10 +3006,10 @@ function installIntoFolder(options) {
|
|
|
2739
3006
|
const outcome = installSkill({
|
|
2740
3007
|
sourceDir: options.sourceDir,
|
|
2741
3008
|
skillsFolder: target.skillsFolder,
|
|
2742
|
-
version: "2.0.
|
|
3009
|
+
version: "2.0.1",
|
|
2743
3010
|
force: options.force
|
|
2744
3011
|
});
|
|
2745
|
-
return describeInstallOutcome({ ...target, outcome, version: "2.0.
|
|
3012
|
+
return describeInstallOutcome({ ...target, outcome, version: "2.0.1" });
|
|
2746
3013
|
} catch (error) {
|
|
2747
3014
|
return describeInstallFailure({ ...target, error });
|
|
2748
3015
|
}
|
|
@@ -2761,8 +3028,8 @@ function removeSkills(agents, options) {
|
|
|
2761
3028
|
}
|
|
2762
3029
|
function formatAgentSkillLine(agent, homeDir = import_os6.default.homedir()) {
|
|
2763
3030
|
const location = displayPath(import_path14.default.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir);
|
|
2764
|
-
const installState = readInstallState(import_path14.default.join(homeDir, agent.skillsFolder), "2.0.
|
|
2765
|
-
const status = describeSkillStatus(installState, "2.0.
|
|
3031
|
+
const installState = readInstallState(import_path14.default.join(homeDir, agent.skillsFolder), "2.0.1");
|
|
3032
|
+
const status = describeSkillStatus(installState, "2.0.1");
|
|
2766
3033
|
return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${import_chalk2.default.dim(location)}`;
|
|
2767
3034
|
}
|
|
2768
3035
|
function formatUndetectedAgentLine(agent) {
|
|
@@ -2875,19 +3142,19 @@ function step(current, total, label) {
|
|
|
2875
3142
|
function runDataDirectoriesStep() {
|
|
2876
3143
|
step(1, TOTAL_STEPS, "Creating data directories");
|
|
2877
3144
|
for (const dir of REQUIRED_DIRS) {
|
|
2878
|
-
if (!
|
|
2879
|
-
|
|
3145
|
+
if (!import_fs18.default.existsSync(dir)) {
|
|
3146
|
+
import_fs18.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2880
3147
|
log.success(`Created ${dir}`);
|
|
2881
3148
|
} else {
|
|
2882
3149
|
try {
|
|
2883
|
-
|
|
3150
|
+
import_fs18.default.chmodSync(dir, 448);
|
|
2884
3151
|
} catch {
|
|
2885
3152
|
}
|
|
2886
3153
|
log.info(`${dir} already exists`);
|
|
2887
3154
|
}
|
|
2888
3155
|
}
|
|
2889
|
-
if (!
|
|
2890
|
-
|
|
3156
|
+
if (!import_fs18.default.existsSync(PROFILES_DIR)) {
|
|
3157
|
+
import_fs18.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
2891
3158
|
}
|
|
2892
3159
|
}
|
|
2893
3160
|
async function initDatabaseAndConfig() {
|
|
@@ -2924,18 +3191,18 @@ async function runShellHookStep(program) {
|
|
|
2924
3191
|
return null;
|
|
2925
3192
|
}
|
|
2926
3193
|
const hookFile = import_path15.default.join(__dirname, "..", "shell", `hook.${shell.name}`);
|
|
2927
|
-
if (!
|
|
3194
|
+
if (!import_fs18.default.existsSync(hookFile)) {
|
|
2928
3195
|
log.warn(`Hook template not found at ${hookFile}`);
|
|
2929
3196
|
return shell;
|
|
2930
3197
|
}
|
|
2931
|
-
const hookContent =
|
|
3198
|
+
const hookContent = import_fs18.default.readFileSync(hookFile, "utf-8");
|
|
2932
3199
|
const binaryPath = resolveWorkscribeBinary();
|
|
2933
3200
|
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2934
3201
|
if (shell.name === "fish") {
|
|
2935
3202
|
const confDir = import_path15.default.dirname(shell.rcFile);
|
|
2936
|
-
if (!
|
|
3203
|
+
if (!import_fs18.default.existsSync(confDir)) import_fs18.default.mkdirSync(confDir, { recursive: true });
|
|
2937
3204
|
}
|
|
2938
|
-
if (!
|
|
3205
|
+
if (!import_fs18.default.existsSync(shell.rcFile)) import_fs18.default.writeFileSync(shell.rcFile, "");
|
|
2939
3206
|
if (!isHookInstalled(shell.rcFile)) {
|
|
2940
3207
|
log.plain("");
|
|
2941
3208
|
log.plain(import_chalk3.default.dim(" Workscribe adds a lightweight hook to your shell that captures each"));
|
|
@@ -2962,7 +3229,7 @@ async function runShellHookStep(program) {
|
|
|
2962
3229
|
log.info(`Shell hook up to date in ${shell.rcFile}`);
|
|
2963
3230
|
}
|
|
2964
3231
|
if (isCompletionsInstalled(shell)) {
|
|
2965
|
-
|
|
3232
|
+
await refreshCompletions(shell, program);
|
|
2966
3233
|
} else {
|
|
2967
3234
|
const { installComps } = await (0, import_prompts2.default)({
|
|
2968
3235
|
type: "confirm",
|
|
@@ -2981,6 +3248,29 @@ async function runShellHookStep(program) {
|
|
|
2981
3248
|
}
|
|
2982
3249
|
return shell;
|
|
2983
3250
|
}
|
|
3251
|
+
async function refreshCompletions(shell, program) {
|
|
3252
|
+
try {
|
|
3253
|
+
writeCompletionScript(shell, getPublicCommands(program));
|
|
3254
|
+
} catch (error) {
|
|
3255
|
+
log.warn(`Could not refresh shell completions: ${error.message}`);
|
|
3256
|
+
}
|
|
3257
|
+
if (!isCompletionsOutdated(shell)) {
|
|
3258
|
+
log.info("Shell completions already installed");
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
const { updateShellCompletions } = await (0, import_prompts2.default)({
|
|
3262
|
+
type: "confirm",
|
|
3263
|
+
name: "updateShellCompletions",
|
|
3264
|
+
message: `Shell completions in ${shell.rcFile} are outdated (they error in new terminals if Workscribe is removed). Update?`,
|
|
3265
|
+
initial: true
|
|
3266
|
+
});
|
|
3267
|
+
if (!updateShellCompletions) {
|
|
3268
|
+
log.info("Shell completions update skipped");
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
updateCompletions(shell);
|
|
3272
|
+
log.success(`Shell completions updated in ${shell.rcFile}`);
|
|
3273
|
+
}
|
|
2984
3274
|
async function runAIToolsStep() {
|
|
2985
3275
|
step(4, TOTAL_STEPS, "Detecting AI coding tools");
|
|
2986
3276
|
const installedTools = loadAIToolRegistry().filter(
|
|
@@ -2994,6 +3284,19 @@ async function runAIToolsStep() {
|
|
|
2994
3284
|
const integration = AI_HOOK_INTEGRATIONS[tool.hook];
|
|
2995
3285
|
if (integration.isHookInstalled()) {
|
|
2996
3286
|
log.info(`${integration.displayName} session hook already installed`);
|
|
3287
|
+
} else if (integration.isHookOutdated?.()) {
|
|
3288
|
+
const { updateAiHook } = await (0, import_prompts2.default)({
|
|
3289
|
+
type: "confirm",
|
|
3290
|
+
name: "updateAiHook",
|
|
3291
|
+
message: `${integration.displayName} session hook is outdated. Update?`,
|
|
3292
|
+
initial: true
|
|
3293
|
+
});
|
|
3294
|
+
if (updateAiHook) {
|
|
3295
|
+
integration.install();
|
|
3296
|
+
log.success(`${integration.displayName} session hook updated`);
|
|
3297
|
+
} else {
|
|
3298
|
+
log.info(`${integration.displayName} hook update skipped \u2014 re-run workscribe init to update later`);
|
|
3299
|
+
}
|
|
2997
3300
|
} else {
|
|
2998
3301
|
const { install } = await (0, import_prompts2.default)({
|
|
2999
3302
|
type: "confirm",
|
|
@@ -3012,10 +3315,10 @@ async function runAIToolsStep() {
|
|
|
3012
3315
|
}
|
|
3013
3316
|
async function offerSkillInstall(group, sourceDir) {
|
|
3014
3317
|
const target = describeTarget(group);
|
|
3015
|
-
const state = readInstallState(target.skillsFolder, "2.0.
|
|
3318
|
+
const state = readInstallState(target.skillsFolder, "2.0.1");
|
|
3016
3319
|
const knownOutcome = outcomeWithoutPrompt(state);
|
|
3017
3320
|
if (knownOutcome) {
|
|
3018
|
-
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "2.0.
|
|
3321
|
+
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "2.0.1" }));
|
|
3019
3322
|
return;
|
|
3020
3323
|
}
|
|
3021
3324
|
const { installSkill: shouldInstall } = await (0, import_prompts2.default)({
|
|
@@ -3112,17 +3415,17 @@ function registerInit(program) {
|
|
|
3112
3415
|
await runAIToolsStep();
|
|
3113
3416
|
await runAgentSkillStep();
|
|
3114
3417
|
await runProfileDetectionStep(config);
|
|
3115
|
-
if (!
|
|
3418
|
+
if (!import_fs18.default.existsSync(CONFIG_PATH)) {
|
|
3116
3419
|
writeConfig(config);
|
|
3117
3420
|
}
|
|
3118
3421
|
printNextSteps(shell);
|
|
3119
3422
|
});
|
|
3120
3423
|
}
|
|
3121
|
-
var
|
|
3424
|
+
var import_fs18, import_path15, import_os7, import_prompts2, import_chalk3, TOTAL_STEPS;
|
|
3122
3425
|
var init_init = __esm({
|
|
3123
3426
|
"src/cli/init.ts"() {
|
|
3124
3427
|
"use strict";
|
|
3125
|
-
|
|
3428
|
+
import_fs18 = __toESM(require("fs"));
|
|
3126
3429
|
import_path15 = __toESM(require("path"));
|
|
3127
3430
|
import_os7 = __toESM(require("os"));
|
|
3128
3431
|
import_prompts2 = __toESM(require("prompts"));
|
|
@@ -3234,7 +3537,7 @@ function registerStatus(program) {
|
|
|
3234
3537
|
const total = getEventCount();
|
|
3235
3538
|
const todayEvents = getEventsForDate(todayDate());
|
|
3236
3539
|
const last = getLastEvent();
|
|
3237
|
-
const isPaused =
|
|
3540
|
+
const isPaused = import_fs19.default.existsSync(PAUSE_FLAG_PATH);
|
|
3238
3541
|
log.plain("");
|
|
3239
3542
|
log.plain(import_chalk4.default.bold(" Workscribe Status"));
|
|
3240
3543
|
log.plain("");
|
|
@@ -3280,6 +3583,18 @@ function registerStatus(program) {
|
|
|
3280
3583
|
);
|
|
3281
3584
|
}
|
|
3282
3585
|
log.plain(import_chalk4.default.dim(" Run workscribe init to repair it"));
|
|
3586
|
+
} else if (hookHealth.installed && hookHealth.nodeMissing) {
|
|
3587
|
+
log.plain("");
|
|
3588
|
+
log.plain(
|
|
3589
|
+
` ${import_chalk4.default.yellow("\u26A0")} The Node.js version the Claude Code hook uses is gone \u2014 Claude Code activity is not being captured.`
|
|
3590
|
+
);
|
|
3591
|
+
log.plain(import_chalk4.default.dim(" Run workscribe init to repair it"));
|
|
3592
|
+
} else if (hookHealth.installed && !hookHealth.guarded) {
|
|
3593
|
+
log.plain("");
|
|
3594
|
+
log.plain(
|
|
3595
|
+
` ${import_chalk4.default.yellow("\u26A0")} Claude Code hook is from an older version \u2014 it will show errors in Claude Code if Workscribe is ever removed.`
|
|
3596
|
+
);
|
|
3597
|
+
log.plain(import_chalk4.default.dim(" Run workscribe init to update it"));
|
|
3283
3598
|
}
|
|
3284
3599
|
const { total: weekTotal, otherCount } = getRecentOtherRate(7);
|
|
3285
3600
|
if (weekTotal > 0) {
|
|
@@ -3299,11 +3614,11 @@ function registerStatus(program) {
|
|
|
3299
3614
|
log.plain("");
|
|
3300
3615
|
});
|
|
3301
3616
|
}
|
|
3302
|
-
var
|
|
3617
|
+
var import_fs19, import_chalk4, AGENT_SKILL_LABEL;
|
|
3303
3618
|
var init_status = __esm({
|
|
3304
3619
|
"src/cli/status.ts"() {
|
|
3305
3620
|
"use strict";
|
|
3306
|
-
|
|
3621
|
+
import_fs19 = __toESM(require("fs"));
|
|
3307
3622
|
import_chalk4 = __toESM(require("chalk"));
|
|
3308
3623
|
init_db();
|
|
3309
3624
|
init_config();
|
|
@@ -3798,13 +4113,13 @@ function isStdinInteractive() {
|
|
|
3798
4113
|
return Boolean(process.stdin.isTTY);
|
|
3799
4114
|
}
|
|
3800
4115
|
function readAllStdin() {
|
|
3801
|
-
return
|
|
4116
|
+
return import_fs20.default.readFileSync(STDIN_FILE_DESCRIPTOR, "utf-8");
|
|
3802
4117
|
}
|
|
3803
|
-
var
|
|
4118
|
+
var import_fs20, STDIN_FILE_DESCRIPTOR;
|
|
3804
4119
|
var init_stdin = __esm({
|
|
3805
4120
|
"src/utils/stdin.ts"() {
|
|
3806
4121
|
"use strict";
|
|
3807
|
-
|
|
4122
|
+
import_fs20 = __toESM(require("fs"));
|
|
3808
4123
|
STDIN_FILE_DESCRIPTOR = 0;
|
|
3809
4124
|
}
|
|
3810
4125
|
});
|
|
@@ -4539,19 +4854,19 @@ function registerExport(program) {
|
|
|
4539
4854
|
const payload = buildDayPayload(date, sessions, locationContextFrom(config));
|
|
4540
4855
|
const summary = await createSummaryWriter(config).summarize(payload);
|
|
4541
4856
|
const outputPath = options.output ?? import_path17.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
|
|
4542
|
-
if (!
|
|
4543
|
-
|
|
4857
|
+
if (!import_fs21.default.existsSync(EXPORTS_DIR)) import_fs21.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
|
|
4858
|
+
import_fs21.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
|
|
4544
4859
|
|
|
4545
4860
|
${summary}
|
|
4546
4861
|
`, { encoding: "utf-8", mode: 384 });
|
|
4547
4862
|
log.success(`Saved: ${outputPath}`);
|
|
4548
4863
|
});
|
|
4549
4864
|
}
|
|
4550
|
-
var
|
|
4865
|
+
var import_fs21, import_path17;
|
|
4551
4866
|
var init_export = __esm({
|
|
4552
4867
|
"src/cli/export.ts"() {
|
|
4553
4868
|
"use strict";
|
|
4554
|
-
|
|
4869
|
+
import_fs21 = __toESM(require("fs"));
|
|
4555
4870
|
import_path17 = __toESM(require("path"));
|
|
4556
4871
|
init_detector();
|
|
4557
4872
|
init_config();
|
|
@@ -4643,27 +4958,27 @@ var init_config2 = __esm({
|
|
|
4643
4958
|
// src/cli/pause.ts
|
|
4644
4959
|
function registerPause(program) {
|
|
4645
4960
|
program.command("pause").description("Pause capture temporarily").action(() => {
|
|
4646
|
-
if (
|
|
4961
|
+
if (import_fs22.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
4647
4962
|
log.info("Capture is already paused. Run workscribe resume to restart.");
|
|
4648
4963
|
return;
|
|
4649
4964
|
}
|
|
4650
|
-
|
|
4965
|
+
import_fs22.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
|
|
4651
4966
|
log.success("Capture paused. Run workscribe resume to restart.");
|
|
4652
4967
|
});
|
|
4653
4968
|
program.command("resume").description("Resume capture after a pause").action(() => {
|
|
4654
|
-
if (!
|
|
4969
|
+
if (!import_fs22.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
4655
4970
|
log.info("Capture is not paused.");
|
|
4656
4971
|
return;
|
|
4657
4972
|
}
|
|
4658
|
-
|
|
4973
|
+
import_fs22.default.rmSync(PAUSE_FLAG_PATH);
|
|
4659
4974
|
log.success("Capture resumed.");
|
|
4660
4975
|
});
|
|
4661
4976
|
}
|
|
4662
|
-
var
|
|
4977
|
+
var import_fs22;
|
|
4663
4978
|
var init_pause = __esm({
|
|
4664
4979
|
"src/cli/pause.ts"() {
|
|
4665
4980
|
"use strict";
|
|
4666
|
-
|
|
4981
|
+
import_fs22 = __toESM(require("fs"));
|
|
4667
4982
|
init_paths();
|
|
4668
4983
|
init_logger();
|
|
4669
4984
|
}
|
|
@@ -4765,6 +5080,22 @@ var init_clear = __esm({
|
|
|
4765
5080
|
});
|
|
4766
5081
|
|
|
4767
5082
|
// src/cli/uninstall.ts
|
|
5083
|
+
function completionRcFiles() {
|
|
5084
|
+
return [".zshrc", ".bashrc", ".bash_profile"].map((file) => import_path18.default.join(import_os11.default.homedir(), file));
|
|
5085
|
+
}
|
|
5086
|
+
function removeCompletionsAndToolHooks() {
|
|
5087
|
+
for (const rcFile of completionRcFiles()) {
|
|
5088
|
+
if (removeCompletions(rcFile)) log.success(`Shell completions removed from ${rcFile}`);
|
|
5089
|
+
}
|
|
5090
|
+
if (removeFishCompletions()) log.success("Fish completions removed");
|
|
5091
|
+
const outcome = claudeCodeIntegration.uninstall?.();
|
|
5092
|
+
if (outcome === "removed") log.success("Claude Code session hook removed from ~/.claude/settings.json");
|
|
5093
|
+
if (outcome === "unreadable") {
|
|
5094
|
+
log.warn(
|
|
5095
|
+
"Couldn't read ~/.claude/settings.json, so the Claude Code session hook was left in place. Fix the file's JSON, then run: workscribe uninstall --hooks-only"
|
|
5096
|
+
);
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
4768
5099
|
function detectShellRcFiles() {
|
|
4769
5100
|
const candidates = [
|
|
4770
5101
|
{ name: "zsh", rcFile: import_path18.default.join(import_os11.default.homedir(), ".zshrc") },
|
|
@@ -4772,10 +5103,15 @@ function detectShellRcFiles() {
|
|
|
4772
5103
|
{ name: "bash", rcFile: import_path18.default.join(import_os11.default.homedir(), ".bash_profile") },
|
|
4773
5104
|
{ name: "fish", rcFile: import_path18.default.join(import_os11.default.homedir(), ".config", "fish", "conf.d", "workscribe.fish") }
|
|
4774
5105
|
];
|
|
4775
|
-
return candidates.filter((candidate) =>
|
|
5106
|
+
return candidates.filter((candidate) => import_fs23.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
4776
5107
|
}
|
|
4777
5108
|
function registerUninstall(program) {
|
|
4778
|
-
program.command("uninstall").description(
|
|
5109
|
+
program.command("uninstall").description(
|
|
5110
|
+
"Remove shell hooks, completions, the Claude Code hook and the agent skill; optionally delete all data"
|
|
5111
|
+
).option(
|
|
5112
|
+
"--hooks-only",
|
|
5113
|
+
"remove shell hooks, completions and the Claude Code hook only \u2014 keep the agent skill and skip the data prompt"
|
|
5114
|
+
).action(async (options) => {
|
|
4779
5115
|
log.plain(import_chalk10.default.bold("\n Workscribe \u2014 Uninstall\n"));
|
|
4780
5116
|
const hooked = detectShellRcFiles();
|
|
4781
5117
|
if (hooked.length === 0) {
|
|
@@ -4783,7 +5119,7 @@ function registerUninstall(program) {
|
|
|
4783
5119
|
} else {
|
|
4784
5120
|
for (const { name, rcFile } of hooked) {
|
|
4785
5121
|
if (name === "fish") {
|
|
4786
|
-
|
|
5122
|
+
import_fs23.default.unlinkSync(rcFile);
|
|
4787
5123
|
log.success(`Shell hook removed: ${rcFile}`);
|
|
4788
5124
|
log.plain(` ${import_chalk10.default.dim("Restart your terminal to take effect")}`);
|
|
4789
5125
|
} else {
|
|
@@ -4796,9 +5132,10 @@ function registerUninstall(program) {
|
|
|
4796
5132
|
}
|
|
4797
5133
|
}
|
|
4798
5134
|
}
|
|
4799
|
-
|
|
5135
|
+
removeCompletionsAndToolHooks();
|
|
5136
|
+
if (options.hooksOnly) return;
|
|
4800
5137
|
removeSkills(SKILL_AGENTS, { reportMissing: false });
|
|
4801
|
-
const existingDirs = [...new Set(REQUIRED_DIRS)].filter((dir) =>
|
|
5138
|
+
const existingDirs = [...new Set(REQUIRED_DIRS)].filter((dir) => import_fs23.default.existsSync(dir));
|
|
4802
5139
|
if (existingDirs.length > 0) {
|
|
4803
5140
|
log.plain("");
|
|
4804
5141
|
const { deleteData } = await (0, import_prompts4.default)({
|
|
@@ -4809,7 +5146,7 @@ function registerUninstall(program) {
|
|
|
4809
5146
|
});
|
|
4810
5147
|
if (deleteData) {
|
|
4811
5148
|
for (const dir of existingDirs) {
|
|
4812
|
-
|
|
5149
|
+
import_fs23.default.rmSync(dir, { recursive: true, force: true });
|
|
4813
5150
|
log.success(`Deleted ${dir}`);
|
|
4814
5151
|
}
|
|
4815
5152
|
} else {
|
|
@@ -4821,17 +5158,18 @@ function registerUninstall(program) {
|
|
|
4821
5158
|
log.plain("");
|
|
4822
5159
|
});
|
|
4823
5160
|
}
|
|
4824
|
-
var
|
|
5161
|
+
var import_fs23, import_path18, import_os11, import_prompts4, import_chalk10;
|
|
4825
5162
|
var init_uninstall = __esm({
|
|
4826
5163
|
"src/cli/uninstall.ts"() {
|
|
4827
5164
|
"use strict";
|
|
4828
|
-
|
|
5165
|
+
import_fs23 = __toESM(require("fs"));
|
|
4829
5166
|
import_path18 = __toESM(require("path"));
|
|
4830
5167
|
import_os11 = __toESM(require("os"));
|
|
4831
5168
|
import_prompts4 = __toESM(require("prompts"));
|
|
4832
5169
|
import_chalk10 = __toESM(require("chalk"));
|
|
4833
5170
|
init_paths();
|
|
4834
5171
|
init_shell_hook();
|
|
5172
|
+
init_claude_code();
|
|
4835
5173
|
init_logger();
|
|
4836
5174
|
init_agents();
|
|
4837
5175
|
init_actions();
|
|
@@ -4882,7 +5220,7 @@ function registerProfile(program) {
|
|
|
4882
5220
|
log.plain(import_chalk11.default.dim(" " + "\u2500".repeat(60)));
|
|
4883
5221
|
for (const entry of entries) {
|
|
4884
5222
|
const badge = activeNames.has(entry.name) ? import_chalk11.default.green(" [active]") : "";
|
|
4885
|
-
const installed =
|
|
5223
|
+
const installed = import_fs24.default.existsSync(import_path19.default.join(PROFILES_DIR, `${entry.name}.json`)) ? import_chalk11.default.dim(" installed") : "";
|
|
4886
5224
|
log.plain(` ${import_chalk11.default.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`);
|
|
4887
5225
|
if (entry.description) log.plain(` ${import_chalk11.default.dim(" ".repeat(18) + entry.description)}`);
|
|
4888
5226
|
}
|
|
@@ -4919,7 +5257,7 @@ function registerProfile(program) {
|
|
|
4919
5257
|
}
|
|
4920
5258
|
activeNames.delete(name);
|
|
4921
5259
|
const cachePath = import_path19.default.join(PROFILES_DIR, `${name}.json`);
|
|
4922
|
-
if (
|
|
5260
|
+
if (import_fs24.default.existsSync(cachePath)) import_fs24.default.unlinkSync(cachePath);
|
|
4923
5261
|
log.success(`Profile "${name}" removed`);
|
|
4924
5262
|
}
|
|
4925
5263
|
config.capture.profiles = [...activeNames];
|
|
@@ -5046,11 +5384,11 @@ function registerProfile(program) {
|
|
|
5046
5384
|
}
|
|
5047
5385
|
});
|
|
5048
5386
|
}
|
|
5049
|
-
var
|
|
5387
|
+
var import_fs24, import_path19, import_chalk11, MIN_SUGGEST_COUNT;
|
|
5050
5388
|
var init_profile = __esm({
|
|
5051
5389
|
"src/cli/profile.ts"() {
|
|
5052
5390
|
"use strict";
|
|
5053
|
-
|
|
5391
|
+
import_fs24 = __toESM(require("fs"));
|
|
5054
5392
|
import_path19 = __toESM(require("path"));
|
|
5055
5393
|
import_chalk11 = __toESM(require("chalk"));
|
|
5056
5394
|
init_config();
|
|
@@ -5063,14 +5401,42 @@ var init_profile = __esm({
|
|
|
5063
5401
|
}
|
|
5064
5402
|
});
|
|
5065
5403
|
|
|
5404
|
+
// src/utils/working-directory.ts
|
|
5405
|
+
function realPath(target) {
|
|
5406
|
+
try {
|
|
5407
|
+
return import_fs25.default.realpathSync(target);
|
|
5408
|
+
} catch {
|
|
5409
|
+
return null;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
function currentDirectory(where = { pwd: process.env.PWD, cwd: process.cwd() }) {
|
|
5413
|
+
if (where.pwd && realPath(where.pwd) === realPath(where.cwd)) return where.pwd;
|
|
5414
|
+
return where.cwd;
|
|
5415
|
+
}
|
|
5416
|
+
function rootForms(cwd, root) {
|
|
5417
|
+
const resolvedCwd = realPath(cwd);
|
|
5418
|
+
if (!resolvedCwd) return [root];
|
|
5419
|
+
const reached = import_path20.default.resolve(cwd, import_path20.default.relative(resolvedCwd, root));
|
|
5420
|
+
return [.../* @__PURE__ */ new Set([root, reached])];
|
|
5421
|
+
}
|
|
5422
|
+
var import_fs25, import_path20;
|
|
5423
|
+
var init_working_directory = __esm({
|
|
5424
|
+
"src/utils/working-directory.ts"() {
|
|
5425
|
+
"use strict";
|
|
5426
|
+
import_fs25 = __toESM(require("fs"));
|
|
5427
|
+
import_path20 = __toESM(require("path"));
|
|
5428
|
+
}
|
|
5429
|
+
});
|
|
5430
|
+
|
|
5066
5431
|
// src/cli/note.ts
|
|
5067
5432
|
function registerNote(program) {
|
|
5068
5433
|
program.command("note <text>").description("Attach a free-text annotation to the current session").action((text) => {
|
|
5069
|
-
const
|
|
5434
|
+
const directory = currentDirectory();
|
|
5435
|
+
const { repoName, branch } = getRepoContext(directory, readConfig());
|
|
5070
5436
|
insertEvent({
|
|
5071
5437
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5072
5438
|
command: "workscribe note",
|
|
5073
|
-
cwd:
|
|
5439
|
+
cwd: directory,
|
|
5074
5440
|
exit_code: null,
|
|
5075
5441
|
duration_ms: null,
|
|
5076
5442
|
repo_name: repoName,
|
|
@@ -5088,6 +5454,8 @@ var init_note = __esm({
|
|
|
5088
5454
|
"use strict";
|
|
5089
5455
|
init_db();
|
|
5090
5456
|
init_git();
|
|
5457
|
+
init_config();
|
|
5458
|
+
init_working_directory();
|
|
5091
5459
|
init_logger();
|
|
5092
5460
|
}
|
|
5093
5461
|
});
|
|
@@ -5118,8 +5486,8 @@ var init_week = __esm({
|
|
|
5118
5486
|
|
|
5119
5487
|
// src/cli/bug-report.ts
|
|
5120
5488
|
function tailFile(filePath, lines) {
|
|
5121
|
-
if (!
|
|
5122
|
-
const content =
|
|
5489
|
+
if (!import_fs26.default.existsSync(filePath)) return "(no debug log found \u2014 set WORKSCRIBE_DEBUG=1 to enable)";
|
|
5490
|
+
const content = import_fs26.default.readFileSync(filePath, "utf-8");
|
|
5123
5491
|
const allLines = content.split("\n");
|
|
5124
5492
|
return allLines.slice(-lines).join("\n");
|
|
5125
5493
|
}
|
|
@@ -5137,9 +5505,9 @@ function redactConfig() {
|
|
|
5137
5505
|
function registerBugReport(program) {
|
|
5138
5506
|
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
5139
5507
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5140
|
-
const reportPath =
|
|
5508
|
+
const reportPath = import_path21.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
5141
5509
|
const systemInfo = [
|
|
5142
|
-
`Workscribe version : ${"2.0.
|
|
5510
|
+
`Workscribe version : ${"2.0.1"}`,
|
|
5143
5511
|
`Node.js version : ${process.version}`,
|
|
5144
5512
|
`Platform : ${process.platform}`,
|
|
5145
5513
|
`Architecture : ${process.arch}`,
|
|
@@ -5161,17 +5529,17 @@ function registerBugReport(program) {
|
|
|
5161
5529
|
tailFile(DEBUG_LOG_PATH, DEBUG_LOG_TAIL_LINES)
|
|
5162
5530
|
].join("\n");
|
|
5163
5531
|
try {
|
|
5164
|
-
if (!
|
|
5165
|
-
|
|
5532
|
+
if (!import_fs26.default.existsSync(WORKSCRIBE_DIR)) {
|
|
5533
|
+
import_fs26.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 448 });
|
|
5166
5534
|
}
|
|
5167
|
-
|
|
5535
|
+
import_fs26.default.writeFileSync(reportPath, report, { mode: 384 });
|
|
5168
5536
|
} catch (error) {
|
|
5169
5537
|
log.error(`Could not write report: ${error.message}`);
|
|
5170
5538
|
process.exit(1);
|
|
5171
5539
|
}
|
|
5172
5540
|
const params = new URLSearchParams({
|
|
5173
|
-
title: `Bug report \u2014 workscribe ${"2.0.
|
|
5174
|
-
body: `<!-- Attach ${
|
|
5541
|
+
title: `Bug report \u2014 workscribe ${"2.0.1"}`,
|
|
5542
|
+
body: `<!-- Attach ${import_path21.default.basename(reportPath)} or paste the relevant sections below -->
|
|
5175
5543
|
|
|
5176
5544
|
**Describe the issue:**
|
|
5177
5545
|
|
|
@@ -5188,13 +5556,13 @@ function registerBugReport(program) {
|
|
|
5188
5556
|
log.plain("");
|
|
5189
5557
|
});
|
|
5190
5558
|
}
|
|
5191
|
-
var
|
|
5559
|
+
var import_fs26, import_os12, import_path21, import_chalk12, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
5192
5560
|
var init_bug_report = __esm({
|
|
5193
5561
|
"src/cli/bug-report.ts"() {
|
|
5194
5562
|
"use strict";
|
|
5195
|
-
|
|
5563
|
+
import_fs26 = __toESM(require("fs"));
|
|
5196
5564
|
import_os12 = __toESM(require("os"));
|
|
5197
|
-
|
|
5565
|
+
import_path21 = __toESM(require("path"));
|
|
5198
5566
|
import_chalk12 = __toESM(require("chalk"));
|
|
5199
5567
|
init_paths();
|
|
5200
5568
|
init_config();
|
|
@@ -5320,7 +5688,7 @@ function printSessionContext(session, heading) {
|
|
|
5320
5688
|
function registerContext(program) {
|
|
5321
5689
|
program.command("context").description("Show the last session for the current repo").option("--file <path>", "narrow to last session that touched this file").option("--json", "output as JSON instead of formatted text \u2014 null when no session is found").action((opts) => {
|
|
5322
5690
|
if (opts.file) {
|
|
5323
|
-
const normalizedFile =
|
|
5691
|
+
const normalizedFile = import_path22.default.normalize(opts.file);
|
|
5324
5692
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
5325
5693
|
if (!session) {
|
|
5326
5694
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
@@ -5331,16 +5699,20 @@ function registerContext(program) {
|
|
|
5331
5699
|
log.plain(JSON.stringify(toContextJson(session)));
|
|
5332
5700
|
return;
|
|
5333
5701
|
}
|
|
5334
|
-
printSessionContext(session, `Last session touching ${
|
|
5702
|
+
printSessionContext(session, `Last session touching ${import_path22.default.basename(normalizedFile)}`);
|
|
5335
5703
|
return;
|
|
5336
5704
|
}
|
|
5337
|
-
const
|
|
5705
|
+
const config = readConfig();
|
|
5706
|
+
const directory = currentDirectory();
|
|
5707
|
+
const { repoName, root, remote } = getRepoContext(directory, config);
|
|
5338
5708
|
if (!repoName) {
|
|
5339
5709
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
5340
5710
|
else log.plain(" Not in a git repository. Run workscribe context from a project directory.");
|
|
5341
5711
|
return;
|
|
5342
5712
|
}
|
|
5343
|
-
const
|
|
5713
|
+
const otherRepoName = remote ? formatRepoName(remote, !config.git.qualifyRepoName) : null;
|
|
5714
|
+
const roots = root ? rootForms(directory, root) : [];
|
|
5715
|
+
const sessions = getRecentSessionsForRepo({ repoName, otherRepoName, roots }, 1);
|
|
5344
5716
|
if (sessions.length === 0) {
|
|
5345
5717
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
5346
5718
|
else log.plain(` No sessions found for ${repoName}`);
|
|
@@ -5353,14 +5725,16 @@ function registerContext(program) {
|
|
|
5353
5725
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
5354
5726
|
});
|
|
5355
5727
|
}
|
|
5356
|
-
var
|
|
5728
|
+
var import_path22, import_chalk14;
|
|
5357
5729
|
var init_context = __esm({
|
|
5358
5730
|
"src/cli/context.ts"() {
|
|
5359
5731
|
"use strict";
|
|
5360
|
-
|
|
5732
|
+
import_path22 = __toESM(require("path"));
|
|
5361
5733
|
import_chalk14 = __toESM(require("chalk"));
|
|
5362
5734
|
init_db();
|
|
5363
5735
|
init_git();
|
|
5736
|
+
init_repo_name();
|
|
5737
|
+
init_working_directory();
|
|
5364
5738
|
init_config();
|
|
5365
5739
|
init_format();
|
|
5366
5740
|
init_logger();
|
|
@@ -5448,7 +5822,7 @@ var init_skill = __esm({
|
|
|
5448
5822
|
// src/utils/update-check.ts
|
|
5449
5823
|
function readCache() {
|
|
5450
5824
|
try {
|
|
5451
|
-
return JSON.parse(
|
|
5825
|
+
return JSON.parse(import_fs27.default.readFileSync(UPDATE_CHECK_PATH, "utf-8"));
|
|
5452
5826
|
} catch {
|
|
5453
5827
|
return null;
|
|
5454
5828
|
}
|
|
@@ -5456,7 +5830,7 @@ function readCache() {
|
|
|
5456
5830
|
function writeCache(updates) {
|
|
5457
5831
|
try {
|
|
5458
5832
|
const current = readCache() ?? {};
|
|
5459
|
-
|
|
5833
|
+
import_fs27.default.writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({ ...current, ...updates }), { mode: 384 });
|
|
5460
5834
|
} catch {
|
|
5461
5835
|
}
|
|
5462
5836
|
}
|
|
@@ -5469,7 +5843,7 @@ function isNewer(latest, current) {
|
|
|
5469
5843
|
return latestPatch > currentPatch;
|
|
5470
5844
|
}
|
|
5471
5845
|
function checkForUpdate() {
|
|
5472
|
-
const current = "2.0.
|
|
5846
|
+
const current = "2.0.1";
|
|
5473
5847
|
const cache = readCache();
|
|
5474
5848
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
5475
5849
|
process.stderr.write(
|
|
@@ -5492,7 +5866,7 @@ function checkForUpdate() {
|
|
|
5492
5866
|
function buildTelemetryPayload(config) {
|
|
5493
5867
|
return {
|
|
5494
5868
|
installId: config.telemetry.installId,
|
|
5495
|
-
cliVersion: "2.0.
|
|
5869
|
+
cliVersion: "2.0.1",
|
|
5496
5870
|
platform: import_os13.default.platform(),
|
|
5497
5871
|
nodeVersion: process.version
|
|
5498
5872
|
};
|
|
@@ -5517,11 +5891,11 @@ function pingTelemetry() {
|
|
|
5517
5891
|
debugLog("pingTelemetry", error);
|
|
5518
5892
|
});
|
|
5519
5893
|
}
|
|
5520
|
-
var
|
|
5894
|
+
var import_fs27, import_os13, CHECK_INTERVAL_MS;
|
|
5521
5895
|
var init_update_check = __esm({
|
|
5522
5896
|
"src/utils/update-check.ts"() {
|
|
5523
5897
|
"use strict";
|
|
5524
|
-
|
|
5898
|
+
import_fs27 = __toESM(require("fs"));
|
|
5525
5899
|
import_os13 = __toESM(require("os"));
|
|
5526
5900
|
init_paths();
|
|
5527
5901
|
init_config();
|
|
@@ -5568,7 +5942,7 @@ var require_main = __commonJS({
|
|
|
5568
5942
|
pingTelemetry();
|
|
5569
5943
|
}
|
|
5570
5944
|
var program = new import_commander.Command();
|
|
5571
|
-
program.name("workscribe").description("Automatic developer work journal").version("2.0.
|
|
5945
|
+
program.name("workscribe").description("Automatic developer work journal").version("2.0.1");
|
|
5572
5946
|
registerCapture(program);
|
|
5573
5947
|
registerHook(program);
|
|
5574
5948
|
registerInit(program);
|