@workscribe/cli 1.13.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/README.md +1 -0
- package/dist/index.js +1589 -993
- 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,20 +417,60 @@ 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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
456
|
+
function parseOwnerAndRepo(url) {
|
|
457
|
+
const normalized = url.trim().replace(/^[^/]+:(?!\/\/)/, (match) => match.replace(":", "/"));
|
|
458
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
459
|
+
if (segments.length < 2) return null;
|
|
460
|
+
const repo = segments[segments.length - 1].replace(/\.git$/, "");
|
|
461
|
+
const owner = segments[segments.length - 2];
|
|
462
|
+
return owner && repo ? { owner, repo } : null;
|
|
463
|
+
}
|
|
464
|
+
function nameFromRemote(root, qualifyWithOwner) {
|
|
440
465
|
try {
|
|
441
466
|
const url = (0, import_child_process.execSync)("git remote get-url origin", {
|
|
442
467
|
cwd: root,
|
|
443
468
|
timeout: 2e3,
|
|
444
469
|
stdio: "pipe"
|
|
445
470
|
}).toString().trim();
|
|
446
|
-
const
|
|
447
|
-
|
|
471
|
+
const parsed = parseOwnerAndRepo(url);
|
|
472
|
+
if (!parsed) return null;
|
|
473
|
+
return qualifyWithOwner ? `${parsed.owner}/${parsed.repo}` : parsed.repo;
|
|
448
474
|
} catch {
|
|
449
475
|
return null;
|
|
450
476
|
}
|
|
@@ -459,14 +485,14 @@ function nameFromPackageJson(root) {
|
|
|
459
485
|
return null;
|
|
460
486
|
}
|
|
461
487
|
}
|
|
462
|
-
function getGitContext(cwd, nameOverride) {
|
|
488
|
+
function getGitContext(cwd, nameOverride, qualifyRepoName = false) {
|
|
463
489
|
try {
|
|
464
490
|
const root = (0, import_child_process.execSync)("git rev-parse --show-toplevel", {
|
|
465
491
|
cwd,
|
|
466
492
|
timeout: 2e3,
|
|
467
493
|
stdio: "pipe"
|
|
468
494
|
}).toString().trim();
|
|
469
|
-
const repoName = nameOverride ?? nameFromRemote(root) ?? nameFromPackageJson(root) ?? import_path2.default.basename(root);
|
|
495
|
+
const repoName = nameOverride ?? nameFromRemote(root, qualifyRepoName) ?? nameFromPackageJson(root) ?? import_path2.default.basename(root);
|
|
470
496
|
let branch = null;
|
|
471
497
|
try {
|
|
472
498
|
branch = (0, import_child_process.execSync)("git branch --show-current", {
|
|
@@ -481,9 +507,17 @@ function getGitContext(cwd, nameOverride) {
|
|
|
481
507
|
return { repoName: null, branch: null };
|
|
482
508
|
}
|
|
483
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
|
+
});
|
|
484
519
|
|
|
485
520
|
// src/capture/git-snapshot.ts
|
|
486
|
-
var import_child_process2 = require("child_process");
|
|
487
521
|
function getCommitsSince(sha, cwd) {
|
|
488
522
|
if (!sha) return [];
|
|
489
523
|
try {
|
|
@@ -493,12 +527,15 @@ function getCommitsSince(sha, cwd) {
|
|
|
493
527
|
return [];
|
|
494
528
|
}
|
|
495
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
|
+
});
|
|
496
537
|
|
|
497
538
|
// src/capture/aider.ts
|
|
498
|
-
var import_fs4 = __toESM(require("fs"));
|
|
499
|
-
var import_path3 = __toESM(require("path"));
|
|
500
|
-
var HISTORY_FILE = ".aider.chat.history.md";
|
|
501
|
-
var PROMPT_MAX_LENGTH = 200;
|
|
502
539
|
function readLastAiderPrompt(cwd) {
|
|
503
540
|
try {
|
|
504
541
|
const histPath = import_path3.default.join(cwd, HISTORY_FILE);
|
|
@@ -510,135 +547,135 @@ function readLastAiderPrompt(cwd) {
|
|
|
510
547
|
return null;
|
|
511
548
|
}
|
|
512
549
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
+
});
|
|
518
560
|
|
|
519
561
|
// src/config/ai-tools.json
|
|
520
|
-
var ai_tools_default
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
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
|
+
});
|
|
630
677
|
|
|
631
678
|
// src/utils/logger.ts
|
|
632
|
-
var import_chalk = __toESM(require("chalk"));
|
|
633
|
-
var import_fs5 = __toESM(require("fs"));
|
|
634
|
-
var isDebug = process.env.WORKSCRIBE_DEBUG === "1";
|
|
635
|
-
var log = {
|
|
636
|
-
info: (msg) => console.log(import_chalk.default.cyan("\u2192"), msg),
|
|
637
|
-
success: (msg) => console.log(import_chalk.default.green("\u2713"), msg),
|
|
638
|
-
warn: (msg) => console.log(import_chalk.default.yellow("\u26A0"), msg),
|
|
639
|
-
error: (msg) => console.error(import_chalk.default.red("\u2717"), msg),
|
|
640
|
-
plain: (msg) => console.log(msg)
|
|
641
|
-
};
|
|
642
679
|
function debugLog(context, error) {
|
|
643
680
|
if (!isDebug) return;
|
|
644
681
|
const entry = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}
|
|
@@ -648,9 +685,25 @@ function debugLog(context, error) {
|
|
|
648
685
|
} catch {
|
|
649
686
|
}
|
|
650
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
|
+
});
|
|
651
705
|
|
|
652
706
|
// src/config/registry.ts
|
|
653
|
-
var USER_REGISTRY_PATH = import_path4.default.join(import_os2.default.homedir(), ".workscribe", "ai-tools.json");
|
|
654
707
|
function loadAIToolRegistry() {
|
|
655
708
|
let userEntries = [];
|
|
656
709
|
try {
|
|
@@ -677,36 +730,20 @@ function loadAIToolRegistry() {
|
|
|
677
730
|
const filteredBuiltins = ai_tools_default.filter((entry) => !userToolSlugs.has(entry.tool));
|
|
678
731
|
return [...filteredBuiltins, ...userEntries];
|
|
679
732
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
+
});
|
|
684
745
|
|
|
685
746
|
// src/config/index.ts
|
|
686
|
-
var import_fs7 = __toESM(require("fs"));
|
|
687
|
-
var DEFAULTS = {
|
|
688
|
-
ai: {
|
|
689
|
-
provider: "",
|
|
690
|
-
apiKey: "",
|
|
691
|
-
model: "",
|
|
692
|
-
baseUrl: ""
|
|
693
|
-
},
|
|
694
|
-
session: {
|
|
695
|
-
idleTimeout: 30
|
|
696
|
-
},
|
|
697
|
-
capture: {
|
|
698
|
-
ignore: ["cd", "ls", "ll", "la", "ls -la", "ls -l", "ls -a", "pwd", "clear", "exit", "history", "man"],
|
|
699
|
-
aiTools: true,
|
|
700
|
-
includeAiderPrompt: true,
|
|
701
|
-
profiles: []
|
|
702
|
-
},
|
|
703
|
-
redact: {
|
|
704
|
-
extra: []
|
|
705
|
-
},
|
|
706
|
-
summary: {
|
|
707
|
-
format: "markdown"
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
747
|
function readConfig() {
|
|
711
748
|
if (!import_fs7.default.existsSync(CONFIG_PATH)) return { ...DEFAULTS };
|
|
712
749
|
try {
|
|
@@ -763,6 +800,40 @@ function deepMerge(defaults, overrides) {
|
|
|
763
800
|
}
|
|
764
801
|
return result;
|
|
765
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
|
+
});
|
|
766
837
|
|
|
767
838
|
// src/profiles/schema.ts
|
|
768
839
|
function isNonEmptyString(value) {
|
|
@@ -783,21 +854,13 @@ function isValidProfile(candidate) {
|
|
|
783
854
|
function isValidManifest(m) {
|
|
784
855
|
return Array.isArray(m) && m.every((e) => e && typeof e === "object" && typeof e.name === "string");
|
|
785
856
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
857
|
+
var init_schema = __esm({
|
|
858
|
+
"src/profiles/schema.ts"() {
|
|
859
|
+
"use strict";
|
|
860
|
+
}
|
|
861
|
+
});
|
|
790
862
|
|
|
791
863
|
// src/utils/fetch.ts
|
|
792
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
793
|
-
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
794
|
-
"ENOTFOUND",
|
|
795
|
-
"ECONNREFUSED",
|
|
796
|
-
"ECONNRESET",
|
|
797
|
-
"ETIMEDOUT",
|
|
798
|
-
"EHOSTUNREACH",
|
|
799
|
-
"ENETUNREACH"
|
|
800
|
-
]);
|
|
801
864
|
function isNetworkError(err) {
|
|
802
865
|
if (!(err instanceof Error)) return false;
|
|
803
866
|
const cause = err.cause;
|
|
@@ -821,9 +884,23 @@ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
821
884
|
clearTimeout(timer);
|
|
822
885
|
}
|
|
823
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
|
+
});
|
|
824
902
|
|
|
825
903
|
// src/profiles/fetcher.ts
|
|
826
|
-
var REGISTRY_BASE = "https://raw.githubusercontent.com/workscribe/profiles/main";
|
|
827
904
|
function manifestCachePath() {
|
|
828
905
|
return import_path5.default.join(PROFILES_DIR, "manifest.json");
|
|
829
906
|
}
|
|
@@ -869,6 +946,19 @@ function ensureProfilesDir() {
|
|
|
869
946
|
import_fs8.default.mkdirSync(PROFILES_DIR, { recursive: true, mode: 448 });
|
|
870
947
|
}
|
|
871
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
|
+
});
|
|
872
962
|
|
|
873
963
|
// src/profiles/loader.ts
|
|
874
964
|
function compileRule(profileName, rule) {
|
|
@@ -947,6 +1037,19 @@ function detectProfilesForDirectory(dir) {
|
|
|
947
1037
|
return indicatorFiles.length > 0 && indicatorFiles.every((file) => import_fs9.default.existsSync(import_path6.default.join(dir, file)));
|
|
948
1038
|
});
|
|
949
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
|
+
});
|
|
950
1053
|
|
|
951
1054
|
// src/normalize/patterns.ts
|
|
952
1055
|
function makeRule(pattern, category, extract = () => ({})) {
|
|
@@ -963,208 +1066,6 @@ function extractPackages(cmd, afterKeyword) {
|
|
|
963
1066
|
if (idx === -1) return [];
|
|
964
1067
|
return cmd.slice(idx + afterKeyword.length).trim().split(/\s+/).filter((part) => part && !part.startsWith("-"));
|
|
965
1068
|
}
|
|
966
|
-
var BASE_RULES = [
|
|
967
|
-
// ─── Git operations ───────────────────────────────────────────────────────
|
|
968
|
-
makeRule(/^git\s+commit\b/, "shipping", (_m, cmd) => {
|
|
969
|
-
const msgMatch = cmd.match(/-m\s+["']?([^"']+)["']?/);
|
|
970
|
-
return { message: msgMatch ? msgMatch[1].trim() : null };
|
|
971
|
-
}),
|
|
972
|
-
makeRule(/^git\s+push\b/, "shipping", (_m, cmd) => {
|
|
973
|
-
const parts = cmd.split(/\s+/);
|
|
974
|
-
return { remote: parts[2] ?? "origin", branch: parts[3] ?? null };
|
|
975
|
-
}),
|
|
976
|
-
makeRule(/^git\s+(pull|fetch)\b/, "versioning", (_m, cmd) => {
|
|
977
|
-
const parts = cmd.split(/\s+/);
|
|
978
|
-
return { subcommand: parts[1], remote: parts[2] ?? "origin" };
|
|
979
|
-
}),
|
|
980
|
-
makeRule(/^git\s+(checkout|switch)\b/, "versioning", (_m, cmd) => {
|
|
981
|
-
const parts = cmd.split(/\s+/);
|
|
982
|
-
const branch = parts.find((part) => !part.startsWith("-") && part !== "git" && part !== parts[1]);
|
|
983
|
-
return { branch: branch ?? null };
|
|
984
|
-
}),
|
|
985
|
-
makeRule(/^git\s+(merge|rebase)\b/, "shipping", (_m, cmd) => {
|
|
986
|
-
const parts = cmd.split(/\s+/);
|
|
987
|
-
return { subcommand: parts[1], target: parts[2] ?? null };
|
|
988
|
-
}),
|
|
989
|
-
makeRule(/^git\s+stash\b/, "versioning", (_m, cmd) => {
|
|
990
|
-
const parts = cmd.split(/\s+/);
|
|
991
|
-
return { subcommand: parts[2] ?? "push" };
|
|
992
|
-
}),
|
|
993
|
-
makeRule(/^git\s+/, "versioning", (_m, cmd) => {
|
|
994
|
-
const parts = cmd.split(/\s+/);
|
|
995
|
-
return { subcommand: parts[1] ?? null };
|
|
996
|
-
}),
|
|
997
|
-
// ─── Releasing ────────────────────────────────────────────────────────────
|
|
998
|
-
makeRule(/^npm\s+publish\b/, "releasing", () => ({ tool: "npm" })),
|
|
999
|
-
makeRule(/^cargo\s+publish\b/, "releasing", () => ({ tool: "cargo" })),
|
|
1000
|
-
makeRule(/^gem\s+push\b/, "releasing", () => ({ tool: "gem" })),
|
|
1001
|
-
makeRule(/^docker\s+push\b/, "releasing", (_m, cmd) => ({ tool: "docker", image: cmd.split(/\s+/)[2] ?? null })),
|
|
1002
|
-
makeRule(/^gh\s+release\b/, "releasing", (_m, cmd) => ({ tool: "gh", subcommand: cmd.split(/\s+/)[2] ?? null })),
|
|
1003
|
-
makeRule(/^fly\s+(deploy|launch)\b/, "releasing", (match) => ({ tool: "fly", subcommand: match[1] })),
|
|
1004
|
-
makeRule(/^vercel\s+(deploy|build|--prod)\b/, "releasing", () => ({ tool: "vercel" })),
|
|
1005
|
-
makeRule(/^netlify\s+deploy\b/, "releasing", () => ({ tool: "netlify" })),
|
|
1006
|
-
makeRule(/^railway\s+up\b/, "releasing", () => ({ tool: "railway" })),
|
|
1007
|
-
makeRule(/^wrangler\s+deploy\b/, "releasing", () => ({ tool: "wrangler" })),
|
|
1008
|
-
// ─── Dependencies ─────────────────────────────────────────────────────────
|
|
1009
|
-
makeRule(/^npm\s+(install|i|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1010
|
-
tool: "npm",
|
|
1011
|
-
packages: extractPackages(cmd, cmd.includes("install") ? "install" : cmd.includes(" i ") ? " i " : "add")
|
|
1012
|
-
})),
|
|
1013
|
-
makeRule(/^(yarn|pnpm)\s+(install|add)\b/, "dependencies", (m, cmd) => ({
|
|
1014
|
-
tool: m[1],
|
|
1015
|
-
packages: extractPackages(cmd, "add")
|
|
1016
|
-
})),
|
|
1017
|
-
makeRule(/^bun\s+(install|add)\b/, "dependencies", (_m, cmd) => ({
|
|
1018
|
-
tool: "bun",
|
|
1019
|
-
packages: extractPackages(cmd, "add")
|
|
1020
|
-
})),
|
|
1021
|
-
makeRule(/^pip\s+(install)\b/, "dependencies", (_m, cmd) => ({
|
|
1022
|
-
tool: "pip",
|
|
1023
|
-
packages: extractPackages(cmd, "install")
|
|
1024
|
-
})),
|
|
1025
|
-
makeRule(/^cargo\s+add\b/, "dependencies", (_m, cmd) => ({
|
|
1026
|
-
tool: "cargo",
|
|
1027
|
-
packages: extractPackages(cmd, "add")
|
|
1028
|
-
})),
|
|
1029
|
-
makeRule(/^(brew)\s+install\b/, "dependencies", (_m, cmd) => ({
|
|
1030
|
-
tool: "brew",
|
|
1031
|
-
packages: extractPackages(cmd, "install")
|
|
1032
|
-
})),
|
|
1033
|
-
makeRule(/^go\s+get\b/, "dependencies", (_m, cmd) => ({
|
|
1034
|
-
tool: "go",
|
|
1035
|
-
packages: extractPackages(cmd, "get")
|
|
1036
|
-
})),
|
|
1037
|
-
makeRule(/^npm\s+(uninstall|remove|rm)\b/, "dependencies", (_m, cmd) => ({
|
|
1038
|
-
tool: "npm",
|
|
1039
|
-
packages: extractPackages(cmd, cmd.split(/\s+/)[1])
|
|
1040
|
-
})),
|
|
1041
|
-
makeRule(/^(yarn|pnpm)\s+remove\b/, "dependencies", (m, cmd) => ({
|
|
1042
|
-
tool: m[1],
|
|
1043
|
-
packages: extractPackages(cmd, "remove")
|
|
1044
|
-
})),
|
|
1045
|
-
makeRule(/^bun\s+remove\b/, "dependencies", (_m, cmd) => ({
|
|
1046
|
-
tool: "bun",
|
|
1047
|
-
packages: extractPackages(cmd, "remove")
|
|
1048
|
-
})),
|
|
1049
|
-
makeRule(/^pip\s+uninstall\b/, "dependencies", (_m, cmd) => ({
|
|
1050
|
-
tool: "pip",
|
|
1051
|
-
packages: extractPackages(cmd, "uninstall")
|
|
1052
|
-
})),
|
|
1053
|
-
// Runtime version managers
|
|
1054
|
-
makeRule(/^nvm\b/, "dependencies", (_m, cmd) => ({ tool: "nvm", subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1055
|
-
makeRule(/^(fnm|volta)\b/, "dependencies", (m, cmd) => ({ tool: m[1], subcommand: cmd.split(/\s+/)[1] ?? null })),
|
|
1056
|
-
makeRule(/^(rbenv|pyenv|asdf)\b/, "dependencies", (m, cmd) => ({
|
|
1057
|
-
tool: m[1],
|
|
1058
|
-
subcommand: cmd.split(/\s+/)[1] ?? null
|
|
1059
|
-
})),
|
|
1060
|
-
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
1061
|
-
makeRule(/^npm\s+(test|run\s+test)\b/, "testing", () => ({ tool: "npm" })),
|
|
1062
|
-
makeRule(/^yarn\s+test\b/, "testing", () => ({ tool: "yarn" })),
|
|
1063
|
-
makeRule(/^pnpm\s+test\b/, "testing", () => ({ tool: "pnpm" })),
|
|
1064
|
-
makeRule(/^bun\s+test\b/, "testing", () => ({ tool: "bun" })),
|
|
1065
|
-
makeRule(/^(jest|vitest)\b/, "testing", (match) => ({ tool: match[1] })),
|
|
1066
|
-
makeRule(/^pytest\b/, "testing", () => ({ tool: "pytest" })),
|
|
1067
|
-
makeRule(/^go\s+test\b/, "testing", () => ({ tool: "go" })),
|
|
1068
|
-
makeRule(/^cargo\s+test\b/, "testing", () => ({ tool: "cargo" })),
|
|
1069
|
-
makeRule(/^rspec\b/, "testing", () => ({ tool: "rspec" })),
|
|
1070
|
-
makeRule(/^(php\s+)?phpunit\b/, "testing", () => ({ tool: "phpunit" })),
|
|
1071
|
-
// ─── Build ────────────────────────────────────────────────────────────────
|
|
1072
|
-
makeRule(/^npm\s+run\s+build\b/, "building", () => ({ tool: "npm" })),
|
|
1073
|
-
makeRule(/^(yarn|pnpm)\s+build\b/, "building", (match) => ({ tool: match[1] })),
|
|
1074
|
-
makeRule(/^bun\s+run\s+build\b/, "building", () => ({ tool: "bun" })),
|
|
1075
|
-
makeRule(/^(make|gmake)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1076
|
-
makeRule(/^cargo\s+build\b/, "building", () => ({ tool: "cargo" })),
|
|
1077
|
-
makeRule(/^go\s+build\b/, "building", () => ({ tool: "go" })),
|
|
1078
|
-
makeRule(/^docker\s+build\b/, "building", () => ({ tool: "docker" })),
|
|
1079
|
-
makeRule(/^(mvn|gradle)\b/, "building", (match) => ({ tool: match[1] })),
|
|
1080
|
-
// ─── Dev server / running ─────────────────────────────────────────────────
|
|
1081
|
-
makeRule(/^npm\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1082
|
-
tool: "npm",
|
|
1083
|
-
script: cmd.split(/\s+/)[2]
|
|
1084
|
-
})),
|
|
1085
|
-
makeRule(/^(yarn|pnpm)\s+(dev|start)\b/, "running", (match) => ({ tool: match[1], script: match[2] })),
|
|
1086
|
-
makeRule(/^bun\s+run\s+(dev|start|serve)\b/, "running", (_m, cmd) => ({
|
|
1087
|
-
tool: "bun",
|
|
1088
|
-
script: cmd.split(/\s+/)[2]
|
|
1089
|
-
})),
|
|
1090
|
-
makeRule(/^python\s+.*manage\.py\s+runserver\b/, "running", () => ({ tool: "django" })),
|
|
1091
|
-
// ─── Infrastructure ───────────────────────────────────────────────────────
|
|
1092
|
-
makeRule(/^ssh\b/, "infrastructure", (_m, cmd) => {
|
|
1093
|
-
const parts = cmd.split(/\s+/);
|
|
1094
|
-
return { tool: "ssh", target: parts[parts.length - 1] };
|
|
1095
|
-
}),
|
|
1096
|
-
makeRule(/^kubectl\b/, "infrastructure", (_m, cmd) => {
|
|
1097
|
-
const parts = cmd.split(/\s+/);
|
|
1098
|
-
return { tool: "kubectl", subcommand: parts[1] ?? null };
|
|
1099
|
-
}),
|
|
1100
|
-
makeRule(/^(helm|terraform|pulumi)\b/, "infrastructure", (m, cmd) => {
|
|
1101
|
-
const parts = cmd.split(/\s+/);
|
|
1102
|
-
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1103
|
-
}),
|
|
1104
|
-
makeRule(/^docker\s+(run|compose|ps|stop|start|rm|exec)\b/, "infrastructure", (_m, cmd) => {
|
|
1105
|
-
const parts = cmd.split(/\s+/);
|
|
1106
|
-
return { tool: "docker", subcommand: parts[1] };
|
|
1107
|
-
}),
|
|
1108
|
-
makeRule(/^(aws|gcloud|az)\b/, "infrastructure", (m, cmd) => {
|
|
1109
|
-
const parts = cmd.split(/\s+/);
|
|
1110
|
-
return { tool: m[1], subcommand: parts[1] ?? null };
|
|
1111
|
-
}),
|
|
1112
|
-
// ─── Database ─────────────────────────────────────────────────────────────
|
|
1113
|
-
makeRule(/^(psql|pgcli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1114
|
-
makeRule(/^(mysql|mycli)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1115
|
-
makeRule(/^sqlite3\b/, "database", () => ({ tool: "sqlite3" })),
|
|
1116
|
-
makeRule(/^(mongosh|mongo)\b/, "database", (m) => ({ tool: m[1] })),
|
|
1117
|
-
makeRule(/^redis-cli\b/, "database", () => ({ tool: "redis-cli" })),
|
|
1118
|
-
// ─── npx ─────────────────────────────────────────────────────────────────
|
|
1119
|
-
makeRule(/^npx\s+(jest|vitest|mocha|ava|tap)\b/, "testing", (_m, cmd) => ({
|
|
1120
|
-
tool: cmd.split(/\s+/)[1]
|
|
1121
|
-
})),
|
|
1122
|
-
makeRule(/^npx\s+tsc\b/, "building", () => ({ tool: "tsc" })),
|
|
1123
|
-
makeRule(/^npx\s+(eslint|tslint|biome)\b/, "linting", (_m, cmd) => ({
|
|
1124
|
-
tool: cmd.split(/\s+/)[1]
|
|
1125
|
-
})),
|
|
1126
|
-
makeRule(/^npx\s+(prettier|dprint)\b/, "linting", (_m, cmd) => ({
|
|
1127
|
-
tool: cmd.split(/\s+/)[1]
|
|
1128
|
-
})),
|
|
1129
|
-
makeRule(/^npx\s+\S+/, "running", (_m, cmd) => ({
|
|
1130
|
-
tool: cmd.split(/\s+/)[1]
|
|
1131
|
-
})),
|
|
1132
|
-
// ─── Scripts ──────────────────────────────────────────────────────────────
|
|
1133
|
-
makeRule(/^(bash|sh|zsh|fish)\s+\S+/, "running", (match) => ({ shell: match[1] })),
|
|
1134
|
-
makeRule(/^node\s+\S+\.(?:js|mjs|cjs)\b/, "running", () => ({ runtime: "node" })),
|
|
1135
|
-
makeRule(/^python3?\s+\S+\.py\b/, "running", () => ({ runtime: "python" })),
|
|
1136
|
-
// ─── Linting / code quality ───────────────────────────────────────────────
|
|
1137
|
-
makeRule(/^(eslint|tslint|biome)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1138
|
-
makeRule(/^(prettier|dprint)\b/, "linting", (match) => ({ tool: match[1] })),
|
|
1139
|
-
// ─── generic run scripts ──────────────────────────────────────────────────
|
|
1140
|
-
makeRule(/^npm\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1141
|
-
tool: "npm",
|
|
1142
|
-
script: cmd.split(/\s+/)[2]
|
|
1143
|
-
})),
|
|
1144
|
-
makeRule(/^bun\s+run\s+\S+/, "running", (_m, cmd) => ({
|
|
1145
|
-
tool: "bun",
|
|
1146
|
-
script: cmd.split(/\s+/)[2]
|
|
1147
|
-
})),
|
|
1148
|
-
// ─── Code search ─────────────────────────────────────────────────────────
|
|
1149
|
-
makeRule(/^(grep|egrep|fgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1150
|
-
makeRule(/^(rg|ag|ack|ripgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1151
|
-
makeRule(/^find\s/, "exploring", () => ({ tool: "find" })),
|
|
1152
|
-
// ─── Code read ────────────────────────────────────────────────────────────
|
|
1153
|
-
makeRule(/^(cat|bat|less|more)\s+\S+/, "exploring", (match) => ({ tool: match[1] })),
|
|
1154
|
-
makeRule(/^(head|tail)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1155
|
-
// ─── File operations ──────────────────────────────────────────────────────
|
|
1156
|
-
makeRule(/^(cp|rsync)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1157
|
-
makeRule(/^(mv)\b/, "files", () => ({ tool: "mv" })),
|
|
1158
|
-
makeRule(/^(rm|rmdir)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1159
|
-
makeRule(/^(mkdir|touch|ln)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1160
|
-
makeRule(/^(chmod|chown|chgrp)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1161
|
-
// ─── Environment ──────────────────────────────────────────────────────────
|
|
1162
|
-
makeRule(/^export\s+\w+=/, "files", (_m, cmd) => {
|
|
1163
|
-
const varName = cmd.match(/^export\s+(\w+)=/)?.[1] ?? null;
|
|
1164
|
-
return { variable: varName };
|
|
1165
|
-
}),
|
|
1166
|
-
makeRule(/^source\s+/, "files", () => ({}))
|
|
1167
|
-
];
|
|
1168
1069
|
function aiToolRules() {
|
|
1169
1070
|
return loadAIToolRegistry().flatMap((entry) => {
|
|
1170
1071
|
try {
|
|
@@ -1178,7 +1079,6 @@ function aiToolRules() {
|
|
|
1178
1079
|
function buildRules(profileRules = []) {
|
|
1179
1080
|
return [...BASE_RULES, ...profileRules, ...aiToolRules()];
|
|
1180
1081
|
}
|
|
1181
|
-
var RULES = buildRules(getActiveProfileRules());
|
|
1182
1082
|
function normalizeCommand(command) {
|
|
1183
1083
|
let trimmed = command.trim().replace(/\s+/g, " ");
|
|
1184
1084
|
trimmed = trimmed.replace(/^(git)\s+-C\s+\S+\s+/, "$1 ");
|
|
@@ -1188,12 +1088,223 @@ function normalizeCommand(command) {
|
|
|
1188
1088
|
}
|
|
1189
1089
|
return { category: "other", metadata: {} };
|
|
1190
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
|
+
});
|
|
1191
1303
|
|
|
1192
1304
|
// src/capture/filter.ts
|
|
1193
1305
|
function normalizeForFilter(cmd) {
|
|
1194
1306
|
return cmd.trim().replace(/\s+/g, " ");
|
|
1195
1307
|
}
|
|
1196
|
-
var BUILTIN_IGNORE = /* @__PURE__ */ new Set(["workscribe"]);
|
|
1197
1308
|
function shouldFilter(command, ignoreList) {
|
|
1198
1309
|
const normalized = normalizeForFilter(command);
|
|
1199
1310
|
if (!normalized) return true;
|
|
@@ -1209,9 +1320,22 @@ function isDuplicate(command, cwd, lastEvent, dedupWindowMs = 5e3, now = Date.no
|
|
|
1209
1320
|
const lastTime = new Date(lastEvent.captured_at).getTime();
|
|
1210
1321
|
return lastEvent.command === normalizeForFilter(command) && lastEvent.cwd === cwd && now - lastTime < dedupWindowMs;
|
|
1211
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
|
+
});
|
|
1212
1330
|
|
|
1213
1331
|
// src/capture/ide.ts
|
|
1214
|
-
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
|
+
});
|
|
1215
1339
|
|
|
1216
1340
|
// src/capture/index.ts
|
|
1217
1341
|
function captureCommand(opts) {
|
|
@@ -1228,7 +1352,7 @@ function captureCommand(opts) {
|
|
|
1228
1352
|
}
|
|
1229
1353
|
const command2 = opts.command ?? opts.file ?? opts.type;
|
|
1230
1354
|
const nameOverride2 = config.projects?.[cwd] ?? null;
|
|
1231
|
-
const { repoName: repoName2, branch: branch2 } = getGitContext(cwd, nameOverride2);
|
|
1355
|
+
const { repoName: repoName2, branch: branch2 } = getGitContext(cwd, nameOverride2, config.git.qualifyRepoName);
|
|
1232
1356
|
insertEvent({
|
|
1233
1357
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1234
1358
|
command: command2,
|
|
@@ -1285,11 +1409,28 @@ function captureCommand(opts) {
|
|
|
1285
1409
|
file_path: null
|
|
1286
1410
|
});
|
|
1287
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
|
+
});
|
|
1288
1430
|
|
|
1289
1431
|
// src/cli/capture.ts
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
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) => {
|
|
1293
1434
|
const hangGuard = setTimeout(() => process.exit(0), CAPTURE_HANG_TIMEOUT_MS);
|
|
1294
1435
|
hangGuard.unref();
|
|
1295
1436
|
try {
|
|
@@ -1310,15 +1451,28 @@ function registerCapture(program2) {
|
|
|
1310
1451
|
}
|
|
1311
1452
|
});
|
|
1312
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
|
+
});
|
|
1313
1463
|
|
|
1314
1464
|
// src/capture/detect-secrets.ts
|
|
1315
1465
|
function containsSecrets(text) {
|
|
1316
1466
|
return redact(text) !== text;
|
|
1317
1467
|
}
|
|
1468
|
+
var init_detect_secrets = __esm({
|
|
1469
|
+
"src/capture/detect-secrets.ts"() {
|
|
1470
|
+
"use strict";
|
|
1471
|
+
init_redact();
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1318
1474
|
|
|
1319
1475
|
// src/db/alerts.ts
|
|
1320
|
-
var import_fs11 = __toESM(require("fs"));
|
|
1321
|
-
var import_path7 = __toESM(require("path"));
|
|
1322
1476
|
function insertAlert(tool, sessionId, context) {
|
|
1323
1477
|
getDb().prepare(
|
|
1324
1478
|
`INSERT INTO alerts (created_at, tool, session_id, context)
|
|
@@ -1342,6 +1496,16 @@ function clearAlerts() {
|
|
|
1342
1496
|
}
|
|
1343
1497
|
return Number(result.changes);
|
|
1344
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
|
+
});
|
|
1345
1509
|
|
|
1346
1510
|
// src/cli/hook.ts
|
|
1347
1511
|
function parseHookPayload(raw) {
|
|
@@ -1362,8 +1526,8 @@ function parseUserPromptPayload(raw) {
|
|
|
1362
1526
|
if (!prompt) return null;
|
|
1363
1527
|
return { session_id: sessionId, prompt, cwd };
|
|
1364
1528
|
}
|
|
1365
|
-
function registerHook(
|
|
1366
|
-
|
|
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) => {
|
|
1367
1531
|
let raw = "";
|
|
1368
1532
|
process.stdin.setEncoding("utf-8");
|
|
1369
1533
|
process.stdin.on("data", (chunk) => {
|
|
@@ -1404,13 +1568,16 @@ function registerHook(program2) {
|
|
|
1404
1568
|
});
|
|
1405
1569
|
});
|
|
1406
1570
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
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
|
+
});
|
|
1414
1581
|
|
|
1415
1582
|
// src/utils/format.ts
|
|
1416
1583
|
function formatDuration(minutes) {
|
|
@@ -1470,57 +1637,18 @@ function isoWeekKey(dateStr) {
|
|
|
1470
1637
|
const weekNum = Math.ceil(((d.getTime() - jan4.getTime()) / 864e5 + jan4.getDay() + 1) / 7);
|
|
1471
1638
|
return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
1472
1639
|
}
|
|
1640
|
+
var init_format = __esm({
|
|
1641
|
+
"src/utils/format.ts"() {
|
|
1642
|
+
"use strict";
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1473
1645
|
|
|
1474
1646
|
// src/ai/prompt.ts
|
|
1475
|
-
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.
|
|
1476
|
-
|
|
1477
|
-
Here is an example:
|
|
1478
|
-
|
|
1479
|
-
INPUT:
|
|
1480
|
-
Project: api-service
|
|
1481
|
-
Branch: feature/auth
|
|
1482
|
-
Duration: 2h 00m
|
|
1483
|
-
Events:
|
|
1484
|
-
- versioning {"branch":"feature/auth"}
|
|
1485
|
-
- 2x dependencies {"tool":"npm","packages":["bcrypt","jsonwebtoken"]}
|
|
1486
|
-
- 8x testing [failed]
|
|
1487
|
-
- testing
|
|
1488
|
-
- 2x files {"tool":"mkdir"}
|
|
1489
|
-
- shipping {"message":"fix: auth token validation"}
|
|
1490
|
-
- shipping {"remote":"origin","branch":"feature/auth"}
|
|
1491
|
-
|
|
1492
|
-
OUTPUT:
|
|
1493
|
-
- Added authentication dependencies (bcrypt, JWT)
|
|
1494
|
-
- Debugged and resolved failing auth tests
|
|
1495
|
-
- Committed and pushed the token validation fix
|
|
1496
|
-
|
|
1497
|
-
RULES \u2014 follow these exactly:
|
|
1498
|
-
|
|
1499
|
-
1. Interpret repeated events as patterns, not individual steps.
|
|
1500
|
-
- Many testing [failed] followed by a passing test = debugging a failure. Say "resolved failing X tests", not "ran tests multiple times".
|
|
1501
|
-
- Many versioning events = branch housekeeping. Fold into context, do not list each git command.
|
|
1502
|
-
|
|
1503
|
-
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.
|
|
1504
|
-
|
|
1505
|
-
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.
|
|
1506
|
-
|
|
1507
|
-
4. For other events with a hint field: describe the developer's intent, not the raw command.
|
|
1508
|
-
|
|
1509
|
-
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.
|
|
1510
|
-
|
|
1511
|
-
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).
|
|
1512
|
-
|
|
1513
|
-
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.
|
|
1514
|
-
|
|
1515
|
-
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.
|
|
1516
|
-
|
|
1517
|
-
Maximum 5 bullets. Output ONLY the bullet points, nothing else.`;
|
|
1518
1647
|
function getSystemPrompt() {
|
|
1519
1648
|
const contexts = getActiveProfileAiContexts();
|
|
1520
1649
|
if (contexts.length === 0) return SYSTEM_PROMPT;
|
|
1521
1650
|
return SYSTEM_PROMPT + "\n\n" + contexts.join("\n");
|
|
1522
1651
|
}
|
|
1523
|
-
var DEFAULT_EXCLUDED_CATEGORIES = /* @__PURE__ */ new Set(["exploring", "note", "linting"]);
|
|
1524
1652
|
function buildExcludedSet(includeCategories = []) {
|
|
1525
1653
|
const excluded = new Set(DEFAULT_EXCLUDED_CATEGORIES);
|
|
1526
1654
|
for (const cat of includeCategories) excluded.delete(cat);
|
|
@@ -1572,39 +1700,6 @@ function buildUserMessage(payload, includeCategories = []) {
|
|
|
1572
1700
|
}
|
|
1573
1701
|
return lines.join("\n");
|
|
1574
1702
|
}
|
|
1575
|
-
var WEEKLY_SYSTEM_PROMPT = `You write short weekly developer summaries. Here is an example:
|
|
1576
|
-
|
|
1577
|
-
INPUT:
|
|
1578
|
-
Week: 2026-05-18 to 2026-05-22
|
|
1579
|
-
|
|
1580
|
-
Monday 2026-05-18:
|
|
1581
|
-
Project: api-service (feature/auth) \u2014 2h 10m
|
|
1582
|
-
Events: 3x code_commit, 2x test_run, dependency_install
|
|
1583
|
-
|
|
1584
|
-
Tuesday 2026-05-19:
|
|
1585
|
-
Project: api-service (feature/auth) \u2014 1h 30m
|
|
1586
|
-
Events: 2x code_commit, test_run, build
|
|
1587
|
-
|
|
1588
|
-
Wednesday 2026-05-20:
|
|
1589
|
-
Project: dashboard (main) \u2014 3h 00m
|
|
1590
|
-
Events: 4x code_commit, 2x build, test_run
|
|
1591
|
-
|
|
1592
|
-
Thursday 2026-05-21:
|
|
1593
|
-
Project: api-service (feature/auth) \u2014 1h 00m
|
|
1594
|
-
Events: code_commit {"message":"feat: merge auth"}, branch_switch
|
|
1595
|
-
|
|
1596
|
-
Friday 2026-05-22:
|
|
1597
|
-
Project: dashboard (main) \u2014 2h 00m
|
|
1598
|
-
Events: 3x code_commit, build
|
|
1599
|
-
|
|
1600
|
-
OUTPUT:
|
|
1601
|
-
## Week of 18 May \u2014 9h 40m across 2 projects
|
|
1602
|
-
|
|
1603
|
-
**api-service** (Mon\u2013Thu): Built and shipped the auth feature \u2014 JWT setup, token validation fixes, and final merge.
|
|
1604
|
-
**dashboard** (Wed, Fri): Active development across 7 commits with builds passing.
|
|
1605
|
-
|
|
1606
|
-
Now summarise the following week. Output ONLY the summary \u2014 a one-line header then one bullet per active project. Maximum 4 bullets.`;
|
|
1607
|
-
var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
1608
1703
|
function buildWeeklyMessage(payload) {
|
|
1609
1704
|
const lines = [`Week: ${payload.weekStart} to ${payload.weekEnd}`, ""];
|
|
1610
1705
|
for (const { date, sessions } of payload.days) {
|
|
@@ -1642,194 +1737,293 @@ function buildWeeklyMessage(payload) {
|
|
|
1642
1737
|
}
|
|
1643
1738
|
return lines.join("\n");
|
|
1644
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.
|
|
1645
1747
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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"];
|
|
1676
1823
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
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
|
+
};
|
|
1687
1875
|
}
|
|
1688
|
-
};
|
|
1876
|
+
});
|
|
1689
1877
|
|
|
1690
1878
|
// src/ai/provider-config.ts
|
|
1691
|
-
var PROVIDER_CONFIGS
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
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
|
-
|
|
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
|
+
};
|
|
1831
2025
|
}
|
|
1832
|
-
};
|
|
2026
|
+
});
|
|
1833
2027
|
|
|
1834
2028
|
// src/ai/router.ts
|
|
1835
2029
|
function getProvider(config) {
|
|
@@ -1862,12 +2056,13 @@ Run: workscribe config set ai.apiKey <your-key>`);
|
|
|
1862
2056
|
config.summary?.includeCategories ?? []
|
|
1863
2057
|
);
|
|
1864
2058
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
2059
|
+
var init_router = __esm({
|
|
2060
|
+
"src/ai/router.ts"() {
|
|
2061
|
+
"use strict";
|
|
2062
|
+
init_generic_provider();
|
|
2063
|
+
init_provider_config();
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
1871
2066
|
|
|
1872
2067
|
// src/utils/completion-scripts.ts
|
|
1873
2068
|
function bashCompletion(commands) {
|
|
@@ -1922,12 +2117,13 @@ function fishCompletion(commands) {
|
|
|
1922
2117
|
];
|
|
1923
2118
|
return lines.join("\n");
|
|
1924
2119
|
}
|
|
2120
|
+
var init_completion_scripts = __esm({
|
|
2121
|
+
"src/utils/completion-scripts.ts"() {
|
|
2122
|
+
"use strict";
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
1925
2125
|
|
|
1926
2126
|
// src/capture/shell-hook.ts
|
|
1927
|
-
var FISH_COMPLETIONS_DIR = import_path8.default.join(import_os3.default.homedir(), ".config", "fish", "completions");
|
|
1928
|
-
var HOOK_START = "# workscribe:hook:start";
|
|
1929
|
-
var HOOK_END = "# workscribe:hook:end";
|
|
1930
|
-
var HOOK_PREAMBLE = "# Workscribe \u2014 developer work journal";
|
|
1931
2127
|
function getInstalledHookBlock(rcFile) {
|
|
1932
2128
|
if (!import_fs12.default.existsSync(rcFile)) return null;
|
|
1933
2129
|
const content = import_fs12.default.readFileSync(rcFile, "utf-8");
|
|
@@ -1982,9 +2178,10 @@ function resolveWorkscribeBinary() {
|
|
|
1982
2178
|
return null;
|
|
1983
2179
|
}
|
|
1984
2180
|
}
|
|
1985
|
-
function injectBinaryPath(hookContent, binaryPath) {
|
|
2181
|
+
function injectBinaryPath(hookContent, binaryPath, nodePath) {
|
|
2182
|
+
const pinnedInvocation = `"${nodePath}" "${binaryPath}"`;
|
|
1986
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}"
|
|
1987
|
-
or return 0`).replaceAll("command workscribe _capture",
|
|
2184
|
+
or return 0`).replaceAll("command workscribe _capture", `${pinnedInvocation} _capture`).replaceAll("command workscribe alerts", `${pinnedInvocation} alerts`);
|
|
1988
2185
|
}
|
|
1989
2186
|
function isCompletionsInstalled(shell) {
|
|
1990
2187
|
if (shell.name === "fish") {
|
|
@@ -2017,14 +2214,25 @@ source "${cacheFile}"
|
|
|
2017
2214
|
import_fs12.default.appendFileSync(shell.rcFile, sourceLine);
|
|
2018
2215
|
}
|
|
2019
2216
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
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
|
+
});
|
|
2025
2234
|
|
|
2026
2235
|
// src/capture/hooks/utils.ts
|
|
2027
|
-
var import_child_process4 = require("child_process");
|
|
2028
2236
|
function isCommandInstalled(cmd) {
|
|
2029
2237
|
try {
|
|
2030
2238
|
(0, import_child_process4.execSync)(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -2033,9 +2241,15 @@ function isCommandInstalled(cmd) {
|
|
|
2033
2241
|
return false;
|
|
2034
2242
|
}
|
|
2035
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
|
+
});
|
|
2036
2251
|
|
|
2037
2252
|
// src/capture/hooks/claude-code.ts
|
|
2038
|
-
var CLAUDE_SETTINGS_PATH = import_path9.default.join(import_os4.default.homedir(), ".claude", "settings.json");
|
|
2039
2253
|
function readSettings() {
|
|
2040
2254
|
if (!import_fs13.default.existsSync(CLAUDE_SETTINGS_PATH)) return {};
|
|
2041
2255
|
try {
|
|
@@ -2044,86 +2258,155 @@ function readSettings() {
|
|
|
2044
2258
|
return {};
|
|
2045
2259
|
}
|
|
2046
2260
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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));
|
|
2070
2293
|
});
|
|
2071
|
-
return
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
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,
|
|
2348
|
+
{
|
|
2349
|
+
matcher: "Bash",
|
|
2350
|
+
hooks: [{ type: "command", command: `${baseCommand} ${HOOK_MARKER}`, async: true }]
|
|
2351
|
+
}
|
|
2352
|
+
],
|
|
2353
|
+
UserPromptSubmit: [
|
|
2354
|
+
...existingUserPromptSubmit,
|
|
2091
2355
|
{
|
|
2092
|
-
|
|
2093
|
-
|
|
2356
|
+
hooks: [
|
|
2357
|
+
{
|
|
2358
|
+
type: "command",
|
|
2359
|
+
command: `${baseCommand} ${HOOK_MARKER} --event UserPromptSubmit`
|
|
2360
|
+
}
|
|
2361
|
+
]
|
|
2094
2362
|
}
|
|
2095
2363
|
]
|
|
2096
|
-
}
|
|
2097
|
-
|
|
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
|
+
}
|
|
2098
2368
|
};
|
|
2099
|
-
import_fs13.default.mkdirSync(import_path9.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
|
|
2100
|
-
import_fs13.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2101
2369
|
}
|
|
2102
|
-
};
|
|
2370
|
+
});
|
|
2103
2371
|
|
|
2104
2372
|
// src/capture/hooks/index.ts
|
|
2105
|
-
var AI_HOOK_INTEGRATIONS
|
|
2106
|
-
|
|
2107
|
-
|
|
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
|
+
});
|
|
2108
2383
|
|
|
2109
2384
|
// src/cli/completions.ts
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
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() }));
|
|
2113
2387
|
}
|
|
2114
|
-
function registerCompletions(
|
|
2115
|
-
|
|
2388
|
+
function registerCompletions(program) {
|
|
2389
|
+
program.command("completions [shell]").description("Print shell completion script (bash, zsh, fish)").action((shell) => {
|
|
2116
2390
|
const detected = shell ?? (process.env.SHELL ?? "").split("/").at(-1);
|
|
2117
2391
|
if (!detected || !["bash", "zsh", "fish"].includes(detected)) {
|
|
2118
2392
|
log.error(`Unknown shell: "${detected ?? ""}". Supported: bash, zsh, fish`);
|
|
2119
2393
|
log.plain(` Usage: workscribe completions <bash|zsh|fish>`);
|
|
2120
2394
|
process.exit(1);
|
|
2121
2395
|
}
|
|
2122
|
-
const commands = getPublicCommands(
|
|
2396
|
+
const commands = getPublicCommands(program);
|
|
2123
2397
|
const script = detected === "bash" ? bashCompletion(commands) : detected === "zsh" ? zshCompletion(commands) : fishCompletion(commands);
|
|
2124
2398
|
process.stdout.write(script + "\n");
|
|
2125
2399
|
});
|
|
2126
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
|
+
});
|
|
2127
2410
|
|
|
2128
2411
|
// src/cli/init.ts
|
|
2129
2412
|
function detectShell() {
|
|
@@ -2140,8 +2423,8 @@ function step(current, total, label) {
|
|
|
2140
2423
|
log.plain(import_chalk2.default.bold(`
|
|
2141
2424
|
[${current}/${total}] ${label}`));
|
|
2142
2425
|
}
|
|
2143
|
-
function registerInit(
|
|
2144
|
-
|
|
2426
|
+
function registerInit(program) {
|
|
2427
|
+
program.command("init").description("Set up Workscribe and install or update shell hooks").action(async () => {
|
|
2145
2428
|
const TOTAL_STEPS = 5;
|
|
2146
2429
|
log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
|
|
2147
2430
|
step(1, TOTAL_STEPS, "Creating data directories");
|
|
@@ -2177,7 +2460,7 @@ function registerInit(program2) {
|
|
|
2177
2460
|
} else {
|
|
2178
2461
|
const hookContent = import_fs14.default.readFileSync(hookFile, "utf-8");
|
|
2179
2462
|
const binaryPath = resolveWorkscribeBinary();
|
|
2180
|
-
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath) : hookContent;
|
|
2463
|
+
const hookWithPath = binaryPath ? injectBinaryPath(hookContent, binaryPath, process.execPath) : hookContent;
|
|
2181
2464
|
if (shell.name === "fish") {
|
|
2182
2465
|
const confDir = import_path10.default.dirname(shell.rcFile);
|
|
2183
2466
|
if (!import_fs14.default.existsSync(confDir)) import_fs14.default.mkdirSync(confDir, { recursive: true });
|
|
@@ -2219,7 +2502,7 @@ function registerInit(program2) {
|
|
|
2219
2502
|
});
|
|
2220
2503
|
if (installComps) {
|
|
2221
2504
|
try {
|
|
2222
|
-
installCompletions(shell, getPublicCommands(
|
|
2505
|
+
installCompletions(shell, getPublicCommands(program));
|
|
2223
2506
|
log.success("Shell completions installed");
|
|
2224
2507
|
} catch (error) {
|
|
2225
2508
|
log.warn(`Completions install failed: ${error.message}`);
|
|
@@ -2390,12 +2673,32 @@ function registerInit(program2) {
|
|
|
2390
2673
|
log.plain("");
|
|
2391
2674
|
});
|
|
2392
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
|
+
});
|
|
2393
2698
|
|
|
2394
2699
|
// src/cli/status.ts
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
function registerStatus(program2) {
|
|
2398
|
-
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(() => {
|
|
2399
2702
|
const config = readConfig();
|
|
2400
2703
|
const total = getEventCount();
|
|
2401
2704
|
const todayEvents = getEventsForDate(todayDate());
|
|
@@ -2430,6 +2733,20 @@ function registerStatus(program2) {
|
|
|
2430
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)")
|
|
2431
2734
|
);
|
|
2432
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
|
+
}
|
|
2433
2750
|
const { total: weekTotal, otherCount } = getRecentOtherRate(7);
|
|
2434
2751
|
if (weekTotal > 0) {
|
|
2435
2752
|
const otherPct = Math.round(otherCount / weekTotal * 100);
|
|
@@ -2448,30 +2765,26 @@ function registerStatus(program2) {
|
|
|
2448
2765
|
log.plain("");
|
|
2449
2766
|
});
|
|
2450
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
|
+
});
|
|
2451
2784
|
|
|
2452
2785
|
// src/cli/events.ts
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
shipping: import_chalk4.default.green,
|
|
2456
|
-
versioning: import_chalk4.default.blue,
|
|
2457
|
-
building: import_chalk4.default.cyan,
|
|
2458
|
-
running: import_chalk4.default.white,
|
|
2459
|
-
testing: import_chalk4.default.magenta,
|
|
2460
|
-
linting: import_chalk4.default.yellow,
|
|
2461
|
-
dependencies: import_chalk4.default.yellow,
|
|
2462
|
-
infrastructure: import_chalk4.default.red,
|
|
2463
|
-
releasing: import_chalk4.default.bold.green,
|
|
2464
|
-
database: import_chalk4.default.bold.blue,
|
|
2465
|
-
files: import_chalk4.default.dim.yellow,
|
|
2466
|
-
exploring: import_chalk4.default.dim.cyan,
|
|
2467
|
-
ai_session: import_chalk4.default.bold.magenta,
|
|
2468
|
-
other: import_chalk4.default.gray
|
|
2469
|
-
};
|
|
2470
|
-
var AI_TOOL_DISPLAY_NAMES = Object.fromEntries(
|
|
2471
|
-
loadAIToolRegistry().map((entry) => [entry.tool, entry.displayName])
|
|
2472
|
-
);
|
|
2473
|
-
function registerEvents(program2) {
|
|
2474
|
-
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) => {
|
|
2475
2788
|
let date = todayDate();
|
|
2476
2789
|
if (opts.date) {
|
|
2477
2790
|
try {
|
|
@@ -2518,12 +2831,38 @@ function registerEvents(program2) {
|
|
|
2518
2831
|
log.plain("");
|
|
2519
2832
|
});
|
|
2520
2833
|
}
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
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
|
+
});
|
|
2524
2864
|
|
|
2525
2865
|
// src/sessions/grouper.ts
|
|
2526
|
-
var LONG_RUNNING_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2527
2866
|
function splitByIdleGap(events, idleMs) {
|
|
2528
2867
|
if (events.length === 0) return [];
|
|
2529
2868
|
const groups = [[events[0]]];
|
|
@@ -2540,7 +2879,6 @@ function splitByIdleGap(events, idleMs) {
|
|
|
2540
2879
|
}
|
|
2541
2880
|
return groups;
|
|
2542
2881
|
}
|
|
2543
|
-
var REPO_SWITCH_GAP_MS = 10 * 60 * 1e3;
|
|
2544
2882
|
function splitByRepo(events, gapMs = 0) {
|
|
2545
2883
|
if (events.length === 0) return [];
|
|
2546
2884
|
const groups = [[events[0]]];
|
|
@@ -2561,6 +2899,14 @@ function splitByRepo(events, gapMs = 0) {
|
|
|
2561
2899
|
function minutesBetween(start, end) {
|
|
2562
2900
|
return (new Date(end).getTime() - new Date(start).getTime()) / 6e4;
|
|
2563
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
|
+
});
|
|
2564
2910
|
|
|
2565
2911
|
// src/sessions/detector.ts
|
|
2566
2912
|
function getOrBuildSessions(date) {
|
|
@@ -2628,10 +2974,18 @@ function buildSessions(date, events, savedSummaries = /* @__PURE__ */ new Map())
|
|
|
2628
2974
|
}
|
|
2629
2975
|
return sessions;
|
|
2630
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
|
+
});
|
|
2631
2985
|
|
|
2632
2986
|
// src/cli/sessions.ts
|
|
2633
|
-
function registerSessions(
|
|
2634
|
-
|
|
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) => {
|
|
2635
2989
|
let date = todayDate();
|
|
2636
2990
|
if (opts.date) {
|
|
2637
2991
|
try {
|
|
@@ -2671,12 +3025,16 @@ function registerSessions(program2) {
|
|
|
2671
3025
|
log.plain("");
|
|
2672
3026
|
});
|
|
2673
3027
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
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
|
+
});
|
|
2680
3038
|
|
|
2681
3039
|
// src/integrations/webhook.ts
|
|
2682
3040
|
async function postToWebhook(url, text) {
|
|
@@ -2694,6 +3052,12 @@ async function postToWebhook(url, text) {
|
|
|
2694
3052
|
throw new Error(`Webhook POST failed ${response.status}: ${body}`);
|
|
2695
3053
|
}
|
|
2696
3054
|
}
|
|
3055
|
+
var init_webhook = __esm({
|
|
3056
|
+
"src/integrations/webhook.ts"() {
|
|
3057
|
+
"use strict";
|
|
3058
|
+
init_fetch();
|
|
3059
|
+
}
|
|
3060
|
+
});
|
|
2697
3061
|
|
|
2698
3062
|
// src/integrations/slack.ts
|
|
2699
3063
|
function formatForSlack(markdown, username) {
|
|
@@ -2704,6 +3068,11 @@ ${body}` : body;
|
|
|
2704
3068
|
function isSlackWebhookUrl(url) {
|
|
2705
3069
|
return url.startsWith("https://hooks.slack.com/");
|
|
2706
3070
|
}
|
|
3071
|
+
var init_slack = __esm({
|
|
3072
|
+
"src/integrations/slack.ts"() {
|
|
3073
|
+
"use strict";
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
2707
3076
|
|
|
2708
3077
|
// src/cli/summary.ts
|
|
2709
3078
|
function parseBullets(text) {
|
|
@@ -2755,8 +3124,8 @@ async function handleWebhook(opts, config, summary) {
|
|
|
2755
3124
|
log.warn("--slack requires integrations.slackWebhook to be set in config");
|
|
2756
3125
|
}
|
|
2757
3126
|
}
|
|
2758
|
-
function registerSummary(
|
|
2759
|
-
|
|
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) => {
|
|
2760
3129
|
const config = readConfig();
|
|
2761
3130
|
let provider;
|
|
2762
3131
|
try {
|
|
@@ -2916,10 +3285,28 @@ ${bullets.trim()}`;
|
|
|
2916
3285
|
await handleWebhook(opts, config, fullSummary);
|
|
2917
3286
|
});
|
|
2918
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
|
+
});
|
|
2919
3308
|
|
|
2920
3309
|
// src/cli/export.ts
|
|
2921
|
-
var import_fs17 = __toESM(require("fs"));
|
|
2922
|
-
var import_path12 = __toESM(require("path"));
|
|
2923
3310
|
function eventRowToPayload2(row) {
|
|
2924
3311
|
let metadata = {};
|
|
2925
3312
|
try {
|
|
@@ -2939,8 +3326,8 @@ function eventRowToPayload2(row) {
|
|
|
2939
3326
|
}
|
|
2940
3327
|
return payload;
|
|
2941
3328
|
}
|
|
2942
|
-
function registerExport(
|
|
2943
|
-
|
|
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) => {
|
|
2944
3331
|
let date = todayDate();
|
|
2945
3332
|
if (opts.date) {
|
|
2946
3333
|
try {
|
|
@@ -2984,11 +3371,24 @@ ${summary}
|
|
|
2984
3371
|
log.success(`Saved: ${outputPath}`);
|
|
2985
3372
|
});
|
|
2986
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
|
+
});
|
|
2987
3388
|
|
|
2988
3389
|
// src/cli/config.ts
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
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(() => {
|
|
2992
3392
|
const config = readConfig();
|
|
2993
3393
|
log.plain("");
|
|
2994
3394
|
log.plain(import_chalk7.default.bold(" Workscribe Config"));
|
|
@@ -3019,16 +3419,6 @@ function registerConfig(program2) {
|
|
|
3019
3419
|
}
|
|
3020
3420
|
});
|
|
3021
3421
|
}
|
|
3022
|
-
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
3023
|
-
"apiKey",
|
|
3024
|
-
"api_key",
|
|
3025
|
-
"secret",
|
|
3026
|
-
"token",
|
|
3027
|
-
"password",
|
|
3028
|
-
"passwd",
|
|
3029
|
-
"webhook",
|
|
3030
|
-
"slackWebhook"
|
|
3031
|
-
]);
|
|
3032
3422
|
function maskValue(key, value) {
|
|
3033
3423
|
if (SENSITIVE_KEYS.has(key) && typeof value === "string" && value.length > 0) {
|
|
3034
3424
|
return import_chalk7.default.dim(`${value.slice(0, 4)}${"*".repeat(8)} (${value.length} chars)`);
|
|
@@ -3047,11 +3437,29 @@ function printConfig(obj, indent) {
|
|
|
3047
3437
|
}
|
|
3048
3438
|
}
|
|
3049
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
|
+
});
|
|
3050
3459
|
|
|
3051
3460
|
// src/cli/pause.ts
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
program2.command("pause").description("Pause capture temporarily").action(() => {
|
|
3461
|
+
function registerPause(program) {
|
|
3462
|
+
program.command("pause").description("Pause capture temporarily").action(() => {
|
|
3055
3463
|
if (import_fs18.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
3056
3464
|
log.info("Capture is already paused. Run workscribe resume to restart.");
|
|
3057
3465
|
return;
|
|
@@ -3059,7 +3467,7 @@ function registerPause(program2) {
|
|
|
3059
3467
|
import_fs18.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
|
|
3060
3468
|
log.success("Capture paused. Run workscribe resume to restart.");
|
|
3061
3469
|
});
|
|
3062
|
-
|
|
3470
|
+
program.command("resume").description("Resume capture after a pause").action(() => {
|
|
3063
3471
|
if (!import_fs18.default.existsSync(PAUSE_FLAG_PATH)) {
|
|
3064
3472
|
log.info("Capture is not paused.");
|
|
3065
3473
|
return;
|
|
@@ -3068,10 +3476,19 @@ function registerPause(program2) {
|
|
|
3068
3476
|
log.success("Capture resumed.");
|
|
3069
3477
|
});
|
|
3070
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
|
+
});
|
|
3071
3488
|
|
|
3072
3489
|
// src/cli/undo.ts
|
|
3073
|
-
function registerUndo(
|
|
3074
|
-
|
|
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) => {
|
|
3075
3492
|
const count = parseInt(opts.count, 10);
|
|
3076
3493
|
if (isNaN(count) || count < 1) {
|
|
3077
3494
|
log.error("--count must be a positive integer");
|
|
@@ -3097,12 +3514,17 @@ function registerUndo(program2) {
|
|
|
3097
3514
|
log.success(`Removed ${ids.length} event${ids.length > 1 ? "s" : ""}.`);
|
|
3098
3515
|
});
|
|
3099
3516
|
}
|
|
3517
|
+
var init_undo = __esm({
|
|
3518
|
+
"src/cli/undo.ts"() {
|
|
3519
|
+
"use strict";
|
|
3520
|
+
init_db();
|
|
3521
|
+
init_logger();
|
|
3522
|
+
}
|
|
3523
|
+
});
|
|
3100
3524
|
|
|
3101
3525
|
// src/cli/clear.ts
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
function registerClear(program2) {
|
|
3105
|
-
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) => {
|
|
3106
3528
|
let date = todayDate();
|
|
3107
3529
|
if (opts.date) {
|
|
3108
3530
|
try {
|
|
@@ -3147,13 +3569,19 @@ function registerClear(program2) {
|
|
|
3147
3569
|
log.success(`Cleared ${deleted} event${deleted !== 1 ? "s" : ""} for ${date}.`);
|
|
3148
3570
|
});
|
|
3149
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
|
+
});
|
|
3150
3583
|
|
|
3151
3584
|
// src/cli/uninstall.ts
|
|
3152
|
-
var import_fs19 = __toESM(require("fs"));
|
|
3153
|
-
var import_path13 = __toESM(require("path"));
|
|
3154
|
-
var import_os7 = __toESM(require("os"));
|
|
3155
|
-
var import_prompts3 = __toESM(require("prompts"));
|
|
3156
|
-
var import_chalk9 = __toESM(require("chalk"));
|
|
3157
3585
|
function detectShellRcFiles() {
|
|
3158
3586
|
const candidates = [
|
|
3159
3587
|
{ name: "zsh", rcFile: import_path13.default.join(import_os7.default.homedir(), ".zshrc") },
|
|
@@ -3163,8 +3591,8 @@ function detectShellRcFiles() {
|
|
|
3163
3591
|
];
|
|
3164
3592
|
return candidates.filter((candidate) => import_fs19.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
3165
3593
|
}
|
|
3166
|
-
function registerUninstall(
|
|
3167
|
-
|
|
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) => {
|
|
3168
3596
|
log.plain(import_chalk9.default.bold("\n Workscribe \u2014 Uninstall\n"));
|
|
3169
3597
|
const hooked = detectShellRcFiles();
|
|
3170
3598
|
if (hooked.length === 0) {
|
|
@@ -3209,14 +3637,24 @@ function registerUninstall(program2) {
|
|
|
3209
3637
|
log.plain("");
|
|
3210
3638
|
});
|
|
3211
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
|
+
});
|
|
3212
3654
|
|
|
3213
3655
|
// src/cli/profile.ts
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
var import_chalk10 = __toESM(require("chalk"));
|
|
3217
|
-
var MIN_SUGGEST_COUNT = 5;
|
|
3218
|
-
function registerProfile(program2) {
|
|
3219
|
-
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");
|
|
3220
3658
|
profile.command("list").description("List available profiles from the registry").action(async () => {
|
|
3221
3659
|
const config = readConfig();
|
|
3222
3660
|
const activeNames = new Set(config.capture.profiles);
|
|
@@ -3422,10 +3860,26 @@ function registerProfile(program2) {
|
|
|
3422
3860
|
}
|
|
3423
3861
|
});
|
|
3424
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
|
+
});
|
|
3425
3879
|
|
|
3426
3880
|
// src/cli/note.ts
|
|
3427
|
-
function registerNote(
|
|
3428
|
-
|
|
3881
|
+
function registerNote(program) {
|
|
3882
|
+
program.command("note <text>").description("Attach a free-text annotation to the current session").action((text) => {
|
|
3429
3883
|
const { repoName, branch } = getGitContext(process.cwd());
|
|
3430
3884
|
insertEvent({
|
|
3431
3885
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3443,12 +3897,16 @@ function registerNote(program2) {
|
|
|
3443
3897
|
log.success(`Note saved${repoName ? ` to ${repoName}` : ""}`);
|
|
3444
3898
|
});
|
|
3445
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
|
+
});
|
|
3446
3908
|
|
|
3447
3909
|
// src/cli/week.ts
|
|
3448
|
-
var import_chalk11 = __toESM(require("chalk"));
|
|
3449
|
-
var import_fs21 = __toESM(require("fs"));
|
|
3450
|
-
var import_path15 = __toESM(require("path"));
|
|
3451
|
-
var import_os8 = __toESM(require("os"));
|
|
3452
3910
|
function eventRowToPayload3(row) {
|
|
3453
3911
|
let metadata = {};
|
|
3454
3912
|
try {
|
|
@@ -3495,8 +3953,8 @@ async function postSummary(opts, config, summary) {
|
|
|
3495
3953
|
log.error(`Webhook failed: ${error.message}`);
|
|
3496
3954
|
}
|
|
3497
3955
|
}
|
|
3498
|
-
function registerWeek(
|
|
3499
|
-
|
|
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) => {
|
|
3500
3958
|
const config = readConfig();
|
|
3501
3959
|
let provider;
|
|
3502
3960
|
try {
|
|
@@ -3547,14 +4005,26 @@ function registerWeek(program2) {
|
|
|
3547
4005
|
}
|
|
3548
4006
|
});
|
|
3549
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
|
+
});
|
|
3550
4026
|
|
|
3551
4027
|
// src/cli/bug-report.ts
|
|
3552
|
-
var import_fs22 = __toESM(require("fs"));
|
|
3553
|
-
var import_os9 = __toESM(require("os"));
|
|
3554
|
-
var import_path16 = __toESM(require("path"));
|
|
3555
|
-
var import_chalk12 = __toESM(require("chalk"));
|
|
3556
|
-
var GITHUB_ISSUES_URL = "https://github.com/workscribe/workscribe/issues/new";
|
|
3557
|
-
var DEBUG_LOG_TAIL_LINES = 100;
|
|
3558
4028
|
function tailFile(filePath, lines) {
|
|
3559
4029
|
if (!import_fs22.default.existsSync(filePath)) return "(no debug log found \u2014 set WORKSCRIBE_DEBUG=1 to enable)";
|
|
3560
4030
|
const content = import_fs22.default.readFileSync(filePath, "utf-8");
|
|
@@ -3572,12 +4042,12 @@ function redactConfig() {
|
|
|
3572
4042
|
return "(could not read config)";
|
|
3573
4043
|
}
|
|
3574
4044
|
}
|
|
3575
|
-
function registerBugReport(
|
|
3576
|
-
|
|
4045
|
+
function registerBugReport(program) {
|
|
4046
|
+
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
3577
4047
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3578
4048
|
const reportPath = import_path16.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
3579
4049
|
const systemInfo = [
|
|
3580
|
-
`Workscribe version : ${"1.
|
|
4050
|
+
`Workscribe version : ${"1.14.1"}`,
|
|
3581
4051
|
`Node.js version : ${process.version}`,
|
|
3582
4052
|
`Platform : ${process.platform}`,
|
|
3583
4053
|
`Architecture : ${process.arch}`,
|
|
@@ -3608,7 +4078,7 @@ function registerBugReport(program2) {
|
|
|
3608
4078
|
process.exit(1);
|
|
3609
4079
|
}
|
|
3610
4080
|
const params = new URLSearchParams({
|
|
3611
|
-
title: `Bug report \u2014 workscribe ${"1.
|
|
4081
|
+
title: `Bug report \u2014 workscribe ${"1.14.1"}`,
|
|
3612
4082
|
body: `<!-- Attach ${import_path16.default.basename(reportPath)} or paste the relevant sections below -->
|
|
3613
4083
|
|
|
3614
4084
|
**Describe the issue:**
|
|
@@ -3626,9 +4096,23 @@ function registerBugReport(program2) {
|
|
|
3626
4096
|
log.plain("");
|
|
3627
4097
|
});
|
|
3628
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
|
+
});
|
|
3629
4114
|
|
|
3630
4115
|
// src/cli/alerts.ts
|
|
3631
|
-
var import_chalk13 = __toESM(require("chalk"));
|
|
3632
4116
|
function formatTimestamp(ms) {
|
|
3633
4117
|
return new Date(ms).toLocaleString([], {
|
|
3634
4118
|
month: "short",
|
|
@@ -3637,8 +4121,8 @@ function formatTimestamp(ms) {
|
|
|
3637
4121
|
minute: "2-digit"
|
|
3638
4122
|
});
|
|
3639
4123
|
}
|
|
3640
|
-
function registerAlerts(
|
|
3641
|
-
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) => {
|
|
3642
4126
|
if (opts.check) {
|
|
3643
4127
|
if (hasPendingAlerts()) {
|
|
3644
4128
|
log.plain(
|
|
@@ -3675,10 +4159,29 @@ function registerAlerts(program2) {
|
|
|
3675
4159
|
}
|
|
3676
4160
|
});
|
|
3677
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
|
+
});
|
|
3678
4171
|
|
|
3679
4172
|
// src/cli/context.ts
|
|
3680
|
-
|
|
3681
|
-
|
|
4173
|
+
function toContextJson(session) {
|
|
4174
|
+
return {
|
|
4175
|
+
date: session.date,
|
|
4176
|
+
startedAt: session.started_at,
|
|
4177
|
+
endedAt: session.ended_at,
|
|
4178
|
+
timeAgo: timeAgo(session.started_at),
|
|
4179
|
+
durationMinutes: (new Date(session.ended_at).getTime() - new Date(session.started_at).getTime()) / 6e4,
|
|
4180
|
+
branch: getSessionBranch(session.id),
|
|
4181
|
+
eventCount: session.event_count,
|
|
4182
|
+
summary: session.summary
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
3682
4185
|
function printSessionContext(session, heading) {
|
|
3683
4186
|
const ago = timeAgo(session.started_at);
|
|
3684
4187
|
const branch = getSessionBranch(session.id);
|
|
@@ -3701,35 +4204,57 @@ function printSessionContext(session, heading) {
|
|
|
3701
4204
|
}
|
|
3702
4205
|
log.plain("");
|
|
3703
4206
|
}
|
|
3704
|
-
function registerContext(
|
|
3705
|
-
|
|
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) => {
|
|
3706
4209
|
if (opts.file) {
|
|
3707
4210
|
const normalizedFile = import_path17.default.normalize(opts.file);
|
|
3708
4211
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
3709
4212
|
if (!session) {
|
|
3710
|
-
log.plain(
|
|
4213
|
+
if (opts.json) log.plain(JSON.stringify(null));
|
|
4214
|
+
else log.plain(` No sessions found touching ${normalizedFile}`);
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
if (opts.json) {
|
|
4218
|
+
log.plain(JSON.stringify(toContextJson(session)));
|
|
3711
4219
|
return;
|
|
3712
4220
|
}
|
|
3713
4221
|
printSessionContext(session, `Last session touching ${import_path17.default.basename(normalizedFile)}`);
|
|
3714
4222
|
return;
|
|
3715
4223
|
}
|
|
3716
|
-
const { repoName } = getGitContext(process.cwd());
|
|
4224
|
+
const { repoName } = getGitContext(process.cwd(), null, readConfig().git.qualifyRepoName);
|
|
3717
4225
|
if (!repoName) {
|
|
3718
|
-
log.plain(
|
|
4226
|
+
if (opts.json) log.plain(JSON.stringify(null));
|
|
4227
|
+
else log.plain(" Not in a git repository. Run workscribe context from a project directory.");
|
|
3719
4228
|
return;
|
|
3720
4229
|
}
|
|
3721
4230
|
const sessions = getRecentSessionsByRepo(repoName, 1);
|
|
3722
4231
|
if (sessions.length === 0) {
|
|
3723
|
-
log.plain(
|
|
4232
|
+
if (opts.json) log.plain(JSON.stringify(null));
|
|
4233
|
+
else log.plain(` No sessions found for ${repoName}`);
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4236
|
+
if (opts.json) {
|
|
4237
|
+
log.plain(JSON.stringify(toContextJson(sessions[0])));
|
|
3724
4238
|
return;
|
|
3725
4239
|
}
|
|
3726
4240
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
3727
4241
|
});
|
|
3728
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
|
+
});
|
|
3729
4256
|
|
|
3730
4257
|
// src/utils/update-check.ts
|
|
3731
|
-
var import_fs23 = __toESM(require("fs"));
|
|
3732
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3733
4258
|
function readCache() {
|
|
3734
4259
|
try {
|
|
3735
4260
|
return JSON.parse(import_fs23.default.readFileSync(UPDATE_CHECK_PATH, "utf-8"));
|
|
@@ -3752,7 +4277,7 @@ function isNewer(latest, current) {
|
|
|
3752
4277
|
return latestPatch > currentPatch;
|
|
3753
4278
|
}
|
|
3754
4279
|
function checkForUpdate() {
|
|
3755
|
-
const current = "1.
|
|
4280
|
+
const current = "1.14.1";
|
|
3756
4281
|
const cache = readCache();
|
|
3757
4282
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
3758
4283
|
process.stderr.write(
|
|
@@ -3772,31 +4297,102 @@ function checkForUpdate() {
|
|
|
3772
4297
|
});
|
|
3773
4298
|
}
|
|
3774
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
|
+
}
|
|
3775
4394
|
|
|
3776
4395
|
// src/index.ts
|
|
3777
4396
|
assertNodeVersion(process.versions.node);
|
|
3778
|
-
|
|
3779
|
-
var program = new import_commander.Command();
|
|
3780
|
-
program.name("workscribe").description("Automatic developer work journal").version("1.13.0");
|
|
3781
|
-
registerCapture(program);
|
|
3782
|
-
registerHook(program);
|
|
3783
|
-
registerInit(program);
|
|
3784
|
-
registerStatus(program);
|
|
3785
|
-
registerEvents(program);
|
|
3786
|
-
registerSessions(program);
|
|
3787
|
-
registerSummary(program);
|
|
3788
|
-
registerExport(program);
|
|
3789
|
-
registerConfig(program);
|
|
3790
|
-
registerPause(program);
|
|
3791
|
-
registerUndo(program);
|
|
3792
|
-
registerClear(program);
|
|
3793
|
-
registerUninstall(program);
|
|
3794
|
-
registerProfile(program);
|
|
3795
|
-
registerNote(program);
|
|
3796
|
-
registerWeek(program);
|
|
3797
|
-
registerBugReport(program);
|
|
3798
|
-
registerCompletions(program);
|
|
3799
|
-
registerAlerts(program);
|
|
3800
|
-
registerContext(program);
|
|
3801
|
-
program.parse();
|
|
4397
|
+
require_main();
|
|
3802
4398
|
//# sourceMappingURL=index.js.map
|