@workscribe/cli 1.14.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +400 -301
- package/dist/index.js.map +1 -1
- package/package.json +3 -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
|
}
|
|
@@ -2103,7 +2125,7 @@ ${commandList}
|
|
|
2103
2125
|
;;
|
|
2104
2126
|
esac
|
|
2105
2127
|
}
|
|
2106
|
-
_workscribe`;
|
|
2128
|
+
compdef _workscribe workscribe`;
|
|
2107
2129
|
}
|
|
2108
2130
|
function fishCompletion(commands) {
|
|
2109
2131
|
const lines = [
|
|
@@ -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;
|
|
@@ -2185,10 +2207,10 @@ function injectBinaryPath(hookContent, binaryPath, nodePath) {
|
|
|
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
|
-
return import_fs12.default.readFileSync(shell.rcFile, "utf-8").includes(
|
|
2213
|
+
return import_fs12.default.readFileSync(shell.rcFile, "utf-8").includes(COMPLETIONS_MARKER);
|
|
2192
2214
|
} catch {
|
|
2193
2215
|
return false;
|
|
2194
2216
|
}
|
|
@@ -2198,37 +2220,40 @@ 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
|
-
|
|
2207
|
-
|
|
2228
|
+
if (!isCompletionsInstalled(shell)) {
|
|
2229
|
+
const sourceLine = shell.name === "zsh" ? `
|
|
2230
|
+
${COMPLETIONS_MARKER}
|
|
2208
2231
|
autoload -Uz compinit && compinit
|
|
2209
2232
|
source "${cacheFile}"
|
|
2210
2233
|
` : `
|
|
2211
|
-
|
|
2234
|
+
${COMPLETIONS_MARKER}
|
|
2212
2235
|
source "${cacheFile}"
|
|
2213
2236
|
`;
|
|
2214
|
-
|
|
2237
|
+
import_fs12.default.appendFileSync(shell.rcFile, sourceLine);
|
|
2238
|
+
}
|
|
2215
2239
|
}
|
|
2216
2240
|
}
|
|
2217
|
-
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;
|
|
2218
2242
|
var init_shell_hook = __esm({
|
|
2219
2243
|
"src/capture/shell-hook.ts"() {
|
|
2220
2244
|
"use strict";
|
|
2221
2245
|
import_fs12 = __toESM(require("fs"));
|
|
2222
|
-
|
|
2246
|
+
import_path9 = __toESM(require("path"));
|
|
2223
2247
|
import_os3 = __toESM(require("os"));
|
|
2224
2248
|
import_child_process3 = require("child_process");
|
|
2225
2249
|
init_logger();
|
|
2226
2250
|
init_completion_scripts();
|
|
2227
2251
|
init_paths();
|
|
2228
|
-
FISH_COMPLETIONS_DIR =
|
|
2252
|
+
FISH_COMPLETIONS_DIR = import_path9.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
2229
2253
|
HOOK_START = "# workscribe:hook:start";
|
|
2230
2254
|
HOOK_END = "# workscribe:hook:end";
|
|
2231
2255
|
HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
2256
|
+
COMPLETIONS_MARKER = "# Workscribe completions";
|
|
2232
2257
|
}
|
|
2233
2258
|
});
|
|
2234
2259
|
|
|
@@ -2308,17 +2333,17 @@ function checkHookHealth() {
|
|
|
2308
2333
|
}
|
|
2309
2334
|
return { installed: true, pinned: false, confirmedBroken };
|
|
2310
2335
|
}
|
|
2311
|
-
var import_fs13,
|
|
2336
|
+
var import_fs13, import_path10, import_os4, import_child_process5, CLAUDE_SETTINGS_PATH, HOOK_MARKER, claudeCodeIntegration;
|
|
2312
2337
|
var init_claude_code = __esm({
|
|
2313
2338
|
"src/capture/hooks/claude-code.ts"() {
|
|
2314
2339
|
"use strict";
|
|
2315
2340
|
import_fs13 = __toESM(require("fs"));
|
|
2316
|
-
|
|
2341
|
+
import_path10 = __toESM(require("path"));
|
|
2317
2342
|
import_os4 = __toESM(require("os"));
|
|
2318
2343
|
import_child_process5 = require("child_process");
|
|
2319
2344
|
init_utils();
|
|
2320
2345
|
init_shell_hook();
|
|
2321
|
-
CLAUDE_SETTINGS_PATH =
|
|
2346
|
+
CLAUDE_SETTINGS_PATH = import_path10.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2322
2347
|
HOOK_MARKER = "_hook --source claude-code";
|
|
2323
2348
|
claudeCodeIntegration = {
|
|
2324
2349
|
displayName: "Claude Code",
|
|
@@ -2362,7 +2387,7 @@ var init_claude_code = __esm({
|
|
|
2362
2387
|
}
|
|
2363
2388
|
]
|
|
2364
2389
|
};
|
|
2365
|
-
import_fs13.default.mkdirSync(
|
|
2390
|
+
import_fs13.default.mkdirSync(import_path10.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2366
2391
|
import_fs13.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2367
2392
|
}
|
|
2368
2393
|
};
|
|
@@ -2382,6 +2407,10 @@ var init_hooks = __esm({
|
|
|
2382
2407
|
});
|
|
2383
2408
|
|
|
2384
2409
|
// src/cli/completions.ts
|
|
2410
|
+
function shouldRunStartupChecks(invokedCommand) {
|
|
2411
|
+
if (invokedCommand === void 0) return true;
|
|
2412
|
+
return !HIDDEN_COMMANDS.has(invokedCommand);
|
|
2413
|
+
}
|
|
2385
2414
|
function getPublicCommands(program) {
|
|
2386
2415
|
return program.commands.filter((command) => !HIDDEN_COMMANDS.has(command.name())).map((command) => ({ name: command.name(), description: command.description() }));
|
|
2387
2416
|
}
|
|
@@ -2411,11 +2440,11 @@ var init_completions = __esm({
|
|
|
2411
2440
|
// src/cli/init.ts
|
|
2412
2441
|
function detectShell() {
|
|
2413
2442
|
const shell = process.env.SHELL ?? "";
|
|
2414
|
-
if (shell.includes("zsh")) return { name: "zsh", rcFile:
|
|
2415
|
-
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") };
|
|
2416
2445
|
if (shell.includes("fish")) {
|
|
2417
|
-
const confDir =
|
|
2418
|
-
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") };
|
|
2419
2448
|
}
|
|
2420
2449
|
return null;
|
|
2421
2450
|
}
|
|
@@ -2423,262 +2452,299 @@ function step(current, total, label) {
|
|
|
2423
2452
|
log.plain(import_chalk2.default.bold(`
|
|
2424
2453
|
[${current}/${total}] ${label}`));
|
|
2425
2454
|
}
|
|
2426
|
-
function
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
if (!import_fs14.default.existsSync(dir)) {
|
|
2433
|
-
import_fs14.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2434
|
-
log.success(`Created ${dir}`);
|
|
2435
|
-
} else {
|
|
2436
|
-
try {
|
|
2437
|
-
import_fs14.default.chmodSync(dir, 448);
|
|
2438
|
-
} catch {
|
|
2439
|
-
}
|
|
2440
|
-
log.info(`${dir} already exists`);
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
if (!import_fs14.default.existsSync(PROFILES_DIR)) {
|
|
2444
|
-
import_fs14.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
2445
|
-
}
|
|
2446
|
-
step(2, TOTAL_STEPS, "Initialising database");
|
|
2447
|
-
getDb();
|
|
2448
|
-
log.success("Database ready");
|
|
2449
|
-
step(3, TOTAL_STEPS, "Installing shell hook");
|
|
2450
|
-
const shell = detectShell();
|
|
2451
|
-
if (!shell) {
|
|
2452
|
-
log.warn("Could not detect shell. Supported: zsh, bash, fish");
|
|
2453
|
-
log.plain(
|
|
2454
|
-
` 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")}`
|
|
2455
|
-
);
|
|
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}`);
|
|
2456
2461
|
} else {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
} else {
|
|
2461
|
-
const hookContent = import_fs14.default.readFileSync(hookFile, "utf-8");
|
|
2462
|
-
const binaryPath = resolveWorkscribeBinary();
|
|
2463
|
-
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2464
|
-
if (shell.name === "fish") {
|
|
2465
|
-
const confDir = import_path10.default.dirname(shell.rcFile);
|
|
2466
|
-
if (!import_fs14.default.existsSync(confDir)) import_fs14.default.mkdirSync(confDir, { recursive: true });
|
|
2467
|
-
}
|
|
2468
|
-
if (!import_fs14.default.existsSync(shell.rcFile)) import_fs14.default.writeFileSync(shell.rcFile, "");
|
|
2469
|
-
if (!isHookInstalled(shell.rcFile)) {
|
|
2470
|
-
log.plain("");
|
|
2471
|
-
log.plain(import_chalk2.default.dim(" Workscribe adds a lightweight hook to your shell that captures each"));
|
|
2472
|
-
log.plain(import_chalk2.default.dim(" command you run \u2014 the text, exit code, duration, and working directory."));
|
|
2473
|
-
log.plain(import_chalk2.default.dim(" It runs in the background and adds no latency to your terminal."));
|
|
2474
|
-
log.plain(import_chalk2.default.dim(" Credentials and secrets are redacted before anything is stored."));
|
|
2475
|
-
log.plain("");
|
|
2476
|
-
installHook(shell.rcFile, hookWithPath);
|
|
2477
|
-
log.success(`Shell hook installed in ${shell.rcFile}`);
|
|
2478
|
-
} else if (isHookOutdated(shell.rcFile, hookWithPath)) {
|
|
2479
|
-
const { update } = await (0, import_prompts.default)({
|
|
2480
|
-
type: "confirm",
|
|
2481
|
-
name: "update",
|
|
2482
|
-
message: `Shell hook in ${shell.rcFile} is outdated. Update?`,
|
|
2483
|
-
initial: true
|
|
2484
|
-
});
|
|
2485
|
-
if (update) {
|
|
2486
|
-
updateHook(shell.rcFile, hookWithPath);
|
|
2487
|
-
log.success(`Shell hook updated in ${shell.rcFile}`);
|
|
2488
|
-
} else {
|
|
2489
|
-
log.info("Shell hook update skipped");
|
|
2490
|
-
}
|
|
2491
|
-
} else {
|
|
2492
|
-
log.info(`Shell hook up to date in ${shell.rcFile}`);
|
|
2493
|
-
}
|
|
2494
|
-
if (isCompletionsInstalled(shell)) {
|
|
2495
|
-
log.info("Shell completions already installed");
|
|
2496
|
-
} else {
|
|
2497
|
-
const { installComps } = await (0, import_prompts.default)({
|
|
2498
|
-
type: "confirm",
|
|
2499
|
-
name: "installComps",
|
|
2500
|
-
message: "Install shell completions (tab-complete workscribe commands)?",
|
|
2501
|
-
initial: true
|
|
2502
|
-
});
|
|
2503
|
-
if (installComps) {
|
|
2504
|
-
try {
|
|
2505
|
-
installCompletions(shell, getPublicCommands(program));
|
|
2506
|
-
log.success("Shell completions installed");
|
|
2507
|
-
} catch (error) {
|
|
2508
|
-
log.warn(`Completions install failed: ${error.message}`);
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2462
|
+
try {
|
|
2463
|
+
import_fs14.default.chmodSync(dir, 448);
|
|
2464
|
+
} catch {
|
|
2512
2465
|
}
|
|
2466
|
+
log.info(`${dir} already exists`);
|
|
2513
2467
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
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")}`
|
|
2517
2503
|
);
|
|
2518
|
-
|
|
2519
|
-
|
|
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}`);
|
|
2520
2538
|
} else {
|
|
2521
|
-
|
|
2522
|
-
const integration = AI_HOOK_INTEGRATIONS[tool.hook];
|
|
2523
|
-
if (integration.isHookInstalled()) {
|
|
2524
|
-
log.info(`${integration.displayName} session hook already installed`);
|
|
2525
|
-
} else {
|
|
2526
|
-
const { install } = await (0, import_prompts.default)({
|
|
2527
|
-
type: "confirm",
|
|
2528
|
-
name: "install",
|
|
2529
|
-
message: `${integration.displayName} detected. Install session capture hook?`,
|
|
2530
|
-
initial: false
|
|
2531
|
-
});
|
|
2532
|
-
if (install) {
|
|
2533
|
-
integration.install();
|
|
2534
|
-
log.success(`${integration.displayName} session hook installed`);
|
|
2535
|
-
} else {
|
|
2536
|
-
log.info(`${integration.displayName} hook skipped \u2014 re-run workscribe init to install later`);
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2539
|
+
log.info("Shell hook update skipped");
|
|
2540
2540
|
}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
log.success(`${entry.displayName} profile enabled`);
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
writeConfig(config);
|
|
2563
|
-
} else if (detected.length === 0) {
|
|
2564
|
-
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}`);
|
|
2565
2559
|
}
|
|
2566
|
-
} catch {
|
|
2567
|
-
log.info("Could not reach profile registry \u2014 run workscribe profile detect after connecting");
|
|
2568
2560
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
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`);
|
|
2573
2577
|
} else {
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
choices: [
|
|
2580
|
-
{ title: "Anthropic (Claude) \u2014 requires API key", value: "anthropic" },
|
|
2581
|
-
{ title: "OpenAI \u2014 requires API key", value: "openai" },
|
|
2582
|
-
{ title: "Ollama (local) \u2014 free, runs on your machine", value: "ollama" },
|
|
2583
|
-
{ title: "Other (OpenAI-compatible) \u2014 Groq, Together, Mistral\u2026", value: "openai-compatible" },
|
|
2584
|
-
{ title: "Skip \u2014 configure later", value: "skip" }
|
|
2585
|
-
]
|
|
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
|
|
2586
2583
|
});
|
|
2587
|
-
if (
|
|
2588
|
-
|
|
2589
|
-
log.
|
|
2584
|
+
if (install) {
|
|
2585
|
+
integration.install();
|
|
2586
|
+
log.success(`${integration.displayName} session hook installed`);
|
|
2590
2587
|
} else {
|
|
2591
|
-
|
|
2592
|
-
if (provider === "ollama") {
|
|
2593
|
-
log.plain(`
|
|
2594
|
-
${import_chalk2.default.dim("Make sure Ollama is running: ollama serve")}`);
|
|
2595
|
-
log.plain(
|
|
2596
|
-
` ${import_chalk2.default.dim("Default model: llama3 (change with: workscribe config set ai.model <model>)")}
|
|
2597
|
-
`
|
|
2598
|
-
);
|
|
2599
|
-
} else {
|
|
2600
|
-
const { apiKey } = await (0, import_prompts.default)({
|
|
2601
|
-
type: "password",
|
|
2602
|
-
name: "apiKey",
|
|
2603
|
-
message: `Enter your ${provider === "openai-compatible" ? "" : provider + " "}API key:`
|
|
2604
|
-
});
|
|
2605
|
-
if (apiKey) config.ai.apiKey = apiKey;
|
|
2606
|
-
if (provider === "openai-compatible") {
|
|
2607
|
-
const { baseUrl } = await (0, import_prompts.default)({
|
|
2608
|
-
type: "text",
|
|
2609
|
-
name: "baseUrl",
|
|
2610
|
-
message: "Enter the base URL (e.g. https://api.groq.com/openai/v1):"
|
|
2611
|
-
});
|
|
2612
|
-
if (baseUrl) config.ai.baseUrl = baseUrl;
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
log.plain("");
|
|
2616
|
-
log.info(`Validating ${import_chalk2.default.cyan(provider)} connection\u2026`);
|
|
2617
|
-
try {
|
|
2618
|
-
const testProvider = getProvider(config);
|
|
2619
|
-
await testProvider.validate();
|
|
2620
|
-
log.success("API key verified");
|
|
2621
|
-
writeConfig(config);
|
|
2622
|
-
providerConfigured = true;
|
|
2623
|
-
} catch (error) {
|
|
2624
|
-
log.warn(`Validation failed: ${error.message}`);
|
|
2625
|
-
const { retry } = await (0, import_prompts.default)({
|
|
2626
|
-
type: "confirm",
|
|
2627
|
-
name: "retry",
|
|
2628
|
-
message: "Save anyway and configure later?",
|
|
2629
|
-
initial: false
|
|
2630
|
-
});
|
|
2631
|
-
if (retry) {
|
|
2632
|
-
writeConfig(config);
|
|
2633
|
-
providerConfigured = false;
|
|
2634
|
-
log.info("Config saved \u2014 run workscribe config set ai.apiKey <key> to update");
|
|
2635
|
-
} else {
|
|
2636
|
-
log.info("API key not saved \u2014 run workscribe init again to configure");
|
|
2637
|
-
providerConfigured = false;
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
2588
|
+
log.info(`${integration.displayName} hook skipped \u2014 re-run workscribe init to install later`);
|
|
2640
2589
|
}
|
|
2641
2590
|
}
|
|
2642
|
-
|
|
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
|
+
}
|
|
2643
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");
|
|
2644
2617
|
}
|
|
2645
|
-
|
|
2646
|
-
log.
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
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;
|
|
2658
2666
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
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");
|
|
2665
2688
|
} else {
|
|
2666
|
-
log.
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2689
|
+
log.info("API key not saved \u2014 run workscribe init again to configure");
|
|
2690
|
+
providerConfigured = false;
|
|
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.`);
|
|
2672
2707
|
}
|
|
2673
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")}`);
|
|
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);
|
|
2674
2740
|
});
|
|
2675
2741
|
}
|
|
2676
|
-
var import_fs14,
|
|
2742
|
+
var import_fs14, import_path11, import_os5, import_prompts, import_chalk2, TOTAL_STEPS;
|
|
2677
2743
|
var init_init = __esm({
|
|
2678
2744
|
"src/cli/init.ts"() {
|
|
2679
2745
|
"use strict";
|
|
2680
2746
|
import_fs14 = __toESM(require("fs"));
|
|
2681
|
-
|
|
2747
|
+
import_path11 = __toESM(require("path"));
|
|
2682
2748
|
import_os5 = __toESM(require("os"));
|
|
2683
2749
|
import_prompts = __toESM(require("prompts"));
|
|
2684
2750
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -2693,6 +2759,7 @@ var init_init = __esm({
|
|
|
2693
2759
|
init_shell_hook();
|
|
2694
2760
|
init_hooks();
|
|
2695
2761
|
init_completions();
|
|
2762
|
+
TOTAL_STEPS = 5;
|
|
2696
2763
|
}
|
|
2697
2764
|
});
|
|
2698
2765
|
|
|
@@ -3150,7 +3217,7 @@ function registerSummary(program) {
|
|
|
3150
3217
|
return;
|
|
3151
3218
|
}
|
|
3152
3219
|
const cacheKey = isoWeekKey(today);
|
|
3153
|
-
const cacheFile =
|
|
3220
|
+
const cacheFile = import_path12.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
|
|
3154
3221
|
if (!opts.fresh && import_fs16.default.existsSync(cacheFile)) {
|
|
3155
3222
|
const cached = import_fs16.default.readFileSync(cacheFile, "utf-8");
|
|
3156
3223
|
log.plain("");
|
|
@@ -3285,14 +3352,14 @@ ${bullets.trim()}`;
|
|
|
3285
3352
|
await handleWebhook(opts, config, fullSummary);
|
|
3286
3353
|
});
|
|
3287
3354
|
}
|
|
3288
|
-
var import_chalk6, import_fs16, import_os6,
|
|
3355
|
+
var import_chalk6, import_fs16, import_os6, import_path12;
|
|
3289
3356
|
var init_summary = __esm({
|
|
3290
3357
|
"src/cli/summary.ts"() {
|
|
3291
3358
|
"use strict";
|
|
3292
3359
|
import_chalk6 = __toESM(require("chalk"));
|
|
3293
3360
|
import_fs16 = __toESM(require("fs"));
|
|
3294
3361
|
import_os6 = __toESM(require("os"));
|
|
3295
|
-
|
|
3362
|
+
import_path12 = __toESM(require("path"));
|
|
3296
3363
|
init_detector();
|
|
3297
3364
|
init_router();
|
|
3298
3365
|
init_config();
|
|
@@ -3362,7 +3429,7 @@ function registerExport(program) {
|
|
|
3362
3429
|
const payload = { date, sessions: sessionPayloads };
|
|
3363
3430
|
log.info(`Generating summary via ${config.ai.provider}\u2026`);
|
|
3364
3431
|
const summary = await provider.summarize(payload);
|
|
3365
|
-
const outputPath = opts.output ??
|
|
3432
|
+
const outputPath = opts.output ?? import_path13.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
|
|
3366
3433
|
if (!import_fs17.default.existsSync(EXPORTS_DIR)) import_fs17.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
|
|
3367
3434
|
import_fs17.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
|
|
3368
3435
|
|
|
@@ -3371,12 +3438,12 @@ ${summary}
|
|
|
3371
3438
|
log.success(`Saved: ${outputPath}`);
|
|
3372
3439
|
});
|
|
3373
3440
|
}
|
|
3374
|
-
var import_fs17,
|
|
3441
|
+
var import_fs17, import_path13;
|
|
3375
3442
|
var init_export = __esm({
|
|
3376
3443
|
"src/cli/export.ts"() {
|
|
3377
3444
|
"use strict";
|
|
3378
3445
|
import_fs17 = __toESM(require("fs"));
|
|
3379
|
-
|
|
3446
|
+
import_path13 = __toESM(require("path"));
|
|
3380
3447
|
init_detector();
|
|
3381
3448
|
init_router();
|
|
3382
3449
|
init_config();
|
|
@@ -3584,10 +3651,10 @@ var init_clear = __esm({
|
|
|
3584
3651
|
// src/cli/uninstall.ts
|
|
3585
3652
|
function detectShellRcFiles() {
|
|
3586
3653
|
const candidates = [
|
|
3587
|
-
{ name: "zsh", rcFile:
|
|
3588
|
-
{ name: "bash", rcFile:
|
|
3589
|
-
{ name: "bash", rcFile:
|
|
3590
|
-
{ 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") }
|
|
3591
3658
|
];
|
|
3592
3659
|
return candidates.filter((candidate) => import_fs19.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
3593
3660
|
}
|
|
@@ -3637,12 +3704,12 @@ function registerUninstall(program) {
|
|
|
3637
3704
|
log.plain("");
|
|
3638
3705
|
});
|
|
3639
3706
|
}
|
|
3640
|
-
var import_fs19,
|
|
3707
|
+
var import_fs19, import_path14, import_os7, import_prompts3, import_chalk9;
|
|
3641
3708
|
var init_uninstall = __esm({
|
|
3642
3709
|
"src/cli/uninstall.ts"() {
|
|
3643
3710
|
"use strict";
|
|
3644
3711
|
import_fs19 = __toESM(require("fs"));
|
|
3645
|
-
|
|
3712
|
+
import_path14 = __toESM(require("path"));
|
|
3646
3713
|
import_os7 = __toESM(require("os"));
|
|
3647
3714
|
import_prompts3 = __toESM(require("prompts"));
|
|
3648
3715
|
import_chalk9 = __toESM(require("chalk"));
|
|
@@ -3696,7 +3763,7 @@ function registerProfile(program) {
|
|
|
3696
3763
|
log.plain(import_chalk10.default.dim(" " + "\u2500".repeat(60)));
|
|
3697
3764
|
for (const entry of entries) {
|
|
3698
3765
|
const badge = activeNames.has(entry.name) ? import_chalk10.default.green(" [active]") : "";
|
|
3699
|
-
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") : "";
|
|
3700
3767
|
log.plain(` ${import_chalk10.default.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`);
|
|
3701
3768
|
if (entry.description) log.plain(` ${import_chalk10.default.dim(" ".repeat(18) + entry.description)}`);
|
|
3702
3769
|
}
|
|
@@ -3732,7 +3799,7 @@ function registerProfile(program) {
|
|
|
3732
3799
|
continue;
|
|
3733
3800
|
}
|
|
3734
3801
|
activeNames.delete(name);
|
|
3735
|
-
const cachePath =
|
|
3802
|
+
const cachePath = import_path15.default.join(PROFILES_DIR, `${name}.json`);
|
|
3736
3803
|
if (import_fs20.default.existsSync(cachePath)) import_fs20.default.unlinkSync(cachePath);
|
|
3737
3804
|
log.success(`Profile "${name}" removed`);
|
|
3738
3805
|
}
|
|
@@ -3860,12 +3927,12 @@ function registerProfile(program) {
|
|
|
3860
3927
|
}
|
|
3861
3928
|
});
|
|
3862
3929
|
}
|
|
3863
|
-
var import_fs20,
|
|
3930
|
+
var import_fs20, import_path15, import_chalk10, MIN_SUGGEST_COUNT;
|
|
3864
3931
|
var init_profile = __esm({
|
|
3865
3932
|
"src/cli/profile.ts"() {
|
|
3866
3933
|
"use strict";
|
|
3867
3934
|
import_fs20 = __toESM(require("fs"));
|
|
3868
|
-
|
|
3935
|
+
import_path15 = __toESM(require("path"));
|
|
3869
3936
|
import_chalk10 = __toESM(require("chalk"));
|
|
3870
3937
|
init_config();
|
|
3871
3938
|
init_paths();
|
|
@@ -3978,7 +4045,7 @@ function registerWeek(program) {
|
|
|
3978
4045
|
return;
|
|
3979
4046
|
}
|
|
3980
4047
|
const cacheKey = isoWeekKey(today);
|
|
3981
|
-
const cacheFile =
|
|
4048
|
+
const cacheFile = import_path16.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
|
|
3982
4049
|
if (!opts.fresh && import_fs21.default.existsSync(cacheFile)) {
|
|
3983
4050
|
const cached = import_fs21.default.readFileSync(cacheFile, "utf-8");
|
|
3984
4051
|
log.plain("");
|
|
@@ -4005,13 +4072,13 @@ function registerWeek(program) {
|
|
|
4005
4072
|
}
|
|
4006
4073
|
});
|
|
4007
4074
|
}
|
|
4008
|
-
var import_chalk11, import_fs21,
|
|
4075
|
+
var import_chalk11, import_fs21, import_path16, import_os8;
|
|
4009
4076
|
var init_week = __esm({
|
|
4010
4077
|
"src/cli/week.ts"() {
|
|
4011
4078
|
"use strict";
|
|
4012
4079
|
import_chalk11 = __toESM(require("chalk"));
|
|
4013
4080
|
import_fs21 = __toESM(require("fs"));
|
|
4014
|
-
|
|
4081
|
+
import_path16 = __toESM(require("path"));
|
|
4015
4082
|
import_os8 = __toESM(require("os"));
|
|
4016
4083
|
init_detector();
|
|
4017
4084
|
init_router();
|
|
@@ -4045,9 +4112,9 @@ function redactConfig() {
|
|
|
4045
4112
|
function registerBugReport(program) {
|
|
4046
4113
|
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
4047
4114
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4048
|
-
const reportPath =
|
|
4115
|
+
const reportPath = import_path17.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
4049
4116
|
const systemInfo = [
|
|
4050
|
-
`Workscribe version : ${"1.
|
|
4117
|
+
`Workscribe version : ${"1.15.0"}`,
|
|
4051
4118
|
`Node.js version : ${process.version}`,
|
|
4052
4119
|
`Platform : ${process.platform}`,
|
|
4053
4120
|
`Architecture : ${process.arch}`,
|
|
@@ -4078,8 +4145,8 @@ function registerBugReport(program) {
|
|
|
4078
4145
|
process.exit(1);
|
|
4079
4146
|
}
|
|
4080
4147
|
const params = new URLSearchParams({
|
|
4081
|
-
title: `Bug report \u2014 workscribe ${"1.
|
|
4082
|
-
body: `<!-- Attach ${
|
|
4148
|
+
title: `Bug report \u2014 workscribe ${"1.15.0"}`,
|
|
4149
|
+
body: `<!-- Attach ${import_path17.default.basename(reportPath)} or paste the relevant sections below -->
|
|
4083
4150
|
|
|
4084
4151
|
**Describe the issue:**
|
|
4085
4152
|
|
|
@@ -4096,13 +4163,13 @@ function registerBugReport(program) {
|
|
|
4096
4163
|
log.plain("");
|
|
4097
4164
|
});
|
|
4098
4165
|
}
|
|
4099
|
-
var import_fs22, import_os9,
|
|
4166
|
+
var import_fs22, import_os9, import_path17, import_chalk12, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
4100
4167
|
var init_bug_report = __esm({
|
|
4101
4168
|
"src/cli/bug-report.ts"() {
|
|
4102
4169
|
"use strict";
|
|
4103
4170
|
import_fs22 = __toESM(require("fs"));
|
|
4104
4171
|
import_os9 = __toESM(require("os"));
|
|
4105
|
-
|
|
4172
|
+
import_path17 = __toESM(require("path"));
|
|
4106
4173
|
import_chalk12 = __toESM(require("chalk"));
|
|
4107
4174
|
init_paths();
|
|
4108
4175
|
init_config();
|
|
@@ -4207,7 +4274,7 @@ function printSessionContext(session, heading) {
|
|
|
4207
4274
|
function registerContext(program) {
|
|
4208
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) => {
|
|
4209
4276
|
if (opts.file) {
|
|
4210
|
-
const normalizedFile =
|
|
4277
|
+
const normalizedFile = import_path18.default.normalize(opts.file);
|
|
4211
4278
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
4212
4279
|
if (!session) {
|
|
4213
4280
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
@@ -4218,7 +4285,7 @@ function registerContext(program) {
|
|
|
4218
4285
|
log.plain(JSON.stringify(toContextJson(session)));
|
|
4219
4286
|
return;
|
|
4220
4287
|
}
|
|
4221
|
-
printSessionContext(session, `Last session touching ${
|
|
4288
|
+
printSessionContext(session, `Last session touching ${import_path18.default.basename(normalizedFile)}`);
|
|
4222
4289
|
return;
|
|
4223
4290
|
}
|
|
4224
4291
|
const { repoName } = getGitContext(process.cwd(), null, readConfig().git.qualifyRepoName);
|
|
@@ -4240,11 +4307,11 @@ function registerContext(program) {
|
|
|
4240
4307
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
4241
4308
|
});
|
|
4242
4309
|
}
|
|
4243
|
-
var
|
|
4310
|
+
var import_path18, import_chalk14;
|
|
4244
4311
|
var init_context = __esm({
|
|
4245
4312
|
"src/cli/context.ts"() {
|
|
4246
4313
|
"use strict";
|
|
4247
|
-
|
|
4314
|
+
import_path18 = __toESM(require("path"));
|
|
4248
4315
|
import_chalk14 = __toESM(require("chalk"));
|
|
4249
4316
|
init_db();
|
|
4250
4317
|
init_git();
|
|
@@ -4262,9 +4329,10 @@ function readCache() {
|
|
|
4262
4329
|
return null;
|
|
4263
4330
|
}
|
|
4264
4331
|
}
|
|
4265
|
-
function writeCache(
|
|
4332
|
+
function writeCache(updates) {
|
|
4266
4333
|
try {
|
|
4267
|
-
|
|
4334
|
+
const current = readCache() ?? {};
|
|
4335
|
+
import_fs23.default.writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({ ...current, ...updates }), { mode: 384 });
|
|
4268
4336
|
} catch {
|
|
4269
4337
|
}
|
|
4270
4338
|
}
|
|
@@ -4277,7 +4345,7 @@ function isNewer(latest, current) {
|
|
|
4277
4345
|
return latestPatch > currentPatch;
|
|
4278
4346
|
}
|
|
4279
4347
|
function checkForUpdate() {
|
|
4280
|
-
const current = "1.
|
|
4348
|
+
const current = "1.15.0";
|
|
4281
4349
|
const cache = readCache();
|
|
4282
4350
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
4283
4351
|
process.stderr.write(
|
|
@@ -4297,12 +4365,35 @@ function checkForUpdate() {
|
|
|
4297
4365
|
});
|
|
4298
4366
|
}
|
|
4299
4367
|
}
|
|
4300
|
-
|
|
4368
|
+
function buildTelemetryPayload(config) {
|
|
4369
|
+
return {
|
|
4370
|
+
installId: config.telemetry.installId,
|
|
4371
|
+
cliVersion: "1.15.0",
|
|
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;
|
|
4301
4390
|
var init_update_check = __esm({
|
|
4302
4391
|
"src/utils/update-check.ts"() {
|
|
4303
4392
|
"use strict";
|
|
4304
4393
|
import_fs23 = __toESM(require("fs"));
|
|
4394
|
+
import_os10 = __toESM(require("os"));
|
|
4305
4395
|
init_paths();
|
|
4396
|
+
init_config();
|
|
4306
4397
|
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
4307
4398
|
}
|
|
4308
4399
|
});
|
|
@@ -4333,9 +4424,17 @@ var require_main = __commonJS({
|
|
|
4333
4424
|
init_alerts3();
|
|
4334
4425
|
init_context();
|
|
4335
4426
|
init_update_check();
|
|
4336
|
-
|
|
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
|
+
}
|
|
4337
4436
|
var program = new import_commander.Command();
|
|
4338
|
-
program.name("workscribe").description("Automatic developer work journal").version("1.
|
|
4437
|
+
program.name("workscribe").description("Automatic developer work journal").version("1.15.0");
|
|
4339
4438
|
registerCapture(program);
|
|
4340
4439
|
registerHook(program);
|
|
4341
4440
|
registerInit(program);
|