@workscribe/cli 1.14.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1547 -986
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,6 +6,12 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
13
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14
|
+
};
|
|
9
15
|
var __copyProps = (to, from, except, desc) => {
|
|
10
16
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
17
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,74 +29,42 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
29
|
mod
|
|
24
30
|
));
|
|
25
31
|
|
|
26
|
-
// src/index.ts
|
|
27
|
-
var import_commander = require("commander");
|
|
28
|
-
|
|
29
|
-
// src/utils/node-version.ts
|
|
30
|
-
function detectVersionManager() {
|
|
31
|
-
if (process.env.NVM_DIR) return "nvm";
|
|
32
|
-
if (process.env.VOLTA_HOME) return "volta";
|
|
33
|
-
if (process.env.FNM_DIR) return "fnm";
|
|
34
|
-
if (process.env.MISE_DATA_DIR || process.env.MISE_SHELL) return "mise";
|
|
35
|
-
if (process.env.ASDF_DIR || process.env.ASDF_DATA_DIR) return "asdf";
|
|
36
|
-
if (process.env.NODENV_ROOT) return "nodenv";
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
var UPGRADE_COMMANDS = {
|
|
40
|
-
nvm: "nvm install 22 && nvm use 22",
|
|
41
|
-
volta: "volta install node@22",
|
|
42
|
-
fnm: "fnm install 22 && fnm use 22",
|
|
43
|
-
mise: "mise use --global node@22",
|
|
44
|
-
asdf: "asdf install nodejs 22 && asdf global nodejs 22",
|
|
45
|
-
nodenv: "nodenv install 22 && nodenv global 22"
|
|
46
|
-
};
|
|
47
|
-
function assertNodeVersion(nodeVersion) {
|
|
48
|
-
const major = Number(nodeVersion.split(".")[0]);
|
|
49
|
-
if (major < 22) {
|
|
50
|
-
const manager = detectVersionManager();
|
|
51
|
-
const upgradeHint = manager ? `Upgrade with ${manager}:
|
|
52
|
-
${UPGRADE_COMMANDS[manager]}` : "Download the latest version from https://nodejs.org";
|
|
53
|
-
process.stderr.write(`workscribe requires Node.js 22 or later (you have v${nodeVersion}).
|
|
54
|
-
|
|
55
|
-
${upgradeHint}
|
|
56
|
-
`);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/capture/index.ts
|
|
62
|
-
var import_fs10 = __toESM(require("fs"));
|
|
63
|
-
|
|
64
|
-
// src/db/index.ts
|
|
65
|
-
var import_node_sqlite = require("node:sqlite");
|
|
66
|
-
var import_fs2 = __toESM(require("fs"));
|
|
67
|
-
|
|
68
32
|
// src/utils/paths.ts
|
|
69
|
-
var import_path
|
|
70
|
-
var
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
33
|
+
var import_path, import_os, import_fs, LEGACY_DIR, USE_XDG, xdgDataDir, xdgConfigDir, xdgStateDir, REQUIRED_DIRS, WORKSCRIBE_DIR, DB_PATH, CONFIG_PATH, DEBUG_LOG_PATH, UPDATE_CHECK_PATH, EXPORTS_DIR, PAUSE_FLAG_PATH, WEEKLY_CACHE_DIR, PROFILES_DIR, ALERTS_PENDING_PATH;
|
|
34
|
+
var init_paths = __esm({
|
|
35
|
+
"src/utils/paths.ts"() {
|
|
36
|
+
"use strict";
|
|
37
|
+
import_path = __toESM(require("path"));
|
|
38
|
+
import_os = __toESM(require("os"));
|
|
39
|
+
import_fs = __toESM(require("fs"));
|
|
40
|
+
LEGACY_DIR = import_path.default.join(import_os.default.homedir(), ".workscribe");
|
|
41
|
+
USE_XDG = !import_fs.default.existsSync(LEGACY_DIR);
|
|
42
|
+
xdgDataDir = USE_XDG ? import_path.default.join(process.env.XDG_DATA_HOME ?? import_path.default.join(import_os.default.homedir(), ".local", "share"), "workscribe") : LEGACY_DIR;
|
|
43
|
+
xdgConfigDir = USE_XDG ? import_path.default.join(process.env.XDG_CONFIG_HOME ?? import_path.default.join(import_os.default.homedir(), ".config"), "workscribe") : LEGACY_DIR;
|
|
44
|
+
xdgStateDir = USE_XDG ? import_path.default.join(process.env.XDG_STATE_HOME ?? import_path.default.join(import_os.default.homedir(), ".local", "state"), "workscribe") : LEGACY_DIR;
|
|
45
|
+
REQUIRED_DIRS = [.../* @__PURE__ */ new Set([xdgDataDir, xdgConfigDir, xdgStateDir, LEGACY_DIR])];
|
|
46
|
+
WORKSCRIBE_DIR = xdgDataDir;
|
|
47
|
+
DB_PATH = import_path.default.join(xdgDataDir, "workscribe.db");
|
|
48
|
+
CONFIG_PATH = import_path.default.join(xdgConfigDir, "config.json");
|
|
49
|
+
DEBUG_LOG_PATH = import_path.default.join(xdgStateDir, "debug.log");
|
|
50
|
+
UPDATE_CHECK_PATH = import_path.default.join(xdgStateDir, "update-check.json");
|
|
51
|
+
EXPORTS_DIR = import_path.default.join(xdgDataDir, "exports");
|
|
52
|
+
PAUSE_FLAG_PATH = import_path.default.join(xdgStateDir, "paused");
|
|
53
|
+
WEEKLY_CACHE_DIR = import_path.default.join(xdgDataDir, "weekly-cache");
|
|
54
|
+
PROFILES_DIR = import_path.default.join(xdgDataDir, "profiles");
|
|
55
|
+
ALERTS_PENDING_PATH = import_path.default.join(LEGACY_DIR, "alerts.pending");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
88
58
|
|
|
89
59
|
// src/db/migrations/001_initial_schema.ts
|
|
90
|
-
var initial_schema_default
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
60
|
+
var initial_schema_default;
|
|
61
|
+
var init_initial_schema = __esm({
|
|
62
|
+
"src/db/migrations/001_initial_schema.ts"() {
|
|
63
|
+
"use strict";
|
|
64
|
+
initial_schema_default = {
|
|
65
|
+
version: 1,
|
|
66
|
+
description: "initial schema",
|
|
67
|
+
sql: `
|
|
94
68
|
CREATE TABLE IF NOT EXISTS events (
|
|
95
69
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
96
70
|
captured_at TEXT NOT NULL,
|
|
@@ -125,13 +99,19 @@ var initial_schema_default = {
|
|
|
125
99
|
PRIMARY KEY (session_id, event_id)
|
|
126
100
|
);
|
|
127
101
|
`
|
|
128
|
-
};
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
129
105
|
|
|
130
106
|
// src/db/migrations/002_semantic_categories.ts
|
|
131
|
-
var migration
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
107
|
+
var migration, semantic_categories_default;
|
|
108
|
+
var init_semantic_categories = __esm({
|
|
109
|
+
"src/db/migrations/002_semantic_categories.ts"() {
|
|
110
|
+
"use strict";
|
|
111
|
+
migration = {
|
|
112
|
+
version: 2,
|
|
113
|
+
description: "Rename granular categories to 13 universal semantic categories",
|
|
114
|
+
sql: `
|
|
135
115
|
UPDATE events SET category = 'shipping' WHERE category IN ('code_commit','code_push','code_merge');
|
|
136
116
|
UPDATE events SET category = 'versioning' WHERE category IN ('code_pull','branch_switch','code_stash','git_other');
|
|
137
117
|
UPDATE events SET category = 'building' WHERE category = 'build';
|
|
@@ -143,14 +123,20 @@ var migration = {
|
|
|
143
123
|
UPDATE events SET category = 'files' WHERE category IN ('file_ops','environment');
|
|
144
124
|
UPDATE events SET category = 'exploring' WHERE category IN ('code_search','code_read');
|
|
145
125
|
`
|
|
146
|
-
};
|
|
147
|
-
|
|
126
|
+
};
|
|
127
|
+
semantic_categories_default = migration;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
148
130
|
|
|
149
131
|
// src/db/migrations/003_alerts.ts
|
|
150
|
-
var migration2
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
132
|
+
var migration2, alerts_default;
|
|
133
|
+
var init_alerts = __esm({
|
|
134
|
+
"src/db/migrations/003_alerts.ts"() {
|
|
135
|
+
"use strict";
|
|
136
|
+
migration2 = {
|
|
137
|
+
version: 3,
|
|
138
|
+
description: "Add alerts table for AI session secret detection",
|
|
139
|
+
sql: `
|
|
154
140
|
CREATE TABLE IF NOT EXISTS alerts (
|
|
155
141
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
156
142
|
created_at INTEGER NOT NULL,
|
|
@@ -161,26 +147,43 @@ var migration2 = {
|
|
|
161
147
|
);
|
|
162
148
|
CREATE INDEX IF NOT EXISTS alerts_cleared ON alerts (cleared);
|
|
163
149
|
`
|
|
164
|
-
};
|
|
165
|
-
|
|
150
|
+
};
|
|
151
|
+
alerts_default = migration2;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
166
154
|
|
|
167
155
|
// src/db/migrations/004_ide_columns.ts
|
|
168
|
-
var migration3
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
156
|
+
var migration3, ide_columns_default;
|
|
157
|
+
var init_ide_columns = __esm({
|
|
158
|
+
"src/db/migrations/004_ide_columns.ts"() {
|
|
159
|
+
"use strict";
|
|
160
|
+
migration3 = {
|
|
161
|
+
version: 4,
|
|
162
|
+
description: "Add source and file_path columns to events for IDE integration",
|
|
163
|
+
sql: `
|
|
172
164
|
ALTER TABLE events ADD COLUMN source TEXT;
|
|
173
165
|
ALTER TABLE events ADD COLUMN file_path TEXT;
|
|
174
166
|
CREATE INDEX IF NOT EXISTS idx_events_file_path ON events (file_path, cwd);
|
|
175
167
|
`
|
|
176
|
-
};
|
|
177
|
-
|
|
168
|
+
};
|
|
169
|
+
ide_columns_default = migration3;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
178
172
|
|
|
179
173
|
// src/db/migrations/index.ts
|
|
180
|
-
var MIGRATIONS
|
|
174
|
+
var MIGRATIONS;
|
|
175
|
+
var init_migrations = __esm({
|
|
176
|
+
"src/db/migrations/index.ts"() {
|
|
177
|
+
"use strict";
|
|
178
|
+
init_initial_schema();
|
|
179
|
+
init_semantic_categories();
|
|
180
|
+
init_alerts();
|
|
181
|
+
init_ide_columns();
|
|
182
|
+
MIGRATIONS = [initial_schema_default, semantic_categories_default, alerts_default, ide_columns_default];
|
|
183
|
+
}
|
|
184
|
+
});
|
|
181
185
|
|
|
182
186
|
// src/db/index.ts
|
|
183
|
-
var _db = null;
|
|
184
187
|
function getDb() {
|
|
185
188
|
if (_db) return _db;
|
|
186
189
|
if (!import_fs2.default.existsSync(WORKSCRIBE_DIR)) {
|
|
@@ -386,36 +389,19 @@ function clearSessionsForDate(date) {
|
|
|
386
389
|
).run({ date });
|
|
387
390
|
db.prepare("DELETE FROM sessions WHERE date = $date").run({ date });
|
|
388
391
|
}
|
|
392
|
+
var import_node_sqlite, import_fs2, _db;
|
|
393
|
+
var init_db = __esm({
|
|
394
|
+
"src/db/index.ts"() {
|
|
395
|
+
"use strict";
|
|
396
|
+
import_node_sqlite = require("node:sqlite");
|
|
397
|
+
import_fs2 = __toESM(require("fs"));
|
|
398
|
+
init_paths();
|
|
399
|
+
init_migrations();
|
|
400
|
+
_db = null;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
389
403
|
|
|
390
404
|
// src/capture/redact.ts
|
|
391
|
-
var BUILTIN_PATTERNS = [
|
|
392
|
-
// AWS access keys
|
|
393
|
-
/AKIA[0-9A-Z]{16}/g,
|
|
394
|
-
// AWS secret access keys (40 char base64-ish after common env vars)
|
|
395
|
-
/(?<=AWS_SECRET_ACCESS_KEY[=\s]+)\S+/gi,
|
|
396
|
-
// GitHub tokens
|
|
397
|
-
/ghp_[a-zA-Z0-9]{36}/g,
|
|
398
|
-
/github_pat_[a-zA-Z0-9_]{82}/g,
|
|
399
|
-
// GitLab tokens
|
|
400
|
-
/glpat-[a-zA-Z0-9\-]{20}/g,
|
|
401
|
-
// Generic JWTs (three base64url segments)
|
|
402
|
-
/eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g,
|
|
403
|
-
// Slack tokens and incoming webhook URLs
|
|
404
|
-
/xox[baprs]-[a-zA-Z0-9\-]+/g,
|
|
405
|
-
/https:\/\/hooks\.slack\.com\/services\/[A-Z0-9]+\/[A-Z0-9]+\/[a-zA-Z0-9]+/g,
|
|
406
|
-
// Stripe keys
|
|
407
|
-
/sk_(live|test)_[a-zA-Z0-9]{24,}/g,
|
|
408
|
-
// Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)
|
|
409
|
-
/(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\s+)\S+/gi,
|
|
410
|
-
// export VAR=VALUE where VAR looks sensitive
|
|
411
|
-
/(?<=export\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\S+/gi,
|
|
412
|
-
// Inline assignment: VAR_WITH_SENSITIVE_KEYWORD=value cmd (e.g. API_KEY=secret npm start)
|
|
413
|
-
// Lookbehind matches only the variable-name= prefix, so only the value is replaced
|
|
414
|
-
/(?<=[A-Z_][A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z0-9_]*=)\S+/gi,
|
|
415
|
-
// Credentials embedded in connection string URLs (e.g. DATABASE_URL=postgres://user:pass@host/db)
|
|
416
|
-
// Matches the password segment between :// user : password @ — preserves scheme and host
|
|
417
|
-
/(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\s/]+:)[^@\s]+(?=@)/g
|
|
418
|
-
];
|
|
419
405
|
function redact(command, extraPatterns = []) {
|
|
420
406
|
let result = command;
|
|
421
407
|
const allPatterns = [...BUILTIN_PATTERNS];
|
|
@@ -431,11 +417,42 @@ function redact(command, extraPatterns = []) {
|
|
|
431
417
|
}
|
|
432
418
|
return result;
|
|
433
419
|
}
|
|
420
|
+
var BUILTIN_PATTERNS;
|
|
421
|
+
var init_redact = __esm({
|
|
422
|
+
"src/capture/redact.ts"() {
|
|
423
|
+
"use strict";
|
|
424
|
+
BUILTIN_PATTERNS = [
|
|
425
|
+
// AWS access keys
|
|
426
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
427
|
+
// AWS secret access keys (40 char base64-ish after common env vars)
|
|
428
|
+
/(?<=AWS_SECRET_ACCESS_KEY[=\s]+)\S+/gi,
|
|
429
|
+
// GitHub tokens
|
|
430
|
+
/ghp_[a-zA-Z0-9]{36}/g,
|
|
431
|
+
/github_pat_[a-zA-Z0-9_]{82}/g,
|
|
432
|
+
// GitLab tokens
|
|
433
|
+
/glpat-[a-zA-Z0-9\-]{20}/g,
|
|
434
|
+
// Generic JWTs (three base64url segments)
|
|
435
|
+
/eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g,
|
|
436
|
+
// Slack tokens and incoming webhook URLs
|
|
437
|
+
/xox[baprs]-[a-zA-Z0-9\-]+/g,
|
|
438
|
+
/https:\/\/hooks\.slack\.com\/services\/[A-Z0-9]+\/[A-Z0-9]+\/[a-zA-Z0-9]+/g,
|
|
439
|
+
// Stripe keys
|
|
440
|
+
/sk_(live|test)_[a-zA-Z0-9]{24,}/g,
|
|
441
|
+
// Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)
|
|
442
|
+
/(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\s+)\S+/gi,
|
|
443
|
+
// export VAR=VALUE where VAR looks sensitive
|
|
444
|
+
/(?<=export\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\S+/gi,
|
|
445
|
+
// Inline assignment: VAR_WITH_SENSITIVE_KEYWORD=value cmd (e.g. API_KEY=secret npm start)
|
|
446
|
+
// Lookbehind matches only the variable-name= prefix, so only the value is replaced
|
|
447
|
+
/(?<=[A-Z_][A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z0-9_]*=)\S+/gi,
|
|
448
|
+
// Credentials embedded in connection string URLs (e.g. DATABASE_URL=postgres://user:pass@host/db)
|
|
449
|
+
// Matches the password segment between :// user : password @ — preserves scheme and host
|
|
450
|
+
/(?<=[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\s/]+:)[^@\s]+(?=@)/g
|
|
451
|
+
];
|
|
452
|
+
}
|
|
453
|
+
});
|
|
434
454
|
|
|
435
455
|
// src/capture/git.ts
|
|
436
|
-
var import_child_process = require("child_process");
|
|
437
|
-
var import_path2 = __toESM(require("path"));
|
|
438
|
-
var import_fs3 = __toESM(require("fs"));
|
|
439
456
|
function parseOwnerAndRepo(url) {
|
|
440
457
|
const normalized = url.trim().replace(/^[^/]+:(?!\/\/)/, (match) => match.replace(":", "/"));
|
|
441
458
|
const segments = normalized.split("/").filter(Boolean);
|
|
@@ -490,9 +507,17 @@ function getGitContext(cwd, nameOverride, qualifyRepoName = false) {
|
|
|
490
507
|
return { repoName: null, branch: null };
|
|
491
508
|
}
|
|
492
509
|
}
|
|
510
|
+
var import_child_process, import_path2, import_fs3;
|
|
511
|
+
var init_git = __esm({
|
|
512
|
+
"src/capture/git.ts"() {
|
|
513
|
+
"use strict";
|
|
514
|
+
import_child_process = require("child_process");
|
|
515
|
+
import_path2 = __toESM(require("path"));
|
|
516
|
+
import_fs3 = __toESM(require("fs"));
|
|
517
|
+
}
|
|
518
|
+
});
|
|
493
519
|
|
|
494
520
|
// src/capture/git-snapshot.ts
|
|
495
|
-
var import_child_process2 = require("child_process");
|
|
496
521
|
function getCommitsSince(sha, cwd) {
|
|
497
522
|
if (!sha) return [];
|
|
498
523
|
try {
|
|
@@ -502,12 +527,15 @@ function getCommitsSince(sha, cwd) {
|
|
|
502
527
|
return [];
|
|
503
528
|
}
|
|
504
529
|
}
|
|
530
|
+
var import_child_process2;
|
|
531
|
+
var init_git_snapshot = __esm({
|
|
532
|
+
"src/capture/git-snapshot.ts"() {
|
|
533
|
+
"use strict";
|
|
534
|
+
import_child_process2 = require("child_process");
|
|
535
|
+
}
|
|
536
|
+
});
|
|
505
537
|
|
|
506
538
|
// src/capture/aider.ts
|
|
507
|
-
var import_fs4 = __toESM(require("fs"));
|
|
508
|
-
var import_path3 = __toESM(require("path"));
|
|
509
|
-
var HISTORY_FILE = ".aider.chat.history.md";
|
|
510
|
-
var PROMPT_MAX_LENGTH = 200;
|
|
511
539
|
function readLastAiderPrompt(cwd) {
|
|
512
540
|
try {
|
|
513
541
|
const histPath = import_path3.default.join(cwd, HISTORY_FILE);
|
|
@@ -519,135 +547,135 @@ function readLastAiderPrompt(cwd) {
|
|
|
519
547
|
return null;
|
|
520
548
|
}
|
|
521
549
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
550
|
+
var import_fs4, import_path3, HISTORY_FILE, PROMPT_MAX_LENGTH;
|
|
551
|
+
var init_aider = __esm({
|
|
552
|
+
"src/capture/aider.ts"() {
|
|
553
|
+
"use strict";
|
|
554
|
+
import_fs4 = __toESM(require("fs"));
|
|
555
|
+
import_path3 = __toESM(require("path"));
|
|
556
|
+
HISTORY_FILE = ".aider.chat.history.md";
|
|
557
|
+
PROMPT_MAX_LENGTH = 200;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
527
560
|
|
|
528
561
|
// src/config/ai-tools.json
|
|
529
|
-
var ai_tools_default
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
562
|
+
var ai_tools_default;
|
|
563
|
+
var init_ai_tools = __esm({
|
|
564
|
+
"src/config/ai-tools.json"() {
|
|
565
|
+
ai_tools_default = [
|
|
566
|
+
{
|
|
567
|
+
tool: "claude",
|
|
568
|
+
displayName: "Claude Code",
|
|
569
|
+
pattern: "^claude\\b",
|
|
570
|
+
hook: "claude-code",
|
|
571
|
+
contextSource: "hook",
|
|
572
|
+
historyFormat: null,
|
|
573
|
+
securityWarning: null
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
tool: "aider",
|
|
577
|
+
displayName: "Aider",
|
|
578
|
+
pattern: "^aider\\b",
|
|
579
|
+
hook: null,
|
|
580
|
+
contextSource: "history-file",
|
|
581
|
+
historyFormat: "markdown",
|
|
582
|
+
securityWarning: null
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
tool: "codex",
|
|
586
|
+
displayName: "Codex CLI",
|
|
587
|
+
pattern: "^codex\\b",
|
|
588
|
+
hook: null,
|
|
589
|
+
contextSource: "history-file",
|
|
590
|
+
historyFormat: "jsonl",
|
|
591
|
+
securityWarning: null
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
tool: "gemini",
|
|
595
|
+
displayName: "Gemini CLI",
|
|
596
|
+
pattern: "^gemini\\b",
|
|
597
|
+
hook: null,
|
|
598
|
+
contextSource: "history-file",
|
|
599
|
+
historyFormat: "json",
|
|
600
|
+
securityWarning: null
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
tool: "gh copilot",
|
|
604
|
+
displayName: "GitHub Copilot",
|
|
605
|
+
pattern: "^gh\\s+copilot\\b",
|
|
606
|
+
hook: null,
|
|
607
|
+
contextSource: "history-file",
|
|
608
|
+
historyFormat: "jsonl",
|
|
609
|
+
securityWarning: null
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
tool: "amazon-q",
|
|
613
|
+
displayName: "Amazon Q",
|
|
614
|
+
pattern: "^q\\s+(chat|dev)\\b",
|
|
615
|
+
hook: null,
|
|
616
|
+
contextSource: "history-file",
|
|
617
|
+
historyFormat: "json",
|
|
618
|
+
securityWarning: null
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
tool: "opencode",
|
|
622
|
+
displayName: "OpenCode",
|
|
623
|
+
pattern: "^opencode\\b",
|
|
624
|
+
hook: null,
|
|
625
|
+
contextSource: "sqlite",
|
|
626
|
+
historyFormat: "sqlite",
|
|
627
|
+
securityWarning: null
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
tool: "kilo",
|
|
631
|
+
displayName: "Kilo Code",
|
|
632
|
+
pattern: "^kilo\\b",
|
|
633
|
+
hook: null,
|
|
634
|
+
contextSource: null,
|
|
635
|
+
historyFormat: null,
|
|
636
|
+
securityWarning: null
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
tool: "grok",
|
|
640
|
+
displayName: "Grok Build",
|
|
641
|
+
pattern: "^grok\\b",
|
|
642
|
+
hook: null,
|
|
643
|
+
contextSource: null,
|
|
644
|
+
historyFormat: null,
|
|
645
|
+
securityWarning: "Grok Build was found to silently upload entire local git repositories \u2014 including .env files and untracked secrets \u2014 to Google Cloud Storage without user disclosure (July 2026 incident). Workscribe does not read Grok Build session files. Consider whether this tool is appropriate for use in repositories containing sensitive credentials."
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
tool: "continue",
|
|
649
|
+
displayName: "Continue",
|
|
650
|
+
pattern: "^continue\\b",
|
|
651
|
+
hook: null,
|
|
652
|
+
contextSource: null,
|
|
653
|
+
historyFormat: null,
|
|
654
|
+
securityWarning: null
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
tool: "cody",
|
|
658
|
+
displayName: "Cody",
|
|
659
|
+
pattern: "^cody\\b",
|
|
660
|
+
hook: null,
|
|
661
|
+
contextSource: null,
|
|
662
|
+
historyFormat: null,
|
|
663
|
+
securityWarning: null
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
tool: "cursor",
|
|
667
|
+
displayName: "Cursor",
|
|
668
|
+
pattern: "^cursor\\b",
|
|
669
|
+
hook: null,
|
|
670
|
+
contextSource: null,
|
|
671
|
+
historyFormat: null,
|
|
672
|
+
securityWarning: null
|
|
673
|
+
}
|
|
674
|
+
];
|
|
675
|
+
}
|
|
676
|
+
});
|
|
639
677
|
|
|
640
678
|
// src/utils/logger.ts
|
|
641
|
-
var import_chalk = __toESM(require("chalk"));
|
|
642
|
-
var import_fs5 = __toESM(require("fs"));
|
|
643
|
-
var isDebug = process.env.WORKSCRIBE_DEBUG === "1";
|
|
644
|
-
var log = {
|
|
645
|
-
info: (msg) => console.log(import_chalk.default.cyan("\u2192"), msg),
|
|
646
|
-
success: (msg) => console.log(import_chalk.default.green("\u2713"), msg),
|
|
647
|
-
warn: (msg) => console.log(import_chalk.default.yellow("\u26A0"), msg),
|
|
648
|
-
error: (msg) => console.error(import_chalk.default.red("\u2717"), msg),
|
|
649
|
-
plain: (msg) => console.log(msg)
|
|
650
|
-
};
|
|
651
679
|
function debugLog(context, error) {
|
|
652
680
|
if (!isDebug) return;
|
|
653
681
|
const entry = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}
|
|
@@ -657,9 +685,25 @@ function debugLog(context, error) {
|
|
|
657
685
|
} catch {
|
|
658
686
|
}
|
|
659
687
|
}
|
|
688
|
+
var import_chalk, import_fs5, isDebug, log;
|
|
689
|
+
var init_logger = __esm({
|
|
690
|
+
"src/utils/logger.ts"() {
|
|
691
|
+
"use strict";
|
|
692
|
+
import_chalk = __toESM(require("chalk"));
|
|
693
|
+
import_fs5 = __toESM(require("fs"));
|
|
694
|
+
init_paths();
|
|
695
|
+
isDebug = process.env.WORKSCRIBE_DEBUG === "1";
|
|
696
|
+
log = {
|
|
697
|
+
info: (msg) => console.log(import_chalk.default.cyan("\u2192"), msg),
|
|
698
|
+
success: (msg) => console.log(import_chalk.default.green("\u2713"), msg),
|
|
699
|
+
warn: (msg) => console.log(import_chalk.default.yellow("\u26A0"), msg),
|
|
700
|
+
error: (msg) => console.error(import_chalk.default.red("\u2717"), msg),
|
|
701
|
+
plain: (msg) => console.log(msg)
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
});
|
|
660
705
|
|
|
661
706
|
// src/config/registry.ts
|
|
662
|
-
var USER_REGISTRY_PATH = import_path4.default.join(import_os2.default.homedir(), ".workscribe", "ai-tools.json");
|
|
663
707
|
function loadAIToolRegistry() {
|
|
664
708
|
let userEntries = [];
|
|
665
709
|
try {
|
|
@@ -686,39 +730,20 @@ function loadAIToolRegistry() {
|
|
|
686
730
|
const filteredBuiltins = ai_tools_default.filter((entry) => !userToolSlugs.has(entry.tool));
|
|
687
731
|
return [...filteredBuiltins, ...userEntries];
|
|
688
732
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
733
|
+
var import_fs6, import_os2, import_path4, USER_REGISTRY_PATH;
|
|
734
|
+
var init_registry = __esm({
|
|
735
|
+
"src/config/registry.ts"() {
|
|
736
|
+
"use strict";
|
|
737
|
+
import_fs6 = __toESM(require("fs"));
|
|
738
|
+
import_os2 = __toESM(require("os"));
|
|
739
|
+
import_path4 = __toESM(require("path"));
|
|
740
|
+
init_ai_tools();
|
|
741
|
+
init_logger();
|
|
742
|
+
USER_REGISTRY_PATH = import_path4.default.join(import_os2.default.homedir(), ".workscribe", "ai-tools.json");
|
|
743
|
+
}
|
|
744
|
+
});
|
|
693
745
|
|
|
694
746
|
// src/config/index.ts
|
|
695
|
-
var import_fs7 = __toESM(require("fs"));
|
|
696
|
-
var DEFAULTS = {
|
|
697
|
-
ai: {
|
|
698
|
-
provider: "",
|
|
699
|
-
apiKey: "",
|
|
700
|
-
model: "",
|
|
701
|
-
baseUrl: ""
|
|
702
|
-
},
|
|
703
|
-
session: {
|
|
704
|
-
idleTimeout: 30
|
|
705
|
-
},
|
|
706
|
-
git: {
|
|
707
|
-
qualifyRepoName: false
|
|
708
|
-
},
|
|
709
|
-
capture: {
|
|
710
|
-
ignore: ["cd", "ls", "ll", "la", "ls -la", "ls -l", "ls -a", "pwd", "clear", "exit", "history", "man"],
|
|
711
|
-
aiTools: true,
|
|
712
|
-
includeAiderPrompt: true,
|
|
713
|
-
profiles: []
|
|
714
|
-
},
|
|
715
|
-
redact: {
|
|
716
|
-
extra: []
|
|
717
|
-
},
|
|
718
|
-
summary: {
|
|
719
|
-
format: "markdown"
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
747
|
function readConfig() {
|
|
723
748
|
if (!import_fs7.default.existsSync(CONFIG_PATH)) return { ...DEFAULTS };
|
|
724
749
|
try {
|
|
@@ -775,6 +800,40 @@ function deepMerge(defaults, overrides) {
|
|
|
775
800
|
}
|
|
776
801
|
return result;
|
|
777
802
|
}
|
|
803
|
+
var import_fs7, DEFAULTS;
|
|
804
|
+
var init_config = __esm({
|
|
805
|
+
"src/config/index.ts"() {
|
|
806
|
+
"use strict";
|
|
807
|
+
import_fs7 = __toESM(require("fs"));
|
|
808
|
+
init_paths();
|
|
809
|
+
DEFAULTS = {
|
|
810
|
+
ai: {
|
|
811
|
+
provider: "",
|
|
812
|
+
apiKey: "",
|
|
813
|
+
model: "",
|
|
814
|
+
baseUrl: ""
|
|
815
|
+
},
|
|
816
|
+
session: {
|
|
817
|
+
idleTimeout: 30
|
|
818
|
+
},
|
|
819
|
+
git: {
|
|
820
|
+
qualifyRepoName: false
|
|
821
|
+
},
|
|
822
|
+
capture: {
|
|
823
|
+
ignore: ["cd", "ls", "ll", "la", "ls -la", "ls -l", "ls -a", "pwd", "clear", "exit", "history", "man"],
|
|
824
|
+
aiTools: true,
|
|
825
|
+
includeAiderPrompt: true,
|
|
826
|
+
profiles: []
|
|
827
|
+
},
|
|
828
|
+
redact: {
|
|
829
|
+
extra: []
|
|
830
|
+
},
|
|
831
|
+
summary: {
|
|
832
|
+
format: "markdown"
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
});
|
|
778
837
|
|
|
779
838
|
// src/profiles/schema.ts
|
|
780
839
|
function isNonEmptyString(value) {
|
|
@@ -795,21 +854,13 @@ function isValidProfile(candidate) {
|
|
|
795
854
|
function isValidManifest(m) {
|
|
796
855
|
return Array.isArray(m) && m.every((e) => e && typeof e === "object" && typeof e.name === "string");
|
|
797
856
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
857
|
+
var init_schema = __esm({
|
|
858
|
+
"src/profiles/schema.ts"() {
|
|
859
|
+
"use strict";
|
|
860
|
+
}
|
|
861
|
+
});
|
|
802
862
|
|
|
803
863
|
// src/utils/fetch.ts
|
|
804
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
805
|
-
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
806
|
-
"ENOTFOUND",
|
|
807
|
-
"ECONNREFUSED",
|
|
808
|
-
"ECONNRESET",
|
|
809
|
-
"ETIMEDOUT",
|
|
810
|
-
"EHOSTUNREACH",
|
|
811
|
-
"ENETUNREACH"
|
|
812
|
-
]);
|
|
813
864
|
function isNetworkError(err) {
|
|
814
865
|
if (!(err instanceof Error)) return false;
|
|
815
866
|
const cause = err.cause;
|
|
@@ -833,9 +884,23 @@ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
833
884
|
clearTimeout(timer);
|
|
834
885
|
}
|
|
835
886
|
}
|
|
887
|
+
var DEFAULT_TIMEOUT_MS, NETWORK_ERROR_CODES;
|
|
888
|
+
var init_fetch = __esm({
|
|
889
|
+
"src/utils/fetch.ts"() {
|
|
890
|
+
"use strict";
|
|
891
|
+
DEFAULT_TIMEOUT_MS = 3e4;
|
|
892
|
+
NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
893
|
+
"ENOTFOUND",
|
|
894
|
+
"ECONNREFUSED",
|
|
895
|
+
"ECONNRESET",
|
|
896
|
+
"ETIMEDOUT",
|
|
897
|
+
"EHOSTUNREACH",
|
|
898
|
+
"ENETUNREACH"
|
|
899
|
+
]);
|
|
900
|
+
}
|
|
901
|
+
});
|
|
836
902
|
|
|
837
903
|
// src/profiles/fetcher.ts
|
|
838
|
-
var REGISTRY_BASE = "https://raw.githubusercontent.com/workscribe/profiles/main";
|
|
839
904
|
function manifestCachePath() {
|
|
840
905
|
return import_path5.default.join(PROFILES_DIR, "manifest.json");
|
|
841
906
|
}
|
|
@@ -881,6 +946,19 @@ function ensureProfilesDir() {
|
|
|
881
946
|
import_fs8.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
882
947
|
}
|
|
883
948
|
}
|
|
949
|
+
var import_fs8, import_path5, REGISTRY_BASE;
|
|
950
|
+
var init_fetcher = __esm({
|
|
951
|
+
"src/profiles/fetcher.ts"() {
|
|
952
|
+
"use strict";
|
|
953
|
+
import_fs8 = __toESM(require("fs"));
|
|
954
|
+
import_path5 = __toESM(require("path"));
|
|
955
|
+
init_paths();
|
|
956
|
+
init_schema();
|
|
957
|
+
init_logger();
|
|
958
|
+
init_fetch();
|
|
959
|
+
REGISTRY_BASE = "https://raw.githubusercontent.com/workscribe/profiles/main";
|
|
960
|
+
}
|
|
961
|
+
});
|
|
884
962
|
|
|
885
963
|
// src/profiles/loader.ts
|
|
886
964
|
function compileRule(profileName, rule) {
|
|
@@ -959,6 +1037,19 @@ function detectProfilesForDirectory(dir) {
|
|
|
959
1037
|
return indicatorFiles.length > 0 && indicatorFiles.every((file) => import_fs9.default.existsSync(import_path6.default.join(dir, file)));
|
|
960
1038
|
});
|
|
961
1039
|
}
|
|
1040
|
+
var import_fs9, import_path6;
|
|
1041
|
+
var init_loader = __esm({
|
|
1042
|
+
"src/profiles/loader.ts"() {
|
|
1043
|
+
"use strict";
|
|
1044
|
+
import_fs9 = __toESM(require("fs"));
|
|
1045
|
+
import_path6 = __toESM(require("path"));
|
|
1046
|
+
init_paths();
|
|
1047
|
+
init_config();
|
|
1048
|
+
init_schema();
|
|
1049
|
+
init_fetcher();
|
|
1050
|
+
init_logger();
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
962
1053
|
|
|
963
1054
|
// src/normalize/patterns.ts
|
|
964
1055
|
function makeRule(pattern, category, extract = () => ({})) {
|
|
@@ -975,208 +1066,6 @@ function extractPackages(cmd, afterKeyword) {
|
|
|
975
1066
|
if (idx === -1) return [];
|
|
976
1067
|
return cmd.slice(idx + afterKeyword.length).trim().split(/\s+/).filter((part) => part && !part.startsWith("-"));
|
|
977
1068
|
}
|
|
978
|
-
var BASE_RULES = [
|
|
979
|
-
// ─── Git operations ───────────────────────────────────────────────────────
|
|
980
|
-
makeRule(/^git\s+commit\b/, "shipping", (_m, cmd) => {
|
|
981
|
-
const msgMatch = cmd.match(/-m\s+["']?([^"']+)["']?/);
|
|
982
|
-
return { message: msgMatch ? msgMatch[1].trim() : null };
|
|
983
|
-
}),
|
|
984
|
-
makeRule(/^git\s+push\b/, "shipping", (_m, cmd) => {
|
|
985
|
-
const parts = cmd.split(/\s+/);
|
|
986
|
-
return { remote: parts[2] ?? "origin", branch: parts[3] ?? null };
|
|
987
|
-
}),
|
|
988
|
-
makeRule(/^git\s+(pull|fetch)\b/, "versioning", (_m, cmd) => {
|
|
989
|
-
const parts = cmd.split(/\s+/);
|
|
990
|
-
return { subcommand: parts[1], remote: parts[2] ?? "origin" };
|
|
991
|
-
}),
|
|
992
|
-
makeRule(/^git\s+(checkout|switch)\b/, "versioning", (_m, cmd) => {
|
|
993
|
-
const parts = cmd.split(/\s+/);
|
|
994
|
-
const branch = parts.find((part) => !part.startsWith("-") && part !== "git" && part !== parts[1]);
|
|
995
|
-
return { branch: branch ?? null };
|
|
996
|
-
}),
|
|
997
|
-
makeRule(/^git\s+(merge|rebase)\b/, "shipping", (_m, cmd) => {
|
|
998
|
-
const parts = cmd.split(/\s+/);
|
|
999
|
-
return { subcommand: parts[1], target: parts[2] ?? null };
|
|
1000
|
-
}),
|
|
1001
|
-
makeRule(/^git\s+stash\b/, "versioning", (_m, cmd) => {
|
|
1002
|
-
const parts = cmd.split(/\s+/);
|
|
1003
|
-
return { subcommand: parts[2] ?? "push" };
|
|
1004
|
-
}),
|
|
1005
|
-
makeRule(/^git\s+/, "versioning", (_m, cmd) => {
|
|
1006
|
-
const parts = cmd.split(/\s+/);
|
|
1007
|
-
return { subcommand: parts[1] ?? null };
|
|
1008
|
-
}),
|
|
1009
|
-
// ─── Releasing ────────────────────────────────────────────────────────────
|
|
1010
|
-
makeRule(/^npm\s+publish\b/, "releasing", () => ({ tool: "npm" })),
|
|
1011
|
-
makeRule(/^cargo\s+publish\b/, "releasing", () => ({ tool: "cargo" })),
|
|
1012
|
-
makeRule(/^gem\s+push\b/, "releasing", () => ({ tool: "gem" })),
|
|
1013
|
-
makeRule(/^docker\s+push\b/, "releasing", (_m, cmd) => ({ tool: "docker", image: cmd.split(/\s+/)[2] ?? null })),
|
|
1014
|
-
makeRule(/^gh\s+release\b/, "releasing", (_m, cmd) => ({ tool: "gh", subcommand: cmd.split(/\s+/)[2] ?? null })),
|
|
1015
|
-
makeRule(/^fly\s+(deploy|launch)\b/, "releasing", (match) => ({ tool: "fly", subcommand: match[1] })),
|
|
1016
|
-
makeRule(/^vercel\s+(deploy|build|--prod)\b/, "releasing", () => ({ tool: "vercel" })),
|
|
1017
|
-
makeRule(/^netlify\s+deploy\b/, "releasing", () => ({ tool: "netlify" })),
|
|
1018
|
-
makeRule(/^railway\s+up\b/, "releasing", () => ({ tool: "railway" })),
|
|
1019
|
-
makeRule(/^wrangler\s+deploy\b/, "releasing", () => ({ tool: "wrangler" })),
|
|
1020
|
-
// ─── Dependencies ─────────────────────────────────────────────────────────
|
|
1021
|
-
makeRule(/^npm\s+(install|i|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1022
|
-
tool: "npm",
|
|
1023
|
-
packages: extractPackages(cmd, cmd.includes("install") ? "install" : cmd.includes(" i ") ? " i " : "add")
|
|
1024
|
-
})),
|
|
1025
|
-
makeRule(/^(yarn|pnpm)\s+(install|add)\b/, "dependencies", (m, cmd) => ({
|
|
1026
|
-
tool: m[1],
|
|
1027
|
-
packages: extractPackages(cmd, "add")
|
|
1028
|
-
})),
|
|
1029
|
-
makeRule(/^bun\s+(install|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1030
|
-
tool: "bun",
|
|
1031
|
-
packages: extractPackages(cmd, "add")
|
|
1032
|
-
})),
|
|
1033
|
-
makeRule(/^pip\s+(install)\b/, "dependencies", (_m, cmd) => ({
|
|
1034
|
-
tool: "pip",
|
|
1035
|
-
packages: extractPackages(cmd, "install")
|
|
1036
|
-
})),
|
|
1037
|
-
makeRule(/^cargo\s+add\b/, "dependencies", (_m, cmd) => ({
|
|
1038
|
-
tool: "cargo",
|
|
1039
|
-
packages: extractPackages(cmd, "add")
|
|
1040
|
-
})),
|
|
1041
|
-
makeRule(/^(brew)\s+install\b/, "dependencies", (_m, cmd) => ({
|
|
1042
|
-
tool: "brew",
|
|
1043
|
-
packages: extractPackages(cmd, "install")
|
|
1044
|
-
})),
|
|
1045
|
-
makeRule(/^go\s+get\b/, "dependencies", (_m, cmd) => ({
|
|
1046
|
-
tool: "go",
|
|
1047
|
-
packages: extractPackages(cmd, "get")
|
|
1048
|
-
})),
|
|
1049
|
-
makeRule(/^npm\s+(uninstall|remove|rm)\b/, "dependencies", (_m, cmd) => ({
|
|
1050
|
-
tool: "npm",
|
|
1051
|
-
packages: extractPackages(cmd, cmd.split(/\s+/)[1])
|
|
1052
|
-
})),
|
|
1053
|
-
makeRule(/^(yarn|pnpm)\s+remove\b/, "dependencies", (m, cmd) => ({
|
|
1054
|
-
tool: m[1],
|
|
1055
|
-
packages: extractPackages(cmd, "remove")
|
|
1056
|
-
})),
|
|
1057
|
-
makeRule(/^bun\s+remove\b/, "dependencies", (_m, cmd) => ({
|
|
1058
|
-
tool: "bun",
|
|
1059
|
-
packages: extractPackages(cmd, "remove")
|
|
1060
|
-
})),
|
|
1061
|
-
makeRule(/^pip\s+uninstall\b/, "dependencies", (_m, cmd) => ({
|
|
1062
|
-
tool: "pip",
|
|
1063
|
-
packages: extractPackages(cmd, "uninstall")
|
|
1064
|
-
})),
|
|
1065
|
-
// Runtime version managers
|
|
1066
|
-
makeRule(/^nvm\b/, "dependencies", (_m, cmd) => ({ tool: "nvm", subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1067
|
-
makeRule(/^(fnm|volta)\b/, "dependencies", (m, cmd) => ({ tool: m[1], subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1068
|
-
makeRule(/^(rbenv|pyenv|asdf)\b/, "dependencies", (m, cmd) => ({
|
|
1069
|
-
tool: m[1],
|
|
1070
|
-
subcommand: cmd.split(/\s+/)[1] ?? null
|
|
1071
|
-
})),
|
|
1072
|
-
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
1073
|
-
makeRule(/^npm\s+(test|run\s+test)\b/, "testing", () => ({ tool: "npm" })),
|
|
1074
|
-
makeRule(/^yarn\s+test\b/, "testing", () => ({ tool: "yarn" })),
|
|
1075
|
-
makeRule(/^pnpm\s+test\b/, "testing", () => ({ tool: "pnpm" })),
|
|
1076
|
-
makeRule(/^bun\s+test\b/, "testing", () => ({ tool: "bun" })),
|
|
1077
|
-
makeRule(/^(jest|vitest)\b/, "testing", (match) => ({ tool: match[1] })),
|
|
1078
|
-
makeRule(/^pytest\b/, "testing", () => ({ tool: "pytest" })),
|
|
1079
|
-
makeRule(/^go\s+test\b/, "testing", () => ({ tool: "go" })),
|
|
1080
|
-
makeRule(/^cargo\s+test\b/, "testing", () => ({ tool: "cargo" })),
|
|
1081
|
-
makeRule(/^rspec\b/, "testing", () => ({ tool: "rspec" })),
|
|
1082
|
-
makeRule(/^(php\s+)?phpunit\b/, "testing", () => ({ tool: "phpunit" })),
|
|
1083
|
-
// ─── Build ────────────────────────────────────────────────────────────────
|
|
1084
|
-
makeRule(/^npm\s+run\s+build\b/, "building", () => ({ tool: "npm" })),
|
|
1085
|
-
makeRule(/^(yarn|pnpm)\s+build\b/, "building", (match) => ({ tool: match[1] })),
|
|
1086
|
-
makeRule(/^bun\s+run\s+build\b/, "building", () => ({ tool: "bun" })),
|
|
1087
|
-
makeRule(/^(make|gmake)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1088
|
-
makeRule(/^cargo\s+build\b/, "building", () => ({ tool: "cargo" })),
|
|
1089
|
-
makeRule(/^go\s+build\b/, "building", () => ({ tool: "go" })),
|
|
1090
|
-
makeRule(/^docker\s+build\b/, "building", () => ({ tool: "docker" })),
|
|
1091
|
-
makeRule(/^(mvn|gradle)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1092
|
-
// ─── Dev server / running ─────────────────────────────────────────────────
|
|
1093
|
-
makeRule(/^npm\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1094
|
-
tool: "npm",
|
|
1095
|
-
script: cmd.split(/\s+/)[2]
|
|
1096
|
-
})),
|
|
1097
|
-
makeRule(/^(yarn|pnpm)\s+(dev|start)\b/, "running", (match) => ({ tool: match[1], script: match[2] })),
|
|
1098
|
-
makeRule(/^bun\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1099
|
-
tool: "bun",
|
|
1100
|
-
script: cmd.split(/\s+/)[2]
|
|
1101
|
-
})),
|
|
1102
|
-
makeRule(/^python\s+.*manage\.py\s+runserver\b/, "running", () => ({ tool: "django" })),
|
|
1103
|
-
// ─── Infrastructure ───────────────────────────────────────────────────────
|
|
1104
|
-
makeRule(/^ssh\b/, "infrastructure", (_m, cmd) => {
|
|
1105
|
-
const parts = cmd.split(/\s+/);
|
|
1106
|
-
return { tool: "ssh", target: parts[parts.length - 1] };
|
|
1107
|
-
}),
|
|
1108
|
-
makeRule(/^kubectl\b/, "infrastructure", (_m, cmd) => {
|
|
1109
|
-
const parts = cmd.split(/\s+/);
|
|
1110
|
-
return { tool: "kubectl", subcommand: parts[1] ?? null };
|
|
1111
|
-
}),
|
|
1112
|
-
makeRule(/^(helm|terraform|pulumi)\b/, "infrastructure", (m, cmd) => {
|
|
1113
|
-
const parts = cmd.split(/\s+/);
|
|
1114
|
-
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1115
|
-
}),
|
|
1116
|
-
makeRule(/^docker\s+(run|compose|ps|stop|start|rm|exec)\b/, "infrastructure", (_m, cmd) => {
|
|
1117
|
-
const parts = cmd.split(/\s+/);
|
|
1118
|
-
return { tool: "docker", subcommand: parts[1] };
|
|
1119
|
-
}),
|
|
1120
|
-
makeRule(/^(aws|gcloud|az)\b/, "infrastructure", (m, cmd) => {
|
|
1121
|
-
const parts = cmd.split(/\s+/);
|
|
1122
|
-
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1123
|
-
}),
|
|
1124
|
-
// ─── Database ─────────────────────────────────────────────────────────────
|
|
1125
|
-
makeRule(/^(psql|pgcli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1126
|
-
makeRule(/^(mysql|mycli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1127
|
-
makeRule(/^sqlite3\b/, "database", () => ({ tool: "sqlite3" })),
|
|
1128
|
-
makeRule(/^(mongosh|mongo)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1129
|
-
makeRule(/^redis-cli\b/, "database", () => ({ tool: "redis-cli" })),
|
|
1130
|
-
// ─── npx ─────────────────────────────────────────────────────────────────
|
|
1131
|
-
makeRule(/^npx\s+(jest|vitest|mocha|ava|tap)\b/, "testing", (_m, cmd) => ({
|
|
1132
|
-
tool: cmd.split(/\s+/)[1]
|
|
1133
|
-
})),
|
|
1134
|
-
makeRule(/^npx\s+tsc\b/, "building", () => ({ tool: "tsc" })),
|
|
1135
|
-
makeRule(/^npx\s+(eslint|tslint|biome)\b/, "linting", (_m, cmd) => ({
|
|
1136
|
-
tool: cmd.split(/\s+/)[1]
|
|
1137
|
-
})),
|
|
1138
|
-
makeRule(/^npx\s+(prettier|dprint)\b/, "linting", (_m, cmd) => ({
|
|
1139
|
-
tool: cmd.split(/\s+/)[1]
|
|
1140
|
-
})),
|
|
1141
|
-
makeRule(/^npx\s+\S+/, "running", (_m, cmd) => ({
|
|
1142
|
-
tool: cmd.split(/\s+/)[1]
|
|
1143
|
-
})),
|
|
1144
|
-
// ─── Scripts ──────────────────────────────────────────────────────────────
|
|
1145
|
-
makeRule(/^(bash|sh|zsh|fish)\s+\S+/, "running", (match) => ({ shell: match[1] })),
|
|
1146
|
-
makeRule(/^node\s+\S+\.(?:js|mjs|cjs)\b/, "running", () => ({ runtime: "node" })),
|
|
1147
|
-
makeRule(/^python3?\s+\S+\.py\b/, "running", () => ({ runtime: "python" })),
|
|
1148
|
-
// ─── Linting / code quality ───────────────────────────────────────────────
|
|
1149
|
-
makeRule(/^(eslint|tslint|biome)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1150
|
-
makeRule(/^(prettier|dprint)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1151
|
-
// ─── generic run scripts ──────────────────────────────────────────────────
|
|
1152
|
-
makeRule(/^npm\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1153
|
-
tool: "npm",
|
|
1154
|
-
script: cmd.split(/\s+/)[2]
|
|
1155
|
-
})),
|
|
1156
|
-
makeRule(/^bun\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1157
|
-
tool: "bun",
|
|
1158
|
-
script: cmd.split(/\s+/)[2]
|
|
1159
|
-
})),
|
|
1160
|
-
// ─── Code search ─────────────────────────────────────────────────────────
|
|
1161
|
-
makeRule(/^(grep|egrep|fgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1162
|
-
makeRule(/^(rg|ag|ack|ripgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1163
|
-
makeRule(/^find\s/, "exploring", () => ({ tool: "find" })),
|
|
1164
|
-
// ─── Code read ────────────────────────────────────────────────────────────
|
|
1165
|
-
makeRule(/^(cat|bat|less|more)\s+\S+/, "exploring", (match) => ({ tool: match[1] })),
|
|
1166
|
-
makeRule(/^(head|tail)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1167
|
-
// ─── File operations ──────────────────────────────────────────────────────
|
|
1168
|
-
makeRule(/^(cp|rsync)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1169
|
-
makeRule(/^(mv)\b/, "files", () => ({ tool: "mv" })),
|
|
1170
|
-
makeRule(/^(rm|rmdir)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1171
|
-
makeRule(/^(mkdir|touch|ln)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1172
|
-
makeRule(/^(chmod|chown|chgrp)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1173
|
-
// ─── Environment ──────────────────────────────────────────────────────────
|
|
1174
|
-
makeRule(/^export\s+\w+=/, "files", (_m, cmd) => {
|
|
1175
|
-
const varName = cmd.match(/^export\s+(\w+)=/)?.[1] ?? null;
|
|
1176
|
-
return { variable: varName };
|
|
1177
|
-
}),
|
|
1178
|
-
makeRule(/^source\s+/, "files", () => ({}))
|
|
1179
|
-
];
|
|
1180
1069
|
function aiToolRules() {
|
|
1181
1070
|
return loadAIToolRegistry().flatMap((entry) => {
|
|
1182
1071
|
try {
|
|
@@ -1190,7 +1079,6 @@ function aiToolRules() {
|
|
|
1190
1079
|
function buildRules(profileRules = []) {
|
|
1191
1080
|
return [...BASE_RULES, ...profileRules, ...aiToolRules()];
|
|
1192
1081
|
}
|
|
1193
|
-
var RULES = buildRules(getActiveProfileRules());
|
|
1194
1082
|
function normalizeCommand(command) {
|
|
1195
1083
|
let trimmed = command.trim().replace(/\s+/g, " ");
|
|
1196
1084
|
trimmed = trimmed.replace(/^(git)\s+-C\s+\S+\s+/, "$1 ");
|
|
@@ -1200,12 +1088,223 @@ function normalizeCommand(command) {
|
|
|
1200
1088
|
}
|
|
1201
1089
|
return { category: "other", metadata: {} };
|
|
1202
1090
|
}
|
|
1091
|
+
var BASE_RULES, RULES;
|
|
1092
|
+
var init_patterns = __esm({
|
|
1093
|
+
"src/normalize/patterns.ts"() {
|
|
1094
|
+
"use strict";
|
|
1095
|
+
init_registry();
|
|
1096
|
+
init_loader();
|
|
1097
|
+
init_logger();
|
|
1098
|
+
BASE_RULES = [
|
|
1099
|
+
// ─── Git operations ───────────────────────────────────────────────────────
|
|
1100
|
+
makeRule(/^git\s+commit\b/, "shipping", (_m, cmd) => {
|
|
1101
|
+
const msgMatch = cmd.match(/-m\s+["']?([^"']+)["']?/);
|
|
1102
|
+
return { message: msgMatch ? msgMatch[1].trim() : null };
|
|
1103
|
+
}),
|
|
1104
|
+
makeRule(/^git\s+push\b/, "shipping", (_m, cmd) => {
|
|
1105
|
+
const parts = cmd.split(/\s+/);
|
|
1106
|
+
return { remote: parts[2] ?? "origin", branch: parts[3] ?? null };
|
|
1107
|
+
}),
|
|
1108
|
+
makeRule(/^git\s+(pull|fetch)\b/, "versioning", (_m, cmd) => {
|
|
1109
|
+
const parts = cmd.split(/\s+/);
|
|
1110
|
+
return { subcommand: parts[1], remote: parts[2] ?? "origin" };
|
|
1111
|
+
}),
|
|
1112
|
+
makeRule(/^git\s+(checkout|switch)\b/, "versioning", (_m, cmd) => {
|
|
1113
|
+
const parts = cmd.split(/\s+/);
|
|
1114
|
+
const branch = parts.find((part) => !part.startsWith("-") && part !== "git" && part !== parts[1]);
|
|
1115
|
+
return { branch: branch ?? null };
|
|
1116
|
+
}),
|
|
1117
|
+
makeRule(/^git\s+(merge|rebase)\b/, "shipping", (_m, cmd) => {
|
|
1118
|
+
const parts = cmd.split(/\s+/);
|
|
1119
|
+
return { subcommand: parts[1], target: parts[2] ?? null };
|
|
1120
|
+
}),
|
|
1121
|
+
makeRule(/^git\s+stash\b/, "versioning", (_m, cmd) => {
|
|
1122
|
+
const parts = cmd.split(/\s+/);
|
|
1123
|
+
return { subcommand: parts[2] ?? "push" };
|
|
1124
|
+
}),
|
|
1125
|
+
makeRule(/^git\s+/, "versioning", (_m, cmd) => {
|
|
1126
|
+
const parts = cmd.split(/\s+/);
|
|
1127
|
+
return { subcommand: parts[1] ?? null };
|
|
1128
|
+
}),
|
|
1129
|
+
// ─── Releasing ────────────────────────────────────────────────────────────
|
|
1130
|
+
makeRule(/^npm\s+publish\b/, "releasing", () => ({ tool: "npm" })),
|
|
1131
|
+
makeRule(/^cargo\s+publish\b/, "releasing", () => ({ tool: "cargo" })),
|
|
1132
|
+
makeRule(/^gem\s+push\b/, "releasing", () => ({ tool: "gem" })),
|
|
1133
|
+
makeRule(/^docker\s+push\b/, "releasing", (_m, cmd) => ({ tool: "docker", image: cmd.split(/\s+/)[2] ?? null })),
|
|
1134
|
+
makeRule(/^gh\s+release\b/, "releasing", (_m, cmd) => ({ tool: "gh", subcommand: cmd.split(/\s+/)[2] ?? null })),
|
|
1135
|
+
makeRule(/^fly\s+(deploy|launch)\b/, "releasing", (match) => ({ tool: "fly", subcommand: match[1] })),
|
|
1136
|
+
makeRule(/^vercel\s+(deploy|build|--prod)\b/, "releasing", () => ({ tool: "vercel" })),
|
|
1137
|
+
makeRule(/^netlify\s+deploy\b/, "releasing", () => ({ tool: "netlify" })),
|
|
1138
|
+
makeRule(/^railway\s+up\b/, "releasing", () => ({ tool: "railway" })),
|
|
1139
|
+
makeRule(/^wrangler\s+deploy\b/, "releasing", () => ({ tool: "wrangler" })),
|
|
1140
|
+
// ─── Dependencies ─────────────────────────────────────────────────────────
|
|
1141
|
+
makeRule(/^npm\s+(install|i|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1142
|
+
tool: "npm",
|
|
1143
|
+
packages: extractPackages(cmd, cmd.includes("install") ? "install" : cmd.includes(" i ") ? " i " : "add")
|
|
1144
|
+
})),
|
|
1145
|
+
makeRule(/^(yarn|pnpm)\s+(install|add)\b/, "dependencies", (m, cmd) => ({
|
|
1146
|
+
tool: m[1],
|
|
1147
|
+
packages: extractPackages(cmd, "add")
|
|
1148
|
+
})),
|
|
1149
|
+
makeRule(/^bun\s+(install|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1150
|
+
tool: "bun",
|
|
1151
|
+
packages: extractPackages(cmd, "add")
|
|
1152
|
+
})),
|
|
1153
|
+
makeRule(/^pip\s+(install)\b/, "dependencies", (_m, cmd) => ({
|
|
1154
|
+
tool: "pip",
|
|
1155
|
+
packages: extractPackages(cmd, "install")
|
|
1156
|
+
})),
|
|
1157
|
+
makeRule(/^cargo\s+add\b/, "dependencies", (_m, cmd) => ({
|
|
1158
|
+
tool: "cargo",
|
|
1159
|
+
packages: extractPackages(cmd, "add")
|
|
1160
|
+
})),
|
|
1161
|
+
makeRule(/^(brew)\s+install\b/, "dependencies", (_m, cmd) => ({
|
|
1162
|
+
tool: "brew",
|
|
1163
|
+
packages: extractPackages(cmd, "install")
|
|
1164
|
+
})),
|
|
1165
|
+
makeRule(/^go\s+get\b/, "dependencies", (_m, cmd) => ({
|
|
1166
|
+
tool: "go",
|
|
1167
|
+
packages: extractPackages(cmd, "get")
|
|
1168
|
+
})),
|
|
1169
|
+
makeRule(/^npm\s+(uninstall|remove|rm)\b/, "dependencies", (_m, cmd) => ({
|
|
1170
|
+
tool: "npm",
|
|
1171
|
+
packages: extractPackages(cmd, cmd.split(/\s+/)[1])
|
|
1172
|
+
})),
|
|
1173
|
+
makeRule(/^(yarn|pnpm)\s+remove\b/, "dependencies", (m, cmd) => ({
|
|
1174
|
+
tool: m[1],
|
|
1175
|
+
packages: extractPackages(cmd, "remove")
|
|
1176
|
+
})),
|
|
1177
|
+
makeRule(/^bun\s+remove\b/, "dependencies", (_m, cmd) => ({
|
|
1178
|
+
tool: "bun",
|
|
1179
|
+
packages: extractPackages(cmd, "remove")
|
|
1180
|
+
})),
|
|
1181
|
+
makeRule(/^pip\s+uninstall\b/, "dependencies", (_m, cmd) => ({
|
|
1182
|
+
tool: "pip",
|
|
1183
|
+
packages: extractPackages(cmd, "uninstall")
|
|
1184
|
+
})),
|
|
1185
|
+
// Runtime version managers
|
|
1186
|
+
makeRule(/^nvm\b/, "dependencies", (_m, cmd) => ({ tool: "nvm", subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1187
|
+
makeRule(/^(fnm|volta)\b/, "dependencies", (m, cmd) => ({ tool: m[1], subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1188
|
+
makeRule(/^(rbenv|pyenv|asdf)\b/, "dependencies", (m, cmd) => ({
|
|
1189
|
+
tool: m[1],
|
|
1190
|
+
subcommand: cmd.split(/\s+/)[1] ?? null
|
|
1191
|
+
})),
|
|
1192
|
+
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
1193
|
+
makeRule(/^npm\s+(test|run\s+test)\b/, "testing", () => ({ tool: "npm" })),
|
|
1194
|
+
makeRule(/^yarn\s+test\b/, "testing", () => ({ tool: "yarn" })),
|
|
1195
|
+
makeRule(/^pnpm\s+test\b/, "testing", () => ({ tool: "pnpm" })),
|
|
1196
|
+
makeRule(/^bun\s+test\b/, "testing", () => ({ tool: "bun" })),
|
|
1197
|
+
makeRule(/^(jest|vitest)\b/, "testing", (match) => ({ tool: match[1] })),
|
|
1198
|
+
makeRule(/^pytest\b/, "testing", () => ({ tool: "pytest" })),
|
|
1199
|
+
makeRule(/^go\s+test\b/, "testing", () => ({ tool: "go" })),
|
|
1200
|
+
makeRule(/^cargo\s+test\b/, "testing", () => ({ tool: "cargo" })),
|
|
1201
|
+
makeRule(/^rspec\b/, "testing", () => ({ tool: "rspec" })),
|
|
1202
|
+
makeRule(/^(php\s+)?phpunit\b/, "testing", () => ({ tool: "phpunit" })),
|
|
1203
|
+
// ─── Build ────────────────────────────────────────────────────────────────
|
|
1204
|
+
makeRule(/^npm\s+run\s+build\b/, "building", () => ({ tool: "npm" })),
|
|
1205
|
+
makeRule(/^(yarn|pnpm)\s+build\b/, "building", (match) => ({ tool: match[1] })),
|
|
1206
|
+
makeRule(/^bun\s+run\s+build\b/, "building", () => ({ tool: "bun" })),
|
|
1207
|
+
makeRule(/^(make|gmake)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1208
|
+
makeRule(/^cargo\s+build\b/, "building", () => ({ tool: "cargo" })),
|
|
1209
|
+
makeRule(/^go\s+build\b/, "building", () => ({ tool: "go" })),
|
|
1210
|
+
makeRule(/^docker\s+build\b/, "building", () => ({ tool: "docker" })),
|
|
1211
|
+
makeRule(/^(mvn|gradle)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1212
|
+
// ─── Dev server / running ─────────────────────────────────────────────────
|
|
1213
|
+
makeRule(/^npm\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1214
|
+
tool: "npm",
|
|
1215
|
+
script: cmd.split(/\s+/)[2]
|
|
1216
|
+
})),
|
|
1217
|
+
makeRule(/^(yarn|pnpm)\s+(dev|start)\b/, "running", (match) => ({ tool: match[1], script: match[2] })),
|
|
1218
|
+
makeRule(/^bun\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1219
|
+
tool: "bun",
|
|
1220
|
+
script: cmd.split(/\s+/)[2]
|
|
1221
|
+
})),
|
|
1222
|
+
makeRule(/^python\s+.*manage\.py\s+runserver\b/, "running", () => ({ tool: "django" })),
|
|
1223
|
+
// ─── Infrastructure ───────────────────────────────────────────────────────
|
|
1224
|
+
makeRule(/^ssh\b/, "infrastructure", (_m, cmd) => {
|
|
1225
|
+
const parts = cmd.split(/\s+/);
|
|
1226
|
+
return { tool: "ssh", target: parts[parts.length - 1] };
|
|
1227
|
+
}),
|
|
1228
|
+
makeRule(/^kubectl\b/, "infrastructure", (_m, cmd) => {
|
|
1229
|
+
const parts = cmd.split(/\s+/);
|
|
1230
|
+
return { tool: "kubectl", subcommand: parts[1] ?? null };
|
|
1231
|
+
}),
|
|
1232
|
+
makeRule(/^(helm|terraform|pulumi)\b/, "infrastructure", (m, cmd) => {
|
|
1233
|
+
const parts = cmd.split(/\s+/);
|
|
1234
|
+
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1235
|
+
}),
|
|
1236
|
+
makeRule(/^docker\s+(run|compose|ps|stop|start|rm|exec)\b/, "infrastructure", (_m, cmd) => {
|
|
1237
|
+
const parts = cmd.split(/\s+/);
|
|
1238
|
+
return { tool: "docker", subcommand: parts[1] };
|
|
1239
|
+
}),
|
|
1240
|
+
makeRule(/^(aws|gcloud|az)\b/, "infrastructure", (m, cmd) => {
|
|
1241
|
+
const parts = cmd.split(/\s+/);
|
|
1242
|
+
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1243
|
+
}),
|
|
1244
|
+
// ─── Database ─────────────────────────────────────────────────────────────
|
|
1245
|
+
makeRule(/^(psql|pgcli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1246
|
+
makeRule(/^(mysql|mycli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1247
|
+
makeRule(/^sqlite3\b/, "database", () => ({ tool: "sqlite3" })),
|
|
1248
|
+
makeRule(/^(mongosh|mongo)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1249
|
+
makeRule(/^redis-cli\b/, "database", () => ({ tool: "redis-cli" })),
|
|
1250
|
+
// ─── npx ─────────────────────────────────────────────────────────────────
|
|
1251
|
+
makeRule(/^npx\s+(jest|vitest|mocha|ava|tap)\b/, "testing", (_m, cmd) => ({
|
|
1252
|
+
tool: cmd.split(/\s+/)[1]
|
|
1253
|
+
})),
|
|
1254
|
+
makeRule(/^npx\s+tsc\b/, "building", () => ({ tool: "tsc" })),
|
|
1255
|
+
makeRule(/^npx\s+(eslint|tslint|biome)\b/, "linting", (_m, cmd) => ({
|
|
1256
|
+
tool: cmd.split(/\s+/)[1]
|
|
1257
|
+
})),
|
|
1258
|
+
makeRule(/^npx\s+(prettier|dprint)\b/, "linting", (_m, cmd) => ({
|
|
1259
|
+
tool: cmd.split(/\s+/)[1]
|
|
1260
|
+
})),
|
|
1261
|
+
makeRule(/^npx\s+\S+/, "running", (_m, cmd) => ({
|
|
1262
|
+
tool: cmd.split(/\s+/)[1]
|
|
1263
|
+
})),
|
|
1264
|
+
// ─── Scripts ──────────────────────────────────────────────────────────────
|
|
1265
|
+
makeRule(/^(bash|sh|zsh|fish)\s+\S+/, "running", (match) => ({ shell: match[1] })),
|
|
1266
|
+
makeRule(/^node\s+\S+\.(?:js|mjs|cjs)\b/, "running", () => ({ runtime: "node" })),
|
|
1267
|
+
makeRule(/^python3?\s+\S+\.py\b/, "running", () => ({ runtime: "python" })),
|
|
1268
|
+
// ─── Linting / code quality ───────────────────────────────────────────────
|
|
1269
|
+
makeRule(/^(eslint|tslint|biome)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1270
|
+
makeRule(/^(prettier|dprint)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1271
|
+
// ─── generic run scripts ──────────────────────────────────────────────────
|
|
1272
|
+
makeRule(/^npm\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1273
|
+
tool: "npm",
|
|
1274
|
+
script: cmd.split(/\s+/)[2]
|
|
1275
|
+
})),
|
|
1276
|
+
makeRule(/^bun\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1277
|
+
tool: "bun",
|
|
1278
|
+
script: cmd.split(/\s+/)[2]
|
|
1279
|
+
})),
|
|
1280
|
+
// ─── Code search ─────────────────────────────────────────────────────────
|
|
1281
|
+
makeRule(/^(grep|egrep|fgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1282
|
+
makeRule(/^(rg|ag|ack|ripgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1283
|
+
makeRule(/^find\s/, "exploring", () => ({ tool: "find" })),
|
|
1284
|
+
// ─── Code read ────────────────────────────────────────────────────────────
|
|
1285
|
+
makeRule(/^(cat|bat|less|more)\s+\S+/, "exploring", (match) => ({ tool: match[1] })),
|
|
1286
|
+
makeRule(/^(head|tail)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1287
|
+
// ─── File operations ──────────────────────────────────────────────────────
|
|
1288
|
+
makeRule(/^(cp|rsync)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1289
|
+
makeRule(/^(mv)\b/, "files", () => ({ tool: "mv" })),
|
|
1290
|
+
makeRule(/^(rm|rmdir)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1291
|
+
makeRule(/^(mkdir|touch|ln)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1292
|
+
makeRule(/^(chmod|chown|chgrp)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1293
|
+
// ─── Environment ──────────────────────────────────────────────────────────
|
|
1294
|
+
makeRule(/^export\s+\w+=/, "files", (_m, cmd) => {
|
|
1295
|
+
const varName = cmd.match(/^export\s+(\w+)=/)?.[1] ?? null;
|
|
1296
|
+
return { variable: varName };
|
|
1297
|
+
}),
|
|
1298
|
+
makeRule(/^source\s+/, "files", () => ({}))
|
|
1299
|
+
];
|
|
1300
|
+
RULES = buildRules(getActiveProfileRules());
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1203
1303
|
|
|
1204
1304
|
// src/capture/filter.ts
|
|
1205
1305
|
function normalizeForFilter(cmd) {
|
|
1206
1306
|
return cmd.trim().replace(/\s+/g, " ");
|
|
1207
1307
|
}
|
|
1208
|
-
var BUILTIN_IGNORE = /* @__PURE__ */ new Set(["workscribe"]);
|
|
1209
1308
|
function shouldFilter(command, ignoreList) {
|
|
1210
1309
|
const normalized = normalizeForFilter(command);
|
|
1211
1310
|
if (!normalized) return true;
|
|
@@ -1221,9 +1320,22 @@ function isDuplicate(command, cwd, lastEvent, dedupWindowMs = 5e3, now = Date.no
|
|
|
1221
1320
|
const lastTime = new Date(lastEvent.captured_at).getTime();
|
|
1222
1321
|
return lastEvent.command === normalizeForFilter(command) && lastEvent.cwd === cwd && now - lastTime < dedupWindowMs;
|
|
1223
1322
|
}
|
|
1323
|
+
var BUILTIN_IGNORE;
|
|
1324
|
+
var init_filter = __esm({
|
|
1325
|
+
"src/capture/filter.ts"() {
|
|
1326
|
+
"use strict";
|
|
1327
|
+
BUILTIN_IGNORE = /* @__PURE__ */ new Set(["workscribe"]);
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1224
1330
|
|
|
1225
1331
|
// src/capture/ide.ts
|
|
1226
|
-
var IDE_DEDUP_WINDOW_MS
|
|
1332
|
+
var IDE_DEDUP_WINDOW_MS;
|
|
1333
|
+
var init_ide = __esm({
|
|
1334
|
+
"src/capture/ide.ts"() {
|
|
1335
|
+
"use strict";
|
|
1336
|
+
IDE_DEDUP_WINDOW_MS = 3e4;
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1227
1339
|
|
|
1228
1340
|
// src/capture/index.ts
|
|
1229
1341
|
function captureCommand(opts) {
|
|
@@ -1297,11 +1409,28 @@ function captureCommand(opts) {
|
|
|
1297
1409
|
file_path: null
|
|
1298
1410
|
});
|
|
1299
1411
|
}
|
|
1412
|
+
var import_fs10;
|
|
1413
|
+
var init_capture = __esm({
|
|
1414
|
+
"src/capture/index.ts"() {
|
|
1415
|
+
"use strict";
|
|
1416
|
+
import_fs10 = __toESM(require("fs"));
|
|
1417
|
+
init_db();
|
|
1418
|
+
init_redact();
|
|
1419
|
+
init_git();
|
|
1420
|
+
init_git_snapshot();
|
|
1421
|
+
init_aider();
|
|
1422
|
+
init_patterns();
|
|
1423
|
+
init_config();
|
|
1424
|
+
init_filter();
|
|
1425
|
+
init_ide();
|
|
1426
|
+
init_paths();
|
|
1427
|
+
init_registry();
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1300
1430
|
|
|
1301
1431
|
// src/cli/capture.ts
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
program2.command("_capture").description("Internal: capture a shell command or IDE event (called by shell hook or IDE plugin)").option("--cmd <command>", "command that was executed (shell events)").option("--cwd <path>", "working directory").option("--exit <code>", "exit code", parseInt).option("--duration <ms>", "duration in milliseconds", parseInt).option("--git-before <sha>", "git HEAD SHA before this command ran").option("--type <type>", "event type override \u2014 skips normalization (IDE integrations)").option("--source <source>", "event source identifier (e.g. vscode, neovim, jupyter)").option("--file <path>", "file path for code_edit events").action((opts) => {
|
|
1432
|
+
function registerCapture(program) {
|
|
1433
|
+
program.command("_capture").description("Internal: capture a shell command or IDE event (called by shell hook or IDE plugin)").option("--cmd <command>", "command that was executed (shell events)").option("--cwd <path>", "working directory").option("--exit <code>", "exit code", parseInt).option("--duration <ms>", "duration in milliseconds", parseInt).option("--git-before <sha>", "git HEAD SHA before this command ran").option("--type <type>", "event type override \u2014 skips normalization (IDE integrations)").option("--source <source>", "event source identifier (e.g. vscode, neovim, jupyter)").option("--file <path>", "file path for code_edit events").action((opts) => {
|
|
1305
1434
|
const hangGuard = setTimeout(() => process.exit(0), CAPTURE_HANG_TIMEOUT_MS);
|
|
1306
1435
|
hangGuard.unref();
|
|
1307
1436
|
try {
|
|
@@ -1322,15 +1451,28 @@ function registerCapture(program2) {
|
|
|
1322
1451
|
}
|
|
1323
1452
|
});
|
|
1324
1453
|
}
|
|
1454
|
+
var CAPTURE_HANG_TIMEOUT_MS;
|
|
1455
|
+
var init_capture2 = __esm({
|
|
1456
|
+
"src/cli/capture.ts"() {
|
|
1457
|
+
"use strict";
|
|
1458
|
+
init_capture();
|
|
1459
|
+
init_logger();
|
|
1460
|
+
CAPTURE_HANG_TIMEOUT_MS = 5e3;
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1325
1463
|
|
|
1326
1464
|
// src/capture/detect-secrets.ts
|
|
1327
1465
|
function containsSecrets(text) {
|
|
1328
1466
|
return redact(text) !== text;
|
|
1329
1467
|
}
|
|
1468
|
+
var init_detect_secrets = __esm({
|
|
1469
|
+
"src/capture/detect-secrets.ts"() {
|
|
1470
|
+
"use strict";
|
|
1471
|
+
init_redact();
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1330
1474
|
|
|
1331
1475
|
// src/db/alerts.ts
|
|
1332
|
-
var import_fs11 = __toESM(require("fs"));
|
|
1333
|
-
var import_path7 = __toESM(require("path"));
|
|
1334
1476
|
function insertAlert(tool, sessionId, context) {
|
|
1335
1477
|
getDb().prepare(
|
|
1336
1478
|
`INSERT INTO alerts (created_at, tool, session_id, context)
|
|
@@ -1354,6 +1496,16 @@ function clearAlerts() {
|
|
|
1354
1496
|
}
|
|
1355
1497
|
return Number(result.changes);
|
|
1356
1498
|
}
|
|
1499
|
+
var import_fs11, import_path7;
|
|
1500
|
+
var init_alerts2 = __esm({
|
|
1501
|
+
"src/db/alerts.ts"() {
|
|
1502
|
+
"use strict";
|
|
1503
|
+
import_fs11 = __toESM(require("fs"));
|
|
1504
|
+
import_path7 = __toESM(require("path"));
|
|
1505
|
+
init_db();
|
|
1506
|
+
init_paths();
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1357
1509
|
|
|
1358
1510
|
// src/cli/hook.ts
|
|
1359
1511
|
function parseHookPayload(raw) {
|
|
@@ -1374,8 +1526,8 @@ function parseUserPromptPayload(raw) {
|
|
|
1374
1526
|
if (!prompt) return null;
|
|
1375
1527
|
return { session_id: sessionId, prompt, cwd };
|
|
1376
1528
|
}
|
|
1377
|
-
function registerHook(
|
|
1378
|
-
|
|
1529
|
+
function registerHook(program) {
|
|
1530
|
+
program.command("_hook").description("Internal: capture events from AI tool hooks (stdin JSON payload)").option("--source <source>", "hook source identifier (e.g. claude-code)").option("--event <event>", "hook event type (PostToolUse or UserPromptSubmit)", "PostToolUse").action((opts) => {
|
|
1379
1531
|
let raw = "";
|
|
1380
1532
|
process.stdin.setEncoding("utf-8");
|
|
1381
1533
|
process.stdin.on("data", (chunk) => {
|
|
@@ -1416,13 +1568,16 @@ function registerHook(program2) {
|
|
|
1416
1568
|
});
|
|
1417
1569
|
});
|
|
1418
1570
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1571
|
+
var init_hook = __esm({
|
|
1572
|
+
"src/cli/hook.ts"() {
|
|
1573
|
+
"use strict";
|
|
1574
|
+
init_capture();
|
|
1575
|
+
init_config();
|
|
1576
|
+
init_logger();
|
|
1577
|
+
init_detect_secrets();
|
|
1578
|
+
init_alerts2();
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1426
1581
|
|
|
1427
1582
|
// src/utils/format.ts
|
|
1428
1583
|
function formatDuration(minutes) {
|
|
@@ -1482,57 +1637,18 @@ function isoWeekKey(dateStr) {
|
|
|
1482
1637
|
const weekNum = Math.ceil(((d.getTime() - jan4.getTime()) / 864e5 + jan4.getDay() + 1) / 7);
|
|
1483
1638
|
return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
1484
1639
|
}
|
|
1640
|
+
var init_format = __esm({
|
|
1641
|
+
"src/utils/format.ts"() {
|
|
1642
|
+
"use strict";
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1485
1645
|
|
|
1486
1646
|
// src/ai/prompt.ts
|
|
1487
|
-
var SYSTEM_PROMPT = `You write short developer work summaries. Focus on what the developer accomplished and what changed \u2014 not which tools or commands were used.
|
|
1488
|
-
|
|
1489
|
-
Here is an example:
|
|
1490
|
-
|
|
1491
|
-
INPUT:
|
|
1492
|
-
Project: api-service
|
|
1493
|
-
Branch: feature/auth
|
|
1494
|
-
Duration: 2h 00m
|
|
1495
|
-
Events:
|
|
1496
|
-
- versioning {"branch":"feature/auth"}
|
|
1497
|
-
- 2x dependencies {"tool":"npm","packages":["bcrypt","jsonwebtoken"]}
|
|
1498
|
-
- 8x testing [failed]
|
|
1499
|
-
- testing
|
|
1500
|
-
- 2x files {"tool":"mkdir"}
|
|
1501
|
-
- shipping {"message":"fix: auth token validation"}
|
|
1502
|
-
- shipping {"remote":"origin","branch":"feature/auth"}
|
|
1503
|
-
|
|
1504
|
-
OUTPUT:
|
|
1505
|
-
- Added authentication dependencies (bcrypt, JWT)
|
|
1506
|
-
- Debugged and resolved failing auth tests
|
|
1507
|
-
- Committed and pushed the token validation fix
|
|
1508
|
-
|
|
1509
|
-
RULES \u2014 follow these exactly:
|
|
1510
|
-
|
|
1511
|
-
1. Interpret repeated events as patterns, not individual steps.
|
|
1512
|
-
- Many testing [failed] followed by a passing test = debugging a failure. Say "resolved failing X tests", not "ran tests multiple times".
|
|
1513
|
-
- Many versioning events = branch housekeeping. Fold into context, do not list each git command.
|
|
1514
|
-
|
|
1515
|
-
2. Omit utility commands entirely. File size checks, file reads, grep searches, and cat commands are research steps \u2014 they are not outcomes and must not appear in the summary.
|
|
1516
|
-
|
|
1517
|
-
3. Do not name the AI tool. For ai_session events: focus on what was built or committed. If a commits array is present in metadata, use those commit messages. Never write "via Claude Code", "using Claude Code", or similar \u2014 omit the tool name entirely.
|
|
1518
|
-
|
|
1519
|
-
4. For other events with a hint field: describe the developer's intent, not the raw command.
|
|
1520
|
-
|
|
1521
|
-
5. For dependencies events: infer the purpose from package names and describe the domain (bcrypt/jsonwebtoken \u2192 authentication, prisma/pg \u2192 database, jest/vitest \u2192 testing). For unfamiliar packages, name them directly.
|
|
1522
|
-
|
|
1523
|
-
6. Fold supporting events into the narrative. Building, shipping, versioning, and file operations are context \u2014 only make them a bullet when they are the primary activity (e.g. a standalone build fix or deploy with no accompanying commits).
|
|
1524
|
-
|
|
1525
|
-
7. Stick to what the events show. Do not infer subject matter from the branch name. Do not add details not derivable from the events.
|
|
1526
|
-
|
|
1527
|
-
8. For code_edit events: these represent file saves from an IDE integration. Describe what was being built based on repository context (branch name, commit messages). Never list individual filenames.
|
|
1528
|
-
|
|
1529
|
-
Maximum 5 bullets. Output ONLY the bullet points, nothing else.`;
|
|
1530
1647
|
function getSystemPrompt() {
|
|
1531
1648
|
const contexts = getActiveProfileAiContexts();
|
|
1532
1649
|
if (contexts.length === 0) return SYSTEM_PROMPT;
|
|
1533
1650
|
return SYSTEM_PROMPT + "\n\n" + contexts.join("\n");
|
|
1534
1651
|
}
|
|
1535
|
-
var DEFAULT_EXCLUDED_CATEGORIES = /* @__PURE__ */ new Set(["exploring", "note", "linting"]);
|
|
1536
1652
|
function buildExcludedSet(includeCategories = []) {
|
|
1537
1653
|
const excluded = new Set(DEFAULT_EXCLUDED_CATEGORIES);
|
|
1538
1654
|
for (const cat of includeCategories) excluded.delete(cat);
|
|
@@ -1584,39 +1700,6 @@ function buildUserMessage(payload, includeCategories = []) {
|
|
|
1584
1700
|
}
|
|
1585
1701
|
return lines.join("\n");
|
|
1586
1702
|
}
|
|
1587
|
-
var WEEKLY_SYSTEM_PROMPT = `You write short weekly developer summaries. Here is an example:
|
|
1588
|
-
|
|
1589
|
-
INPUT:
|
|
1590
|
-
Week: 2026-05-18 to 2026-05-22
|
|
1591
|
-
|
|
1592
|
-
Monday 2026-05-18:
|
|
1593
|
-
Project: api-service (feature/auth) \u2014 2h 10m
|
|
1594
|
-
Events: 3x code_commit, 2x test_run, dependency_install
|
|
1595
|
-
|
|
1596
|
-
Tuesday 2026-05-19:
|
|
1597
|
-
Project: api-service (feature/auth) \u2014 1h 30m
|
|
1598
|
-
Events: 2x code_commit, test_run, build
|
|
1599
|
-
|
|
1600
|
-
Wednesday 2026-05-20:
|
|
1601
|
-
Project: dashboard (main) \u2014 3h 00m
|
|
1602
|
-
Events: 4x code_commit, 2x build, test_run
|
|
1603
|
-
|
|
1604
|
-
Thursday 2026-05-21:
|
|
1605
|
-
Project: api-service (feature/auth) \u2014 1h 00m
|
|
1606
|
-
Events: code_commit {"message":"feat: merge auth"}, branch_switch
|
|
1607
|
-
|
|
1608
|
-
Friday 2026-05-22:
|
|
1609
|
-
Project: dashboard (main) \u2014 2h 00m
|
|
1610
|
-
Events: 3x code_commit, build
|
|
1611
|
-
|
|
1612
|
-
OUTPUT:
|
|
1613
|
-
## Week of 18 May \u2014 9h 40m across 2 projects
|
|
1614
|
-
|
|
1615
|
-
**api-service** (Mon\u2013Thu): Built and shipped the auth feature \u2014 JWT setup, token validation fixes, and final merge.
|
|
1616
|
-
**dashboard** (Wed, Fri): Active development across 7 commits with builds passing.
|
|
1617
|
-
|
|
1618
|
-
Now summarise the following week. Output ONLY the summary \u2014 a one-line header then one bullet per active project. Maximum 4 bullets.`;
|
|
1619
|
-
var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
1620
1703
|
function buildWeeklyMessage(payload) {
|
|
1621
1704
|
const lines = [`Week: ${payload.weekStart} to ${payload.weekEnd}`, ""];
|
|
1622
1705
|
for (const { date, sessions } of payload.days) {
|
|
@@ -1654,194 +1737,293 @@ function buildWeeklyMessage(payload) {
|
|
|
1654
1737
|
}
|
|
1655
1738
|
return lines.join("\n");
|
|
1656
1739
|
}
|
|
1740
|
+
var SYSTEM_PROMPT, DEFAULT_EXCLUDED_CATEGORIES, WEEKLY_SYSTEM_PROMPT, DAY_NAMES;
|
|
1741
|
+
var init_prompt = __esm({
|
|
1742
|
+
"src/ai/prompt.ts"() {
|
|
1743
|
+
"use strict";
|
|
1744
|
+
init_format();
|
|
1745
|
+
init_loader();
|
|
1746
|
+
SYSTEM_PROMPT = `You write short developer work summaries. Focus on what the developer accomplished and what changed \u2014 not which tools or commands were used.
|
|
1657
1747
|
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1748
|
+
Here is an example:
|
|
1749
|
+
|
|
1750
|
+
INPUT:
|
|
1751
|
+
Project: api-service
|
|
1752
|
+
Branch: feature/auth
|
|
1753
|
+
Duration: 2h 00m
|
|
1754
|
+
Events:
|
|
1755
|
+
- versioning {"branch":"feature/auth"}
|
|
1756
|
+
- 2x dependencies {"tool":"npm","packages":["bcrypt","jsonwebtoken"]}
|
|
1757
|
+
- 8x testing [failed]
|
|
1758
|
+
- testing
|
|
1759
|
+
- 2x files {"tool":"mkdir"}
|
|
1760
|
+
- shipping {"message":"fix: auth token validation"}
|
|
1761
|
+
- shipping {"remote":"origin","branch":"feature/auth"}
|
|
1762
|
+
|
|
1763
|
+
OUTPUT:
|
|
1764
|
+
- Added authentication dependencies (bcrypt, JWT)
|
|
1765
|
+
- Debugged and resolved failing auth tests
|
|
1766
|
+
- Committed and pushed the token validation fix
|
|
1767
|
+
|
|
1768
|
+
RULES \u2014 follow these exactly:
|
|
1769
|
+
|
|
1770
|
+
1. Interpret repeated events as patterns, not individual steps.
|
|
1771
|
+
- Many testing [failed] followed by a passing test = debugging a failure. Say "resolved failing X tests", not "ran tests multiple times".
|
|
1772
|
+
- Many versioning events = branch housekeeping. Fold into context, do not list each git command.
|
|
1773
|
+
|
|
1774
|
+
2. Omit utility commands entirely. File size checks, file reads, grep searches, and cat commands are research steps \u2014 they are not outcomes and must not appear in the summary.
|
|
1775
|
+
|
|
1776
|
+
3. Do not name the AI tool. For ai_session events: focus on what was built or committed. If a commits array is present in metadata, use those commit messages. Never write "via Claude Code", "using Claude Code", or similar \u2014 omit the tool name entirely.
|
|
1777
|
+
|
|
1778
|
+
4. For other events with a hint field: describe the developer's intent, not the raw command.
|
|
1779
|
+
|
|
1780
|
+
5. For dependencies events: infer the purpose from package names and describe the domain (bcrypt/jsonwebtoken \u2192 authentication, prisma/pg \u2192 database, jest/vitest \u2192 testing). For unfamiliar packages, name them directly.
|
|
1781
|
+
|
|
1782
|
+
6. Fold supporting events into the narrative. Building, shipping, versioning, and file operations are context \u2014 only make them a bullet when they are the primary activity (e.g. a standalone build fix or deploy with no accompanying commits).
|
|
1783
|
+
|
|
1784
|
+
7. Stick to what the events show. Do not infer subject matter from the branch name. Do not add details not derivable from the events.
|
|
1785
|
+
|
|
1786
|
+
8. For code_edit events: these represent file saves from an IDE integration. Describe what was being built based on repository context (branch name, commit messages). Never list individual filenames.
|
|
1787
|
+
|
|
1788
|
+
Maximum 5 bullets. Output ONLY the bullet points, nothing else.`;
|
|
1789
|
+
DEFAULT_EXCLUDED_CATEGORIES = /* @__PURE__ */ new Set(["exploring", "note", "linting"]);
|
|
1790
|
+
WEEKLY_SYSTEM_PROMPT = `You write short weekly developer summaries. Here is an example:
|
|
1791
|
+
|
|
1792
|
+
INPUT:
|
|
1793
|
+
Week: 2026-05-18 to 2026-05-22
|
|
1794
|
+
|
|
1795
|
+
Monday 2026-05-18:
|
|
1796
|
+
Project: api-service (feature/auth) \u2014 2h 10m
|
|
1797
|
+
Events: 3x code_commit, 2x test_run, dependency_install
|
|
1798
|
+
|
|
1799
|
+
Tuesday 2026-05-19:
|
|
1800
|
+
Project: api-service (feature/auth) \u2014 1h 30m
|
|
1801
|
+
Events: 2x code_commit, test_run, build
|
|
1802
|
+
|
|
1803
|
+
Wednesday 2026-05-20:
|
|
1804
|
+
Project: dashboard (main) \u2014 3h 00m
|
|
1805
|
+
Events: 4x code_commit, 2x build, test_run
|
|
1806
|
+
|
|
1807
|
+
Thursday 2026-05-21:
|
|
1808
|
+
Project: api-service (feature/auth) \u2014 1h 00m
|
|
1809
|
+
Events: code_commit {"message":"feat: merge auth"}, branch_switch
|
|
1810
|
+
|
|
1811
|
+
Friday 2026-05-22:
|
|
1812
|
+
Project: dashboard (main) \u2014 2h 00m
|
|
1813
|
+
Events: 3x code_commit, build
|
|
1814
|
+
|
|
1815
|
+
OUTPUT:
|
|
1816
|
+
## Week of 18 May \u2014 9h 40m across 2 projects
|
|
1817
|
+
|
|
1818
|
+
**api-service** (Mon\u2013Thu): Built and shipped the auth feature \u2014 JWT setup, token validation fixes, and final merge.
|
|
1819
|
+
**dashboard** (Wed, Fri): Active development across 7 commits with builds passing.
|
|
1820
|
+
|
|
1821
|
+
Now summarise the following week. Output ONLY the summary \u2014 a one-line header then one bullet per active project. Maximum 4 bullets.`;
|
|
1822
|
+
DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
1688
1823
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
// src/ai/generic-provider.ts
|
|
1827
|
+
var GenericProvider;
|
|
1828
|
+
var init_generic_provider = __esm({
|
|
1829
|
+
"src/ai/generic-provider.ts"() {
|
|
1830
|
+
"use strict";
|
|
1831
|
+
init_prompt();
|
|
1832
|
+
init_fetch();
|
|
1833
|
+
GenericProvider = class {
|
|
1834
|
+
constructor(config, model, apiKey, baseUrl, includeCategories = []) {
|
|
1835
|
+
this.config = config;
|
|
1836
|
+
this.model = model;
|
|
1837
|
+
this.apiKey = apiKey;
|
|
1838
|
+
this.baseUrl = baseUrl;
|
|
1839
|
+
this.includeCategories = includeCategories;
|
|
1840
|
+
}
|
|
1841
|
+
config;
|
|
1842
|
+
model;
|
|
1843
|
+
apiKey;
|
|
1844
|
+
baseUrl;
|
|
1845
|
+
includeCategories;
|
|
1846
|
+
async call(system, userMsg, maxTokens) {
|
|
1847
|
+
const response = await fetchWithTimeout(this.config.chatUrl(this.baseUrl, this.apiKey, this.model), {
|
|
1848
|
+
method: "POST",
|
|
1849
|
+
headers: this.config.headers(this.apiKey),
|
|
1850
|
+
body: JSON.stringify(this.config.buildChatBody(this.model, system, userMsg, maxTokens))
|
|
1851
|
+
});
|
|
1852
|
+
if (!response.ok) {
|
|
1853
|
+
throw new Error(this.config.chatError(response.status, await response.text()));
|
|
1854
|
+
}
|
|
1855
|
+
return this.config.parseText(await response.json());
|
|
1856
|
+
}
|
|
1857
|
+
async summarize(payload) {
|
|
1858
|
+
return this.call(getSystemPrompt(), buildUserMessage(payload, this.includeCategories), this.config.dailyTokens);
|
|
1859
|
+
}
|
|
1860
|
+
async summarizeWeek(payload) {
|
|
1861
|
+
return this.call(WEEKLY_SYSTEM_PROMPT, buildWeeklyMessage(payload), this.config.weeklyTokens);
|
|
1862
|
+
}
|
|
1863
|
+
async validate() {
|
|
1864
|
+
const isPost = this.config.validateMethod === "POST";
|
|
1865
|
+
const response = await fetchWithTimeout(this.config.validateUrl(this.baseUrl, this.apiKey, this.model), {
|
|
1866
|
+
method: this.config.validateMethod,
|
|
1867
|
+
headers: isPost ? this.config.headers(this.apiKey) : void 0,
|
|
1868
|
+
body: isPost && this.config.buildValidateBody ? JSON.stringify(this.config.buildValidateBody(this.model)) : void 0
|
|
1869
|
+
});
|
|
1870
|
+
if (!response.ok) {
|
|
1871
|
+
throw new Error(this.config.validateError(response.status, await response.text(), this.baseUrl));
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1699
1875
|
}
|
|
1700
|
-
};
|
|
1876
|
+
});
|
|
1701
1877
|
|
|
1702
1878
|
// src/ai/provider-config.ts
|
|
1703
|
-
var PROVIDER_CONFIGS
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1879
|
+
var PROVIDER_CONFIGS;
|
|
1880
|
+
var init_provider_config = __esm({
|
|
1881
|
+
"src/ai/provider-config.ts"() {
|
|
1882
|
+
"use strict";
|
|
1883
|
+
PROVIDER_CONFIGS = {
|
|
1884
|
+
anthropic: {
|
|
1885
|
+
defaultModel: "claude-haiku-4-5-20251001",
|
|
1886
|
+
defaultBaseUrl: "https://api.anthropic.com/v1",
|
|
1887
|
+
dailyTokens: 1024,
|
|
1888
|
+
weeklyTokens: 1024,
|
|
1889
|
+
chatUrl: (baseUrl) => `${baseUrl}/messages`,
|
|
1890
|
+
validateUrl: (baseUrl) => `${baseUrl}/messages`,
|
|
1891
|
+
validateMethod: "POST",
|
|
1892
|
+
headers: (apiKey) => ({
|
|
1893
|
+
"Content-Type": "application/json",
|
|
1894
|
+
"x-api-key": apiKey,
|
|
1895
|
+
"anthropic-version": "2023-06-01"
|
|
1896
|
+
}),
|
|
1897
|
+
buildChatBody: (model, system, userMsg, maxTokens) => ({
|
|
1898
|
+
model,
|
|
1899
|
+
max_tokens: maxTokens,
|
|
1900
|
+
system,
|
|
1901
|
+
messages: [{ role: "user", content: userMsg }]
|
|
1902
|
+
}),
|
|
1903
|
+
buildValidateBody: (model) => ({
|
|
1904
|
+
model,
|
|
1905
|
+
max_tokens: 1,
|
|
1906
|
+
messages: [{ role: "user", content: "." }]
|
|
1907
|
+
}),
|
|
1908
|
+
parseText: (json) => {
|
|
1909
|
+
const data = json;
|
|
1910
|
+
return data.content.find((b) => b.type === "text")?.text ?? "";
|
|
1911
|
+
},
|
|
1912
|
+
chatError: (status, body) => `Anthropic API error ${status}: ${body}`,
|
|
1913
|
+
validateError: (status, body) => `Anthropic API error ${status}: ${body}`
|
|
1914
|
+
},
|
|
1915
|
+
openai: {
|
|
1916
|
+
defaultModel: "gpt-4o-mini",
|
|
1917
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
1918
|
+
dailyTokens: 1024,
|
|
1919
|
+
weeklyTokens: 1024,
|
|
1920
|
+
chatUrl: (baseUrl) => `${baseUrl}/chat/completions`,
|
|
1921
|
+
validateUrl: (baseUrl) => `${baseUrl}/chat/completions`,
|
|
1922
|
+
validateMethod: "POST",
|
|
1923
|
+
headers: (apiKey) => ({
|
|
1924
|
+
"Content-Type": "application/json",
|
|
1925
|
+
Authorization: `Bearer ${apiKey}`
|
|
1926
|
+
}),
|
|
1927
|
+
buildChatBody: (model, system, userMsg, maxTokens) => ({
|
|
1928
|
+
model,
|
|
1929
|
+
messages: [
|
|
1930
|
+
{ role: "system", content: system },
|
|
1931
|
+
{ role: "user", content: userMsg }
|
|
1932
|
+
],
|
|
1933
|
+
max_tokens: maxTokens
|
|
1934
|
+
}),
|
|
1935
|
+
buildValidateBody: (model) => ({
|
|
1936
|
+
model,
|
|
1937
|
+
messages: [{ role: "user", content: "." }],
|
|
1938
|
+
max_tokens: 1
|
|
1939
|
+
}),
|
|
1940
|
+
parseText: (json) => {
|
|
1941
|
+
const data = json;
|
|
1942
|
+
return data.choices[0]?.message?.content ?? "";
|
|
1943
|
+
},
|
|
1944
|
+
chatError: (status, body) => `OpenAI API error ${status}: ${body}`,
|
|
1945
|
+
validateError: (status, body) => `OpenAI API error ${status}: ${body}`
|
|
1946
|
+
},
|
|
1947
|
+
ollama: {
|
|
1948
|
+
defaultModel: "llama3",
|
|
1949
|
+
defaultBaseUrl: "http://localhost:11434",
|
|
1950
|
+
dailyTokens: 300,
|
|
1951
|
+
weeklyTokens: 400,
|
|
1952
|
+
chatUrl: (baseUrl) => `${baseUrl}/api/chat`,
|
|
1953
|
+
validateUrl: (baseUrl) => `${baseUrl}/api/tags`,
|
|
1954
|
+
validateMethod: "GET",
|
|
1955
|
+
headers: () => ({ "Content-Type": "application/json" }),
|
|
1956
|
+
buildChatBody: (model, system, userMsg, maxTokens) => ({
|
|
1957
|
+
model,
|
|
1958
|
+
stream: false,
|
|
1959
|
+
messages: [
|
|
1960
|
+
{ role: "system", content: system },
|
|
1961
|
+
{ role: "user", content: userMsg }
|
|
1962
|
+
],
|
|
1963
|
+
options: { num_predict: maxTokens, temperature: 0.3 }
|
|
1964
|
+
}),
|
|
1965
|
+
parseText: (json) => {
|
|
1966
|
+
return json.message?.content ?? "";
|
|
1967
|
+
},
|
|
1968
|
+
chatError: (status, body) => `Ollama error ${status}: ${body}. Is Ollama running? Try: ollama serve`,
|
|
1969
|
+
validateError: (_status, _body, baseUrl) => `Ollama is not reachable at ${baseUrl}. Is it running? Try: ollama serve`
|
|
1970
|
+
},
|
|
1971
|
+
gemini: {
|
|
1972
|
+
defaultModel: "gemini-2.0-flash-lite",
|
|
1973
|
+
defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
1974
|
+
dailyTokens: 1024,
|
|
1975
|
+
weeklyTokens: 1024,
|
|
1976
|
+
// API key and model are embedded in the URL — not in headers
|
|
1977
|
+
chatUrl: (baseUrl, apiKey, model) => `${baseUrl}/models/${model}:generateContent?key=${apiKey}`,
|
|
1978
|
+
validateUrl: (baseUrl, apiKey, model) => `${baseUrl}/models/${model}:generateContent?key=${apiKey}`,
|
|
1979
|
+
validateMethod: "POST",
|
|
1980
|
+
headers: () => ({ "Content-Type": "application/json" }),
|
|
1981
|
+
buildChatBody: (_model, system, userMsg, maxTokens) => ({
|
|
1982
|
+
system_instruction: { parts: [{ text: system }] },
|
|
1983
|
+
contents: [{ role: "user", parts: [{ text: userMsg }] }],
|
|
1984
|
+
generationConfig: { maxOutputTokens: maxTokens, temperature: 0.3 }
|
|
1985
|
+
}),
|
|
1986
|
+
buildValidateBody: () => ({
|
|
1987
|
+
contents: [{ role: "user", parts: [{ text: "." }] }],
|
|
1988
|
+
generationConfig: { maxOutputTokens: 1 }
|
|
1989
|
+
}),
|
|
1990
|
+
parseText: (json) => {
|
|
1991
|
+
const data = json;
|
|
1992
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text ?? "";
|
|
1993
|
+
},
|
|
1994
|
+
chatError: (status, body) => `Gemini API error ${status}: ${body}`,
|
|
1995
|
+
validateError: (status, body) => `Gemini API error ${status}: ${body}`
|
|
1996
|
+
},
|
|
1997
|
+
groq: {
|
|
1998
|
+
defaultModel: "llama-3.1-8b-instant",
|
|
1999
|
+
defaultBaseUrl: "https://api.groq.com/openai/v1",
|
|
2000
|
+
dailyTokens: 1024,
|
|
2001
|
+
weeklyTokens: 1024,
|
|
2002
|
+
chatUrl: (baseUrl) => `${baseUrl}/chat/completions`,
|
|
2003
|
+
validateUrl: (baseUrl) => `${baseUrl}/models`,
|
|
2004
|
+
validateMethod: "GET",
|
|
2005
|
+
headers: (apiKey) => ({
|
|
2006
|
+
"Content-Type": "application/json",
|
|
2007
|
+
Authorization: `Bearer ${apiKey}`
|
|
2008
|
+
}),
|
|
2009
|
+
buildChatBody: (model, system, userMsg, maxTokens) => ({
|
|
2010
|
+
model,
|
|
2011
|
+
messages: [
|
|
2012
|
+
{ role: "system", content: system },
|
|
2013
|
+
{ role: "user", content: userMsg }
|
|
2014
|
+
],
|
|
2015
|
+
max_tokens: maxTokens
|
|
2016
|
+
}),
|
|
2017
|
+
parseText: (json) => {
|
|
2018
|
+
const data = json;
|
|
2019
|
+
return data.choices?.[0]?.message?.content ?? "";
|
|
2020
|
+
},
|
|
2021
|
+
chatError: (status, body) => `Groq API error ${status}: ${body}`,
|
|
2022
|
+
validateError: (status, body) => `Groq API error ${status}: ${body}`
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
1843
2025
|
}
|
|
1844
|
-
};
|
|
2026
|
+
});
|
|
1845
2027
|
|
|
1846
2028
|
// src/ai/router.ts
|
|
1847
2029
|
function getProvider(config) {
|
|
@@ -1874,12 +2056,13 @@ Run: workscribe config set ai.apiKey <your-key>`);
|
|
|
1874
2056
|
config.summary?.includeCategories ?? []
|
|
1875
2057
|
);
|
|
1876
2058
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
2059
|
+
var init_router = __esm({
|
|
2060
|
+
"src/ai/router.ts"() {
|
|
2061
|
+
"use strict";
|
|
2062
|
+
init_generic_provider();
|
|
2063
|
+
init_provider_config();
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
1883
2066
|
|
|
1884
2067
|
// src/utils/completion-scripts.ts
|
|
1885
2068
|
function bashCompletion(commands) {
|
|
@@ -1934,12 +2117,13 @@ function fishCompletion(commands) {
|
|
|
1934
2117
|
];
|
|
1935
2118
|
return lines.join("\n");
|
|
1936
2119
|
}
|
|
2120
|
+
var init_completion_scripts = __esm({
|
|
2121
|
+
"src/utils/completion-scripts.ts"() {
|
|
2122
|
+
"use strict";
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
1937
2125
|
|
|
1938
2126
|
// src/capture/shell-hook.ts
|
|
1939
|
-
var FISH_COMPLETIONS_DIR = import_path8.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
1940
|
-
var HOOK_START = "# workscribe:hook:start";
|
|
1941
|
-
var HOOK_END = "# workscribe:hook:end";
|
|
1942
|
-
var HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
1943
2127
|
function getInstalledHookBlock(rcFile) {
|
|
1944
2128
|
if (!import_fs12.default.existsSync(rcFile)) return null;
|
|
1945
2129
|
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
@@ -1994,9 +2178,10 @@ function resolveWorkscribeBinary() {
|
|
|
1994
2178
|
return null;
|
|
1995
2179
|
}
|
|
1996
2180
|
}
|
|
1997
|
-
function injectBinaryPath(hookContent, binaryPath) {
|
|
2181
|
+
function injectBinaryPath(hookContent, binaryPath, nodePath) {
|
|
2182
|
+
const pinnedInvocation = `"${nodePath}" "${binaryPath}"`;
|
|
1998
2183
|
return hookContent.replace("command -v workscribe > /dev/null 2>&1 || return 0", `[[ -x "${binaryPath}" ]] || return 0`).replace("command -v workscribe &>/dev/null || return 0", `[[ -x "${binaryPath}" ]] || return 0`).replace("type -q workscribe\n or return 0", `test -x "${binaryPath}"
|
|
1999
|
-
or return 0`).replaceAll("command workscribe _capture",
|
|
2184
|
+
or return 0`).replaceAll("command workscribe _capture", `${pinnedInvocation} _capture`).replaceAll("command workscribe alerts", `${pinnedInvocation} alerts`);
|
|
2000
2185
|
}
|
|
2001
2186
|
function isCompletionsInstalled(shell) {
|
|
2002
2187
|
if (shell.name === "fish") {
|
|
@@ -2029,14 +2214,25 @@ source "${cacheFile}"
|
|
|
2029
2214
|
import_fs12.default.appendFileSync(shell.rcFile, sourceLine);
|
|
2030
2215
|
}
|
|
2031
2216
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2217
|
+
var import_fs12, import_path8, import_os3, import_child_process3, FISH_COMPLETIONS_DIR, HOOK_START, HOOK_END, HOOK_PREAMBLE;
|
|
2218
|
+
var init_shell_hook = __esm({
|
|
2219
|
+
"src/capture/shell-hook.ts"() {
|
|
2220
|
+
"use strict";
|
|
2221
|
+
import_fs12 = __toESM(require("fs"));
|
|
2222
|
+
import_path8 = __toESM(require("path"));
|
|
2223
|
+
import_os3 = __toESM(require("os"));
|
|
2224
|
+
import_child_process3 = require("child_process");
|
|
2225
|
+
init_logger();
|
|
2226
|
+
init_completion_scripts();
|
|
2227
|
+
init_paths();
|
|
2228
|
+
FISH_COMPLETIONS_DIR = import_path8.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
2229
|
+
HOOK_START = "# workscribe:hook:start";
|
|
2230
|
+
HOOK_END = "# workscribe:hook:end";
|
|
2231
|
+
HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2037
2234
|
|
|
2038
2235
|
// src/capture/hooks/utils.ts
|
|
2039
|
-
var import_child_process4 = require("child_process");
|
|
2040
2236
|
function isCommandInstalled(cmd) {
|
|
2041
2237
|
try {
|
|
2042
2238
|
(0, import_child_process4.execSync)(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -2045,9 +2241,15 @@ function isCommandInstalled(cmd) {
|
|
|
2045
2241
|
return false;
|
|
2046
2242
|
}
|
|
2047
2243
|
}
|
|
2244
|
+
var import_child_process4;
|
|
2245
|
+
var init_utils = __esm({
|
|
2246
|
+
"src/capture/hooks/utils.ts"() {
|
|
2247
|
+
"use strict";
|
|
2248
|
+
import_child_process4 = require("child_process");
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2048
2251
|
|
|
2049
2252
|
// src/capture/hooks/claude-code.ts
|
|
2050
|
-
var CLAUDE_SETTINGS_PATH = import_path9.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2051
2253
|
function readSettings() {
|
|
2052
2254
|
if (!import_fs13.default.existsSync(CLAUDE_SETTINGS_PATH)) return {};
|
|
2053
2255
|
try {
|
|
@@ -2056,86 +2258,155 @@ function readSettings() {
|
|
|
2056
2258
|
return {};
|
|
2057
2259
|
}
|
|
2058
2260
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2261
|
+
function commandsForEvent(hooks, eventName) {
|
|
2262
|
+
const entries = hooks[eventName] ?? [];
|
|
2263
|
+
const commands = [];
|
|
2264
|
+
for (const entry of entries) {
|
|
2265
|
+
const entryHooks = entry.hooks ?? [];
|
|
2266
|
+
for (const hookItem of entryHooks) {
|
|
2267
|
+
const hook = hookItem;
|
|
2268
|
+
if (typeof hook.command === "string" && hook.command.includes(HOOK_MARKER)) {
|
|
2269
|
+
commands.push(hook.command);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
return commands;
|
|
2274
|
+
}
|
|
2275
|
+
function findWorkscribeHookCommands(settings) {
|
|
2276
|
+
const hooks = settings.hooks ?? {};
|
|
2277
|
+
return {
|
|
2278
|
+
postToolUse: commandsForEvent(hooks, "PostToolUse"),
|
|
2279
|
+
userPromptSubmit: commandsForEvent(hooks, "UserPromptSubmit")
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
function isPinnedCommand(command) {
|
|
2283
|
+
const trimmed = command.trimStart();
|
|
2284
|
+
return trimmed.startsWith('"') || trimmed.startsWith("/");
|
|
2285
|
+
}
|
|
2286
|
+
function withoutWorkscribeEntries(entries) {
|
|
2287
|
+
return entries.map((entry) => {
|
|
2288
|
+
const record = entry;
|
|
2289
|
+
const entryHooks = record.hooks ?? [];
|
|
2290
|
+
const filteredHooks = entryHooks.filter((hookItem) => {
|
|
2291
|
+
const hook = hookItem;
|
|
2292
|
+
return !(typeof hook.command === "string" && hook.command.includes(HOOK_MARKER));
|
|
2082
2293
|
});
|
|
2083
|
-
return
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2294
|
+
return { ...record, hooks: filteredHooks };
|
|
2295
|
+
}).filter((entry) => entry.hooks.length > 0);
|
|
2296
|
+
}
|
|
2297
|
+
function checkHookHealth() {
|
|
2298
|
+
const { postToolUse, userPromptSubmit } = findWorkscribeHookCommands(readSettings());
|
|
2299
|
+
const allCommands = [...postToolUse, ...userPromptSubmit];
|
|
2300
|
+
if (allCommands.length === 0) return { installed: false, pinned: true, confirmedBroken: null };
|
|
2301
|
+
if (allCommands.every(isPinnedCommand)) return { installed: true, pinned: true, confirmedBroken: null };
|
|
2302
|
+
let confirmedBroken;
|
|
2303
|
+
try {
|
|
2304
|
+
(0, import_child_process5.execFileSync)("workscribe", ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
2305
|
+
confirmedBroken = false;
|
|
2306
|
+
} catch {
|
|
2307
|
+
confirmedBroken = true;
|
|
2308
|
+
}
|
|
2309
|
+
return { installed: true, pinned: false, confirmedBroken };
|
|
2310
|
+
}
|
|
2311
|
+
var import_fs13, import_path9, import_os4, import_child_process5, CLAUDE_SETTINGS_PATH, HOOK_MARKER, claudeCodeIntegration;
|
|
2312
|
+
var init_claude_code = __esm({
|
|
2313
|
+
"src/capture/hooks/claude-code.ts"() {
|
|
2314
|
+
"use strict";
|
|
2315
|
+
import_fs13 = __toESM(require("fs"));
|
|
2316
|
+
import_path9 = __toESM(require("path"));
|
|
2317
|
+
import_os4 = __toESM(require("os"));
|
|
2318
|
+
import_child_process5 = require("child_process");
|
|
2319
|
+
init_utils();
|
|
2320
|
+
init_shell_hook();
|
|
2321
|
+
CLAUDE_SETTINGS_PATH = import_path9.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2322
|
+
HOOK_MARKER = "_hook --source claude-code";
|
|
2323
|
+
claudeCodeIntegration = {
|
|
2324
|
+
displayName: "Claude Code",
|
|
2325
|
+
isInstalled() {
|
|
2326
|
+
return isCommandInstalled("claude");
|
|
2327
|
+
},
|
|
2328
|
+
// "Installed" means a *correctly pinned* hook is present, not just any
|
|
2329
|
+
// workscribe hook — this is deliberate: it makes `workscribe init`'s
|
|
2330
|
+
// existing prompt-and-install flow naturally offer to repair a stale,
|
|
2331
|
+
// unpinned hook from before this fix, with no new UI code needed there.
|
|
2332
|
+
isHookInstalled() {
|
|
2333
|
+
const { postToolUse, userPromptSubmit } = findWorkscribeHookCommands(readSettings());
|
|
2334
|
+
if (postToolUse.length === 0 || userPromptSubmit.length === 0) return false;
|
|
2335
|
+
return [...postToolUse, ...userPromptSubmit].every(isPinnedCommand);
|
|
2336
|
+
},
|
|
2337
|
+
install() {
|
|
2338
|
+
const settings = readSettings();
|
|
2339
|
+
const existingHooks = settings.hooks ?? {};
|
|
2340
|
+
const existingPostToolUse = withoutWorkscribeEntries(existingHooks.PostToolUse ?? []);
|
|
2341
|
+
const existingUserPromptSubmit = withoutWorkscribeEntries(existingHooks.UserPromptSubmit ?? []);
|
|
2342
|
+
const workscribeBinary = resolveWorkscribeBinary();
|
|
2343
|
+
const baseCommand = workscribeBinary ? `"${process.execPath}" "${workscribeBinary}"` : "workscribe";
|
|
2344
|
+
settings.hooks = {
|
|
2345
|
+
...existingHooks,
|
|
2346
|
+
PostToolUse: [
|
|
2347
|
+
...existingPostToolUse,
|
|
2103
2348
|
{
|
|
2104
|
-
|
|
2105
|
-
|
|
2349
|
+
matcher: "Bash",
|
|
2350
|
+
hooks: [{ type: "command", command: `${baseCommand} ${HOOK_MARKER}`, async: true }]
|
|
2351
|
+
}
|
|
2352
|
+
],
|
|
2353
|
+
UserPromptSubmit: [
|
|
2354
|
+
...existingUserPromptSubmit,
|
|
2355
|
+
{
|
|
2356
|
+
hooks: [
|
|
2357
|
+
{
|
|
2358
|
+
type: "command",
|
|
2359
|
+
command: `${baseCommand} ${HOOK_MARKER} --event UserPromptSubmit`
|
|
2360
|
+
}
|
|
2361
|
+
]
|
|
2106
2362
|
}
|
|
2107
2363
|
]
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2364
|
+
};
|
|
2365
|
+
import_fs13.default.mkdirSync(import_path9.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2366
|
+
import_fs13.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2367
|
+
}
|
|
2110
2368
|
};
|
|
2111
|
-
import_fs13.default.mkdirSync(import_path9.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2112
|
-
import_fs13.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2113
2369
|
}
|
|
2114
|
-
};
|
|
2370
|
+
});
|
|
2115
2371
|
|
|
2116
2372
|
// src/capture/hooks/index.ts
|
|
2117
|
-
var AI_HOOK_INTEGRATIONS
|
|
2118
|
-
|
|
2119
|
-
|
|
2373
|
+
var AI_HOOK_INTEGRATIONS;
|
|
2374
|
+
var init_hooks = __esm({
|
|
2375
|
+
"src/capture/hooks/index.ts"() {
|
|
2376
|
+
"use strict";
|
|
2377
|
+
init_claude_code();
|
|
2378
|
+
AI_HOOK_INTEGRATIONS = {
|
|
2379
|
+
"claude-code": claudeCodeIntegration
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2120
2383
|
|
|
2121
2384
|
// src/cli/completions.ts
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
return program2.commands.filter((command) => !HIDDEN_COMMANDS.has(command.name())).map((command) => ({ name: command.name(), description: command.description() }));
|
|
2385
|
+
function getPublicCommands(program) {
|
|
2386
|
+
return program.commands.filter((command) => !HIDDEN_COMMANDS.has(command.name())).map((command) => ({ name: command.name(), description: command.description() }));
|
|
2125
2387
|
}
|
|
2126
|
-
function registerCompletions(
|
|
2127
|
-
|
|
2388
|
+
function registerCompletions(program) {
|
|
2389
|
+
program.command("completions [shell]").description("Print shell completion script (bash, zsh, fish)").action((shell) => {
|
|
2128
2390
|
const detected = shell ?? (process.env.SHELL ?? "").split("/").at(-1);
|
|
2129
2391
|
if (!detected || !["bash", "zsh", "fish"].includes(detected)) {
|
|
2130
2392
|
log.error(`Unknown shell: "${detected ?? ""}". Supported: bash, zsh, fish`);
|
|
2131
2393
|
log.plain(` Usage: workscribe completions <bash|zsh|fish>`);
|
|
2132
2394
|
process.exit(1);
|
|
2133
2395
|
}
|
|
2134
|
-
const commands = getPublicCommands(
|
|
2396
|
+
const commands = getPublicCommands(program);
|
|
2135
2397
|
const script = detected === "bash" ? bashCompletion(commands) : detected === "zsh" ? zshCompletion(commands) : fishCompletion(commands);
|
|
2136
2398
|
process.stdout.write(script + "\n");
|
|
2137
2399
|
});
|
|
2138
2400
|
}
|
|
2401
|
+
var HIDDEN_COMMANDS;
|
|
2402
|
+
var init_completions = __esm({
|
|
2403
|
+
"src/cli/completions.ts"() {
|
|
2404
|
+
"use strict";
|
|
2405
|
+
init_logger();
|
|
2406
|
+
init_completion_scripts();
|
|
2407
|
+
HIDDEN_COMMANDS = /* @__PURE__ */ new Set(["_capture", "_hook"]);
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2139
2410
|
|
|
2140
2411
|
// src/cli/init.ts
|
|
2141
2412
|
function detectShell() {
|
|
@@ -2152,8 +2423,8 @@ function step(current, total, label) {
|
|
|
2152
2423
|
log.plain(import_chalk2.default.bold(`
|
|
2153
2424
|
[${current}/${total}] ${label}`));
|
|
2154
2425
|
}
|
|
2155
|
-
function registerInit(
|
|
2156
|
-
|
|
2426
|
+
function registerInit(program) {
|
|
2427
|
+
program.command("init").description("Set up Workscribe and install or update shell hooks").action(async () => {
|
|
2157
2428
|
const TOTAL_STEPS = 5;
|
|
2158
2429
|
log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
|
|
2159
2430
|
step(1, TOTAL_STEPS, "Creating data directories");
|
|
@@ -2189,7 +2460,7 @@ function registerInit(program2) {
|
|
|
2189
2460
|
} else {
|
|
2190
2461
|
const hookContent = import_fs14.default.readFileSync(hookFile, "utf-8");
|
|
2191
2462
|
const binaryPath = resolveWorkscribeBinary();
|
|
2192
|
-
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath) : hookContent;
|
|
2463
|
+
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2193
2464
|
if (shell.name === "fish") {
|
|
2194
2465
|
const confDir = import_path10.default.dirname(shell.rcFile);
|
|
2195
2466
|
if (!import_fs14.default.existsSync(confDir)) import_fs14.default.mkdirSync(confDir, { recursive: true });
|
|
@@ -2231,7 +2502,7 @@ function registerInit(program2) {
|
|
|
2231
2502
|
});
|
|
2232
2503
|
if (installComps) {
|
|
2233
2504
|
try {
|
|
2234
|
-
installCompletions(shell, getPublicCommands(
|
|
2505
|
+
installCompletions(shell, getPublicCommands(program));
|
|
2235
2506
|
log.success("Shell completions installed");
|
|
2236
2507
|
} catch (error) {
|
|
2237
2508
|
log.warn(`Completions install failed: ${error.message}`);
|
|
@@ -2402,12 +2673,32 @@ function registerInit(program2) {
|
|
|
2402
2673
|
log.plain("");
|
|
2403
2674
|
});
|
|
2404
2675
|
}
|
|
2676
|
+
var import_fs14, import_path10, import_os5, import_prompts, import_chalk2;
|
|
2677
|
+
var init_init = __esm({
|
|
2678
|
+
"src/cli/init.ts"() {
|
|
2679
|
+
"use strict";
|
|
2680
|
+
import_fs14 = __toESM(require("fs"));
|
|
2681
|
+
import_path10 = __toESM(require("path"));
|
|
2682
|
+
import_os5 = __toESM(require("os"));
|
|
2683
|
+
import_prompts = __toESM(require("prompts"));
|
|
2684
|
+
import_chalk2 = __toESM(require("chalk"));
|
|
2685
|
+
init_paths();
|
|
2686
|
+
init_config();
|
|
2687
|
+
init_loader();
|
|
2688
|
+
init_fetcher();
|
|
2689
|
+
init_db();
|
|
2690
|
+
init_router();
|
|
2691
|
+
init_logger();
|
|
2692
|
+
init_registry();
|
|
2693
|
+
init_shell_hook();
|
|
2694
|
+
init_hooks();
|
|
2695
|
+
init_completions();
|
|
2696
|
+
}
|
|
2697
|
+
});
|
|
2405
2698
|
|
|
2406
2699
|
// src/cli/status.ts
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
function registerStatus(program2) {
|
|
2410
|
-
program2.command("status").description("Show capture status and event counts").action(() => {
|
|
2700
|
+
function registerStatus(program) {
|
|
2701
|
+
program.command("status").description("Show capture status and event counts").action(() => {
|
|
2411
2702
|
const config = readConfig();
|
|
2412
2703
|
const total = getEventCount();
|
|
2413
2704
|
const todayEvents = getEventsForDate(todayDate());
|
|
@@ -2442,6 +2733,20 @@ function registerStatus(program2) {
|
|
|
2442
2733
|
` ${import_chalk3.default.yellow("!")} ${import_chalk3.default.yellow(String(pendingAlerts.length))} pending security alert${pendingAlerts.length === 1 ? "" : "s"}` + import_chalk3.default.dim(" (run: workscribe alerts)")
|
|
2443
2734
|
);
|
|
2444
2735
|
}
|
|
2736
|
+
const hookHealth = checkHookHealth();
|
|
2737
|
+
if (hookHealth.installed && !hookHealth.pinned) {
|
|
2738
|
+
log.plain("");
|
|
2739
|
+
if (hookHealth.confirmedBroken) {
|
|
2740
|
+
log.plain(
|
|
2741
|
+
` ${import_chalk3.default.yellow("\u26A0")} Claude Code hook is using an unpinned command and is failing right now \u2014 Claude Code activity is not being captured.`
|
|
2742
|
+
);
|
|
2743
|
+
} else {
|
|
2744
|
+
log.plain(
|
|
2745
|
+
` ${import_chalk3.default.yellow("\u26A0")} Claude Code hook is using an unpinned command, which can silently break if your default Node version changes.`
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
log.plain(import_chalk3.default.dim(" Run workscribe init to repair it"));
|
|
2749
|
+
}
|
|
2445
2750
|
const { total: weekTotal, otherCount } = getRecentOtherRate(7);
|
|
2446
2751
|
if (weekTotal > 0) {
|
|
2447
2752
|
const otherPct = Math.round(otherCount / weekTotal * 100);
|
|
@@ -2460,30 +2765,26 @@ function registerStatus(program2) {
|
|
|
2460
2765
|
log.plain("");
|
|
2461
2766
|
});
|
|
2462
2767
|
}
|
|
2768
|
+
var import_fs15, import_chalk3;
|
|
2769
|
+
var init_status = __esm({
|
|
2770
|
+
"src/cli/status.ts"() {
|
|
2771
|
+
"use strict";
|
|
2772
|
+
import_fs15 = __toESM(require("fs"));
|
|
2773
|
+
import_chalk3 = __toESM(require("chalk"));
|
|
2774
|
+
init_db();
|
|
2775
|
+
init_config();
|
|
2776
|
+
init_logger();
|
|
2777
|
+
init_format();
|
|
2778
|
+
init_db();
|
|
2779
|
+
init_paths();
|
|
2780
|
+
init_alerts2();
|
|
2781
|
+
init_claude_code();
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2463
2784
|
|
|
2464
2785
|
// src/cli/events.ts
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
shipping: import_chalk4.default.green,
|
|
2468
|
-
versioning: import_chalk4.default.blue,
|
|
2469
|
-
building: import_chalk4.default.cyan,
|
|
2470
|
-
running: import_chalk4.default.white,
|
|
2471
|
-
testing: import_chalk4.default.magenta,
|
|
2472
|
-
linting: import_chalk4.default.yellow,
|
|
2473
|
-
dependencies: import_chalk4.default.yellow,
|
|
2474
|
-
infrastructure: import_chalk4.default.red,
|
|
2475
|
-
releasing: import_chalk4.default.bold.green,
|
|
2476
|
-
database: import_chalk4.default.bold.blue,
|
|
2477
|
-
files: import_chalk4.default.dim.yellow,
|
|
2478
|
-
exploring: import_chalk4.default.dim.cyan,
|
|
2479
|
-
ai_session: import_chalk4.default.bold.magenta,
|
|
2480
|
-
other: import_chalk4.default.gray
|
|
2481
|
-
};
|
|
2482
|
-
var AI_TOOL_DISPLAY_NAMES = Object.fromEntries(
|
|
2483
|
-
loadAIToolRegistry().map((entry) => [entry.tool, entry.displayName])
|
|
2484
|
-
);
|
|
2485
|
-
function registerEvents(program2) {
|
|
2486
|
-
program2.command("events").description("List captured events").option("--date <YYYY-MM-DD>", "date to list events for (default: today)").option("--limit <n>", "max events to show", parseInt).action((opts) => {
|
|
2786
|
+
function registerEvents(program) {
|
|
2787
|
+
program.command("events").description("List captured events").option("--date <YYYY-MM-DD>", "date to list events for (default: today)").option("--limit <n>", "max events to show", parseInt).action((opts) => {
|
|
2487
2788
|
let date = todayDate();
|
|
2488
2789
|
if (opts.date) {
|
|
2489
2790
|
try {
|
|
@@ -2530,12 +2831,38 @@ function registerEvents(program2) {
|
|
|
2530
2831
|
log.plain("");
|
|
2531
2832
|
});
|
|
2532
2833
|
}
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2834
|
+
var import_chalk4, CATEGORY_COLORS, AI_TOOL_DISPLAY_NAMES;
|
|
2835
|
+
var init_events = __esm({
|
|
2836
|
+
"src/cli/events.ts"() {
|
|
2837
|
+
"use strict";
|
|
2838
|
+
import_chalk4 = __toESM(require("chalk"));
|
|
2839
|
+
init_db();
|
|
2840
|
+
init_logger();
|
|
2841
|
+
init_format();
|
|
2842
|
+
init_registry();
|
|
2843
|
+
CATEGORY_COLORS = {
|
|
2844
|
+
shipping: import_chalk4.default.green,
|
|
2845
|
+
versioning: import_chalk4.default.blue,
|
|
2846
|
+
building: import_chalk4.default.cyan,
|
|
2847
|
+
running: import_chalk4.default.white,
|
|
2848
|
+
testing: import_chalk4.default.magenta,
|
|
2849
|
+
linting: import_chalk4.default.yellow,
|
|
2850
|
+
dependencies: import_chalk4.default.yellow,
|
|
2851
|
+
infrastructure: import_chalk4.default.red,
|
|
2852
|
+
releasing: import_chalk4.default.bold.green,
|
|
2853
|
+
database: import_chalk4.default.bold.blue,
|
|
2854
|
+
files: import_chalk4.default.dim.yellow,
|
|
2855
|
+
exploring: import_chalk4.default.dim.cyan,
|
|
2856
|
+
ai_session: import_chalk4.default.bold.magenta,
|
|
2857
|
+
other: import_chalk4.default.gray
|
|
2858
|
+
};
|
|
2859
|
+
AI_TOOL_DISPLAY_NAMES = Object.fromEntries(
|
|
2860
|
+
loadAIToolRegistry().map((entry) => [entry.tool, entry.displayName])
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2536
2864
|
|
|
2537
2865
|
// src/sessions/grouper.ts
|
|
2538
|
-
var LONG_RUNNING_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2539
2866
|
function splitByIdleGap(events, idleMs) {
|
|
2540
2867
|
if (events.length === 0) return [];
|
|
2541
2868
|
const groups = [[events[0]]];
|
|
@@ -2552,7 +2879,6 @@ function splitByIdleGap(events, idleMs) {
|
|
|
2552
2879
|
}
|
|
2553
2880
|
return groups;
|
|
2554
2881
|
}
|
|
2555
|
-
var REPO_SWITCH_GAP_MS = 10 * 60 * 1e3;
|
|
2556
2882
|
function splitByRepo(events, gapMs = 0) {
|
|
2557
2883
|
if (events.length === 0) return [];
|
|
2558
2884
|
const groups = [[events[0]]];
|
|
@@ -2573,6 +2899,14 @@ function splitByRepo(events, gapMs = 0) {
|
|
|
2573
2899
|
function minutesBetween(start, end) {
|
|
2574
2900
|
return (new Date(end).getTime() - new Date(start).getTime()) / 6e4;
|
|
2575
2901
|
}
|
|
2902
|
+
var LONG_RUNNING_THRESHOLD_MS, REPO_SWITCH_GAP_MS;
|
|
2903
|
+
var init_grouper = __esm({
|
|
2904
|
+
"src/sessions/grouper.ts"() {
|
|
2905
|
+
"use strict";
|
|
2906
|
+
LONG_RUNNING_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2907
|
+
REPO_SWITCH_GAP_MS = 10 * 60 * 1e3;
|
|
2908
|
+
}
|
|
2909
|
+
});
|
|
2576
2910
|
|
|
2577
2911
|
// src/sessions/detector.ts
|
|
2578
2912
|
function getOrBuildSessions(date) {
|
|
@@ -2640,10 +2974,18 @@ function buildSessions(date, events, savedSummaries = /* @__PURE__ */ new Map())
|
|
|
2640
2974
|
}
|
|
2641
2975
|
return sessions;
|
|
2642
2976
|
}
|
|
2977
|
+
var init_detector = __esm({
|
|
2978
|
+
"src/sessions/detector.ts"() {
|
|
2979
|
+
"use strict";
|
|
2980
|
+
init_db();
|
|
2981
|
+
init_config();
|
|
2982
|
+
init_grouper();
|
|
2983
|
+
}
|
|
2984
|
+
});
|
|
2643
2985
|
|
|
2644
2986
|
// src/cli/sessions.ts
|
|
2645
|
-
function registerSessions(
|
|
2646
|
-
|
|
2987
|
+
function registerSessions(program) {
|
|
2988
|
+
program.command("sessions").description("List detected work sessions").option("--date <YYYY-MM-DD>", "date to show sessions for (default: today)").action((opts) => {
|
|
2647
2989
|
let date = todayDate();
|
|
2648
2990
|
if (opts.date) {
|
|
2649
2991
|
try {
|
|
@@ -2683,12 +3025,16 @@ function registerSessions(program2) {
|
|
|
2683
3025
|
log.plain("");
|
|
2684
3026
|
});
|
|
2685
3027
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
3028
|
+
var import_chalk5;
|
|
3029
|
+
var init_sessions = __esm({
|
|
3030
|
+
"src/cli/sessions.ts"() {
|
|
3031
|
+
"use strict";
|
|
3032
|
+
import_chalk5 = __toESM(require("chalk"));
|
|
3033
|
+
init_detector();
|
|
3034
|
+
init_logger();
|
|
3035
|
+
init_format();
|
|
3036
|
+
}
|
|
3037
|
+
});
|
|
2692
3038
|
|
|
2693
3039
|
// src/integrations/webhook.ts
|
|
2694
3040
|
async function postToWebhook(url, text) {
|
|
@@ -2706,6 +3052,12 @@ async function postToWebhook(url, text) {
|
|
|
2706
3052
|
throw new Error(`Webhook POST failed ${response.status}: ${body}`);
|
|
2707
3053
|
}
|
|
2708
3054
|
}
|
|
3055
|
+
var init_webhook = __esm({
|
|
3056
|
+
"src/integrations/webhook.ts"() {
|
|
3057
|
+
"use strict";
|
|
3058
|
+
init_fetch();
|
|
3059
|
+
}
|
|
3060
|
+
});
|
|
2709
3061
|
|
|
2710
3062
|
// src/integrations/slack.ts
|
|
2711
3063
|
function formatForSlack(markdown, username) {
|
|
@@ -2716,6 +3068,11 @@ ${body}` : body;
|
|
|
2716
3068
|
function isSlackWebhookUrl(url) {
|
|
2717
3069
|
return url.startsWith("https://hooks.slack.com/");
|
|
2718
3070
|
}
|
|
3071
|
+
var init_slack = __esm({
|
|
3072
|
+
"src/integrations/slack.ts"() {
|
|
3073
|
+
"use strict";
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
2719
3076
|
|
|
2720
3077
|
// src/cli/summary.ts
|
|
2721
3078
|
function parseBullets(text) {
|
|
@@ -2767,8 +3124,8 @@ async function handleWebhook(opts, config, summary) {
|
|
|
2767
3124
|
log.warn("--slack requires integrations.slackWebhook to be set in config");
|
|
2768
3125
|
}
|
|
2769
3126
|
}
|
|
2770
|
-
function registerSummary(
|
|
2771
|
-
|
|
3127
|
+
function registerSummary(program) {
|
|
3128
|
+
program.command("summary").description("Generate an AI work summary").option("--date <YYYY-MM-DD>", "date to summarize (default: today)").option("--week", "summarize the current week").option("--fresh", "bypass cache and regenerate").option("--verbose", "show all captured events including those not sent to AI").option("--slack", "post summary to Slack webhook from config").option("--webhook <url>", "post summary to a custom webhook URL").action(async (opts) => {
|
|
2772
3129
|
const config = readConfig();
|
|
2773
3130
|
let provider;
|
|
2774
3131
|
try {
|
|
@@ -2928,10 +3285,28 @@ ${bullets.trim()}`;
|
|
|
2928
3285
|
await handleWebhook(opts, config, fullSummary);
|
|
2929
3286
|
});
|
|
2930
3287
|
}
|
|
3288
|
+
var import_chalk6, import_fs16, import_os6, import_path11;
|
|
3289
|
+
var init_summary = __esm({
|
|
3290
|
+
"src/cli/summary.ts"() {
|
|
3291
|
+
"use strict";
|
|
3292
|
+
import_chalk6 = __toESM(require("chalk"));
|
|
3293
|
+
import_fs16 = __toESM(require("fs"));
|
|
3294
|
+
import_os6 = __toESM(require("os"));
|
|
3295
|
+
import_path11 = __toESM(require("path"));
|
|
3296
|
+
init_detector();
|
|
3297
|
+
init_router();
|
|
3298
|
+
init_config();
|
|
3299
|
+
init_db();
|
|
3300
|
+
init_logger();
|
|
3301
|
+
init_format();
|
|
3302
|
+
init_webhook();
|
|
3303
|
+
init_slack();
|
|
3304
|
+
init_paths();
|
|
3305
|
+
init_prompt();
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
2931
3308
|
|
|
2932
3309
|
// src/cli/export.ts
|
|
2933
|
-
var import_fs17 = __toESM(require("fs"));
|
|
2934
|
-
var import_path12 = __toESM(require("path"));
|
|
2935
3310
|
function eventRowToPayload2(row) {
|
|
2936
3311
|
let metadata = {};
|
|
2937
3312
|
try {
|
|
@@ -2951,8 +3326,8 @@ function eventRowToPayload2(row) {
|
|
|
2951
3326
|
}
|
|
2952
3327
|
return payload;
|
|
2953
3328
|
}
|
|
2954
|
-
function registerExport(
|
|
2955
|
-
|
|
3329
|
+
function registerExport(program) {
|
|
3330
|
+
program.command("export").description("Export work summary as a markdown file").option("--date <YYYY-MM-DD>", "date to export (default: today)").option("--output <path>", "output file path (default: ~/.workscribe/exports/)").action(async (opts) => {
|
|
2956
3331
|
let date = todayDate();
|
|
2957
3332
|
if (opts.date) {
|
|
2958
3333
|
try {
|
|
@@ -2996,11 +3371,24 @@ ${summary}
|
|
|
2996
3371
|
log.success(`Saved: ${outputPath}`);
|
|
2997
3372
|
});
|
|
2998
3373
|
}
|
|
3374
|
+
var import_fs17, import_path12;
|
|
3375
|
+
var init_export = __esm({
|
|
3376
|
+
"src/cli/export.ts"() {
|
|
3377
|
+
"use strict";
|
|
3378
|
+
import_fs17 = __toESM(require("fs"));
|
|
3379
|
+
import_path12 = __toESM(require("path"));
|
|
3380
|
+
init_detector();
|
|
3381
|
+
init_router();
|
|
3382
|
+
init_config();
|
|
3383
|
+
init_logger();
|
|
3384
|
+
init_format();
|
|
3385
|
+
init_paths();
|
|
3386
|
+
}
|
|
3387
|
+
});
|
|
2999
3388
|
|
|
3000
3389
|
// src/cli/config.ts
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
const configCmd = program2.command("config").description("View or update Workscribe configuration").action(() => {
|
|
3390
|
+
function registerConfig(program) {
|
|
3391
|
+
const configCmd = program.command("config").description("View or update Workscribe configuration").action(() => {
|
|
3004
3392
|
const config = readConfig();
|
|
3005
3393
|
log.plain("");
|
|
3006
3394
|
log.plain(import_chalk7.default.bold(" Workscribe Config"));
|
|
@@ -3031,16 +3419,6 @@ function registerConfig(program2) {
|
|
|
3031
3419
|
}
|
|
3032
3420
|
});
|
|
3033
3421
|
}
|
|
3034
|
-
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
3035
|
-
"apiKey",
|
|
3036
|
-
"api_key",
|
|
3037
|
-
"secret",
|
|
3038
|
-
"token",
|
|
3039
|
-
"password",
|
|
3040
|
-
"passwd",
|
|
3041
|
-
"webhook",
|
|
3042
|
-
"slackWebhook"
|
|
3043
|
-
]);
|
|
3044
3422
|
function maskValue(key, value) {
|
|
3045
3423
|
if (SENSITIVE_KEYS.has(key) && typeof value === "string" && value.length > 0) {
|
|
3046
3424
|
return import_chalk7.default.dim(`${value.slice(0, 4)}${"*".repeat(8)} (${value.length} chars)`);
|
|
@@ -3059,11 +3437,29 @@ function printConfig(obj, indent) {
|
|
|
3059
3437
|
}
|
|
3060
3438
|
}
|
|
3061
3439
|
}
|
|
3440
|
+
var import_chalk7, SENSITIVE_KEYS;
|
|
3441
|
+
var init_config2 = __esm({
|
|
3442
|
+
"src/cli/config.ts"() {
|
|
3443
|
+
"use strict";
|
|
3444
|
+
import_chalk7 = __toESM(require("chalk"));
|
|
3445
|
+
init_config();
|
|
3446
|
+
init_logger();
|
|
3447
|
+
SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
3448
|
+
"apiKey",
|
|
3449
|
+
"api_key",
|
|
3450
|
+
"secret",
|
|
3451
|
+
"token",
|
|
3452
|
+
"password",
|
|
3453
|
+
"passwd",
|
|
3454
|
+
"webhook",
|
|
3455
|
+
"slackWebhook"
|
|
3456
|
+
]);
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3062
3459
|
|
|
3063
3460
|
// src/cli/pause.ts
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
program2.command("pause").description("Pause capture temporarily").action(() => {
|
|
3461
|
+
function registerPause(program) {
|
|
3462
|
+
program.command("pause").description("Pause capture temporarily").action(() => {
|
|
3067
3463
|
if (import_fs18.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
3068
3464
|
log.info("Capture is already paused. Run workscribe resume to restart.");
|
|
3069
3465
|
return;
|
|
@@ -3071,7 +3467,7 @@ function registerPause(program2) {
|
|
|
3071
3467
|
import_fs18.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
|
|
3072
3468
|
log.success("Capture paused. Run workscribe resume to restart.");
|
|
3073
3469
|
});
|
|
3074
|
-
|
|
3470
|
+
program.command("resume").description("Resume capture after a pause").action(() => {
|
|
3075
3471
|
if (!import_fs18.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
3076
3472
|
log.info("Capture is not paused.");
|
|
3077
3473
|
return;
|
|
@@ -3080,10 +3476,19 @@ function registerPause(program2) {
|
|
|
3080
3476
|
log.success("Capture resumed.");
|
|
3081
3477
|
});
|
|
3082
3478
|
}
|
|
3479
|
+
var import_fs18;
|
|
3480
|
+
var init_pause = __esm({
|
|
3481
|
+
"src/cli/pause.ts"() {
|
|
3482
|
+
"use strict";
|
|
3483
|
+
import_fs18 = __toESM(require("fs"));
|
|
3484
|
+
init_paths();
|
|
3485
|
+
init_logger();
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3083
3488
|
|
|
3084
3489
|
// src/cli/undo.ts
|
|
3085
|
-
function registerUndo(
|
|
3086
|
-
|
|
3490
|
+
function registerUndo(program) {
|
|
3491
|
+
program.command("undo").description("Remove the last N captured events").option("-n, --count <number>", "number of events to remove", "1").option("--dry-run", "preview what would be deleted without deleting").action((opts) => {
|
|
3087
3492
|
const count = parseInt(opts.count, 10);
|
|
3088
3493
|
if (isNaN(count) || count < 1) {
|
|
3089
3494
|
log.error("--count must be a positive integer");
|
|
@@ -3109,12 +3514,17 @@ function registerUndo(program2) {
|
|
|
3109
3514
|
log.success(`Removed ${ids.length} event${ids.length > 1 ? "s" : ""}.`);
|
|
3110
3515
|
});
|
|
3111
3516
|
}
|
|
3517
|
+
var init_undo = __esm({
|
|
3518
|
+
"src/cli/undo.ts"() {
|
|
3519
|
+
"use strict";
|
|
3520
|
+
init_db();
|
|
3521
|
+
init_logger();
|
|
3522
|
+
}
|
|
3523
|
+
});
|
|
3112
3524
|
|
|
3113
3525
|
// src/cli/clear.ts
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
function registerClear(program2) {
|
|
3117
|
-
program2.command("clear").description("Delete all captured events for a date").option("--date <YYYY-MM-DD>", "date to clear (default: today)").option("--dry-run", "preview what would be deleted without deleting").option("--yes", "skip confirmation prompt").action(async (opts) => {
|
|
3526
|
+
function registerClear(program) {
|
|
3527
|
+
program.command("clear").description("Delete all captured events for a date").option("--date <YYYY-MM-DD>", "date to clear (default: today)").option("--dry-run", "preview what would be deleted without deleting").option("--yes", "skip confirmation prompt").action(async (opts) => {
|
|
3118
3528
|
let date = todayDate();
|
|
3119
3529
|
if (opts.date) {
|
|
3120
3530
|
try {
|
|
@@ -3159,13 +3569,19 @@ function registerClear(program2) {
|
|
|
3159
3569
|
log.success(`Cleared ${deleted} event${deleted !== 1 ? "s" : ""} for ${date}.`);
|
|
3160
3570
|
});
|
|
3161
3571
|
}
|
|
3572
|
+
var import_chalk8, import_prompts2;
|
|
3573
|
+
var init_clear = __esm({
|
|
3574
|
+
"src/cli/clear.ts"() {
|
|
3575
|
+
"use strict";
|
|
3576
|
+
import_chalk8 = __toESM(require("chalk"));
|
|
3577
|
+
import_prompts2 = __toESM(require("prompts"));
|
|
3578
|
+
init_db();
|
|
3579
|
+
init_logger();
|
|
3580
|
+
init_format();
|
|
3581
|
+
}
|
|
3582
|
+
});
|
|
3162
3583
|
|
|
3163
3584
|
// src/cli/uninstall.ts
|
|
3164
|
-
var import_fs19 = __toESM(require("fs"));
|
|
3165
|
-
var import_path13 = __toESM(require("path"));
|
|
3166
|
-
var import_os7 = __toESM(require("os"));
|
|
3167
|
-
var import_prompts3 = __toESM(require("prompts"));
|
|
3168
|
-
var import_chalk9 = __toESM(require("chalk"));
|
|
3169
3585
|
function detectShellRcFiles() {
|
|
3170
3586
|
const candidates = [
|
|
3171
3587
|
{ name: "zsh", rcFile: import_path13.default.join(import_os7.default.homedir(), ".zshrc") },
|
|
@@ -3175,8 +3591,8 @@ function detectShellRcFiles() {
|
|
|
3175
3591
|
];
|
|
3176
3592
|
return candidates.filter((candidate) => import_fs19.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
3177
3593
|
}
|
|
3178
|
-
function registerUninstall(
|
|
3179
|
-
|
|
3594
|
+
function registerUninstall(program) {
|
|
3595
|
+
program.command("uninstall").description("Remove shell hooks and optionally delete all Workscribe data").option("--hooks-only", "remove shell hooks only, skip data deletion prompt (used by npm preuninstall)").action(async (opts) => {
|
|
3180
3596
|
log.plain(import_chalk9.default.bold("\n Workscribe \u2014 Uninstall\n"));
|
|
3181
3597
|
const hooked = detectShellRcFiles();
|
|
3182
3598
|
if (hooked.length === 0) {
|
|
@@ -3221,14 +3637,24 @@ function registerUninstall(program2) {
|
|
|
3221
3637
|
log.plain("");
|
|
3222
3638
|
});
|
|
3223
3639
|
}
|
|
3640
|
+
var import_fs19, import_path13, import_os7, import_prompts3, import_chalk9;
|
|
3641
|
+
var init_uninstall = __esm({
|
|
3642
|
+
"src/cli/uninstall.ts"() {
|
|
3643
|
+
"use strict";
|
|
3644
|
+
import_fs19 = __toESM(require("fs"));
|
|
3645
|
+
import_path13 = __toESM(require("path"));
|
|
3646
|
+
import_os7 = __toESM(require("os"));
|
|
3647
|
+
import_prompts3 = __toESM(require("prompts"));
|
|
3648
|
+
import_chalk9 = __toESM(require("chalk"));
|
|
3649
|
+
init_paths();
|
|
3650
|
+
init_shell_hook();
|
|
3651
|
+
init_logger();
|
|
3652
|
+
}
|
|
3653
|
+
});
|
|
3224
3654
|
|
|
3225
3655
|
// src/cli/profile.ts
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
var import_chalk10 = __toESM(require("chalk"));
|
|
3229
|
-
var MIN_SUGGEST_COUNT = 5;
|
|
3230
|
-
function registerProfile(program2) {
|
|
3231
|
-
const profile = program2.command("profile").description("Manage stack profiles for better command tracking");
|
|
3656
|
+
function registerProfile(program) {
|
|
3657
|
+
const profile = program.command("profile").description("Manage stack profiles for better command tracking");
|
|
3232
3658
|
profile.command("list").description("List available profiles from the registry").action(async () => {
|
|
3233
3659
|
const config = readConfig();
|
|
3234
3660
|
const activeNames = new Set(config.capture.profiles);
|
|
@@ -3434,10 +3860,26 @@ function registerProfile(program2) {
|
|
|
3434
3860
|
}
|
|
3435
3861
|
});
|
|
3436
3862
|
}
|
|
3863
|
+
var import_fs20, import_path14, import_chalk10, MIN_SUGGEST_COUNT;
|
|
3864
|
+
var init_profile = __esm({
|
|
3865
|
+
"src/cli/profile.ts"() {
|
|
3866
|
+
"use strict";
|
|
3867
|
+
import_fs20 = __toESM(require("fs"));
|
|
3868
|
+
import_path14 = __toESM(require("path"));
|
|
3869
|
+
import_chalk10 = __toESM(require("chalk"));
|
|
3870
|
+
init_config();
|
|
3871
|
+
init_paths();
|
|
3872
|
+
init_loader();
|
|
3873
|
+
init_fetcher();
|
|
3874
|
+
init_db();
|
|
3875
|
+
init_logger();
|
|
3876
|
+
MIN_SUGGEST_COUNT = 5;
|
|
3877
|
+
}
|
|
3878
|
+
});
|
|
3437
3879
|
|
|
3438
3880
|
// src/cli/note.ts
|
|
3439
|
-
function registerNote(
|
|
3440
|
-
|
|
3881
|
+
function registerNote(program) {
|
|
3882
|
+
program.command("note <text>").description("Attach a free-text annotation to the current session").action((text) => {
|
|
3441
3883
|
const { repoName, branch } = getGitContext(process.cwd());
|
|
3442
3884
|
insertEvent({
|
|
3443
3885
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3455,12 +3897,16 @@ function registerNote(program2) {
|
|
|
3455
3897
|
log.success(`Note saved${repoName ? ` to ${repoName}` : ""}`);
|
|
3456
3898
|
});
|
|
3457
3899
|
}
|
|
3900
|
+
var init_note = __esm({
|
|
3901
|
+
"src/cli/note.ts"() {
|
|
3902
|
+
"use strict";
|
|
3903
|
+
init_db();
|
|
3904
|
+
init_git();
|
|
3905
|
+
init_logger();
|
|
3906
|
+
}
|
|
3907
|
+
});
|
|
3458
3908
|
|
|
3459
3909
|
// src/cli/week.ts
|
|
3460
|
-
var import_chalk11 = __toESM(require("chalk"));
|
|
3461
|
-
var import_fs21 = __toESM(require("fs"));
|
|
3462
|
-
var import_path15 = __toESM(require("path"));
|
|
3463
|
-
var import_os8 = __toESM(require("os"));
|
|
3464
3910
|
function eventRowToPayload3(row) {
|
|
3465
3911
|
let metadata = {};
|
|
3466
3912
|
try {
|
|
@@ -3507,8 +3953,8 @@ async function postSummary(opts, config, summary) {
|
|
|
3507
3953
|
log.error(`Webhook failed: ${error.message}`);
|
|
3508
3954
|
}
|
|
3509
3955
|
}
|
|
3510
|
-
function registerWeek(
|
|
3511
|
-
|
|
3956
|
+
function registerWeek(program) {
|
|
3957
|
+
program.command("week").description("Generate an AI weekly digest (Mon\u2013today)").option("--fresh", "bypass cache and regenerate").option("--slack", "post to Slack webhook from config").option("--webhook <url>", "post to a custom webhook URL").action(async (opts) => {
|
|
3512
3958
|
const config = readConfig();
|
|
3513
3959
|
let provider;
|
|
3514
3960
|
try {
|
|
@@ -3559,14 +4005,26 @@ function registerWeek(program2) {
|
|
|
3559
4005
|
}
|
|
3560
4006
|
});
|
|
3561
4007
|
}
|
|
4008
|
+
var import_chalk11, import_fs21, import_path15, import_os8;
|
|
4009
|
+
var init_week = __esm({
|
|
4010
|
+
"src/cli/week.ts"() {
|
|
4011
|
+
"use strict";
|
|
4012
|
+
import_chalk11 = __toESM(require("chalk"));
|
|
4013
|
+
import_fs21 = __toESM(require("fs"));
|
|
4014
|
+
import_path15 = __toESM(require("path"));
|
|
4015
|
+
import_os8 = __toESM(require("os"));
|
|
4016
|
+
init_detector();
|
|
4017
|
+
init_router();
|
|
4018
|
+
init_config();
|
|
4019
|
+
init_logger();
|
|
4020
|
+
init_format();
|
|
4021
|
+
init_webhook();
|
|
4022
|
+
init_slack();
|
|
4023
|
+
init_paths();
|
|
4024
|
+
}
|
|
4025
|
+
});
|
|
3562
4026
|
|
|
3563
4027
|
// src/cli/bug-report.ts
|
|
3564
|
-
var import_fs22 = __toESM(require("fs"));
|
|
3565
|
-
var import_os9 = __toESM(require("os"));
|
|
3566
|
-
var import_path16 = __toESM(require("path"));
|
|
3567
|
-
var import_chalk12 = __toESM(require("chalk"));
|
|
3568
|
-
var GITHUB_ISSUES_URL = "https://github.com/workscribe/workscribe/issues/new";
|
|
3569
|
-
var DEBUG_LOG_TAIL_LINES = 100;
|
|
3570
4028
|
function tailFile(filePath, lines) {
|
|
3571
4029
|
if (!import_fs22.default.existsSync(filePath)) return "(no debug log found \u2014 set WORKSCRIBE_DEBUG=1 to enable)";
|
|
3572
4030
|
const content = import_fs22.default.readFileSync(filePath, "utf-8");
|
|
@@ -3584,12 +4042,12 @@ function redactConfig() {
|
|
|
3584
4042
|
return "(could not read config)";
|
|
3585
4043
|
}
|
|
3586
4044
|
}
|
|
3587
|
-
function registerBugReport(
|
|
3588
|
-
|
|
4045
|
+
function registerBugReport(program) {
|
|
4046
|
+
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
3589
4047
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3590
4048
|
const reportPath = import_path16.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
3591
4049
|
const systemInfo = [
|
|
3592
|
-
`Workscribe version : ${"1.14.
|
|
4050
|
+
`Workscribe version : ${"1.14.1"}`,
|
|
3593
4051
|
`Node.js version : ${process.version}`,
|
|
3594
4052
|
`Platform : ${process.platform}`,
|
|
3595
4053
|
`Architecture : ${process.arch}`,
|
|
@@ -3620,7 +4078,7 @@ function registerBugReport(program2) {
|
|
|
3620
4078
|
process.exit(1);
|
|
3621
4079
|
}
|
|
3622
4080
|
const params = new URLSearchParams({
|
|
3623
|
-
title: `Bug report \u2014 workscribe ${"1.14.
|
|
4081
|
+
title: `Bug report \u2014 workscribe ${"1.14.1"}`,
|
|
3624
4082
|
body: `<!-- Attach ${import_path16.default.basename(reportPath)} or paste the relevant sections below -->
|
|
3625
4083
|
|
|
3626
4084
|
**Describe the issue:**
|
|
@@ -3638,9 +4096,23 @@ function registerBugReport(program2) {
|
|
|
3638
4096
|
log.plain("");
|
|
3639
4097
|
});
|
|
3640
4098
|
}
|
|
4099
|
+
var import_fs22, import_os9, import_path16, import_chalk12, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
4100
|
+
var init_bug_report = __esm({
|
|
4101
|
+
"src/cli/bug-report.ts"() {
|
|
4102
|
+
"use strict";
|
|
4103
|
+
import_fs22 = __toESM(require("fs"));
|
|
4104
|
+
import_os9 = __toESM(require("os"));
|
|
4105
|
+
import_path16 = __toESM(require("path"));
|
|
4106
|
+
import_chalk12 = __toESM(require("chalk"));
|
|
4107
|
+
init_paths();
|
|
4108
|
+
init_config();
|
|
4109
|
+
init_logger();
|
|
4110
|
+
GITHUB_ISSUES_URL = "https://github.com/workscribe/workscribe/issues/new";
|
|
4111
|
+
DEBUG_LOG_TAIL_LINES = 100;
|
|
4112
|
+
}
|
|
4113
|
+
});
|
|
3641
4114
|
|
|
3642
4115
|
// src/cli/alerts.ts
|
|
3643
|
-
var import_chalk13 = __toESM(require("chalk"));
|
|
3644
4116
|
function formatTimestamp(ms) {
|
|
3645
4117
|
return new Date(ms).toLocaleString([], {
|
|
3646
4118
|
month: "short",
|
|
@@ -3649,8 +4121,8 @@ function formatTimestamp(ms) {
|
|
|
3649
4121
|
minute: "2-digit"
|
|
3650
4122
|
});
|
|
3651
4123
|
}
|
|
3652
|
-
function registerAlerts(
|
|
3653
|
-
const alertsCmd =
|
|
4124
|
+
function registerAlerts(program) {
|
|
4125
|
+
const alertsCmd = program.command("alerts").description("Show and manage security alerts from AI session monitoring").option("--check", "Print warning if alerts are pending (used by shell hooks)").action((opts) => {
|
|
3654
4126
|
if (opts.check) {
|
|
3655
4127
|
if (hasPendingAlerts()) {
|
|
3656
4128
|
log.plain(
|
|
@@ -3687,10 +4159,17 @@ function registerAlerts(program2) {
|
|
|
3687
4159
|
}
|
|
3688
4160
|
});
|
|
3689
4161
|
}
|
|
4162
|
+
var import_chalk13;
|
|
4163
|
+
var init_alerts3 = __esm({
|
|
4164
|
+
"src/cli/alerts.ts"() {
|
|
4165
|
+
"use strict";
|
|
4166
|
+
import_chalk13 = __toESM(require("chalk"));
|
|
4167
|
+
init_alerts2();
|
|
4168
|
+
init_logger();
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
3690
4171
|
|
|
3691
4172
|
// src/cli/context.ts
|
|
3692
|
-
var import_path17 = __toESM(require("path"));
|
|
3693
|
-
var import_chalk14 = __toESM(require("chalk"));
|
|
3694
4173
|
function toContextJson(session) {
|
|
3695
4174
|
return {
|
|
3696
4175
|
date: session.date,
|
|
@@ -3725,8 +4204,8 @@ function printSessionContext(session, heading) {
|
|
|
3725
4204
|
}
|
|
3726
4205
|
log.plain("");
|
|
3727
4206
|
}
|
|
3728
|
-
function registerContext(
|
|
3729
|
-
|
|
4207
|
+
function registerContext(program) {
|
|
4208
|
+
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) => {
|
|
3730
4209
|
if (opts.file) {
|
|
3731
4210
|
const normalizedFile = import_path17.default.normalize(opts.file);
|
|
3732
4211
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
@@ -3761,10 +4240,21 @@ function registerContext(program2) {
|
|
|
3761
4240
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
3762
4241
|
});
|
|
3763
4242
|
}
|
|
4243
|
+
var import_path17, import_chalk14;
|
|
4244
|
+
var init_context = __esm({
|
|
4245
|
+
"src/cli/context.ts"() {
|
|
4246
|
+
"use strict";
|
|
4247
|
+
import_path17 = __toESM(require("path"));
|
|
4248
|
+
import_chalk14 = __toESM(require("chalk"));
|
|
4249
|
+
init_db();
|
|
4250
|
+
init_git();
|
|
4251
|
+
init_config();
|
|
4252
|
+
init_format();
|
|
4253
|
+
init_logger();
|
|
4254
|
+
}
|
|
4255
|
+
});
|
|
3764
4256
|
|
|
3765
4257
|
// src/utils/update-check.ts
|
|
3766
|
-
var import_fs23 = __toESM(require("fs"));
|
|
3767
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3768
4258
|
function readCache() {
|
|
3769
4259
|
try {
|
|
3770
4260
|
return JSON.parse(import_fs23.default.readFileSync(UPDATE_CHECK_PATH, "utf-8"));
|
|
@@ -3787,7 +4277,7 @@ function isNewer(latest, current) {
|
|
|
3787
4277
|
return latestPatch > currentPatch;
|
|
3788
4278
|
}
|
|
3789
4279
|
function checkForUpdate() {
|
|
3790
|
-
const current = "1.14.
|
|
4280
|
+
const current = "1.14.1";
|
|
3791
4281
|
const cache = readCache();
|
|
3792
4282
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
3793
4283
|
process.stderr.write(
|
|
@@ -3807,31 +4297,102 @@ function checkForUpdate() {
|
|
|
3807
4297
|
});
|
|
3808
4298
|
}
|
|
3809
4299
|
}
|
|
4300
|
+
var import_fs23, CHECK_INTERVAL_MS;
|
|
4301
|
+
var init_update_check = __esm({
|
|
4302
|
+
"src/utils/update-check.ts"() {
|
|
4303
|
+
"use strict";
|
|
4304
|
+
import_fs23 = __toESM(require("fs"));
|
|
4305
|
+
init_paths();
|
|
4306
|
+
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
4307
|
+
}
|
|
4308
|
+
});
|
|
4309
|
+
|
|
4310
|
+
// src/main.ts
|
|
4311
|
+
var require_main = __commonJS({
|
|
4312
|
+
"src/main.ts"() {
|
|
4313
|
+
"use strict";
|
|
4314
|
+
var import_commander = require("commander");
|
|
4315
|
+
init_capture2();
|
|
4316
|
+
init_hook();
|
|
4317
|
+
init_init();
|
|
4318
|
+
init_status();
|
|
4319
|
+
init_events();
|
|
4320
|
+
init_sessions();
|
|
4321
|
+
init_summary();
|
|
4322
|
+
init_export();
|
|
4323
|
+
init_config2();
|
|
4324
|
+
init_pause();
|
|
4325
|
+
init_undo();
|
|
4326
|
+
init_clear();
|
|
4327
|
+
init_uninstall();
|
|
4328
|
+
init_profile();
|
|
4329
|
+
init_note();
|
|
4330
|
+
init_week();
|
|
4331
|
+
init_bug_report();
|
|
4332
|
+
init_completions();
|
|
4333
|
+
init_alerts3();
|
|
4334
|
+
init_context();
|
|
4335
|
+
init_update_check();
|
|
4336
|
+
checkForUpdate();
|
|
4337
|
+
var program = new import_commander.Command();
|
|
4338
|
+
program.name("workscribe").description("Automatic developer work journal").version("1.14.1");
|
|
4339
|
+
registerCapture(program);
|
|
4340
|
+
registerHook(program);
|
|
4341
|
+
registerInit(program);
|
|
4342
|
+
registerStatus(program);
|
|
4343
|
+
registerEvents(program);
|
|
4344
|
+
registerSessions(program);
|
|
4345
|
+
registerSummary(program);
|
|
4346
|
+
registerExport(program);
|
|
4347
|
+
registerConfig(program);
|
|
4348
|
+
registerPause(program);
|
|
4349
|
+
registerUndo(program);
|
|
4350
|
+
registerClear(program);
|
|
4351
|
+
registerUninstall(program);
|
|
4352
|
+
registerProfile(program);
|
|
4353
|
+
registerNote(program);
|
|
4354
|
+
registerWeek(program);
|
|
4355
|
+
registerBugReport(program);
|
|
4356
|
+
registerCompletions(program);
|
|
4357
|
+
registerAlerts(program);
|
|
4358
|
+
registerContext(program);
|
|
4359
|
+
program.parse();
|
|
4360
|
+
}
|
|
4361
|
+
});
|
|
4362
|
+
|
|
4363
|
+
// src/utils/node-version.ts
|
|
4364
|
+
function detectVersionManager() {
|
|
4365
|
+
if (process.env.NVM_DIR) return "nvm";
|
|
4366
|
+
if (process.env.VOLTA_HOME) return "volta";
|
|
4367
|
+
if (process.env.FNM_DIR) return "fnm";
|
|
4368
|
+
if (process.env.MISE_DATA_DIR || process.env.MISE_SHELL) return "mise";
|
|
4369
|
+
if (process.env.ASDF_DIR || process.env.ASDF_DATA_DIR) return "asdf";
|
|
4370
|
+
if (process.env.NODENV_ROOT) return "nodenv";
|
|
4371
|
+
return null;
|
|
4372
|
+
}
|
|
4373
|
+
var UPGRADE_COMMANDS = {
|
|
4374
|
+
nvm: "nvm install 22 && nvm use 22",
|
|
4375
|
+
volta: "volta install node@22",
|
|
4376
|
+
fnm: "fnm install 22 && fnm use 22",
|
|
4377
|
+
mise: "mise use --global node@22",
|
|
4378
|
+
asdf: "asdf install nodejs 22 && asdf global nodejs 22",
|
|
4379
|
+
nodenv: "nodenv install 22 && nodenv global 22"
|
|
4380
|
+
};
|
|
4381
|
+
function assertNodeVersion(nodeVersion) {
|
|
4382
|
+
const major = Number(nodeVersion.split(".")[0]);
|
|
4383
|
+
if (major < 22) {
|
|
4384
|
+
const manager = detectVersionManager();
|
|
4385
|
+
const upgradeHint = manager ? `Upgrade with ${manager}:
|
|
4386
|
+
${UPGRADE_COMMANDS[manager]}` : "Download the latest version from https://nodejs.org";
|
|
4387
|
+
process.stderr.write(`workscribe requires Node.js 22 or later (you have v${nodeVersion}).
|
|
4388
|
+
|
|
4389
|
+
${upgradeHint}
|
|
4390
|
+
`);
|
|
4391
|
+
process.exit(1);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
3810
4394
|
|
|
3811
4395
|
// src/index.ts
|
|
3812
4396
|
assertNodeVersion(process.versions.node);
|
|
3813
|
-
|
|
3814
|
-
var program = new import_commander.Command();
|
|
3815
|
-
program.name("workscribe").description("Automatic developer work journal").version("1.14.0");
|
|
3816
|
-
registerCapture(program);
|
|
3817
|
-
registerHook(program);
|
|
3818
|
-
registerInit(program);
|
|
3819
|
-
registerStatus(program);
|
|
3820
|
-
registerEvents(program);
|
|
3821
|
-
registerSessions(program);
|
|
3822
|
-
registerSummary(program);
|
|
3823
|
-
registerExport(program);
|
|
3824
|
-
registerConfig(program);
|
|
3825
|
-
registerPause(program);
|
|
3826
|
-
registerUndo(program);
|
|
3827
|
-
registerClear(program);
|
|
3828
|
-
registerUninstall(program);
|
|
3829
|
-
registerProfile(program);
|
|
3830
|
-
registerNote(program);
|
|
3831
|
-
registerWeek(program);
|
|
3832
|
-
registerBugReport(program);
|
|
3833
|
-
registerCompletions(program);
|
|
3834
|
-
registerAlerts(program);
|
|
3835
|
-
registerContext(program);
|
|
3836
|
-
program.parse();
|
|
4397
|
+
require_main();
|
|
3837
4398
|
//# sourceMappingURL=index.js.map
|