@workscribe/cli 3.0.0 → 3.1.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 +507 -96
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/shell/hook.bash +26 -5
- package/shell/hook.zsh +14 -4
package/dist/index.js
CHANGED
|
@@ -378,7 +378,7 @@ var init_logger = __esm({
|
|
|
378
378
|
success: (msg) => console.log(import_chalk.default.green("\u2713"), msg),
|
|
379
379
|
warn: (msg) => console.log(import_chalk.default.yellow("\u26A0"), msg),
|
|
380
380
|
error: (msg) => console.error(import_chalk.default.red("\u2717"), msg),
|
|
381
|
-
/** A warning on stderr — keeps stdout clean for JSON output
|
|
381
|
+
/** A warning on stderr — keeps stdout clean for JSON and plain text output, and pipes. */
|
|
382
382
|
notice: (msg) => process.stderr.write(`${import_chalk.default.yellow("\u26A0")} ${msg}
|
|
383
383
|
`),
|
|
384
384
|
plain: (msg) => console.log(msg)
|
|
@@ -2635,6 +2635,29 @@ var init_db = __esm({
|
|
|
2635
2635
|
});
|
|
2636
2636
|
|
|
2637
2637
|
// src/capture/redact.ts
|
|
2638
|
+
function sameCommand(maxParts) {
|
|
2639
|
+
const quoted = String.raw`"[^"]{0,${MAX_QUOTED_LENGTH}}"|'[^']{0,${MAX_QUOTED_LENGTH}}'`;
|
|
2640
|
+
return String.raw`(?:(?:${quoted}|\\\n|[^|;&\n"']){0,${maxParts}}?|[^|;&\n]{0,${maxParts}}?)`;
|
|
2641
|
+
}
|
|
2642
|
+
function valueAfter(prefix, flags = "g") {
|
|
2643
|
+
return new RegExp(`(?<=${prefix})${VALUE}`, flags);
|
|
2644
|
+
}
|
|
2645
|
+
function toolFlagValue(tools, flag) {
|
|
2646
|
+
return valueAfter(String.raw`${TOOL_WORD_START}(?:${tools})${TOOL_WORD_END}${SAME_COMMAND}\s${flag}`);
|
|
2647
|
+
}
|
|
2648
|
+
function quotedValuesAfterName(prefix, name, flags = "g") {
|
|
2649
|
+
return [
|
|
2650
|
+
new RegExp(String.raw`(?<=${prefix}"${name})[^"\s][^"\n]*`, flags),
|
|
2651
|
+
new RegExp(String.raw`(?<=${prefix}'${name})[^'\s][^'\n]*`, flags)
|
|
2652
|
+
];
|
|
2653
|
+
}
|
|
2654
|
+
function valuesAfterName(prefix, name, flags = "g") {
|
|
2655
|
+
return [
|
|
2656
|
+
...quotedValuesAfterName(prefix, name, flags),
|
|
2657
|
+
new RegExp(String.raw`(?<=${prefix}${name})(?:${QUOTED})`, flags),
|
|
2658
|
+
new RegExp(String.raw`(?<=${prefix}${name})[^\s"'|;&]+`, flags)
|
|
2659
|
+
];
|
|
2660
|
+
}
|
|
2638
2661
|
function redact(command, extraPatterns = []) {
|
|
2639
2662
|
let result = command;
|
|
2640
2663
|
const allPatterns = [...BUILTIN_PATTERNS];
|
|
@@ -2646,22 +2669,73 @@ function redact(command, extraPatterns = []) {
|
|
|
2646
2669
|
}
|
|
2647
2670
|
for (const pattern of allPatterns) {
|
|
2648
2671
|
pattern.lastIndex = 0;
|
|
2649
|
-
result = result.replace(pattern,
|
|
2672
|
+
result = result.replace(pattern, REDACTED_TEXT);
|
|
2650
2673
|
}
|
|
2651
2674
|
return result;
|
|
2652
2675
|
}
|
|
2653
|
-
var BUILTIN_PATTERNS;
|
|
2676
|
+
var QUOTED, WORD_REST, VALUE, VALUE_OR_DASHED, MAX_COMMAND_PARTS, MAX_QUOTED_LENGTH, MAX_PARTS_BEFORE_LOGIN, MAX_PARTS_AFTER_LOGIN, SAME_COMMAND, NOT_PART_OF_A_WORD, NOT_A_NAME, NOT_IN_A_MESSAGE, TOOL_WORD_START, TOOL_WORD_END, NOT_A_PORT_MAPPING, LONG_SECRET_FLAGS, LONG_FLAG_VALUE_START, MAX_SORT_FIELD_DIGITS, SORT_FIELD, NOT_A_KEY, MAX_SLUG_WORD_LENGTH, MAX_SLUG_WORDS_AFTER_FIRST, SLUG_WORD, UUID_IN_A_KEY, SLUG_AFTER_SK, CONTAINER_LOGIN_TOOLS, EVERYDAY_HEADERS, DISTINCTIVE_HEADERS, HEADER_FLAG, SENSITIVE_VARIABLE_WORDS, QUERY_SECRET_NAME, JSON_SECRET_NAME, BUILTIN_PATTERNS, REDACTED_TEXT;
|
|
2654
2677
|
var init_redact = __esm({
|
|
2655
2678
|
"src/capture/redact.ts"() {
|
|
2656
2679
|
"use strict";
|
|
2680
|
+
QUOTED = String.raw`"[^"]*"|'[^']*'`;
|
|
2681
|
+
WORD_REST = String.raw`(?:[^\s"');&|]|["');&|](?=[^\s;&|)]))*`;
|
|
2682
|
+
VALUE = String.raw`(?:${QUOTED}|[^\s"'\-]${WORD_REST})`;
|
|
2683
|
+
VALUE_OR_DASHED = String.raw`(?:${QUOTED}|[^\s"']${WORD_REST})`;
|
|
2684
|
+
MAX_COMMAND_PARTS = 1e3;
|
|
2685
|
+
MAX_QUOTED_LENGTH = 64e3;
|
|
2686
|
+
MAX_PARTS_BEFORE_LOGIN = 200;
|
|
2687
|
+
MAX_PARTS_AFTER_LOGIN = 300;
|
|
2688
|
+
SAME_COMMAND = sameCommand(MAX_COMMAND_PARTS);
|
|
2689
|
+
NOT_PART_OF_A_WORD = String.raw`(?<![\w.:@-])`;
|
|
2690
|
+
NOT_A_NAME = String.raw`(?<!--name(?:\s+|=)|\bssh\s+)`;
|
|
2691
|
+
NOT_IN_A_MESSAGE = String.raw`(?<!\s(?:-[as]*m|--message|--title|--body|--subject)[\s=]*["'][^"'\n]{0,${MAX_COMMAND_PARTS}})`;
|
|
2692
|
+
TOOL_WORD_START = `${NOT_IN_A_MESSAGE}${NOT_A_NAME}${NOT_PART_OF_A_WORD}`;
|
|
2693
|
+
TOOL_WORD_END = String.raw`(?:\.exe)?["'\x60)]?(?=\s)`;
|
|
2694
|
+
NOT_A_PORT_MAPPING = String.raw`(?!(?:[\d.]+:)?\d+:\d+(?:\/\w+)?(?=\s|$))`;
|
|
2695
|
+
LONG_SECRET_FLAGS = [
|
|
2696
|
+
"password",
|
|
2697
|
+
"(?!no-)[\\w-]*-password",
|
|
2698
|
+
"passwd",
|
|
2699
|
+
"passphrase",
|
|
2700
|
+
"pass",
|
|
2701
|
+
"pwd",
|
|
2702
|
+
"secret(?:-key)?",
|
|
2703
|
+
"client[-_]secret",
|
|
2704
|
+
"token",
|
|
2705
|
+
"access-token",
|
|
2706
|
+
"refresh-token",
|
|
2707
|
+
"auth[-_]?token",
|
|
2708
|
+
"private-key",
|
|
2709
|
+
"api[-_]?key"
|
|
2710
|
+
].join("|");
|
|
2711
|
+
LONG_FLAG_VALUE_START = String.raw`(?:=|\s+(?!--))`;
|
|
2712
|
+
MAX_SORT_FIELD_DIGITS = 2;
|
|
2713
|
+
SORT_FIELD = String.raw`\d{1,${MAX_SORT_FIELD_DIGITS}}`;
|
|
2714
|
+
NOT_A_KEY = String.raw`(?:${SORT_FIELD}(?:[.,]${SORT_FIELD})*|[\w./~-]*\.(?:key|pem|crt|p12|pfx|der)|[\w./~-]*\/id_\w+)(?=\s|$)`;
|
|
2715
|
+
MAX_SLUG_WORD_LENGTH = 15;
|
|
2716
|
+
MAX_SLUG_WORDS_AFTER_FIRST = 8;
|
|
2717
|
+
SLUG_WORD = `[a-z0-9]{1,${MAX_SLUG_WORD_LENGTH}}`;
|
|
2718
|
+
UUID_IN_A_KEY = String.raw`[a-z-]{0,20}[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`;
|
|
2719
|
+
SLUG_AFTER_SK = String.raw`(?!${UUID_IN_A_KEY})${SLUG_WORD}(?:-${SLUG_WORD}){0,${MAX_SLUG_WORDS_AFTER_FIRST}}(?![\w-])`;
|
|
2720
|
+
CONTAINER_LOGIN_TOOLS = "docker|podman|podman-remote|nerdctl|helm";
|
|
2721
|
+
EVERYDAY_HEADERS = "api-key|cookie|set-cookie";
|
|
2722
|
+
DISTINCTIVE_HEADERS = "x-api-key|x-auth-token|x-access-token";
|
|
2723
|
+
HEADER_FLAG = String.raw`(?<![\w-])(?:-\w*H|--(?:proxy-)?header)[\s=]*`;
|
|
2724
|
+
SENSITIVE_VARIABLE_WORDS = "TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL|_PWD";
|
|
2725
|
+
QUERY_SECRET_NAME = String.raw`key|[\w-]*(?:token|secret|password|passwd|pwd|api[-_]?key)`;
|
|
2726
|
+
JSON_SECRET_NAME = String.raw`[\w-]*(?:token|secret|password|passwd|pwd|api[-_]?key)`;
|
|
2657
2727
|
BUILTIN_PATTERNS = [
|
|
2658
2728
|
// AWS access keys
|
|
2659
2729
|
/AKIA[0-9A-Z]{16}/g,
|
|
2660
2730
|
// AWS secret access keys (40 char base64-ish after common env vars)
|
|
2661
|
-
|
|
2731
|
+
new RegExp(String.raw`(?<=AWS_SECRET_ACCESS_KEY[=\s]+)${VALUE_OR_DASHED}`, "gi"),
|
|
2662
2732
|
// GitHub tokens
|
|
2663
|
-
|
|
2733
|
+
/\bgh[pousr]_[a-zA-Z0-9]{36}/g,
|
|
2664
2734
|
/github_pat_[a-zA-Z0-9_]{82}/g,
|
|
2735
|
+
// AI provider, Google and npm keys
|
|
2736
|
+
new RegExp(String.raw`\bsk-(?!${SLUG_AFTER_SK})[\w-]{20,}`, "g"),
|
|
2737
|
+
/\bAIza[a-zA-Z0-9_-]{30,}/g,
|
|
2738
|
+
/\bnpm_[a-zA-Z0-9]{30,}/g,
|
|
2665
2739
|
// GitLab tokens
|
|
2666
2740
|
/glpat-[a-zA-Z0-9\-]{20}/g,
|
|
2667
2741
|
// Generic JWTs (three base64url segments)
|
|
@@ -2671,17 +2745,85 @@ var init_redact = __esm({
|
|
|
2671
2745
|
/https:\/\/hooks\.slack\.com\/services\/[A-Z0-9]+\/[A-Z0-9]+\/[a-zA-Z0-9]+/g,
|
|
2672
2746
|
// Stripe keys
|
|
2673
2747
|
/sk_(live|test)_[a-zA-Z0-9]{24,}/g,
|
|
2674
|
-
// Values after long-form secret flags
|
|
2675
|
-
|
|
2748
|
+
// Values after long-form secret flags, `--flag value` or `--flag=value`. `--password-stdin` isn't one: no match on its
|
|
2749
|
+
// `-stdin`. A bare short flag means nothing without knowing the tool: see below
|
|
2750
|
+
new RegExp(String.raw`(?<=(?<![\w-])--(?:${LONG_SECRET_FLAGS})${LONG_FLAG_VALUE_START})${VALUE_OR_DASHED}`, "gi"),
|
|
2751
|
+
new RegExp(String.raw`(?<=(?<![\w-])--key${LONG_FLAG_VALUE_START})(?!${NOT_A_KEY})${VALUE_OR_DASHED}`, "gi"),
|
|
2752
|
+
// A password attached to the flag: `mysql -pSECRET`. `mysql -p db` prompts, so there's no value to remove, and
|
|
2753
|
+
// `psql -p 5432` is a port, so psql isn't listed
|
|
2754
|
+
toolFlagValue(
|
|
2755
|
+
"mysql|mysqldump|mysqladmin|mysqlimport|mysqlshow|mysqlcheck|mariadb|mariadb-dump|mariadb-admin|mariadb-import|mariadb-show|mariadb-check",
|
|
2756
|
+
"-p"
|
|
2757
|
+
),
|
|
2758
|
+
// Short flags that take a secret, for the tools where they do
|
|
2759
|
+
toolFlagValue("sshpass", String.raw`-p\s*`),
|
|
2760
|
+
toolFlagValue("mongosh|mongo|mongodump|mongorestore|mongoexport|mongoimport", String.raw`-p\s*${NOT_A_PORT_MAPPING}`),
|
|
2761
|
+
toolFlagValue("redis-cli", String.raw`-a\s*`),
|
|
2762
|
+
toolFlagValue("ssh-keygen", String.raw`-[NP]\s*`),
|
|
2763
|
+
toolFlagValue("zip|unzip", String.raw`-P\s*`),
|
|
2764
|
+
valueAfter(
|
|
2765
|
+
String.raw`${TOOL_WORD_START}(?:${CONTAINER_LOGIN_TOOLS})${TOOL_WORD_END}[^|;&\n]{0,${MAX_PARTS_BEFORE_LOGIN}}?\slogin` + String.raw`${TOOL_WORD_END}${sameCommand(MAX_PARTS_AFTER_LOGIN)}\s-p\s*${NOT_A_PORT_MAPPING}`
|
|
2766
|
+
),
|
|
2767
|
+
// openssl's `-pass pass:SECRET`, also -passin and -passout
|
|
2768
|
+
/(?<=\s-pass(?:in|out|word)?\s+pass:)\S+/g,
|
|
2769
|
+
// curl's cookies, which also covers a cookie file's name: `curl -b s=SECRET`
|
|
2770
|
+
valueAfter(String.raw`\bcurl\b["']?${SAME_COMMAND}\s(?:--cookie|-\w*b)[\s=]+`),
|
|
2771
|
+
// The password half of `curl -u user:password`
|
|
2772
|
+
...valuesAfterName(String.raw`\bcurl\b["']?${SAME_COMMAND}\s(?:-\w*u[\s=]*|--user[\s=]+)`, String.raw`[^:\s"']*:`),
|
|
2773
|
+
// Tokens handed over on stdin: `echo SECRET | docker login --password-stdin`, `gh auth login --with-token <<< SECRET`
|
|
2774
|
+
new RegExp(
|
|
2775
|
+
String.raw`(?<=\b(?:echo(?:\s+-[neE]+)*|printf(?:\s+["']?%s(?:\\n)?["']?)?)\s+)${VALUE}` + String.raw`(?=\s*\|[^;&\n]{0,${MAX_COMMAND_PARTS}}?--(?:password-stdin|with-token))`,
|
|
2776
|
+
"g"
|
|
2777
|
+
),
|
|
2778
|
+
valueAfter(String.raw`--(?:password-stdin|with-token)\s*<<<\s*`),
|
|
2779
|
+
// Headers. A scheme or a header flag is required for Authorization, so prose like "Authorization: added middleware"
|
|
2780
|
+
// stays. An everyday word needs to look like a header, so `git commit -m "cookie: rotate on login"` stays: a header
|
|
2781
|
+
// flag, `Cookie:value` with no space, or a quoted `"Cookie: value"` outside a message
|
|
2782
|
+
/(?<=\bauthorization:\s*(?:bearer|basic|token)\s+)[^\s"']+/gi,
|
|
2783
|
+
/(?<=\bbearer\s+)(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{8,}/gi,
|
|
2784
|
+
...valuesAfterName(HEADER_FLAG, String.raw`(?:${EVERYDAY_HEADERS}):\s*`, "gi"),
|
|
2785
|
+
...valuesAfterName(HEADER_FLAG, String.raw`authorization:\s*(?!(?:bearer|basic|token)\s)`, "gi"),
|
|
2786
|
+
...quotedValuesAfterName("", String.raw`${NOT_IN_A_MESSAGE}(?:${EVERYDAY_HEADERS}):\s*`, "gi"),
|
|
2787
|
+
...valuesAfterName("", String.raw`(?<![\w-])(?:${EVERYDAY_HEADERS}):`, "gi"),
|
|
2788
|
+
...valuesAfterName("", String.raw`\b(?:${DISTINCTIVE_HEADERS}):\s*`, "gi"),
|
|
2789
|
+
// Private keys, whole blocks, as heredocs and `echo` can carry them
|
|
2790
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
2791
|
+
// password=... inside a string or query: `psql "host=x password=SECRET"`, `--set db.password=SECRET`
|
|
2792
|
+
/(?<=(?:^|[\s"'?&=.])(?:password|passwd|pwd)=)[^\s"';&]+/gi,
|
|
2793
|
+
// A secret in a query string or in JSON: `curl "x.io?token=SECRET"`, `curl -d '{"password":"SECRET"}'`
|
|
2794
|
+
new RegExp(String.raw`(?<=[?&](?:${QUERY_SECRET_NAME})=)[^\s"'&#]+`, "gi"),
|
|
2795
|
+
new RegExp(String.raw`(?<=["'](?:${JSON_SECRET_NAME})\\?["']\s*:\s*\\?")[^"\\\n]+`, "gi"),
|
|
2796
|
+
// npm's auth settings, with or without `=`: `npm config set //registry/:_password SECRET`
|
|
2797
|
+
/(?<=:_(?:password|auth|authToken)[=\s]+)\S+/g,
|
|
2676
2798
|
// export VAR=VALUE where VAR looks sensitive
|
|
2677
|
-
|
|
2799
|
+
new RegExp(String.raw`(?<=export\s+(?:[A-Z_]*(?:${SENSITIVE_VARIABLE_WORDS})[A-Z_]*)=)${VALUE_OR_DASHED}`, "gi"),
|
|
2678
2800
|
// Inline assignment: VAR_WITH_SENSITIVE_KEYWORD=value cmd (e.g. API_KEY=secret npm start)
|
|
2679
2801
|
// Lookbehind matches only the variable-name= prefix, so only the value is replaced
|
|
2680
|
-
|
|
2802
|
+
new RegExp(String.raw`(?<=[A-Z_][A-Z0-9_]*(?:${SENSITIVE_VARIABLE_WORDS})[A-Z0-9_]*=)${VALUE_OR_DASHED}`, "gi"),
|
|
2803
|
+
// The same with nothing before the keyword: `TOKEN=SECRET npm start`. Capitals only, so `--author=me` and `key=value` stay
|
|
2804
|
+
new RegExp(
|
|
2805
|
+
String.raw`(?<=(?:^|[\s;&|(])(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIALS?)=)${VALUE_OR_DASHED}`,
|
|
2806
|
+
"g"
|
|
2807
|
+
),
|
|
2681
2808
|
// Credentials embedded in connection string URLs (e.g. DATABASE_URL=postgres://user:pass@host/db)
|
|
2682
2809
|
// Matches the password segment between :// user : password @ — preserves scheme and host
|
|
2683
|
-
/(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\s/]
|
|
2810
|
+
/(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\s/]*:)[^@\s]+(?=@)/g,
|
|
2811
|
+
// A token as the whole userinfo of an https URL: `git clone https://TOKEN@github.com/org/repo`
|
|
2812
|
+
/(?<=https?:\/\/)[^\s:@/?#]+(?=@)/g
|
|
2684
2813
|
];
|
|
2814
|
+
REDACTED_TEXT = "[REDACTED]";
|
|
2815
|
+
}
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
// src/capture/hint.ts
|
|
2819
|
+
function commandHint(safeCommand) {
|
|
2820
|
+
return safeCommand.trim().slice(0, HINT_LENGTH);
|
|
2821
|
+
}
|
|
2822
|
+
var HINT_LENGTH;
|
|
2823
|
+
var init_hint = __esm({
|
|
2824
|
+
"src/capture/hint.ts"() {
|
|
2825
|
+
"use strict";
|
|
2826
|
+
HINT_LENGTH = 120;
|
|
2685
2827
|
}
|
|
2686
2828
|
});
|
|
2687
2829
|
|
|
@@ -2853,13 +2995,13 @@ var init_git_snapshot = __esm({
|
|
|
2853
2995
|
});
|
|
2854
2996
|
|
|
2855
2997
|
// src/capture/aider.ts
|
|
2856
|
-
function readLastAiderPrompt(cwd) {
|
|
2998
|
+
function readLastAiderPrompt(cwd, extraRedactions = []) {
|
|
2857
2999
|
try {
|
|
2858
3000
|
const histPath = import_path8.default.join(cwd, HISTORY_FILE);
|
|
2859
3001
|
const content = import_fs10.default.readFileSync(histPath, "utf-8");
|
|
2860
3002
|
const matches = [...content.matchAll(/^#### (.+)$/gm)];
|
|
2861
3003
|
if (!matches.length) return null;
|
|
2862
|
-
return matches[matches.length - 1][1].trim().slice(0, PROMPT_MAX_LENGTH);
|
|
3004
|
+
return redact(matches[matches.length - 1][1].trim(), extraRedactions).slice(0, PROMPT_MAX_LENGTH);
|
|
2863
3005
|
} catch {
|
|
2864
3006
|
return null;
|
|
2865
3007
|
}
|
|
@@ -2870,6 +3012,7 @@ var init_aider = __esm({
|
|
|
2870
3012
|
"use strict";
|
|
2871
3013
|
import_fs10 = __toESM(require("fs"));
|
|
2872
3014
|
import_path8 = __toESM(require("path"));
|
|
3015
|
+
init_redact();
|
|
2873
3016
|
HISTORY_FILE = ".aider.chat.history.md";
|
|
2874
3017
|
PROMPT_MAX_LENGTH = 200;
|
|
2875
3018
|
}
|
|
@@ -3115,7 +3258,7 @@ function captureCommand(opts) {
|
|
|
3115
3258
|
if (Date.now() - lastTime < TASK_RUN_DEDUP_WINDOW_MS) return;
|
|
3116
3259
|
}
|
|
3117
3260
|
}
|
|
3118
|
-
const command2 = opts.command
|
|
3261
|
+
const command2 = opts.command === void 0 ? opts.file ?? opts.type : redact(opts.command, config.redact.extra);
|
|
3119
3262
|
const context2 = getRepoContext(cwd, config);
|
|
3120
3263
|
const { repoName: repoName2, branch: branch2 } = context2;
|
|
3121
3264
|
const ideEvent = {
|
|
@@ -3149,17 +3292,17 @@ function captureCommand(opts) {
|
|
|
3149
3292
|
if (category === "other") {
|
|
3150
3293
|
const firstToken = safeCommand.trim().split(/\s+/)[0];
|
|
3151
3294
|
if (firstToken) metadata.tool = firstToken;
|
|
3152
|
-
metadata.hint = safeCommand
|
|
3295
|
+
metadata.hint = commandHint(safeCommand);
|
|
3153
3296
|
}
|
|
3154
3297
|
if (category === "ai_session") {
|
|
3155
3298
|
if (opts.gitBefore) {
|
|
3156
3299
|
const commits = getCommitsSince(opts.gitBefore, workDirectory);
|
|
3157
|
-
if (commits.length > 0) metadata.commits = commits;
|
|
3300
|
+
if (commits.length > 0) metadata.commits = commits.map((subject) => redact(subject, config.redact.extra));
|
|
3158
3301
|
}
|
|
3159
3302
|
if (config.capture.includeAiderPrompt !== false) {
|
|
3160
3303
|
const toolEntry = loadAIToolRegistry().find((entry) => entry.tool === metadata.tool);
|
|
3161
3304
|
if (toolEntry?.contextSource === "history-file") {
|
|
3162
|
-
const prompt = readLastAiderPrompt(cwd);
|
|
3305
|
+
const prompt = readLastAiderPrompt(cwd, config.redact.extra);
|
|
3163
3306
|
if (prompt) metadata.prompt = prompt;
|
|
3164
3307
|
}
|
|
3165
3308
|
}
|
|
@@ -3192,6 +3335,7 @@ var init_capture = __esm({
|
|
|
3192
3335
|
import_fs14 = __toESM(require("fs"));
|
|
3193
3336
|
init_db();
|
|
3194
3337
|
init_redact();
|
|
3338
|
+
init_hint();
|
|
3195
3339
|
init_git();
|
|
3196
3340
|
init_git_snapshot();
|
|
3197
3341
|
init_aider();
|
|
@@ -3531,16 +3675,58 @@ var init_completions_block = __esm({
|
|
|
3531
3675
|
});
|
|
3532
3676
|
|
|
3533
3677
|
// src/capture/shell-hook.ts
|
|
3678
|
+
function detectShell() {
|
|
3679
|
+
const shell = process.env.SHELL ?? "";
|
|
3680
|
+
if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path14.default.join(import_os3.default.homedir(), ".zshrc") };
|
|
3681
|
+
if (shell.includes("bash")) return { name: "bash", rcFile: import_path14.default.join(import_os3.default.homedir(), ".bashrc") };
|
|
3682
|
+
if (shell.includes("fish")) {
|
|
3683
|
+
return { name: "fish", rcFile: import_path14.default.join(import_os3.default.homedir(), ".config", "fish", "conf.d", "workscribe.fish") };
|
|
3684
|
+
}
|
|
3685
|
+
return null;
|
|
3686
|
+
}
|
|
3687
|
+
function blockRegion(content, markers) {
|
|
3688
|
+
const blockStart = content.indexOf(markers.start);
|
|
3689
|
+
const blockEnd = content.indexOf(markers.end, blockStart);
|
|
3690
|
+
if (blockStart === -1 || blockEnd === -1) return null;
|
|
3691
|
+
const lines = content.slice(0, blockStart).split("\n");
|
|
3692
|
+
lines.pop();
|
|
3693
|
+
let headerLines = 0;
|
|
3694
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
3695
|
+
const line = lines[index];
|
|
3696
|
+
if (!markers.preamble.test(line) && line.trim() !== "") break;
|
|
3697
|
+
headerLines = lines.length - index;
|
|
3698
|
+
}
|
|
3699
|
+
while (headerLines > 0 && lines[lines.length - headerLines].trim() === "") headerLines -= 1;
|
|
3700
|
+
const header = lines.slice(lines.length - headerLines);
|
|
3701
|
+
const headerLength = header.reduce((length, line) => length + line.length + 1, 0);
|
|
3702
|
+
return { start: blockStart - headerLength, end: blockEnd + markers.end.length };
|
|
3703
|
+
}
|
|
3704
|
+
function hookRegion(content) {
|
|
3705
|
+
return blockRegion(content, { start: HOOK_START, end: HOOK_END, preamble: PREAMBLE_LINE });
|
|
3706
|
+
}
|
|
3707
|
+
function findChronicleHook(content) {
|
|
3708
|
+
return blockRegion(content, { start: CHRONICLE_START, end: CHRONICLE_END, preamble: CHRONICLE_PREAMBLE_LINE });
|
|
3709
|
+
}
|
|
3710
|
+
function hookBlockOf(hookContent) {
|
|
3711
|
+
const startIndex = hookContent.indexOf(HOOK_START);
|
|
3712
|
+
const endIndex = hookContent.indexOf(HOOK_END);
|
|
3713
|
+
if (startIndex === -1 || endIndex === -1) return hookContent.trim();
|
|
3714
|
+
return hookContent.slice(startIndex, endIndex + HOOK_END.length);
|
|
3715
|
+
}
|
|
3716
|
+
function installedHookVersion(block) {
|
|
3717
|
+
const match = HOOK_VERSION_LINE.exec(block);
|
|
3718
|
+
return match ? Number(match[1]) : 1;
|
|
3719
|
+
}
|
|
3534
3720
|
function backUp(rcFile) {
|
|
3535
3721
|
log.info(`Backed up ${rcFile} \u2192 ${import_path14.default.basename(backUpFile(rcFile))}`);
|
|
3536
3722
|
}
|
|
3537
3723
|
function getInstalledHookBlock(rcFile) {
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
const
|
|
3541
|
-
const
|
|
3542
|
-
if (
|
|
3543
|
-
return content.slice(
|
|
3724
|
+
const content = import_fs17.default.existsSync(rcFile) ? readRcFile(rcFile) : null;
|
|
3725
|
+
if (content === null) return null;
|
|
3726
|
+
const startIndex = content.indexOf(HOOK_START);
|
|
3727
|
+
const endIndex = content.indexOf(HOOK_END);
|
|
3728
|
+
if (startIndex === -1 || endIndex === -1) return null;
|
|
3729
|
+
return content.slice(startIndex, endIndex + HOOK_END.length);
|
|
3544
3730
|
}
|
|
3545
3731
|
function isHookInstalled(rcFile) {
|
|
3546
3732
|
return getInstalledHookBlock(rcFile) !== null;
|
|
@@ -3548,7 +3734,7 @@ function isHookInstalled(rcFile) {
|
|
|
3548
3734
|
function isHookOutdated(rcFile, hookContent) {
|
|
3549
3735
|
const installed = getInstalledHookBlock(rcFile);
|
|
3550
3736
|
if (!installed) return false;
|
|
3551
|
-
return installed.trim() !== hookContent.trim();
|
|
3737
|
+
return installed.trim() !== hookBlockOf(hookContent).trim();
|
|
3552
3738
|
}
|
|
3553
3739
|
function installHook(rcFile, hookContent) {
|
|
3554
3740
|
backUp(rcFile);
|
|
@@ -3559,20 +3745,41 @@ ${hookContent}
|
|
|
3559
3745
|
function updateHook(rcFile, hookContent) {
|
|
3560
3746
|
backUp(rcFile);
|
|
3561
3747
|
const content = import_fs17.default.readFileSync(rcFile, "utf-8");
|
|
3562
|
-
const
|
|
3563
|
-
|
|
3564
|
-
import_fs17.default.writeFileSync(rcFile, content.slice(0,
|
|
3748
|
+
const region = hookRegion(content);
|
|
3749
|
+
if (!region) return;
|
|
3750
|
+
import_fs17.default.writeFileSync(rcFile, content.slice(0, region.start) + hookContent.trimEnd() + content.slice(region.end));
|
|
3751
|
+
}
|
|
3752
|
+
function withoutRegion(content, region) {
|
|
3753
|
+
const before = content.slice(0, region.start).replace(/\n+$/, "\n");
|
|
3754
|
+
const after = content.slice(region.end).replace(/^\n/, "");
|
|
3755
|
+
return before + after;
|
|
3565
3756
|
}
|
|
3566
3757
|
function removeHook(rcFile) {
|
|
3567
3758
|
backUp(rcFile);
|
|
3568
3759
|
const content = import_fs17.default.readFileSync(rcFile, "utf-8");
|
|
3569
|
-
const
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
const
|
|
3575
|
-
|
|
3760
|
+
const region = hookRegion(content);
|
|
3761
|
+
if (!region) return;
|
|
3762
|
+
import_fs17.default.writeFileSync(rcFile, withoutRegion(content, region));
|
|
3763
|
+
}
|
|
3764
|
+
function hasChronicleHook(rcFile) {
|
|
3765
|
+
const content = readRcFile(rcFile);
|
|
3766
|
+
return content !== null && findChronicleHook(content) !== null;
|
|
3767
|
+
}
|
|
3768
|
+
function removeChronicleHook(rcFile) {
|
|
3769
|
+
const content = readRcFile(rcFile);
|
|
3770
|
+
const region = content === null ? null : findChronicleHook(content);
|
|
3771
|
+
if (content === null || !region) return false;
|
|
3772
|
+
backUp(rcFile);
|
|
3773
|
+
import_fs17.default.writeFileSync(rcFile, withoutRegion(content, region));
|
|
3774
|
+
return true;
|
|
3775
|
+
}
|
|
3776
|
+
function shellHookHealth(shell) {
|
|
3777
|
+
const installed = getInstalledHookBlock(shell.rcFile);
|
|
3778
|
+
const currentVersion = SHELL_HOOK_VERSIONS[shell.name] ?? 1;
|
|
3779
|
+
return {
|
|
3780
|
+
isOutdated: installed !== null && installedHookVersion(installed) < currentVersion,
|
|
3781
|
+
hasChronicleHook: hasChronicleHook(shell.rcFile)
|
|
3782
|
+
};
|
|
3576
3783
|
}
|
|
3577
3784
|
function resolveWorkscribeBinary() {
|
|
3578
3785
|
try {
|
|
@@ -3651,7 +3858,7 @@ function removeFishCompletions() {
|
|
|
3651
3858
|
import_fs17.default.rmSync(FISH_COMPLETIONS_FILE, { force: true });
|
|
3652
3859
|
return true;
|
|
3653
3860
|
}
|
|
3654
|
-
var import_fs17, import_path14, import_os3, import_child_process3, FISH_COMPLETIONS_DIR, HOOK_START, HOOK_END,
|
|
3861
|
+
var import_fs17, import_path14, import_os3, import_child_process3, FISH_COMPLETIONS_DIR, HOOK_START, HOOK_END, PREAMBLE_LINE, HOOK_VERSION_LINE, SHELL_HOOK_VERSIONS, CHRONICLE_START, CHRONICLE_END, CHRONICLE_PREAMBLE_LINE, FISH_COMPLETIONS_FILE;
|
|
3655
3862
|
var init_shell_hook = __esm({
|
|
3656
3863
|
"src/capture/shell-hook.ts"() {
|
|
3657
3864
|
"use strict";
|
|
@@ -3668,7 +3875,12 @@ var init_shell_hook = __esm({
|
|
|
3668
3875
|
FISH_COMPLETIONS_DIR = import_path14.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
3669
3876
|
HOOK_START = "# workscribe:hook:start";
|
|
3670
3877
|
HOOK_END = "# workscribe:hook:end";
|
|
3671
|
-
|
|
3878
|
+
PREAMBLE_LINE = /^# (?:Workscribe — developer work journal|Do not edit this (?:block|file) manually\. Managed by `workscribe init`\.)$/;
|
|
3879
|
+
HOOK_VERSION_LINE = /^# workscribe:hook-version (\d+)$/m;
|
|
3880
|
+
SHELL_HOOK_VERSIONS = Object.freeze({ zsh: 2, bash: 2, fish: 1 });
|
|
3881
|
+
CHRONICLE_START = "# chronicle:hook:start";
|
|
3882
|
+
CHRONICLE_END = "# chronicle:hook:end";
|
|
3883
|
+
CHRONICLE_PREAMBLE_LINE = /^# (?:Chronicle — developer work journal|Do not edit this block manually\. Managed by `chronicle init`\.)$/;
|
|
3672
3884
|
FISH_COMPLETIONS_FILE = import_path14.default.join(FISH_COMPLETIONS_DIR, "workscribe.fish");
|
|
3673
3885
|
}
|
|
3674
3886
|
});
|
|
@@ -4305,10 +4517,10 @@ function installIntoFolder(options) {
|
|
|
4305
4517
|
const outcome = installSkill({
|
|
4306
4518
|
sourceDir: options.sourceDir,
|
|
4307
4519
|
skillsFolder: target.skillsFolder,
|
|
4308
|
-
version: "3.
|
|
4520
|
+
version: "3.1.0",
|
|
4309
4521
|
force: options.force
|
|
4310
4522
|
});
|
|
4311
|
-
return describeInstallOutcome({ ...target, outcome, version: "3.
|
|
4523
|
+
return describeInstallOutcome({ ...target, outcome, version: "3.1.0" });
|
|
4312
4524
|
} catch (error) {
|
|
4313
4525
|
return describeInstallFailure({ ...target, error });
|
|
4314
4526
|
}
|
|
@@ -4327,8 +4539,8 @@ function removeSkills(agents, options) {
|
|
|
4327
4539
|
}
|
|
4328
4540
|
function formatAgentSkillLine(agent, homeDir = import_os6.default.homedir()) {
|
|
4329
4541
|
const location = displayPath(import_path19.default.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir);
|
|
4330
|
-
const installState = readInstallState(import_path19.default.join(homeDir, agent.skillsFolder), "3.
|
|
4331
|
-
const status = describeSkillStatus(installState, "3.
|
|
4542
|
+
const installState = readInstallState(import_path19.default.join(homeDir, agent.skillsFolder), "3.1.0");
|
|
4543
|
+
const status = describeSkillStatus(installState, "3.1.0");
|
|
4332
4544
|
return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${import_chalk2.default.dim(location)}`;
|
|
4333
4545
|
}
|
|
4334
4546
|
function formatUndetectedAgentLine(agent) {
|
|
@@ -4424,16 +4636,6 @@ var init_legacy_ai_cleanup = __esm({
|
|
|
4424
4636
|
});
|
|
4425
4637
|
|
|
4426
4638
|
// src/cli/init.ts
|
|
4427
|
-
function detectShell() {
|
|
4428
|
-
const shell = process.env.SHELL ?? "";
|
|
4429
|
-
if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path20.default.join(import_os7.default.homedir(), ".zshrc") };
|
|
4430
|
-
if (shell.includes("bash")) return { name: "bash", rcFile: import_path20.default.join(import_os7.default.homedir(), ".bashrc") };
|
|
4431
|
-
if (shell.includes("fish")) {
|
|
4432
|
-
const confDir = import_path20.default.join(import_os7.default.homedir(), ".config", "fish", "conf.d");
|
|
4433
|
-
return { name: "fish", rcFile: import_path20.default.join(confDir, "workscribe.fish") };
|
|
4434
|
-
}
|
|
4435
|
-
return null;
|
|
4436
|
-
}
|
|
4437
4639
|
function step(current, total, label) {
|
|
4438
4640
|
log.plain(import_chalk3.default.bold(`
|
|
4439
4641
|
[${current}/${total}] ${label}`));
|
|
@@ -4479,6 +4681,21 @@ async function runTelemetryStep(config) {
|
|
|
4479
4681
|
config.telemetry.enabled = telemetryEnabled;
|
|
4480
4682
|
config.telemetry.installId = ensureInstallId(config);
|
|
4481
4683
|
}
|
|
4684
|
+
async function offerChronicleCleanup(shell) {
|
|
4685
|
+
if (!hasChronicleHook(shell.rcFile)) return;
|
|
4686
|
+
const { removeChronicle } = await (0, import_prompts2.default)({
|
|
4687
|
+
type: "confirm",
|
|
4688
|
+
name: "removeChronicle",
|
|
4689
|
+
message: `An old Chronicle hook in ${shell.rcFile} errors at every prompt. Remove it?`,
|
|
4690
|
+
initial: true
|
|
4691
|
+
});
|
|
4692
|
+
if (!removeChronicle) {
|
|
4693
|
+
log.info("Chronicle hook left in place");
|
|
4694
|
+
return;
|
|
4695
|
+
}
|
|
4696
|
+
removeChronicleHook(shell.rcFile);
|
|
4697
|
+
log.success(`Removed the old Chronicle hook from ${shell.rcFile}`);
|
|
4698
|
+
}
|
|
4482
4699
|
async function runShellHookStep(program) {
|
|
4483
4700
|
step(3, TOTAL_STEPS, "Installing shell hook");
|
|
4484
4701
|
const shell = detectShell();
|
|
@@ -4527,6 +4744,7 @@ async function runShellHookStep(program) {
|
|
|
4527
4744
|
} else {
|
|
4528
4745
|
log.info(`Shell hook up to date in ${shell.rcFile}`);
|
|
4529
4746
|
}
|
|
4747
|
+
await offerChronicleCleanup(shell);
|
|
4530
4748
|
if (isCompletionsInstalled(shell)) {
|
|
4531
4749
|
await refreshCompletions(shell, program);
|
|
4532
4750
|
} else {
|
|
@@ -4614,10 +4832,10 @@ async function runAIToolsStep() {
|
|
|
4614
4832
|
}
|
|
4615
4833
|
async function offerSkillInstall(group, sourceDir) {
|
|
4616
4834
|
const target = describeTarget(group);
|
|
4617
|
-
const state = readInstallState(target.skillsFolder, "3.
|
|
4835
|
+
const state = readInstallState(target.skillsFolder, "3.1.0");
|
|
4618
4836
|
const knownOutcome = outcomeWithoutPrompt(state);
|
|
4619
4837
|
if (knownOutcome) {
|
|
4620
|
-
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "3.
|
|
4838
|
+
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "3.1.0" }));
|
|
4621
4839
|
return;
|
|
4622
4840
|
}
|
|
4623
4841
|
const { installSkill: shouldInstall } = await (0, import_prompts2.default)({
|
|
@@ -4720,13 +4938,12 @@ function registerInit(program) {
|
|
|
4720
4938
|
printNextSteps(shell);
|
|
4721
4939
|
});
|
|
4722
4940
|
}
|
|
4723
|
-
var import_fs22, import_path20,
|
|
4941
|
+
var import_fs22, import_path20, import_prompts2, import_chalk3, TOTAL_STEPS;
|
|
4724
4942
|
var init_init = __esm({
|
|
4725
4943
|
"src/cli/init.ts"() {
|
|
4726
4944
|
"use strict";
|
|
4727
4945
|
import_fs22 = __toESM(require("fs"));
|
|
4728
4946
|
import_path20 = __toESM(require("path"));
|
|
4729
|
-
import_os7 = __toESM(require("os"));
|
|
4730
4947
|
import_prompts2 = __toESM(require("prompts"));
|
|
4731
4948
|
import_chalk3 = __toESM(require("chalk"));
|
|
4732
4949
|
init_paths();
|
|
@@ -4823,14 +5040,14 @@ var init_format = __esm({
|
|
|
4823
5040
|
|
|
4824
5041
|
// src/cli/notices.ts
|
|
4825
5042
|
function printLegacyAiNotice(config) {
|
|
4826
|
-
const notice = describeLegacyAiSettings(config, displayPath(CONFIG_PATH,
|
|
5043
|
+
const notice = describeLegacyAiSettings(config, displayPath(CONFIG_PATH, import_os7.default.homedir()));
|
|
4827
5044
|
if (notice) log.notice(notice);
|
|
4828
5045
|
}
|
|
4829
|
-
var
|
|
5046
|
+
var import_os7;
|
|
4830
5047
|
var init_notices = __esm({
|
|
4831
5048
|
"src/cli/notices.ts"() {
|
|
4832
5049
|
"use strict";
|
|
4833
|
-
|
|
5050
|
+
import_os7 = __toESM(require("os"));
|
|
4834
5051
|
init_logger();
|
|
4835
5052
|
init_paths();
|
|
4836
5053
|
init_legacy_ai();
|
|
@@ -5595,6 +5812,117 @@ var init_ticket_refs = __esm({
|
|
|
5595
5812
|
}
|
|
5596
5813
|
});
|
|
5597
5814
|
|
|
5815
|
+
// src/db/jobs/scrub-secrets.ts
|
|
5816
|
+
function scrubValue(value, command) {
|
|
5817
|
+
if (command.after.includes(value)) return value;
|
|
5818
|
+
const redacted = redact(value);
|
|
5819
|
+
if (redacted !== value) return redacted;
|
|
5820
|
+
return command.before.includes(value) ? REDACTED_TEXT : value;
|
|
5821
|
+
}
|
|
5822
|
+
function scrubStrings(value, command) {
|
|
5823
|
+
if (typeof value === "string") return scrubValue(value, command);
|
|
5824
|
+
if (Array.isArray(value)) return value.map((item) => scrubStrings(item, command));
|
|
5825
|
+
if (value === null || typeof value !== "object") return value;
|
|
5826
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubStrings(item, command)]));
|
|
5827
|
+
}
|
|
5828
|
+
function hasHint(value) {
|
|
5829
|
+
return typeof value?.hint === "string";
|
|
5830
|
+
}
|
|
5831
|
+
function scrubMetadata(metadata, command) {
|
|
5832
|
+
if (metadata === null) return null;
|
|
5833
|
+
let parsed;
|
|
5834
|
+
try {
|
|
5835
|
+
parsed = JSON.parse(metadata);
|
|
5836
|
+
} catch {
|
|
5837
|
+
return redact(metadata);
|
|
5838
|
+
}
|
|
5839
|
+
const scrubbed = scrubStrings(parsed, command);
|
|
5840
|
+
if (command.after !== command.before && hasHint(scrubbed)) scrubbed.hint = commandHint(command.after);
|
|
5841
|
+
const rewritten = JSON.stringify(scrubbed);
|
|
5842
|
+
return rewritten === JSON.stringify(parsed) ? metadata : rewritten;
|
|
5843
|
+
}
|
|
5844
|
+
function scrubEvent({ command, metadata }) {
|
|
5845
|
+
const scrubbedCommand = redact(command);
|
|
5846
|
+
const scrubbedMetadata = scrubMetadata(metadata, { before: command, after: scrubbedCommand });
|
|
5847
|
+
if (scrubbedCommand === command && scrubbedMetadata === metadata) return null;
|
|
5848
|
+
return { command: scrubbedCommand, metadata: scrubbedMetadata };
|
|
5849
|
+
}
|
|
5850
|
+
function readEvents(db, afterId, limit) {
|
|
5851
|
+
return db.prepare(
|
|
5852
|
+
`SELECT id, command, metadata FROM events
|
|
5853
|
+
WHERE id > ? AND command IS NOT file_path
|
|
5854
|
+
ORDER BY id LIMIT ?`
|
|
5855
|
+
).all(afterId, limit);
|
|
5856
|
+
}
|
|
5857
|
+
function runBatch5(db, afterId, limit) {
|
|
5858
|
+
const events = readEvents(db, afterId, limit);
|
|
5859
|
+
if (events.length === 0) return { lastId: null, issues: [] };
|
|
5860
|
+
const update = db.prepare("UPDATE events SET command = ?, metadata = ? WHERE id = ?");
|
|
5861
|
+
for (const event of events) {
|
|
5862
|
+
const scrubbed = scrubEvent(event);
|
|
5863
|
+
if (scrubbed) update.run(scrubbed.command, scrubbed.metadata, event.id);
|
|
5864
|
+
}
|
|
5865
|
+
return { lastId: events[events.length - 1].id, issues: [] };
|
|
5866
|
+
}
|
|
5867
|
+
function verify4(db) {
|
|
5868
|
+
let checked = 0;
|
|
5869
|
+
let unresolved = 0;
|
|
5870
|
+
let afterId = 0;
|
|
5871
|
+
for (; ; ) {
|
|
5872
|
+
const events = readEvents(db, afterId, VERIFY_PAGE_SIZE);
|
|
5873
|
+
if (events.length === 0) break;
|
|
5874
|
+
checked += events.length;
|
|
5875
|
+
unresolved += events.filter((event) => scrubEvent(event) !== null).length;
|
|
5876
|
+
afterId = events[events.length - 1].id;
|
|
5877
|
+
}
|
|
5878
|
+
const notes = unresolved > 0 ? [`events that would still change: ${unresolved}`] : [];
|
|
5879
|
+
return { checked, resolved: checked - unresolved, unresolved, hasPassed: true, notes };
|
|
5880
|
+
}
|
|
5881
|
+
function cleanUpFile(db) {
|
|
5882
|
+
db.exec(`PRAGMA busy_timeout = ${CLEANUP_LOCK_WAIT_MS}`);
|
|
5883
|
+
try {
|
|
5884
|
+
if (getMetaValue(db, CLEANUP_KEY) === CLEANUP_NEEDS_VACUUM) {
|
|
5885
|
+
db.exec("VACUUM");
|
|
5886
|
+
setMetaValue(db, CLEANUP_KEY, CLEANUP_NEEDS_CHECKPOINT);
|
|
5887
|
+
}
|
|
5888
|
+
const result = db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
5889
|
+
if (result?.busy) {
|
|
5890
|
+
debugLog(`data job ${JOB_NAME}: file cleanup`, "left for later, a reader is busy");
|
|
5891
|
+
return;
|
|
5892
|
+
}
|
|
5893
|
+
deleteMetaValue(db, CLEANUP_KEY);
|
|
5894
|
+
} finally {
|
|
5895
|
+
db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
var JOB_NAME, VERIFY_PAGE_SIZE, CLEANUP_KEY, CLEANUP_NEEDS_VACUUM, CLEANUP_NEEDS_CHECKPOINT, CLEANUP_LOCK_WAIT_MS, scrubSecretsJob;
|
|
5899
|
+
var init_scrub_secrets = __esm({
|
|
5900
|
+
"src/db/jobs/scrub-secrets.ts"() {
|
|
5901
|
+
"use strict";
|
|
5902
|
+
init_transaction();
|
|
5903
|
+
init_store2();
|
|
5904
|
+
init_hint();
|
|
5905
|
+
init_redact();
|
|
5906
|
+
init_logger();
|
|
5907
|
+
JOB_NAME = "scrub-secrets";
|
|
5908
|
+
VERIFY_PAGE_SIZE = 500;
|
|
5909
|
+
CLEANUP_KEY = "scrub-secrets:file-cleanup";
|
|
5910
|
+
CLEANUP_NEEDS_VACUUM = "vacuum";
|
|
5911
|
+
CLEANUP_NEEDS_CHECKPOINT = "checkpoint";
|
|
5912
|
+
CLEANUP_LOCK_WAIT_MS = 200;
|
|
5913
|
+
scrubSecretsJob = {
|
|
5914
|
+
name: JOB_NAME,
|
|
5915
|
+
version: 1,
|
|
5916
|
+
description: "Remove secrets from stored commands and metadata",
|
|
5917
|
+
// Redacting what's already redacted changes nothing, so a rerun only needs the file cleaned again
|
|
5918
|
+
prepare: (db) => setMetaValue(db, CLEANUP_KEY, CLEANUP_NEEDS_VACUUM),
|
|
5919
|
+
runBatch: runBatch5,
|
|
5920
|
+
verify: verify4,
|
|
5921
|
+
cleanup: { isPending: (db) => getMetaValue(db, CLEANUP_KEY) !== null, run: cleanUpFile }
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5924
|
+
});
|
|
5925
|
+
|
|
5598
5926
|
// src/db/jobs/index.ts
|
|
5599
5927
|
var JOBS;
|
|
5600
5928
|
var init_jobs = __esm({
|
|
@@ -5605,10 +5933,12 @@ var init_jobs = __esm({
|
|
|
5605
5933
|
init_summary_anchors();
|
|
5606
5934
|
init_drop_old_sessions();
|
|
5607
5935
|
init_ticket_refs();
|
|
5936
|
+
init_scrub_secrets();
|
|
5608
5937
|
JOBS = [
|
|
5609
5938
|
createProjectDimensionsJob({ probeDirectory }),
|
|
5610
5939
|
summaryAnchorsJob,
|
|
5611
5940
|
ticketRefsJob,
|
|
5941
|
+
scrubSecretsJob,
|
|
5612
5942
|
dropOldSessionsJob
|
|
5613
5943
|
];
|
|
5614
5944
|
}
|
|
@@ -5773,9 +6103,22 @@ function recordFailure(db, job, message) {
|
|
|
5773
6103
|
debugLog(`data job ${job.name}: couldn't record the failure`, error);
|
|
5774
6104
|
}
|
|
5775
6105
|
}
|
|
6106
|
+
function runPendingCleanups(db, jobs) {
|
|
6107
|
+
for (const job of jobs) {
|
|
6108
|
+
if (!job.cleanup || !isJobDone(getJobRow(db, job.name), job)) continue;
|
|
6109
|
+
try {
|
|
6110
|
+
if (job.cleanup.isPending(db)) job.cleanup.run(db);
|
|
6111
|
+
} catch (error) {
|
|
6112
|
+
debugLog(`data job ${job.name}: cleanup`, error);
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
5776
6116
|
function runPendingJobs(db, jobs, options) {
|
|
5777
6117
|
const pending = jobs.filter((job) => !isJobDone(getJobRow(db, job.name), job));
|
|
5778
|
-
if (pending.length === 0)
|
|
6118
|
+
if (pending.length === 0) {
|
|
6119
|
+
runPendingCleanups(db, jobs);
|
|
6120
|
+
return { backup: "not-needed", jobs: [] };
|
|
6121
|
+
}
|
|
5779
6122
|
const backup = options.ensureBackup();
|
|
5780
6123
|
if (backup !== "ready") return { backup, jobs: [] };
|
|
5781
6124
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -5792,6 +6135,7 @@ function runPendingJobs(db, jobs, options) {
|
|
|
5792
6135
|
results.push(result);
|
|
5793
6136
|
if (result.outcome !== "done") break;
|
|
5794
6137
|
}
|
|
6138
|
+
runPendingCleanups(db, jobs);
|
|
5795
6139
|
return { backup: "ready", jobs: results };
|
|
5796
6140
|
}
|
|
5797
6141
|
var DEFAULT_BATCH_SIZE, BATCH_LOCK_WAIT_MS, NEWER_VERSION_ERROR, SupersededError;
|
|
@@ -5935,6 +6279,23 @@ function formatDataLines(status) {
|
|
|
5935
6279
|
if (status.latestBackup) lines.push(import_chalk4.default.dim(` backup: ${status.latestBackup}`));
|
|
5936
6280
|
return lines;
|
|
5937
6281
|
}
|
|
6282
|
+
function printShellHookWarnings() {
|
|
6283
|
+
const shell = detectShell();
|
|
6284
|
+
if (!shell) return;
|
|
6285
|
+
const health = shellHookHealth(shell);
|
|
6286
|
+
if (health.isOutdated) {
|
|
6287
|
+
log.plain("");
|
|
6288
|
+
log.plain(
|
|
6289
|
+
` ${import_chalk4.default.yellow("\u26A0")} Your ${shell.name} hook is from an older version and may not record terminal commands.`
|
|
6290
|
+
);
|
|
6291
|
+
log.plain(import_chalk4.default.dim(" Run workscribe init to update it, then open a new terminal"));
|
|
6292
|
+
}
|
|
6293
|
+
if (health.hasChronicleHook) {
|
|
6294
|
+
log.plain("");
|
|
6295
|
+
log.plain(` ${import_chalk4.default.yellow("\u26A0")} An old Chronicle hook in ${shell.rcFile} errors at every prompt.`);
|
|
6296
|
+
log.plain(import_chalk4.default.dim(" Run workscribe init to remove it"));
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
5938
6299
|
function registerStatus(program) {
|
|
5939
6300
|
program.command("status").description("Show capture status and event counts").action(() => {
|
|
5940
6301
|
const config = readConfig();
|
|
@@ -5976,6 +6337,7 @@ function registerStatus(program) {
|
|
|
5976
6337
|
` ${import_chalk4.default.yellow("!")} ${import_chalk4.default.yellow(String(pendingAlerts.length))} pending security alert${pendingAlerts.length === 1 ? "" : "s"}` + import_chalk4.default.dim(" (run: workscribe alerts)")
|
|
5977
6338
|
);
|
|
5978
6339
|
}
|
|
6340
|
+
printShellHookWarnings();
|
|
5979
6341
|
const hookHealth = checkHookHealth();
|
|
5980
6342
|
if (hookHealth.installed && !hookHealth.pinned) {
|
|
5981
6343
|
log.plain("");
|
|
@@ -6034,6 +6396,7 @@ var init_status = __esm({
|
|
|
6034
6396
|
init_paths();
|
|
6035
6397
|
init_alerts2();
|
|
6036
6398
|
init_claude_code();
|
|
6399
|
+
init_shell_hook();
|
|
6037
6400
|
init_detect();
|
|
6038
6401
|
init_actions();
|
|
6039
6402
|
init_notices();
|
|
@@ -6455,7 +6818,7 @@ var init_heading = __esm({
|
|
|
6455
6818
|
|
|
6456
6819
|
// src/summary/payload.ts
|
|
6457
6820
|
function locationContextFrom(config) {
|
|
6458
|
-
return { projects: config.projects ?? {}, homeDir:
|
|
6821
|
+
return { projects: config.projects ?? {}, homeDir: import_os8.default.homedir() };
|
|
6459
6822
|
}
|
|
6460
6823
|
function gitMovesField(row) {
|
|
6461
6824
|
if (!MENTIONS_GIT.test(row.command)) return {};
|
|
@@ -6536,11 +6899,11 @@ function threadToPayload(thread, block, options = {}) {
|
|
|
6536
6899
|
alongside: block.threads.filter((other) => other.id !== thread.id).map((other) => sessionLocation(other, options.context) ?? UNKNOWN_LOCATION)
|
|
6537
6900
|
};
|
|
6538
6901
|
}
|
|
6539
|
-
var
|
|
6902
|
+
var import_os8, import_path25, DEFAULT_LOCATION_CONTEXT, MENTIONS_GIT;
|
|
6540
6903
|
var init_payload = __esm({
|
|
6541
6904
|
"src/summary/payload.ts"() {
|
|
6542
6905
|
"use strict";
|
|
6543
|
-
|
|
6906
|
+
import_os8 = __toESM(require("os"));
|
|
6544
6907
|
import_path25 = __toESM(require("path"));
|
|
6545
6908
|
init_format();
|
|
6546
6909
|
init_event_metadata();
|
|
@@ -6550,7 +6913,7 @@ var init_payload = __esm({
|
|
|
6550
6913
|
init_grouper();
|
|
6551
6914
|
init_blocks();
|
|
6552
6915
|
init_heading();
|
|
6553
|
-
DEFAULT_LOCATION_CONTEXT = { projects: {}, homeDir:
|
|
6916
|
+
DEFAULT_LOCATION_CONTEXT = { projects: {}, homeDir: import_os8.default.homedir() };
|
|
6554
6917
|
MENTIONS_GIT = /(^|[\s;&|(])git\s/;
|
|
6555
6918
|
}
|
|
6556
6919
|
});
|
|
@@ -7865,18 +8228,18 @@ async function postSummary({
|
|
|
7865
8228
|
}
|
|
7866
8229
|
try {
|
|
7867
8230
|
status.info("Posting to webhook\u2026");
|
|
7868
|
-
const body = isSlackWebhookUrl(webhookUrl) ? formatForSlack(summary,
|
|
8231
|
+
const body = isSlackWebhookUrl(webhookUrl) ? formatForSlack(summary, import_os9.default.userInfo().username) : summary;
|
|
7869
8232
|
await postToWebhook(webhookUrl, body);
|
|
7870
8233
|
status.success("Posted.");
|
|
7871
8234
|
} catch (error) {
|
|
7872
8235
|
log.error(`Webhook failed: ${error.message}`);
|
|
7873
8236
|
}
|
|
7874
8237
|
}
|
|
7875
|
-
var
|
|
8238
|
+
var import_os9;
|
|
7876
8239
|
var init_webhook2 = __esm({
|
|
7877
8240
|
"src/summary/webhook.ts"() {
|
|
7878
8241
|
"use strict";
|
|
7879
|
-
|
|
8242
|
+
import_os9 = __toESM(require("os"));
|
|
7880
8243
|
init_logger();
|
|
7881
8244
|
init_webhook();
|
|
7882
8245
|
init_slack();
|
|
@@ -8028,6 +8391,28 @@ var init_day = __esm({
|
|
|
8028
8391
|
}
|
|
8029
8392
|
});
|
|
8030
8393
|
|
|
8394
|
+
// src/summary/output-format.ts
|
|
8395
|
+
function keepsStdoutClean(format) {
|
|
8396
|
+
return format === "json" || format === "text";
|
|
8397
|
+
}
|
|
8398
|
+
var SUMMARY_FORMATS;
|
|
8399
|
+
var init_output_format = __esm({
|
|
8400
|
+
"src/summary/output-format.ts"() {
|
|
8401
|
+
"use strict";
|
|
8402
|
+
SUMMARY_FORMATS = ["markdown", "json", "text"];
|
|
8403
|
+
}
|
|
8404
|
+
});
|
|
8405
|
+
|
|
8406
|
+
// src/summary/plain-text.ts
|
|
8407
|
+
function toPlainText(markdown) {
|
|
8408
|
+
return markdown.replace(/^#{1,6} +/gm, "").replace(/^([ \t]*)[*•] +/gm, "$1- ").replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/`([^`\n]+)`/g, "$1").replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, "$1 ($2)").trim();
|
|
8409
|
+
}
|
|
8410
|
+
var init_plain_text = __esm({
|
|
8411
|
+
"src/summary/plain-text.ts"() {
|
|
8412
|
+
"use strict";
|
|
8413
|
+
}
|
|
8414
|
+
});
|
|
8415
|
+
|
|
8031
8416
|
// src/cli/day-notes.ts
|
|
8032
8417
|
function printExcludedFooter(sessionPayloads, excluded) {
|
|
8033
8418
|
const excludedCounts = countExcludedEvents(sessionPayloads, excluded);
|
|
@@ -8103,10 +8488,11 @@ async function runWeeklySummary({ options, config, provider, loadDay }) {
|
|
|
8103
8488
|
}
|
|
8104
8489
|
const payload = { weekStart, weekEnd: today, by: options.by ?? "project", days };
|
|
8105
8490
|
const summary = await writeWeeklySummary(provider, payload);
|
|
8106
|
-
const
|
|
8107
|
-
if (
|
|
8491
|
+
const format = options.format ?? config.summary?.format;
|
|
8492
|
+
if (format === "json") log.plain(weekJson(payload, config.summary?.includeCategories ?? []));
|
|
8493
|
+
else if (format === "text") log.plain(toPlainText(summary));
|
|
8108
8494
|
else printSummary(summary);
|
|
8109
|
-
await postSummary({ options, config, summary, statusToStderr:
|
|
8495
|
+
await postSummary({ options, config, summary, statusToStderr: keepsStdoutClean(format) });
|
|
8110
8496
|
}
|
|
8111
8497
|
var WEEK_GIT_BUDGET_MS;
|
|
8112
8498
|
var init_weekly = __esm({
|
|
@@ -8117,6 +8503,8 @@ var init_weekly = __esm({
|
|
|
8117
8503
|
init_payload();
|
|
8118
8504
|
init_webhook2();
|
|
8119
8505
|
init_day_commits();
|
|
8506
|
+
init_output_format();
|
|
8507
|
+
init_plain_text();
|
|
8120
8508
|
init_week();
|
|
8121
8509
|
WEEK_GIT_BUDGET_MS = DAY_GIT_BUDGET_MS;
|
|
8122
8510
|
}
|
|
@@ -8132,14 +8520,24 @@ function parseWeekLayout(value) {
|
|
|
8132
8520
|
if (!layout) fail("--by must be project or day");
|
|
8133
8521
|
return layout;
|
|
8134
8522
|
}
|
|
8523
|
+
function addSummaryFormatOption(command) {
|
|
8524
|
+
return command.option("--format <format>", "print as markdown, json or text, overriding summary.format");
|
|
8525
|
+
}
|
|
8526
|
+
function parseSummaryFormat(value) {
|
|
8527
|
+
if (value === void 0) return void 0;
|
|
8528
|
+
const format = SUMMARY_FORMATS.find((candidate) => candidate === value);
|
|
8529
|
+
if (!format) fail("--format must be markdown, json or text");
|
|
8530
|
+
return format;
|
|
8531
|
+
}
|
|
8135
8532
|
async function runWeekCommand(options) {
|
|
8136
8533
|
const by = parseWeekLayout(options.by);
|
|
8534
|
+
const format = parseSummaryFormat(options.format);
|
|
8137
8535
|
const config = readConfig();
|
|
8138
8536
|
printLegacyAiNotice(config);
|
|
8139
8537
|
requireThreadData();
|
|
8140
8538
|
try {
|
|
8141
8539
|
await runWeeklySummary({
|
|
8142
|
-
options: { ...options, by },
|
|
8540
|
+
options: { ...options, by, format },
|
|
8143
8541
|
config,
|
|
8144
8542
|
provider: createSummaryWriter(config),
|
|
8145
8543
|
loadDay: loadDayThreads
|
|
@@ -8150,13 +8548,14 @@ async function runWeekCommand(options) {
|
|
|
8150
8548
|
}
|
|
8151
8549
|
function registerWeek(program) {
|
|
8152
8550
|
const weekCommand = program.command("week").description("Summarize the current week (Mon\u2013today) \u2014 the same as summary --week").option("--fresh", "accepted for 1.x scripts \u2014 the week is always rendered fresh").option("--slack", "post to Slack webhook from config").option("--webhook <url>", "post to a custom webhook URL");
|
|
8153
|
-
addWeekLayoutOption(weekCommand).action(runWeekCommand);
|
|
8551
|
+
addSummaryFormatOption(addWeekLayoutOption(weekCommand)).action(runWeekCommand);
|
|
8154
8552
|
}
|
|
8155
8553
|
var WEEK_LAYOUTS;
|
|
8156
8554
|
var init_week2 = __esm({
|
|
8157
8555
|
"src/cli/week.ts"() {
|
|
8158
8556
|
"use strict";
|
|
8159
8557
|
init_config();
|
|
8558
|
+
init_output_format();
|
|
8160
8559
|
init_weekly();
|
|
8161
8560
|
init_writer();
|
|
8162
8561
|
init_notices();
|
|
@@ -8201,8 +8600,9 @@ function printCapturedEvents(sessionPayloads, excluded) {
|
|
|
8201
8600
|
}
|
|
8202
8601
|
function registerSummary(program) {
|
|
8203
8602
|
const summaryCommand = program.command("summary").description("Generate a work summary").option("--date <YYYY-MM-DD>", "date to summarize (default: today)").option("--week", "summarize the current week").option("--fresh", "ignore saved summaries and render the template").option("--verbose", "show all captured events, including those left out of the summary").option("--slack", "post summary to Slack webhook from config").option("--webhook <url>", "post summary to a custom webhook URL");
|
|
8204
|
-
addWeekLayoutOption(summaryCommand).action(async (options) => {
|
|
8603
|
+
addSummaryFormatOption(addWeekLayoutOption(summaryCommand)).action(async (options) => {
|
|
8205
8604
|
if (options.by !== void 0 && !options.week) fail("--by only applies to --week");
|
|
8605
|
+
const formatOption = parseSummaryFormat(options.format);
|
|
8206
8606
|
if (options.week) {
|
|
8207
8607
|
await runWeekCommand(options);
|
|
8208
8608
|
return;
|
|
@@ -8225,7 +8625,8 @@ function registerSummary(program) {
|
|
|
8225
8625
|
log.plain(`No activity captured for ${date}`);
|
|
8226
8626
|
return;
|
|
8227
8627
|
}
|
|
8228
|
-
const
|
|
8628
|
+
const format = formatOption ?? config.summary?.format;
|
|
8629
|
+
const keepsStdoutToOutput = keepsStdoutClean(format);
|
|
8229
8630
|
let day;
|
|
8230
8631
|
try {
|
|
8231
8632
|
day = await renderDay({ date, blocks, config, provider, fresh: options.fresh });
|
|
@@ -8236,11 +8637,13 @@ function registerSummary(program) {
|
|
|
8236
8637
|
const { sessionPayloads, saved, fullSummary, needsGeneration } = day;
|
|
8237
8638
|
const excluded = buildExcludedSet(config.summary?.includeCategories ?? []);
|
|
8238
8639
|
if (needsGeneration && options.verbose) {
|
|
8239
|
-
if (
|
|
8640
|
+
if (keepsStdoutToOutput) log.notice(`--verbose is skipped with the ${format} format, to keep the output clean`);
|
|
8240
8641
|
else printCapturedEvents(sessionPayloads, excluded);
|
|
8241
8642
|
}
|
|
8242
|
-
if (
|
|
8643
|
+
if (format === "json") {
|
|
8243
8644
|
log.plain(toJsonOutput(day, date));
|
|
8645
|
+
} else if (format === "text") {
|
|
8646
|
+
log.plain(toPlainText(fullSummary));
|
|
8244
8647
|
} else {
|
|
8245
8648
|
log.plain("");
|
|
8246
8649
|
log.plain(fullSummary);
|
|
@@ -8248,7 +8651,7 @@ function registerSummary(program) {
|
|
|
8248
8651
|
printSavedNote(saved);
|
|
8249
8652
|
if (needsGeneration) printExcludedFooter(sessionPayloads, excluded);
|
|
8250
8653
|
}
|
|
8251
|
-
await postSummary({ options, config, summary: fullSummary, statusToStderr:
|
|
8654
|
+
await postSummary({ options, config, summary: fullSummary, statusToStderr: keepsStdoutToOutput });
|
|
8252
8655
|
});
|
|
8253
8656
|
}
|
|
8254
8657
|
var import_chalk8;
|
|
@@ -8265,6 +8668,8 @@ var init_summary = __esm({
|
|
|
8265
8668
|
init_notices();
|
|
8266
8669
|
init_webhook2();
|
|
8267
8670
|
init_day();
|
|
8671
|
+
init_output_format();
|
|
8672
|
+
init_plain_text();
|
|
8268
8673
|
init_day_notes();
|
|
8269
8674
|
init_thread_day();
|
|
8270
8675
|
init_week2();
|
|
@@ -8560,12 +8965,15 @@ var init_clear = __esm({
|
|
|
8560
8965
|
|
|
8561
8966
|
// src/cli/uninstall.ts
|
|
8562
8967
|
function completionRcFiles() {
|
|
8563
|
-
return [".zshrc", ".bashrc", ".bash_profile"].map((file) => import_path28.default.join(
|
|
8968
|
+
return [".zshrc", ".bashrc", ".bash_profile"].map((file) => import_path28.default.join(import_os10.default.homedir(), file));
|
|
8564
8969
|
}
|
|
8565
8970
|
function removeCompletionsAndToolHooks() {
|
|
8566
8971
|
for (const rcFile of completionRcFiles()) {
|
|
8567
8972
|
if (removeCompletions(rcFile)) log.success(`Shell completions removed from ${rcFile}`);
|
|
8568
8973
|
}
|
|
8974
|
+
for (const rcFile of CHRONICLE_RC_FILES.map((file) => import_path28.default.join(import_os10.default.homedir(), file))) {
|
|
8975
|
+
if (removeChronicleHook(rcFile)) log.success(`Old Chronicle hook removed from ${rcFile}`);
|
|
8976
|
+
}
|
|
8569
8977
|
if (removeFishCompletions()) log.success("Fish completions removed");
|
|
8570
8978
|
const outcome = claudeCodeIntegration.uninstall?.();
|
|
8571
8979
|
if (outcome === "removed") log.success("Claude Code session hook removed from ~/.claude/settings.json");
|
|
@@ -8577,10 +8985,10 @@ function removeCompletionsAndToolHooks() {
|
|
|
8577
8985
|
}
|
|
8578
8986
|
function detectShellRcFiles() {
|
|
8579
8987
|
const candidates = [
|
|
8580
|
-
{ name: "zsh", rcFile: import_path28.default.join(
|
|
8581
|
-
{ name: "bash", rcFile: import_path28.default.join(
|
|
8582
|
-
{ name: "bash", rcFile: import_path28.default.join(
|
|
8583
|
-
{ name: "fish", rcFile: import_path28.default.join(
|
|
8988
|
+
{ name: "zsh", rcFile: import_path28.default.join(import_os10.default.homedir(), ".zshrc") },
|
|
8989
|
+
{ name: "bash", rcFile: import_path28.default.join(import_os10.default.homedir(), ".bashrc") },
|
|
8990
|
+
{ name: "bash", rcFile: import_path28.default.join(import_os10.default.homedir(), ".bash_profile") },
|
|
8991
|
+
{ name: "fish", rcFile: import_path28.default.join(import_os10.default.homedir(), ".config", "fish", "conf.d", "workscribe.fish") }
|
|
8584
8992
|
];
|
|
8585
8993
|
return candidates.filter((candidate) => import_fs28.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
8586
8994
|
}
|
|
@@ -8637,13 +9045,13 @@ function registerUninstall(program) {
|
|
|
8637
9045
|
log.plain("");
|
|
8638
9046
|
});
|
|
8639
9047
|
}
|
|
8640
|
-
var import_fs28, import_path28,
|
|
9048
|
+
var import_fs28, import_path28, import_os10, import_prompts4, import_chalk11, CHRONICLE_RC_FILES;
|
|
8641
9049
|
var init_uninstall = __esm({
|
|
8642
9050
|
"src/cli/uninstall.ts"() {
|
|
8643
9051
|
"use strict";
|
|
8644
9052
|
import_fs28 = __toESM(require("fs"));
|
|
8645
9053
|
import_path28 = __toESM(require("path"));
|
|
8646
|
-
|
|
9054
|
+
import_os10 = __toESM(require("os"));
|
|
8647
9055
|
import_prompts4 = __toESM(require("prompts"));
|
|
8648
9056
|
import_chalk11 = __toESM(require("chalk"));
|
|
8649
9057
|
init_paths();
|
|
@@ -8652,6 +9060,7 @@ var init_uninstall = __esm({
|
|
|
8652
9060
|
init_logger();
|
|
8653
9061
|
init_agents();
|
|
8654
9062
|
init_actions();
|
|
9063
|
+
CHRONICLE_RC_FILES = [".zshrc", ".bashrc"];
|
|
8655
9064
|
}
|
|
8656
9065
|
});
|
|
8657
9066
|
|
|
@@ -8884,7 +9293,8 @@ var init_profile = __esm({
|
|
|
8884
9293
|
function registerNote(program) {
|
|
8885
9294
|
program.command("note <text>").description("Attach a free-text annotation to the current session").action((text) => {
|
|
8886
9295
|
const directory = currentDirectory();
|
|
8887
|
-
const
|
|
9296
|
+
const config = readConfig();
|
|
9297
|
+
const context = getRepoContext(directory, config);
|
|
8888
9298
|
const { repoName, branch } = context;
|
|
8889
9299
|
insertEvent({
|
|
8890
9300
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8895,7 +9305,7 @@ function registerNote(program) {
|
|
|
8895
9305
|
repo_name: repoName,
|
|
8896
9306
|
branch,
|
|
8897
9307
|
category: "note",
|
|
8898
|
-
metadata: JSON.stringify({ text }),
|
|
9308
|
+
metadata: JSON.stringify({ text: redact(text, config.redact.extra) }),
|
|
8899
9309
|
source: null,
|
|
8900
9310
|
file_path: null,
|
|
8901
9311
|
dimensions: eventDimensions(context)
|
|
@@ -8909,6 +9319,7 @@ var init_note = __esm({
|
|
|
8909
9319
|
init_db();
|
|
8910
9320
|
init_git();
|
|
8911
9321
|
init_dimensions3();
|
|
9322
|
+
init_redact();
|
|
8912
9323
|
init_config();
|
|
8913
9324
|
init_working_directory();
|
|
8914
9325
|
init_logger();
|
|
@@ -8938,11 +9349,11 @@ function registerBugReport(program) {
|
|
|
8938
9349
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8939
9350
|
const reportPath = import_path30.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
8940
9351
|
const systemInfo = [
|
|
8941
|
-
`Workscribe version : ${"3.
|
|
9352
|
+
`Workscribe version : ${"3.1.0"}`,
|
|
8942
9353
|
`Node.js version : ${process.version}`,
|
|
8943
9354
|
`Platform : ${process.platform}`,
|
|
8944
9355
|
`Architecture : ${process.arch}`,
|
|
8945
|
-
`OS release : ${
|
|
9356
|
+
`OS release : ${import_os11.default.release()}`,
|
|
8946
9357
|
`Config path : ${CONFIG_PATH}`,
|
|
8947
9358
|
`Data directory : ${WORKSCRIBE_DIR}`
|
|
8948
9359
|
].join("\n");
|
|
@@ -8969,7 +9380,7 @@ function registerBugReport(program) {
|
|
|
8969
9380
|
process.exit(1);
|
|
8970
9381
|
}
|
|
8971
9382
|
const params = new URLSearchParams({
|
|
8972
|
-
title: `Bug report \u2014 workscribe ${"3.
|
|
9383
|
+
title: `Bug report \u2014 workscribe ${"3.1.0"}`,
|
|
8973
9384
|
body: `<!-- Attach ${import_path30.default.basename(reportPath)} or paste the relevant sections below -->
|
|
8974
9385
|
|
|
8975
9386
|
**Describe the issue:**
|
|
@@ -8987,12 +9398,12 @@ function registerBugReport(program) {
|
|
|
8987
9398
|
log.plain("");
|
|
8988
9399
|
});
|
|
8989
9400
|
}
|
|
8990
|
-
var import_fs30,
|
|
9401
|
+
var import_fs30, import_os11, import_path30, import_chalk13, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
8991
9402
|
var init_bug_report = __esm({
|
|
8992
9403
|
"src/cli/bug-report.ts"() {
|
|
8993
9404
|
"use strict";
|
|
8994
9405
|
import_fs30 = __toESM(require("fs"));
|
|
8995
|
-
|
|
9406
|
+
import_os11 = __toESM(require("os"));
|
|
8996
9407
|
import_path30 = __toESM(require("path"));
|
|
8997
9408
|
import_chalk13 = __toESM(require("chalk"));
|
|
8998
9409
|
init_paths();
|
|
@@ -9060,7 +9471,7 @@ function reportRun(result) {
|
|
|
9060
9471
|
function registerUpgrade(program) {
|
|
9061
9472
|
program.command("upgrade").description("Bring your stored data up to date after updating Workscribe (backs it up first)").action(() => {
|
|
9062
9473
|
const announced = /* @__PURE__ */ new Set();
|
|
9063
|
-
const result = runDataJobs("3.
|
|
9474
|
+
const result = runDataJobs("3.1.0", {
|
|
9064
9475
|
onProgress: ({ job }) => {
|
|
9065
9476
|
if (announced.has(job.name)) return;
|
|
9066
9477
|
announced.add(job.name);
|
|
@@ -9550,7 +9961,7 @@ function isNewer(latest, current) {
|
|
|
9550
9961
|
return latestPatch > currentPatch;
|
|
9551
9962
|
}
|
|
9552
9963
|
function checkForUpdate() {
|
|
9553
|
-
const current = "3.
|
|
9964
|
+
const current = "3.1.0";
|
|
9554
9965
|
const cache = readCache();
|
|
9555
9966
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
9556
9967
|
process.stderr.write(
|
|
@@ -9573,8 +9984,8 @@ function checkForUpdate() {
|
|
|
9573
9984
|
function buildTelemetryPayload(config) {
|
|
9574
9985
|
return {
|
|
9575
9986
|
installId: config.telemetry.installId,
|
|
9576
|
-
cliVersion: "3.
|
|
9577
|
-
platform:
|
|
9987
|
+
cliVersion: "3.1.0",
|
|
9988
|
+
platform: import_os12.default.platform(),
|
|
9578
9989
|
nodeVersion: process.version
|
|
9579
9990
|
};
|
|
9580
9991
|
}
|
|
@@ -9598,12 +10009,12 @@ function pingTelemetry() {
|
|
|
9598
10009
|
debugLog("pingTelemetry", error);
|
|
9599
10010
|
});
|
|
9600
10011
|
}
|
|
9601
|
-
var import_fs31,
|
|
10012
|
+
var import_fs31, import_os12, CHECK_INTERVAL_MS;
|
|
9602
10013
|
var init_update_check = __esm({
|
|
9603
10014
|
"src/utils/update-check.ts"() {
|
|
9604
10015
|
"use strict";
|
|
9605
10016
|
import_fs31 = __toESM(require("fs"));
|
|
9606
|
-
|
|
10017
|
+
import_os12 = __toESM(require("os"));
|
|
9607
10018
|
init_paths();
|
|
9608
10019
|
init_config();
|
|
9609
10020
|
init_logger();
|
|
@@ -9651,10 +10062,10 @@ var require_main = __commonJS({
|
|
|
9651
10062
|
pingTelemetry();
|
|
9652
10063
|
}
|
|
9653
10064
|
if (shouldRunDataJobs(process.argv.slice(2))) {
|
|
9654
|
-
runDataJobsBeforeCommand(invokedCommand, "3.
|
|
10065
|
+
runDataJobsBeforeCommand(invokedCommand, "3.1.0");
|
|
9655
10066
|
}
|
|
9656
10067
|
var program = new import_commander.Command();
|
|
9657
|
-
program.name("workscribe").description("Automatic developer work journal").version("3.
|
|
10068
|
+
program.name("workscribe").description("Automatic developer work journal").version("3.1.0");
|
|
9658
10069
|
registerCapture(program);
|
|
9659
10070
|
registerHook(program);
|
|
9660
10071
|
registerInit(program);
|