@workscribe/cli 1.14.2 → 1.15.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 +392 -296
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/shell/hook.bash +4 -1
- package/shell/hook.fish +4 -1
- package/shell/hook.zsh +4 -1
package/dist/index.js
CHANGED
|
@@ -755,12 +755,28 @@ function readConfig() {
|
|
|
755
755
|
}
|
|
756
756
|
}
|
|
757
757
|
function writeConfig(config) {
|
|
758
|
+
import_fs7.default.mkdirSync(import_path5.default.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
|
|
758
759
|
import_fs7.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
759
760
|
try {
|
|
760
761
|
import_fs7.default.chmodSync(CONFIG_PATH, 384);
|
|
761
762
|
} catch {
|
|
762
763
|
}
|
|
763
764
|
}
|
|
765
|
+
function ensureInstallId(config) {
|
|
766
|
+
if (config.telemetry.installId) return config.telemetry.installId;
|
|
767
|
+
const installId = import_crypto.default.randomUUID();
|
|
768
|
+
writeConfig({ ...config, telemetry: { ...config.telemetry, installId } });
|
|
769
|
+
return installId;
|
|
770
|
+
}
|
|
771
|
+
function ensureTelemetryFallback() {
|
|
772
|
+
const config = readConfig();
|
|
773
|
+
if (config.telemetry.installId) return;
|
|
774
|
+
config.telemetry.enabled = true;
|
|
775
|
+
config.telemetry.installId = ensureInstallId(config);
|
|
776
|
+
process.stderr.write(
|
|
777
|
+
"\n Workscribe collects anonymous install stats (install ID, CLI version, platform, Node version).\n Opt out: workscribe config set telemetry.enabled false\n\n"
|
|
778
|
+
);
|
|
779
|
+
}
|
|
764
780
|
function getConfigValue(config, key) {
|
|
765
781
|
const parts = key.split(".");
|
|
766
782
|
let cur = config;
|
|
@@ -800,11 +816,13 @@ function deepMerge(defaults, overrides) {
|
|
|
800
816
|
}
|
|
801
817
|
return result;
|
|
802
818
|
}
|
|
803
|
-
var import_fs7, DEFAULTS;
|
|
819
|
+
var import_crypto, import_fs7, import_path5, DEFAULTS;
|
|
804
820
|
var init_config = __esm({
|
|
805
821
|
"src/config/index.ts"() {
|
|
806
822
|
"use strict";
|
|
823
|
+
import_crypto = __toESM(require("crypto"));
|
|
807
824
|
import_fs7 = __toESM(require("fs"));
|
|
825
|
+
import_path5 = __toESM(require("path"));
|
|
808
826
|
init_paths();
|
|
809
827
|
DEFAULTS = {
|
|
810
828
|
ai: {
|
|
@@ -830,6 +848,10 @@ var init_config = __esm({
|
|
|
830
848
|
},
|
|
831
849
|
summary: {
|
|
832
850
|
format: "markdown"
|
|
851
|
+
},
|
|
852
|
+
telemetry: {
|
|
853
|
+
enabled: true,
|
|
854
|
+
installId: ""
|
|
833
855
|
}
|
|
834
856
|
};
|
|
835
857
|
}
|
|
@@ -902,7 +924,7 @@ var init_fetch = __esm({
|
|
|
902
924
|
|
|
903
925
|
// src/profiles/fetcher.ts
|
|
904
926
|
function manifestCachePath() {
|
|
905
|
-
return
|
|
927
|
+
return import_path6.default.join(PROFILES_DIR, "manifest.json");
|
|
906
928
|
}
|
|
907
929
|
async function fetchManifest() {
|
|
908
930
|
const response = await fetchWithTimeout(`${REGISTRY_BASE}/manifest.json`, {});
|
|
@@ -922,7 +944,7 @@ async function fetchProfile(name) {
|
|
|
922
944
|
async function installProfile(name) {
|
|
923
945
|
ensureProfilesDir();
|
|
924
946
|
const profile = await fetchProfile(name);
|
|
925
|
-
const dest =
|
|
947
|
+
const dest = import_path6.default.join(PROFILES_DIR, `${name}.json`);
|
|
926
948
|
import_fs8.default.writeFileSync(dest, JSON.stringify(profile, null, 2), { encoding: "utf-8", mode: 384 });
|
|
927
949
|
debugLog("profiles", `Installed profile "${name}" to ${dest}`);
|
|
928
950
|
}
|
|
@@ -946,12 +968,12 @@ function ensureProfilesDir() {
|
|
|
946
968
|
import_fs8.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
947
969
|
}
|
|
948
970
|
}
|
|
949
|
-
var import_fs8,
|
|
971
|
+
var import_fs8, import_path6, REGISTRY_BASE;
|
|
950
972
|
var init_fetcher = __esm({
|
|
951
973
|
"src/profiles/fetcher.ts"() {
|
|
952
974
|
"use strict";
|
|
953
975
|
import_fs8 = __toESM(require("fs"));
|
|
954
|
-
|
|
976
|
+
import_path6 = __toESM(require("path"));
|
|
955
977
|
init_paths();
|
|
956
978
|
init_schema();
|
|
957
979
|
init_logger();
|
|
@@ -982,7 +1004,7 @@ function compileRule(profileName, rule) {
|
|
|
982
1004
|
};
|
|
983
1005
|
}
|
|
984
1006
|
function loadProfileFromDisk(name) {
|
|
985
|
-
const filePath =
|
|
1007
|
+
const filePath = import_path7.default.join(PROFILES_DIR, `${name}.json`);
|
|
986
1008
|
if (!import_fs9.default.existsSync(filePath)) {
|
|
987
1009
|
debugLog("profiles", `Profile "${name}" not found in cache \u2014 run: workscribe profile add ${name}`);
|
|
988
1010
|
return null;
|
|
@@ -1034,15 +1056,15 @@ function detectProfilesForDirectory(dir) {
|
|
|
1034
1056
|
return manifest.filter((entry) => {
|
|
1035
1057
|
if (activeNames.has(entry.name)) return false;
|
|
1036
1058
|
const indicatorFiles = entry.detect?.files ?? [];
|
|
1037
|
-
return indicatorFiles.length > 0 && indicatorFiles.every((file) => import_fs9.default.existsSync(
|
|
1059
|
+
return indicatorFiles.length > 0 && indicatorFiles.every((file) => import_fs9.default.existsSync(import_path7.default.join(dir, file)));
|
|
1038
1060
|
});
|
|
1039
1061
|
}
|
|
1040
|
-
var import_fs9,
|
|
1062
|
+
var import_fs9, import_path7;
|
|
1041
1063
|
var init_loader = __esm({
|
|
1042
1064
|
"src/profiles/loader.ts"() {
|
|
1043
1065
|
"use strict";
|
|
1044
1066
|
import_fs9 = __toESM(require("fs"));
|
|
1045
|
-
|
|
1067
|
+
import_path7 = __toESM(require("path"));
|
|
1046
1068
|
init_paths();
|
|
1047
1069
|
init_config();
|
|
1048
1070
|
init_schema();
|
|
@@ -1478,7 +1500,7 @@ function insertAlert(tool, sessionId, context) {
|
|
|
1478
1500
|
`INSERT INTO alerts (created_at, tool, session_id, context)
|
|
1479
1501
|
VALUES ($created_at, $tool, $session_id, $context)`
|
|
1480
1502
|
).run({ created_at: Date.now(), tool, session_id: sessionId ?? null, context });
|
|
1481
|
-
import_fs11.default.mkdirSync(
|
|
1503
|
+
import_fs11.default.mkdirSync(import_path8.default.dirname(ALERTS_PENDING_PATH), { recursive: true });
|
|
1482
1504
|
import_fs11.default.writeFileSync(ALERTS_PENDING_PATH, "", { flag: "w" });
|
|
1483
1505
|
}
|
|
1484
1506
|
function getPendingAlerts() {
|
|
@@ -1496,12 +1518,12 @@ function clearAlerts() {
|
|
|
1496
1518
|
}
|
|
1497
1519
|
return Number(result.changes);
|
|
1498
1520
|
}
|
|
1499
|
-
var import_fs11,
|
|
1521
|
+
var import_fs11, import_path8;
|
|
1500
1522
|
var init_alerts2 = __esm({
|
|
1501
1523
|
"src/db/alerts.ts"() {
|
|
1502
1524
|
"use strict";
|
|
1503
1525
|
import_fs11 = __toESM(require("fs"));
|
|
1504
|
-
|
|
1526
|
+
import_path8 = __toESM(require("path"));
|
|
1505
1527
|
init_db();
|
|
1506
1528
|
init_paths();
|
|
1507
1529
|
}
|
|
@@ -2143,7 +2165,7 @@ function isHookOutdated(rcFile, hookContent) {
|
|
|
2143
2165
|
function installHook(rcFile, hookContent) {
|
|
2144
2166
|
const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
|
|
2145
2167
|
import_fs12.default.copyFileSync(rcFile, backup);
|
|
2146
|
-
log.info(`Backed up ${rcFile} \u2192 ${
|
|
2168
|
+
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2147
2169
|
import_fs12.default.appendFileSync(rcFile, `
|
|
2148
2170
|
${hookContent}
|
|
2149
2171
|
`);
|
|
@@ -2151,7 +2173,7 @@ ${hookContent}
|
|
|
2151
2173
|
function updateHook(rcFile, hookContent) {
|
|
2152
2174
|
const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
|
|
2153
2175
|
import_fs12.default.copyFileSync(rcFile, backup);
|
|
2154
|
-
log.info(`Backed up ${rcFile} \u2192 ${
|
|
2176
|
+
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2155
2177
|
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
2156
2178
|
const startIdx = content.indexOf(HOOK_START);
|
|
2157
2179
|
const endIdx = content.indexOf(HOOK_END) + HOOK_END.length;
|
|
@@ -2160,7 +2182,7 @@ function updateHook(rcFile, hookContent) {
|
|
|
2160
2182
|
function removeHook(rcFile) {
|
|
2161
2183
|
const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
|
|
2162
2184
|
import_fs12.default.copyFileSync(rcFile, backup);
|
|
2163
|
-
log.info(`Backed up ${rcFile} \u2192 ${
|
|
2185
|
+
log.info(`Backed up ${rcFile} \u2192 ${import_path9.default.basename(backup)}`);
|
|
2164
2186
|
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
2165
2187
|
const startIdx = content.indexOf(HOOK_START);
|
|
2166
2188
|
const endIdx = content.indexOf(HOOK_END) + HOOK_END.length;
|
|
@@ -2179,13 +2201,13 @@ function resolveWorkscribeBinary() {
|
|
|
2179
2201
|
}
|
|
2180
2202
|
}
|
|
2181
2203
|
function injectBinaryPath(hookContent, binaryPath, nodePath) {
|
|
2182
|
-
const pinnedInvocation = `"${nodePath}" "${binaryPath}"`;
|
|
2204
|
+
const pinnedInvocation = `"${nodePath}" --disable-warning=ExperimentalWarning "${binaryPath}"`;
|
|
2183
2205
|
return hookContent.replace("command -v workscribe > /dev/null 2>&1 || return 0", `[[ -x "${binaryPath}" ]] || return 0`).replace("command -v workscribe &>/dev/null || return 0", `[[ -x "${binaryPath}" ]] || return 0`).replace("type -q workscribe\n or return 0", `test -x "${binaryPath}"
|
|
2184
2206
|
or return 0`).replaceAll("command workscribe _capture", `${pinnedInvocation} _capture`).replaceAll("command workscribe alerts", `${pinnedInvocation} alerts`);
|
|
2185
2207
|
}
|
|
2186
2208
|
function isCompletionsInstalled(shell) {
|
|
2187
2209
|
if (shell.name === "fish") {
|
|
2188
|
-
return import_fs12.default.existsSync(
|
|
2210
|
+
return import_fs12.default.existsSync(import_path9.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish"));
|
|
2189
2211
|
}
|
|
2190
2212
|
try {
|
|
2191
2213
|
return import_fs12.default.readFileSync(shell.rcFile, "utf-8").includes(COMPLETIONS_MARKER);
|
|
@@ -2198,10 +2220,10 @@ function installCompletions(shell, commands) {
|
|
|
2198
2220
|
if (!import_fs12.default.existsSync(FISH_COMPLETIONS_DIR)) {
|
|
2199
2221
|
import_fs12.default.mkdirSync(FISH_COMPLETIONS_DIR, { recursive: true });
|
|
2200
2222
|
}
|
|
2201
|
-
import_fs12.default.writeFileSync(
|
|
2223
|
+
import_fs12.default.writeFileSync(import_path9.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish"), fishCompletion(commands), { mode: 420 });
|
|
2202
2224
|
} else {
|
|
2203
2225
|
const script = shell.name === "zsh" ? zshCompletion(commands) : bashCompletion(commands);
|
|
2204
|
-
const cacheFile =
|
|
2226
|
+
const cacheFile = import_path9.default.join(WORKSCRIBE_DIR, `completion.${shell.name}`);
|
|
2205
2227
|
import_fs12.default.writeFileSync(cacheFile, script, { mode: 420 });
|
|
2206
2228
|
if (!isCompletionsInstalled(shell)) {
|
|
2207
2229
|
const sourceLine = shell.name === "zsh" ? `
|
|
@@ -2216,18 +2238,18 @@ source "${cacheFile}"
|
|
|
2216
2238
|
}
|
|
2217
2239
|
}
|
|
2218
2240
|
}
|
|
2219
|
-
var import_fs12,
|
|
2241
|
+
var import_fs12, import_path9, import_os3, import_child_process3, FISH_COMPLETIONS_DIR, HOOK_START, HOOK_END, HOOK_PREAMBLE, COMPLETIONS_MARKER;
|
|
2220
2242
|
var init_shell_hook = __esm({
|
|
2221
2243
|
"src/capture/shell-hook.ts"() {
|
|
2222
2244
|
"use strict";
|
|
2223
2245
|
import_fs12 = __toESM(require("fs"));
|
|
2224
|
-
|
|
2246
|
+
import_path9 = __toESM(require("path"));
|
|
2225
2247
|
import_os3 = __toESM(require("os"));
|
|
2226
2248
|
import_child_process3 = require("child_process");
|
|
2227
2249
|
init_logger();
|
|
2228
2250
|
init_completion_scripts();
|
|
2229
2251
|
init_paths();
|
|
2230
|
-
FISH_COMPLETIONS_DIR =
|
|
2252
|
+
FISH_COMPLETIONS_DIR = import_path9.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
2231
2253
|
HOOK_START = "# workscribe:hook:start";
|
|
2232
2254
|
HOOK_END = "# workscribe:hook:end";
|
|
2233
2255
|
HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
@@ -2311,17 +2333,17 @@ function checkHookHealth() {
|
|
|
2311
2333
|
}
|
|
2312
2334
|
return { installed: true, pinned: false, confirmedBroken };
|
|
2313
2335
|
}
|
|
2314
|
-
var import_fs13,
|
|
2336
|
+
var import_fs13, import_path10, import_os4, import_child_process5, CLAUDE_SETTINGS_PATH, HOOK_MARKER, claudeCodeIntegration;
|
|
2315
2337
|
var init_claude_code = __esm({
|
|
2316
2338
|
"src/capture/hooks/claude-code.ts"() {
|
|
2317
2339
|
"use strict";
|
|
2318
2340
|
import_fs13 = __toESM(require("fs"));
|
|
2319
|
-
|
|
2341
|
+
import_path10 = __toESM(require("path"));
|
|
2320
2342
|
import_os4 = __toESM(require("os"));
|
|
2321
2343
|
import_child_process5 = require("child_process");
|
|
2322
2344
|
init_utils();
|
|
2323
2345
|
init_shell_hook();
|
|
2324
|
-
CLAUDE_SETTINGS_PATH =
|
|
2346
|
+
CLAUDE_SETTINGS_PATH = import_path10.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2325
2347
|
HOOK_MARKER = "_hook --source claude-code";
|
|
2326
2348
|
claudeCodeIntegration = {
|
|
2327
2349
|
displayName: "Claude Code",
|
|
@@ -2365,7 +2387,7 @@ var init_claude_code = __esm({
|
|
|
2365
2387
|
}
|
|
2366
2388
|
]
|
|
2367
2389
|
};
|
|
2368
|
-
import_fs13.default.mkdirSync(
|
|
2390
|
+
import_fs13.default.mkdirSync(import_path10.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2369
2391
|
import_fs13.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2370
2392
|
}
|
|
2371
2393
|
};
|
|
@@ -2385,6 +2407,10 @@ var init_hooks = __esm({
|
|
|
2385
2407
|
});
|
|
2386
2408
|
|
|
2387
2409
|
// src/cli/completions.ts
|
|
2410
|
+
function shouldRunStartupChecks(invokedCommand) {
|
|
2411
|
+
if (invokedCommand === void 0) return true;
|
|
2412
|
+
return !HIDDEN_COMMANDS.has(invokedCommand);
|
|
2413
|
+
}
|
|
2388
2414
|
function getPublicCommands(program) {
|
|
2389
2415
|
return program.commands.filter((command) => !HIDDEN_COMMANDS.has(command.name())).map((command) => ({ name: command.name(), description: command.description() }));
|
|
2390
2416
|
}
|
|
@@ -2414,11 +2440,11 @@ var init_completions = __esm({
|
|
|
2414
2440
|
// src/cli/init.ts
|
|
2415
2441
|
function detectShell() {
|
|
2416
2442
|
const shell = process.env.SHELL ?? "";
|
|
2417
|
-
if (shell.includes("zsh")) return { name: "zsh", rcFile:
|
|
2418
|
-
if (shell.includes("bash")) return { name: "bash", rcFile:
|
|
2443
|
+
if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path11.default.join(import_os5.default.homedir(), ".zshrc") };
|
|
2444
|
+
if (shell.includes("bash")) return { name: "bash", rcFile: import_path11.default.join(import_os5.default.homedir(), ".bashrc") };
|
|
2419
2445
|
if (shell.includes("fish")) {
|
|
2420
|
-
const confDir =
|
|
2421
|
-
return { name: "fish", rcFile:
|
|
2446
|
+
const confDir = import_path11.default.join(import_os5.default.homedir(), ".config", "fish", "conf.d");
|
|
2447
|
+
return { name: "fish", rcFile: import_path11.default.join(confDir, "workscribe.fish") };
|
|
2422
2448
|
}
|
|
2423
2449
|
return null;
|
|
2424
2450
|
}
|
|
@@ -2426,262 +2452,299 @@ function step(current, total, label) {
|
|
|
2426
2452
|
log.plain(import_chalk2.default.bold(`
|
|
2427
2453
|
[${current}/${total}] ${label}`));
|
|
2428
2454
|
}
|
|
2429
|
-
function
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
if (!import_fs14.default.existsSync(dir)) {
|
|
2436
|
-
import_fs14.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2437
|
-
log.success(`Created ${dir}`);
|
|
2438
|
-
} else {
|
|
2439
|
-
try {
|
|
2440
|
-
import_fs14.default.chmodSync(dir, 448);
|
|
2441
|
-
} catch {
|
|
2442
|
-
}
|
|
2443
|
-
log.info(`${dir} already exists`);
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
if (!import_fs14.default.existsSync(PROFILES_DIR)) {
|
|
2447
|
-
import_fs14.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
2448
|
-
}
|
|
2449
|
-
step(2, TOTAL_STEPS, "Initialising database");
|
|
2450
|
-
getDb();
|
|
2451
|
-
log.success("Database ready");
|
|
2452
|
-
step(3, TOTAL_STEPS, "Installing shell hook");
|
|
2453
|
-
const shell = detectShell();
|
|
2454
|
-
if (!shell) {
|
|
2455
|
-
log.warn("Could not detect shell. Supported: zsh, bash, fish");
|
|
2456
|
-
log.plain(
|
|
2457
|
-
` Manually add the hook from: ${import_chalk2.default.cyan("shell/hook.zsh")}, ${import_chalk2.default.cyan("shell/hook.bash")}, or ${import_chalk2.default.cyan("shell/hook.fish")}`
|
|
2458
|
-
);
|
|
2455
|
+
function runDataDirectoriesStep() {
|
|
2456
|
+
step(1, TOTAL_STEPS, "Creating data directories");
|
|
2457
|
+
for (const dir of REQUIRED_DIRS) {
|
|
2458
|
+
if (!import_fs14.default.existsSync(dir)) {
|
|
2459
|
+
import_fs14.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2460
|
+
log.success(`Created ${dir}`);
|
|
2459
2461
|
} else {
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
} else {
|
|
2464
|
-
const hookContent = import_fs14.default.readFileSync(hookFile, "utf-8");
|
|
2465
|
-
const binaryPath = resolveWorkscribeBinary();
|
|
2466
|
-
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2467
|
-
if (shell.name === "fish") {
|
|
2468
|
-
const confDir = import_path10.default.dirname(shell.rcFile);
|
|
2469
|
-
if (!import_fs14.default.existsSync(confDir)) import_fs14.default.mkdirSync(confDir, { recursive: true });
|
|
2470
|
-
}
|
|
2471
|
-
if (!import_fs14.default.existsSync(shell.rcFile)) import_fs14.default.writeFileSync(shell.rcFile, "");
|
|
2472
|
-
if (!isHookInstalled(shell.rcFile)) {
|
|
2473
|
-
log.plain("");
|
|
2474
|
-
log.plain(import_chalk2.default.dim(" Workscribe adds a lightweight hook to your shell that captures each"));
|
|
2475
|
-
log.plain(import_chalk2.default.dim(" command you run \u2014 the text, exit code, duration, and working directory."));
|
|
2476
|
-
log.plain(import_chalk2.default.dim(" It runs in the background and adds no latency to your terminal."));
|
|
2477
|
-
log.plain(import_chalk2.default.dim(" Credentials and secrets are redacted before anything is stored."));
|
|
2478
|
-
log.plain("");
|
|
2479
|
-
installHook(shell.rcFile, hookWithPath);
|
|
2480
|
-
log.success(`Shell hook installed in ${shell.rcFile}`);
|
|
2481
|
-
} else if (isHookOutdated(shell.rcFile, hookWithPath)) {
|
|
2482
|
-
const { update } = await (0, import_prompts.default)({
|
|
2483
|
-
type: "confirm",
|
|
2484
|
-
name: "update",
|
|
2485
|
-
message: `Shell hook in ${shell.rcFile} is outdated. Update?`,
|
|
2486
|
-
initial: true
|
|
2487
|
-
});
|
|
2488
|
-
if (update) {
|
|
2489
|
-
updateHook(shell.rcFile, hookWithPath);
|
|
2490
|
-
log.success(`Shell hook updated in ${shell.rcFile}`);
|
|
2491
|
-
} else {
|
|
2492
|
-
log.info("Shell hook update skipped");
|
|
2493
|
-
}
|
|
2494
|
-
} else {
|
|
2495
|
-
log.info(`Shell hook up to date in ${shell.rcFile}`);
|
|
2496
|
-
}
|
|
2497
|
-
if (isCompletionsInstalled(shell)) {
|
|
2498
|
-
log.info("Shell completions already installed");
|
|
2499
|
-
} else {
|
|
2500
|
-
const { installComps } = await (0, import_prompts.default)({
|
|
2501
|
-
type: "confirm",
|
|
2502
|
-
name: "installComps",
|
|
2503
|
-
message: "Install shell completions (tab-complete workscribe commands)?",
|
|
2504
|
-
initial: true
|
|
2505
|
-
});
|
|
2506
|
-
if (installComps) {
|
|
2507
|
-
try {
|
|
2508
|
-
installCompletions(shell, getPublicCommands(program));
|
|
2509
|
-
log.success("Shell completions installed");
|
|
2510
|
-
} catch (error) {
|
|
2511
|
-
log.warn(`Completions install failed: ${error.message}`);
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2462
|
+
try {
|
|
2463
|
+
import_fs14.default.chmodSync(dir, 448);
|
|
2464
|
+
} catch {
|
|
2515
2465
|
}
|
|
2466
|
+
log.info(`${dir} already exists`);
|
|
2516
2467
|
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2468
|
+
}
|
|
2469
|
+
if (!import_fs14.default.existsSync(PROFILES_DIR)) {
|
|
2470
|
+
import_fs14.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
function initDatabaseAndConfig() {
|
|
2474
|
+
step(2, TOTAL_STEPS, "Initialising database");
|
|
2475
|
+
const config = readConfig();
|
|
2476
|
+
getDb();
|
|
2477
|
+
log.success("Database ready");
|
|
2478
|
+
return config;
|
|
2479
|
+
}
|
|
2480
|
+
async function runTelemetryStep(config) {
|
|
2481
|
+
log.plain("");
|
|
2482
|
+
log.plain(import_chalk2.default.dim(" Workscribe can send anonymous install stats: a random install ID,"));
|
|
2483
|
+
log.plain(import_chalk2.default.dim(" CLI version, OS platform, and Node.js version. No command, event, or"));
|
|
2484
|
+
log.plain(import_chalk2.default.dim(" summary data is ever sent. Opt out anytime:"));
|
|
2485
|
+
log.plain(import_chalk2.default.dim(" workscribe config set telemetry.enabled false"));
|
|
2486
|
+
log.plain("");
|
|
2487
|
+
const { telemetryEnabled } = await (0, import_prompts.default)({
|
|
2488
|
+
type: "confirm",
|
|
2489
|
+
name: "telemetryEnabled",
|
|
2490
|
+
message: "Send anonymous install stats?",
|
|
2491
|
+
initial: true
|
|
2492
|
+
});
|
|
2493
|
+
config.telemetry.enabled = telemetryEnabled;
|
|
2494
|
+
config.telemetry.installId = ensureInstallId(config);
|
|
2495
|
+
}
|
|
2496
|
+
async function runShellHookStep(program) {
|
|
2497
|
+
step(3, TOTAL_STEPS, "Installing shell hook");
|
|
2498
|
+
const shell = detectShell();
|
|
2499
|
+
if (!shell) {
|
|
2500
|
+
log.warn("Could not detect shell. Supported: zsh, bash, fish");
|
|
2501
|
+
log.plain(
|
|
2502
|
+
` Manually add the hook from: ${import_chalk2.default.cyan("shell/hook.zsh")}, ${import_chalk2.default.cyan("shell/hook.bash")}, or ${import_chalk2.default.cyan("shell/hook.fish")}`
|
|
2520
2503
|
);
|
|
2521
|
-
|
|
2522
|
-
|
|
2504
|
+
return null;
|
|
2505
|
+
}
|
|
2506
|
+
const hookFile = import_path11.default.join(__dirname, "..", "shell", `hook.${shell.name}`);
|
|
2507
|
+
if (!import_fs14.default.existsSync(hookFile)) {
|
|
2508
|
+
log.warn(`Hook template not found at ${hookFile}`);
|
|
2509
|
+
return shell;
|
|
2510
|
+
}
|
|
2511
|
+
const hookContent = import_fs14.default.readFileSync(hookFile, "utf-8");
|
|
2512
|
+
const binaryPath = resolveWorkscribeBinary();
|
|
2513
|
+
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2514
|
+
if (shell.name === "fish") {
|
|
2515
|
+
const confDir = import_path11.default.dirname(shell.rcFile);
|
|
2516
|
+
if (!import_fs14.default.existsSync(confDir)) import_fs14.default.mkdirSync(confDir, { recursive: true });
|
|
2517
|
+
}
|
|
2518
|
+
if (!import_fs14.default.existsSync(shell.rcFile)) import_fs14.default.writeFileSync(shell.rcFile, "");
|
|
2519
|
+
if (!isHookInstalled(shell.rcFile)) {
|
|
2520
|
+
log.plain("");
|
|
2521
|
+
log.plain(import_chalk2.default.dim(" Workscribe adds a lightweight hook to your shell that captures each"));
|
|
2522
|
+
log.plain(import_chalk2.default.dim(" command you run \u2014 the text, exit code, duration, and working directory."));
|
|
2523
|
+
log.plain(import_chalk2.default.dim(" It runs in the background and adds no latency to your terminal."));
|
|
2524
|
+
log.plain(import_chalk2.default.dim(" Credentials and secrets are redacted before anything is stored."));
|
|
2525
|
+
log.plain("");
|
|
2526
|
+
installHook(shell.rcFile, hookWithPath);
|
|
2527
|
+
log.success(`Shell hook installed in ${shell.rcFile}`);
|
|
2528
|
+
} else if (isHookOutdated(shell.rcFile, hookWithPath)) {
|
|
2529
|
+
const { update } = await (0, import_prompts.default)({
|
|
2530
|
+
type: "confirm",
|
|
2531
|
+
name: "update",
|
|
2532
|
+
message: `Shell hook in ${shell.rcFile} is outdated. Update?`,
|
|
2533
|
+
initial: true
|
|
2534
|
+
});
|
|
2535
|
+
if (update) {
|
|
2536
|
+
updateHook(shell.rcFile, hookWithPath);
|
|
2537
|
+
log.success(`Shell hook updated in ${shell.rcFile}`);
|
|
2523
2538
|
} else {
|
|
2524
|
-
|
|
2525
|
-
const integration = AI_HOOK_INTEGRATIONS[tool.hook];
|
|
2526
|
-
if (integration.isHookInstalled()) {
|
|
2527
|
-
log.info(`${integration.displayName} session hook already installed`);
|
|
2528
|
-
} else {
|
|
2529
|
-
const { install } = await (0, import_prompts.default)({
|
|
2530
|
-
type: "confirm",
|
|
2531
|
-
name: "install",
|
|
2532
|
-
message: `${integration.displayName} detected. Install session capture hook?`,
|
|
2533
|
-
initial: false
|
|
2534
|
-
});
|
|
2535
|
-
if (install) {
|
|
2536
|
-
integration.install();
|
|
2537
|
-
log.success(`${integration.displayName} session hook installed`);
|
|
2538
|
-
} else {
|
|
2539
|
-
log.info(`${integration.displayName} hook skipped \u2014 re-run workscribe init to install later`);
|
|
2540
|
-
}
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2539
|
+
log.info("Shell hook update skipped");
|
|
2543
2540
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
log.success(`${entry.displayName} profile enabled`);
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
writeConfig(config);
|
|
2566
|
-
} else if (detected.length === 0) {
|
|
2567
|
-
log.info("No project profiles detected \u2014 run workscribe profile detect from your project directory");
|
|
2541
|
+
} else {
|
|
2542
|
+
log.info(`Shell hook up to date in ${shell.rcFile}`);
|
|
2543
|
+
}
|
|
2544
|
+
if (isCompletionsInstalled(shell)) {
|
|
2545
|
+
log.info("Shell completions already installed");
|
|
2546
|
+
} else {
|
|
2547
|
+
const { installComps } = await (0, import_prompts.default)({
|
|
2548
|
+
type: "confirm",
|
|
2549
|
+
name: "installComps",
|
|
2550
|
+
message: "Install shell completions (tab-complete workscribe commands)?",
|
|
2551
|
+
initial: true
|
|
2552
|
+
});
|
|
2553
|
+
if (installComps) {
|
|
2554
|
+
try {
|
|
2555
|
+
installCompletions(shell, getPublicCommands(program));
|
|
2556
|
+
log.success("Shell completions installed");
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
log.warn(`Completions install failed: ${error.message}`);
|
|
2568
2559
|
}
|
|
2569
|
-
} catch {
|
|
2570
|
-
log.info("Could not reach profile registry \u2014 run workscribe profile detect after connecting");
|
|
2571
2560
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2561
|
+
}
|
|
2562
|
+
return shell;
|
|
2563
|
+
}
|
|
2564
|
+
async function runAIToolsStep() {
|
|
2565
|
+
step(4, TOTAL_STEPS, "Detecting AI coding tools");
|
|
2566
|
+
const installedTools = loadAIToolRegistry().filter(
|
|
2567
|
+
(entry) => entry.hook !== null && AI_HOOK_INTEGRATIONS[entry.hook]?.isInstalled()
|
|
2568
|
+
);
|
|
2569
|
+
if (installedTools.length === 0) {
|
|
2570
|
+
log.info("No AI coding tools detected \u2014 re-run workscribe init after installing one");
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
for (const tool of installedTools) {
|
|
2574
|
+
const integration = AI_HOOK_INTEGRATIONS[tool.hook];
|
|
2575
|
+
if (integration.isHookInstalled()) {
|
|
2576
|
+
log.info(`${integration.displayName} session hook already installed`);
|
|
2576
2577
|
} else {
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
choices: [
|
|
2583
|
-
{ title: "Anthropic (Claude) \u2014 requires API key", value: "anthropic" },
|
|
2584
|
-
{ title: "OpenAI \u2014 requires API key", value: "openai" },
|
|
2585
|
-
{ title: "Ollama (local) \u2014 free, runs on your machine", value: "ollama" },
|
|
2586
|
-
{ title: "Other (OpenAI-compatible) \u2014 Groq, Together, Mistral\u2026", value: "openai-compatible" },
|
|
2587
|
-
{ title: "Skip \u2014 configure later", value: "skip" }
|
|
2588
|
-
]
|
|
2578
|
+
const { install } = await (0, import_prompts.default)({
|
|
2579
|
+
type: "confirm",
|
|
2580
|
+
name: "install",
|
|
2581
|
+
message: `${integration.displayName} detected. Install session capture hook?`,
|
|
2582
|
+
initial: false
|
|
2589
2583
|
});
|
|
2590
|
-
if (
|
|
2591
|
-
|
|
2592
|
-
log.
|
|
2584
|
+
if (install) {
|
|
2585
|
+
integration.install();
|
|
2586
|
+
log.success(`${integration.displayName} session hook installed`);
|
|
2593
2587
|
} else {
|
|
2594
|
-
|
|
2595
|
-
if (provider === "ollama") {
|
|
2596
|
-
log.plain(`
|
|
2597
|
-
${import_chalk2.default.dim("Make sure Ollama is running: ollama serve")}`);
|
|
2598
|
-
log.plain(
|
|
2599
|
-
` ${import_chalk2.default.dim("Default model: llama3 (change with: workscribe config set ai.model <model>)")}
|
|
2600
|
-
`
|
|
2601
|
-
);
|
|
2602
|
-
} else {
|
|
2603
|
-
const { apiKey } = await (0, import_prompts.default)({
|
|
2604
|
-
type: "password",
|
|
2605
|
-
name: "apiKey",
|
|
2606
|
-
message: `Enter your ${provider === "openai-compatible" ? "" : provider + " "}API key:`
|
|
2607
|
-
});
|
|
2608
|
-
if (apiKey) config.ai.apiKey = apiKey;
|
|
2609
|
-
if (provider === "openai-compatible") {
|
|
2610
|
-
const { baseUrl } = await (0, import_prompts.default)({
|
|
2611
|
-
type: "text",
|
|
2612
|
-
name: "baseUrl",
|
|
2613
|
-
message: "Enter the base URL (e.g. https://api.groq.com/openai/v1):"
|
|
2614
|
-
});
|
|
2615
|
-
if (baseUrl) config.ai.baseUrl = baseUrl;
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
log.plain("");
|
|
2619
|
-
log.info(`Validating ${import_chalk2.default.cyan(provider)} connection\u2026`);
|
|
2620
|
-
try {
|
|
2621
|
-
const testProvider = getProvider(config);
|
|
2622
|
-
await testProvider.validate();
|
|
2623
|
-
log.success("API key verified");
|
|
2624
|
-
writeConfig(config);
|
|
2625
|
-
providerConfigured = true;
|
|
2626
|
-
} catch (error) {
|
|
2627
|
-
log.warn(`Validation failed: ${error.message}`);
|
|
2628
|
-
const { retry } = await (0, import_prompts.default)({
|
|
2629
|
-
type: "confirm",
|
|
2630
|
-
name: "retry",
|
|
2631
|
-
message: "Save anyway and configure later?",
|
|
2632
|
-
initial: false
|
|
2633
|
-
});
|
|
2634
|
-
if (retry) {
|
|
2635
|
-
writeConfig(config);
|
|
2636
|
-
providerConfigured = false;
|
|
2637
|
-
log.info("Config saved \u2014 run workscribe config set ai.apiKey <key> to update");
|
|
2638
|
-
} else {
|
|
2639
|
-
log.info("API key not saved \u2014 run workscribe init again to configure");
|
|
2640
|
-
providerConfigured = false;
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2588
|
+
log.info(`${integration.displayName} hook skipped \u2014 re-run workscribe init to install later`);
|
|
2643
2589
|
}
|
|
2644
2590
|
}
|
|
2645
|
-
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
async function runProfileDetectionStep(config) {
|
|
2594
|
+
try {
|
|
2595
|
+
await cacheManifest();
|
|
2596
|
+
const detected = detectProfilesForDirectory(process.cwd());
|
|
2597
|
+
const alreadyActive = new Set(config.capture.profiles);
|
|
2598
|
+
const toPrompt = detected.filter((entry) => !alreadyActive.has(entry.name));
|
|
2599
|
+
if (toPrompt.length > 0) {
|
|
2600
|
+
log.plain("");
|
|
2601
|
+
for (const entry of toPrompt) {
|
|
2602
|
+
const { install } = await (0, import_prompts.default)({
|
|
2603
|
+
type: "confirm",
|
|
2604
|
+
name: "install",
|
|
2605
|
+
message: `${entry.displayName} project detected. Enable the ${entry.displayName} profile?`,
|
|
2606
|
+
initial: true
|
|
2607
|
+
});
|
|
2608
|
+
if (install) {
|
|
2609
|
+
await installProfile(entry.name);
|
|
2610
|
+
config.capture.profiles = [.../* @__PURE__ */ new Set([...config.capture.profiles, entry.name])];
|
|
2611
|
+
log.success(`${entry.displayName} profile enabled`);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2646
2614
|
writeConfig(config);
|
|
2615
|
+
} else if (detected.length === 0) {
|
|
2616
|
+
log.info("No project profiles detected \u2014 run workscribe profile detect from your project directory");
|
|
2647
2617
|
}
|
|
2648
|
-
|
|
2649
|
-
log.
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2618
|
+
} catch {
|
|
2619
|
+
log.info("Could not reach profile registry \u2014 run workscribe profile detect after connecting");
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
async function runAIProviderStep(config) {
|
|
2623
|
+
step(5, TOTAL_STEPS, "Configuring AI provider");
|
|
2624
|
+
let providerConfigured = !!config.ai.provider;
|
|
2625
|
+
if (config.ai.provider) {
|
|
2626
|
+
log.info(`AI provider already configured: ${import_chalk2.default.cyan(config.ai.provider)}`);
|
|
2627
|
+
return providerConfigured;
|
|
2628
|
+
}
|
|
2629
|
+
log.plain("");
|
|
2630
|
+
const { provider } = await (0, import_prompts.default)({
|
|
2631
|
+
type: "select",
|
|
2632
|
+
name: "provider",
|
|
2633
|
+
message: "Which AI provider do you want to use?",
|
|
2634
|
+
choices: [
|
|
2635
|
+
{ title: "Anthropic (Claude) \u2014 requires API key", value: "anthropic" },
|
|
2636
|
+
{ title: "OpenAI \u2014 requires API key", value: "openai" },
|
|
2637
|
+
{ title: "Ollama (local) \u2014 free, runs on your machine", value: "ollama" },
|
|
2638
|
+
{ title: "Other (OpenAI-compatible) \u2014 Groq, Together, Mistral\u2026", value: "openai-compatible" },
|
|
2639
|
+
{ title: "Skip \u2014 configure later", value: "skip" }
|
|
2640
|
+
]
|
|
2641
|
+
});
|
|
2642
|
+
if (!provider || provider === "skip") {
|
|
2643
|
+
log.info("Skipped \u2014 run later: workscribe config set ai.provider <provider>");
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
config.ai.provider = provider;
|
|
2647
|
+
if (provider === "ollama") {
|
|
2648
|
+
log.plain(`
|
|
2649
|
+
${import_chalk2.default.dim("Make sure Ollama is running: ollama serve")}`);
|
|
2650
|
+
log.plain(` ${import_chalk2.default.dim("Default model: llama3 (change with: workscribe config set ai.model <model>)")}
|
|
2651
|
+
`);
|
|
2652
|
+
} else {
|
|
2653
|
+
const { apiKey } = await (0, import_prompts.default)({
|
|
2654
|
+
type: "password",
|
|
2655
|
+
name: "apiKey",
|
|
2656
|
+
message: `Enter your ${provider === "openai-compatible" ? "" : provider + " "}API key:`
|
|
2657
|
+
});
|
|
2658
|
+
if (apiKey) config.ai.apiKey = apiKey;
|
|
2659
|
+
if (provider === "openai-compatible") {
|
|
2660
|
+
const { baseUrl } = await (0, import_prompts.default)({
|
|
2661
|
+
type: "text",
|
|
2662
|
+
name: "baseUrl",
|
|
2663
|
+
message: "Enter the base URL (e.g. https://api.groq.com/openai/v1):"
|
|
2664
|
+
});
|
|
2665
|
+
if (baseUrl) config.ai.baseUrl = baseUrl;
|
|
2661
2666
|
}
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2667
|
+
}
|
|
2668
|
+
log.plain("");
|
|
2669
|
+
log.info(`Validating ${import_chalk2.default.cyan(provider)} connection\u2026`);
|
|
2670
|
+
try {
|
|
2671
|
+
const testProvider = getProvider(config);
|
|
2672
|
+
await testProvider.validate();
|
|
2673
|
+
log.success("API key verified");
|
|
2674
|
+
writeConfig(config);
|
|
2675
|
+
providerConfigured = true;
|
|
2676
|
+
} catch (error) {
|
|
2677
|
+
log.warn(`Validation failed: ${error.message}`);
|
|
2678
|
+
const { retry } = await (0, import_prompts.default)({
|
|
2679
|
+
type: "confirm",
|
|
2680
|
+
name: "retry",
|
|
2681
|
+
message: "Save anyway and configure later?",
|
|
2682
|
+
initial: false
|
|
2683
|
+
});
|
|
2684
|
+
if (retry) {
|
|
2685
|
+
writeConfig(config);
|
|
2686
|
+
providerConfigured = false;
|
|
2687
|
+
log.info("Config saved \u2014 run workscribe config set ai.apiKey <key> to update");
|
|
2668
2688
|
} else {
|
|
2669
|
-
log.
|
|
2670
|
-
|
|
2671
|
-
log.plain(` ${import_chalk2.default.cyan("workscribe config set ai.apiKey <your-key>")}`);
|
|
2672
|
-
log.plain("");
|
|
2673
|
-
log.plain(` Then run:`);
|
|
2674
|
-
log.plain(` ${import_chalk2.default.cyan("workscribe summary")}`);
|
|
2689
|
+
log.info("API key not saved \u2014 run workscribe init again to configure");
|
|
2690
|
+
providerConfigured = false;
|
|
2675
2691
|
}
|
|
2692
|
+
}
|
|
2693
|
+
return providerConfigured;
|
|
2694
|
+
}
|
|
2695
|
+
function printNextSteps(shell, providerConfigured) {
|
|
2696
|
+
log.plain("");
|
|
2697
|
+
log.plain(import_chalk2.default.bold(" Setup complete!"));
|
|
2698
|
+
log.plain("");
|
|
2699
|
+
if (shell) {
|
|
2700
|
+
log.plain(` Activate capture now:`);
|
|
2701
|
+
if (shell.name === "fish") {
|
|
2702
|
+
log.plain(` Restart your terminal`);
|
|
2703
|
+
log.plain(` ${import_chalk2.default.dim(`or run: source ${shell.rcFile}`)}`);
|
|
2704
|
+
} else {
|
|
2705
|
+
log.plain(` ${import_chalk2.default.cyan(`source ${shell.rcFile}`)}`);
|
|
2706
|
+
log.plain(` Or restart your terminal.`);
|
|
2707
|
+
}
|
|
2708
|
+
log.plain("");
|
|
2709
|
+
}
|
|
2710
|
+
if (providerConfigured) {
|
|
2711
|
+
log.plain(` Then run a few commands and generate your first summary:`);
|
|
2712
|
+
log.plain(` ${import_chalk2.default.cyan("workscribe summary")}`);
|
|
2676
2713
|
log.plain("");
|
|
2714
|
+
log.plain(` Check capture is working:`);
|
|
2715
|
+
log.plain(` ${import_chalk2.default.cyan("workscribe status")}`);
|
|
2716
|
+
} else {
|
|
2717
|
+
log.plain(` Before generating summaries, configure an AI provider:`);
|
|
2718
|
+
log.plain(` ${import_chalk2.default.cyan("workscribe config set ai.provider anthropic")}`);
|
|
2719
|
+
log.plain(` ${import_chalk2.default.cyan("workscribe config set ai.apiKey <your-key>")}`);
|
|
2720
|
+
log.plain("");
|
|
2721
|
+
log.plain(` Then run:`);
|
|
2722
|
+
log.plain(` ${import_chalk2.default.cyan("workscribe summary")}`);
|
|
2723
|
+
}
|
|
2724
|
+
log.plain("");
|
|
2725
|
+
}
|
|
2726
|
+
function registerInit(program) {
|
|
2727
|
+
program.command("init").description("Set up Workscribe and install or update shell hooks").action(async () => {
|
|
2728
|
+
log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
|
|
2729
|
+
runDataDirectoriesStep();
|
|
2730
|
+
const config = initDatabaseAndConfig();
|
|
2731
|
+
await runTelemetryStep(config);
|
|
2732
|
+
const shell = await runShellHookStep(program);
|
|
2733
|
+
await runAIToolsStep();
|
|
2734
|
+
await runProfileDetectionStep(config);
|
|
2735
|
+
const providerConfigured = await runAIProviderStep(config);
|
|
2736
|
+
if (!import_fs14.default.existsSync(CONFIG_PATH)) {
|
|
2737
|
+
writeConfig(config);
|
|
2738
|
+
}
|
|
2739
|
+
printNextSteps(shell, providerConfigured);
|
|
2677
2740
|
});
|
|
2678
2741
|
}
|
|
2679
|
-
var import_fs14,
|
|
2742
|
+
var import_fs14, import_path11, import_os5, import_prompts, import_chalk2, TOTAL_STEPS;
|
|
2680
2743
|
var init_init = __esm({
|
|
2681
2744
|
"src/cli/init.ts"() {
|
|
2682
2745
|
"use strict";
|
|
2683
2746
|
import_fs14 = __toESM(require("fs"));
|
|
2684
|
-
|
|
2747
|
+
import_path11 = __toESM(require("path"));
|
|
2685
2748
|
import_os5 = __toESM(require("os"));
|
|
2686
2749
|
import_prompts = __toESM(require("prompts"));
|
|
2687
2750
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -2696,6 +2759,7 @@ var init_init = __esm({
|
|
|
2696
2759
|
init_shell_hook();
|
|
2697
2760
|
init_hooks();
|
|
2698
2761
|
init_completions();
|
|
2762
|
+
TOTAL_STEPS = 5;
|
|
2699
2763
|
}
|
|
2700
2764
|
});
|
|
2701
2765
|
|
|
@@ -3153,7 +3217,7 @@ function registerSummary(program) {
|
|
|
3153
3217
|
return;
|
|
3154
3218
|
}
|
|
3155
3219
|
const cacheKey = isoWeekKey(today);
|
|
3156
|
-
const cacheFile =
|
|
3220
|
+
const cacheFile = import_path12.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
|
|
3157
3221
|
if (!opts.fresh && import_fs16.default.existsSync(cacheFile)) {
|
|
3158
3222
|
const cached = import_fs16.default.readFileSync(cacheFile, "utf-8");
|
|
3159
3223
|
log.plain("");
|
|
@@ -3288,14 +3352,14 @@ ${bullets.trim()}`;
|
|
|
3288
3352
|
await handleWebhook(opts, config, fullSummary);
|
|
3289
3353
|
});
|
|
3290
3354
|
}
|
|
3291
|
-
var import_chalk6, import_fs16, import_os6,
|
|
3355
|
+
var import_chalk6, import_fs16, import_os6, import_path12;
|
|
3292
3356
|
var init_summary = __esm({
|
|
3293
3357
|
"src/cli/summary.ts"() {
|
|
3294
3358
|
"use strict";
|
|
3295
3359
|
import_chalk6 = __toESM(require("chalk"));
|
|
3296
3360
|
import_fs16 = __toESM(require("fs"));
|
|
3297
3361
|
import_os6 = __toESM(require("os"));
|
|
3298
|
-
|
|
3362
|
+
import_path12 = __toESM(require("path"));
|
|
3299
3363
|
init_detector();
|
|
3300
3364
|
init_router();
|
|
3301
3365
|
init_config();
|
|
@@ -3365,7 +3429,7 @@ function registerExport(program) {
|
|
|
3365
3429
|
const payload = { date, sessions: sessionPayloads };
|
|
3366
3430
|
log.info(`Generating summary via ${config.ai.provider}\u2026`);
|
|
3367
3431
|
const summary = await provider.summarize(payload);
|
|
3368
|
-
const outputPath = opts.output ??
|
|
3432
|
+
const outputPath = opts.output ?? import_path13.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
|
|
3369
3433
|
if (!import_fs17.default.existsSync(EXPORTS_DIR)) import_fs17.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
|
|
3370
3434
|
import_fs17.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
|
|
3371
3435
|
|
|
@@ -3374,12 +3438,12 @@ ${summary}
|
|
|
3374
3438
|
log.success(`Saved: ${outputPath}`);
|
|
3375
3439
|
});
|
|
3376
3440
|
}
|
|
3377
|
-
var import_fs17,
|
|
3441
|
+
var import_fs17, import_path13;
|
|
3378
3442
|
var init_export = __esm({
|
|
3379
3443
|
"src/cli/export.ts"() {
|
|
3380
3444
|
"use strict";
|
|
3381
3445
|
import_fs17 = __toESM(require("fs"));
|
|
3382
|
-
|
|
3446
|
+
import_path13 = __toESM(require("path"));
|
|
3383
3447
|
init_detector();
|
|
3384
3448
|
init_router();
|
|
3385
3449
|
init_config();
|
|
@@ -3587,10 +3651,10 @@ var init_clear = __esm({
|
|
|
3587
3651
|
// src/cli/uninstall.ts
|
|
3588
3652
|
function detectShellRcFiles() {
|
|
3589
3653
|
const candidates = [
|
|
3590
|
-
{ name: "zsh", rcFile:
|
|
3591
|
-
{ name: "bash", rcFile:
|
|
3592
|
-
{ name: "bash", rcFile:
|
|
3593
|
-
{ name: "fish", rcFile:
|
|
3654
|
+
{ name: "zsh", rcFile: import_path14.default.join(import_os7.default.homedir(), ".zshrc") },
|
|
3655
|
+
{ name: "bash", rcFile: import_path14.default.join(import_os7.default.homedir(), ".bashrc") },
|
|
3656
|
+
{ name: "bash", rcFile: import_path14.default.join(import_os7.default.homedir(), ".bash_profile") },
|
|
3657
|
+
{ name: "fish", rcFile: import_path14.default.join(import_os7.default.homedir(), ".config", "fish", "conf.d", "workscribe.fish") }
|
|
3594
3658
|
];
|
|
3595
3659
|
return candidates.filter((candidate) => import_fs19.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
3596
3660
|
}
|
|
@@ -3640,12 +3704,12 @@ function registerUninstall(program) {
|
|
|
3640
3704
|
log.plain("");
|
|
3641
3705
|
});
|
|
3642
3706
|
}
|
|
3643
|
-
var import_fs19,
|
|
3707
|
+
var import_fs19, import_path14, import_os7, import_prompts3, import_chalk9;
|
|
3644
3708
|
var init_uninstall = __esm({
|
|
3645
3709
|
"src/cli/uninstall.ts"() {
|
|
3646
3710
|
"use strict";
|
|
3647
3711
|
import_fs19 = __toESM(require("fs"));
|
|
3648
|
-
|
|
3712
|
+
import_path14 = __toESM(require("path"));
|
|
3649
3713
|
import_os7 = __toESM(require("os"));
|
|
3650
3714
|
import_prompts3 = __toESM(require("prompts"));
|
|
3651
3715
|
import_chalk9 = __toESM(require("chalk"));
|
|
@@ -3699,7 +3763,7 @@ function registerProfile(program) {
|
|
|
3699
3763
|
log.plain(import_chalk10.default.dim(" " + "\u2500".repeat(60)));
|
|
3700
3764
|
for (const entry of entries) {
|
|
3701
3765
|
const badge = activeNames.has(entry.name) ? import_chalk10.default.green(" [active]") : "";
|
|
3702
|
-
const installed = import_fs20.default.existsSync(
|
|
3766
|
+
const installed = import_fs20.default.existsSync(import_path15.default.join(PROFILES_DIR, `${entry.name}.json`)) ? import_chalk10.default.dim(" installed") : "";
|
|
3703
3767
|
log.plain(` ${import_chalk10.default.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`);
|
|
3704
3768
|
if (entry.description) log.plain(` ${import_chalk10.default.dim(" ".repeat(18) + entry.description)}`);
|
|
3705
3769
|
}
|
|
@@ -3735,7 +3799,7 @@ function registerProfile(program) {
|
|
|
3735
3799
|
continue;
|
|
3736
3800
|
}
|
|
3737
3801
|
activeNames.delete(name);
|
|
3738
|
-
const cachePath =
|
|
3802
|
+
const cachePath = import_path15.default.join(PROFILES_DIR, `${name}.json`);
|
|
3739
3803
|
if (import_fs20.default.existsSync(cachePath)) import_fs20.default.unlinkSync(cachePath);
|
|
3740
3804
|
log.success(`Profile "${name}" removed`);
|
|
3741
3805
|
}
|
|
@@ -3863,12 +3927,12 @@ function registerProfile(program) {
|
|
|
3863
3927
|
}
|
|
3864
3928
|
});
|
|
3865
3929
|
}
|
|
3866
|
-
var import_fs20,
|
|
3930
|
+
var import_fs20, import_path15, import_chalk10, MIN_SUGGEST_COUNT;
|
|
3867
3931
|
var init_profile = __esm({
|
|
3868
3932
|
"src/cli/profile.ts"() {
|
|
3869
3933
|
"use strict";
|
|
3870
3934
|
import_fs20 = __toESM(require("fs"));
|
|
3871
|
-
|
|
3935
|
+
import_path15 = __toESM(require("path"));
|
|
3872
3936
|
import_chalk10 = __toESM(require("chalk"));
|
|
3873
3937
|
init_config();
|
|
3874
3938
|
init_paths();
|
|
@@ -3981,7 +4045,7 @@ function registerWeek(program) {
|
|
|
3981
4045
|
return;
|
|
3982
4046
|
}
|
|
3983
4047
|
const cacheKey = isoWeekKey(today);
|
|
3984
|
-
const cacheFile =
|
|
4048
|
+
const cacheFile = import_path16.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
|
|
3985
4049
|
if (!opts.fresh && import_fs21.default.existsSync(cacheFile)) {
|
|
3986
4050
|
const cached = import_fs21.default.readFileSync(cacheFile, "utf-8");
|
|
3987
4051
|
log.plain("");
|
|
@@ -4008,13 +4072,13 @@ function registerWeek(program) {
|
|
|
4008
4072
|
}
|
|
4009
4073
|
});
|
|
4010
4074
|
}
|
|
4011
|
-
var import_chalk11, import_fs21,
|
|
4075
|
+
var import_chalk11, import_fs21, import_path16, import_os8;
|
|
4012
4076
|
var init_week = __esm({
|
|
4013
4077
|
"src/cli/week.ts"() {
|
|
4014
4078
|
"use strict";
|
|
4015
4079
|
import_chalk11 = __toESM(require("chalk"));
|
|
4016
4080
|
import_fs21 = __toESM(require("fs"));
|
|
4017
|
-
|
|
4081
|
+
import_path16 = __toESM(require("path"));
|
|
4018
4082
|
import_os8 = __toESM(require("os"));
|
|
4019
4083
|
init_detector();
|
|
4020
4084
|
init_router();
|
|
@@ -4048,9 +4112,9 @@ function redactConfig() {
|
|
|
4048
4112
|
function registerBugReport(program) {
|
|
4049
4113
|
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
4050
4114
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4051
|
-
const reportPath =
|
|
4115
|
+
const reportPath = import_path17.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
4052
4116
|
const systemInfo = [
|
|
4053
|
-
`Workscribe version : ${"1.
|
|
4117
|
+
`Workscribe version : ${"1.15.1"}`,
|
|
4054
4118
|
`Node.js version : ${process.version}`,
|
|
4055
4119
|
`Platform : ${process.platform}`,
|
|
4056
4120
|
`Architecture : ${process.arch}`,
|
|
@@ -4081,8 +4145,8 @@ function registerBugReport(program) {
|
|
|
4081
4145
|
process.exit(1);
|
|
4082
4146
|
}
|
|
4083
4147
|
const params = new URLSearchParams({
|
|
4084
|
-
title: `Bug report \u2014 workscribe ${"1.
|
|
4085
|
-
body: `<!-- Attach ${
|
|
4148
|
+
title: `Bug report \u2014 workscribe ${"1.15.1"}`,
|
|
4149
|
+
body: `<!-- Attach ${import_path17.default.basename(reportPath)} or paste the relevant sections below -->
|
|
4086
4150
|
|
|
4087
4151
|
**Describe the issue:**
|
|
4088
4152
|
|
|
@@ -4099,13 +4163,13 @@ function registerBugReport(program) {
|
|
|
4099
4163
|
log.plain("");
|
|
4100
4164
|
});
|
|
4101
4165
|
}
|
|
4102
|
-
var import_fs22, import_os9,
|
|
4166
|
+
var import_fs22, import_os9, import_path17, import_chalk12, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
4103
4167
|
var init_bug_report = __esm({
|
|
4104
4168
|
"src/cli/bug-report.ts"() {
|
|
4105
4169
|
"use strict";
|
|
4106
4170
|
import_fs22 = __toESM(require("fs"));
|
|
4107
4171
|
import_os9 = __toESM(require("os"));
|
|
4108
|
-
|
|
4172
|
+
import_path17 = __toESM(require("path"));
|
|
4109
4173
|
import_chalk12 = __toESM(require("chalk"));
|
|
4110
4174
|
init_paths();
|
|
4111
4175
|
init_config();
|
|
@@ -4210,7 +4274,7 @@ function printSessionContext(session, heading) {
|
|
|
4210
4274
|
function registerContext(program) {
|
|
4211
4275
|
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) => {
|
|
4212
4276
|
if (opts.file) {
|
|
4213
|
-
const normalizedFile =
|
|
4277
|
+
const normalizedFile = import_path18.default.normalize(opts.file);
|
|
4214
4278
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
4215
4279
|
if (!session) {
|
|
4216
4280
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
@@ -4221,7 +4285,7 @@ function registerContext(program) {
|
|
|
4221
4285
|
log.plain(JSON.stringify(toContextJson(session)));
|
|
4222
4286
|
return;
|
|
4223
4287
|
}
|
|
4224
|
-
printSessionContext(session, `Last session touching ${
|
|
4288
|
+
printSessionContext(session, `Last session touching ${import_path18.default.basename(normalizedFile)}`);
|
|
4225
4289
|
return;
|
|
4226
4290
|
}
|
|
4227
4291
|
const { repoName } = getGitContext(process.cwd(), null, readConfig().git.qualifyRepoName);
|
|
@@ -4243,11 +4307,11 @@ function registerContext(program) {
|
|
|
4243
4307
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
4244
4308
|
});
|
|
4245
4309
|
}
|
|
4246
|
-
var
|
|
4310
|
+
var import_path18, import_chalk14;
|
|
4247
4311
|
var init_context = __esm({
|
|
4248
4312
|
"src/cli/context.ts"() {
|
|
4249
4313
|
"use strict";
|
|
4250
|
-
|
|
4314
|
+
import_path18 = __toESM(require("path"));
|
|
4251
4315
|
import_chalk14 = __toESM(require("chalk"));
|
|
4252
4316
|
init_db();
|
|
4253
4317
|
init_git();
|
|
@@ -4265,9 +4329,10 @@ function readCache() {
|
|
|
4265
4329
|
return null;
|
|
4266
4330
|
}
|
|
4267
4331
|
}
|
|
4268
|
-
function writeCache(
|
|
4332
|
+
function writeCache(updates) {
|
|
4269
4333
|
try {
|
|
4270
|
-
|
|
4334
|
+
const current = readCache() ?? {};
|
|
4335
|
+
import_fs23.default.writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({ ...current, ...updates }), { mode: 384 });
|
|
4271
4336
|
} catch {
|
|
4272
4337
|
}
|
|
4273
4338
|
}
|
|
@@ -4280,7 +4345,7 @@ function isNewer(latest, current) {
|
|
|
4280
4345
|
return latestPatch > currentPatch;
|
|
4281
4346
|
}
|
|
4282
4347
|
function checkForUpdate() {
|
|
4283
|
-
const current = "1.
|
|
4348
|
+
const current = "1.15.1";
|
|
4284
4349
|
const cache = readCache();
|
|
4285
4350
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
4286
4351
|
process.stderr.write(
|
|
@@ -4300,12 +4365,35 @@ function checkForUpdate() {
|
|
|
4300
4365
|
});
|
|
4301
4366
|
}
|
|
4302
4367
|
}
|
|
4303
|
-
|
|
4368
|
+
function buildTelemetryPayload(config) {
|
|
4369
|
+
return {
|
|
4370
|
+
installId: config.telemetry.installId,
|
|
4371
|
+
cliVersion: "1.15.1",
|
|
4372
|
+
platform: import_os10.default.platform(),
|
|
4373
|
+
nodeVersion: process.version
|
|
4374
|
+
};
|
|
4375
|
+
}
|
|
4376
|
+
function pingTelemetry() {
|
|
4377
|
+
const config = readConfig();
|
|
4378
|
+
if (!config.telemetry.enabled || !config.telemetry.installId) return;
|
|
4379
|
+
const cache = readCache();
|
|
4380
|
+
const now = Date.now();
|
|
4381
|
+
if (cache?.lastTelemetryPing && now - cache.lastTelemetryPing <= CHECK_INTERVAL_MS) return;
|
|
4382
|
+
fetch("https://workscribe.co/api/telemetry/ping", {
|
|
4383
|
+
method: "POST",
|
|
4384
|
+
headers: { "Content-Type": "application/json" },
|
|
4385
|
+
body: JSON.stringify(buildTelemetryPayload(config))
|
|
4386
|
+
}).then(() => writeCache({ lastTelemetryPing: now })).catch(() => {
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
var import_fs23, import_os10, CHECK_INTERVAL_MS;
|
|
4304
4390
|
var init_update_check = __esm({
|
|
4305
4391
|
"src/utils/update-check.ts"() {
|
|
4306
4392
|
"use strict";
|
|
4307
4393
|
import_fs23 = __toESM(require("fs"));
|
|
4394
|
+
import_os10 = __toESM(require("os"));
|
|
4308
4395
|
init_paths();
|
|
4396
|
+
init_config();
|
|
4309
4397
|
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
4310
4398
|
}
|
|
4311
4399
|
});
|
|
@@ -4336,9 +4424,17 @@ var require_main = __commonJS({
|
|
|
4336
4424
|
init_alerts3();
|
|
4337
4425
|
init_context();
|
|
4338
4426
|
init_update_check();
|
|
4339
|
-
|
|
4427
|
+
init_config();
|
|
4428
|
+
var invokedCommand = process.argv[2];
|
|
4429
|
+
if (shouldRunStartupChecks(invokedCommand)) {
|
|
4430
|
+
checkForUpdate();
|
|
4431
|
+
if (invokedCommand !== "init") {
|
|
4432
|
+
ensureTelemetryFallback();
|
|
4433
|
+
}
|
|
4434
|
+
pingTelemetry();
|
|
4435
|
+
}
|
|
4340
4436
|
var program = new import_commander.Command();
|
|
4341
|
-
program.name("workscribe").description("Automatic developer work journal").version("1.
|
|
4437
|
+
program.name("workscribe").description("Automatic developer work journal").version("1.15.1");
|
|
4342
4438
|
registerCapture(program);
|
|
4343
4439
|
registerHook(program);
|
|
4344
4440
|
registerInit(program);
|