@workscribe/cli 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +344 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/workscribe/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ On feature/auth:
|
|
|
19
19
|
- Committed: add JWT validation middleware
|
|
20
20
|
- Added dependencies: bcrypt, jsonwebtoken
|
|
21
21
|
- Pushed to origin/feature/auth
|
|
22
|
-
With
|
|
22
|
+
With Claude Code (2 sessions) · 3 file saves in the editor
|
|
23
23
|
|
|
24
24
|
## infrastructure — 45m
|
|
25
25
|
- Note: staging was flaky after the compose upgrade
|
package/dist/index.js
CHANGED
|
@@ -972,6 +972,13 @@ function maskQuotedText(text) {
|
|
|
972
972
|
}
|
|
973
973
|
return masked.join("");
|
|
974
974
|
}
|
|
975
|
+
function maskLiteralText(text) {
|
|
976
|
+
const masked = Array.from({ length: text.length }, () => "_");
|
|
977
|
+
for (const { index, quote } of quotedCharacters(text)) {
|
|
978
|
+
if (quote !== "'") masked[index] = text[index];
|
|
979
|
+
}
|
|
980
|
+
return masked.join("");
|
|
981
|
+
}
|
|
975
982
|
function startsWithWord(step2, word) {
|
|
976
983
|
return step2 === word || step2.startsWith(`${word} `) || step2.startsWith(`${word} `);
|
|
977
984
|
}
|
|
@@ -1175,57 +1182,6 @@ var init_exclusions = __esm({
|
|
|
1175
1182
|
}
|
|
1176
1183
|
});
|
|
1177
1184
|
|
|
1178
|
-
// src/normalize/compound.ts
|
|
1179
|
-
function rankOf(category) {
|
|
1180
|
-
const index = STEP_CATEGORY_PRIORITY.indexOf(category);
|
|
1181
|
-
if (index !== -1) return index;
|
|
1182
|
-
if (category === UNCLASSIFIED.category) return UNCLASSIFIED_RANK;
|
|
1183
|
-
if (DEFAULT_EXCLUDED_CATEGORIES.has(category)) return EXCLUDED_CATEGORY_RANK;
|
|
1184
|
-
return OTHER_CATEGORY_RANK;
|
|
1185
|
-
}
|
|
1186
|
-
function primaryStepEvent(steps, matchCommand) {
|
|
1187
|
-
const events = steps.map((step2) => ({ ...matchCommand(simpleCommandOf(step2), step2), matchedStep: step2 }));
|
|
1188
|
-
return events.reduce(
|
|
1189
|
-
(best, event) => rankOf(event.category) < rankOf(best.category) ? event : best,
|
|
1190
|
-
UNCLASSIFIED
|
|
1191
|
-
);
|
|
1192
|
-
}
|
|
1193
|
-
function classifyCompound(command, matchCommand) {
|
|
1194
|
-
const workSteps = commandSteps(command).filter((step2) => !isGlueStep(step2));
|
|
1195
|
-
const wholeCommandEvent = {
|
|
1196
|
-
...matchCommand(command, command),
|
|
1197
|
-
matchedStep: workSteps.length === 1 ? workSteps[0] : command
|
|
1198
|
-
};
|
|
1199
|
-
const isRecognisedSingleCommand = workSteps.length <= 1 && wholeCommandEvent.category !== UNCLASSIFIED.category;
|
|
1200
|
-
if (isRecognisedSingleCommand || workSteps.length === 0) return wholeCommandEvent;
|
|
1201
|
-
const primaryEvent = primaryStepEvent(workSteps, matchCommand);
|
|
1202
|
-
return primaryEvent.category === UNCLASSIFIED.category ? wholeCommandEvent : primaryEvent;
|
|
1203
|
-
}
|
|
1204
|
-
var UNCLASSIFIED, STEP_CATEGORY_PRIORITY, OTHER_CATEGORY_RANK, EXCLUDED_CATEGORY_RANK, UNCLASSIFIED_RANK;
|
|
1205
|
-
var init_compound = __esm({
|
|
1206
|
-
"src/normalize/compound.ts"() {
|
|
1207
|
-
"use strict";
|
|
1208
|
-
init_command_steps();
|
|
1209
|
-
init_exclusions();
|
|
1210
|
-
UNCLASSIFIED = { category: "other", metadata: {} };
|
|
1211
|
-
STEP_CATEGORY_PRIORITY = [
|
|
1212
|
-
"testing",
|
|
1213
|
-
"building",
|
|
1214
|
-
"releasing",
|
|
1215
|
-
"shipping",
|
|
1216
|
-
"dependencies",
|
|
1217
|
-
"database",
|
|
1218
|
-
"infrastructure",
|
|
1219
|
-
"running",
|
|
1220
|
-
"files",
|
|
1221
|
-
"versioning"
|
|
1222
|
-
];
|
|
1223
|
-
OTHER_CATEGORY_RANK = STEP_CATEGORY_PRIORITY.length;
|
|
1224
|
-
EXCLUDED_CATEGORY_RANK = OTHER_CATEGORY_RANK + 1;
|
|
1225
|
-
UNCLASSIFIED_RANK = EXCLUDED_CATEGORY_RANK + 1;
|
|
1226
|
-
}
|
|
1227
|
-
});
|
|
1228
|
-
|
|
1229
1185
|
// src/normalize/git-invocation.ts
|
|
1230
1186
|
function parseGitInvocation(command) {
|
|
1231
1187
|
const words = shellWords(command.trim());
|
|
@@ -1282,6 +1238,198 @@ var init_git_invocation = __esm({
|
|
|
1282
1238
|
}
|
|
1283
1239
|
});
|
|
1284
1240
|
|
|
1241
|
+
// src/normalize/read-only.ts
|
|
1242
|
+
function hasNoOption(args, options) {
|
|
1243
|
+
return !args.some((arg) => options.some((option) => arg === option || arg.startsWith(`${option}=`)));
|
|
1244
|
+
}
|
|
1245
|
+
function runsInlineCode(args, inlineFlags) {
|
|
1246
|
+
const firstMeaningful = args.find(
|
|
1247
|
+
(arg) => inlineFlags.includes(arg) || arg.startsWith("--eval=") || !arg.startsWith("-")
|
|
1248
|
+
);
|
|
1249
|
+
return firstMeaningful !== void 0 && (inlineFlags.includes(firstMeaningful) || firstMeaningful.startsWith("--eval="));
|
|
1250
|
+
}
|
|
1251
|
+
function readSedShortOptions(option, nextArg, parsed) {
|
|
1252
|
+
const letters = option.slice(1);
|
|
1253
|
+
const expressionAt = letters.indexOf("e");
|
|
1254
|
+
if (SED_UNSAFE_SHORT_OPTION.test(letters) || expressionAt !== -1 && expressionAt !== letters.length - 1) {
|
|
1255
|
+
parsed.isSafe = false;
|
|
1256
|
+
}
|
|
1257
|
+
if (letters.includes("n")) parsed.isQuiet = true;
|
|
1258
|
+
if (expressionAt === -1) return 0;
|
|
1259
|
+
parsed.scripts.push(nextArg ?? "");
|
|
1260
|
+
return 1;
|
|
1261
|
+
}
|
|
1262
|
+
function readSedArguments(args) {
|
|
1263
|
+
const parsed = { isQuiet: false, isSafe: true, scripts: [] };
|
|
1264
|
+
const positional = [];
|
|
1265
|
+
for (let index = 0; index < args.length; index++) {
|
|
1266
|
+
const arg = args[index];
|
|
1267
|
+
if (arg === "--quiet" || arg === "--silent") parsed.isQuiet = true;
|
|
1268
|
+
else if (arg.startsWith("--in-place") || arg.startsWith("--file")) parsed.isSafe = false;
|
|
1269
|
+
else if (arg === "--expression") parsed.scripts.push(args[++index] ?? "");
|
|
1270
|
+
else if (arg.startsWith("--expression=")) parsed.scripts.push(arg.slice("--expression=".length));
|
|
1271
|
+
else if (SED_SHORT_OPTIONS.test(arg)) index += readSedShortOptions(arg, args[index + 1], parsed);
|
|
1272
|
+
else if (!arg.startsWith("-")) positional.push(arg);
|
|
1273
|
+
}
|
|
1274
|
+
return { ...parsed, positional };
|
|
1275
|
+
}
|
|
1276
|
+
function sedOnlyPrints(args) {
|
|
1277
|
+
const { isQuiet, isSafe, scripts, positional } = readSedArguments(args);
|
|
1278
|
+
const allScripts = scripts.length > 0 ? scripts : positional.slice(0, 1);
|
|
1279
|
+
if (!isQuiet || !isSafe || allScripts.length === 0) return false;
|
|
1280
|
+
return allScripts.every(
|
|
1281
|
+
(script) => script.split(";").filter((part) => part.trim() !== "").every((part) => SED_PRINT_COMMAND.test(part.trim()))
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
function isGitRead(command) {
|
|
1285
|
+
const invocation = parseGitInvocation(command);
|
|
1286
|
+
if (!invocation?.subcommand) return false;
|
|
1287
|
+
const args = invocation.args.map(({ word }) => word);
|
|
1288
|
+
if (invocation.subcommand === "branch") return args.length === 0;
|
|
1289
|
+
return GIT_READS.has(invocation.subcommand) && hasNoOption(args, GIT_UNSAFE_OPTIONS);
|
|
1290
|
+
}
|
|
1291
|
+
function isReadingStage(stage, checks) {
|
|
1292
|
+
const command = simpleCommandOf(stage);
|
|
1293
|
+
const [name, ...args] = shellWords(command).map(({ word }) => word);
|
|
1294
|
+
if (name === "git") return isGitRead(command);
|
|
1295
|
+
return Object.hasOwn(checks, name ?? "") && checks[name](args);
|
|
1296
|
+
}
|
|
1297
|
+
function substitutionsOnlyRead(step2) {
|
|
1298
|
+
if (!containsCommandSubstitution(step2)) return true;
|
|
1299
|
+
if (PROCESS_SUBSTITUTION.test(maskQuotedText(step2))) return false;
|
|
1300
|
+
const literalMasked = maskLiteralText(step2);
|
|
1301
|
+
const commands = substitutionCommands(literalMasked);
|
|
1302
|
+
const openers = literalMasked.split("$(").length - 1 + Math.ceil((literalMasked.split("`").length - 1) / 2);
|
|
1303
|
+
return commands.length === openers && commands.every(isReadOnlyCommand);
|
|
1304
|
+
}
|
|
1305
|
+
function isReadOnlyStep(step2) {
|
|
1306
|
+
if (!substitutionsOnlyRead(step2)) return false;
|
|
1307
|
+
const [firstStage = "", ...laterStages] = splitOutsideQuotes(step2, PIPE_SEPARATORS2);
|
|
1308
|
+
return isReadingStage(firstStage, READERS) && laterStages.every((stage) => isReadingStage(stage, { ...READERS, ...PIPE_FILTERS }));
|
|
1309
|
+
}
|
|
1310
|
+
function writesToFile(step2) {
|
|
1311
|
+
return [...maskQuotedText(step2).matchAll(OUTPUT_REDIRECTION)].some(([, target]) => !HARMLESS_TARGET.test(target));
|
|
1312
|
+
}
|
|
1313
|
+
function isReadOnlyCommand(command) {
|
|
1314
|
+
const steps = commandSteps(command);
|
|
1315
|
+
if (steps.some(writesToFile)) return false;
|
|
1316
|
+
const workSteps = steps.filter((step2) => !isGlueStep(step2));
|
|
1317
|
+
return workSteps.length > 0 && workSteps.every(isReadOnlyStep);
|
|
1318
|
+
}
|
|
1319
|
+
function readOnlyMetadata(command) {
|
|
1320
|
+
const firstWorkStep = commandSteps(command).find((step2) => !isGlueStep(step2)) ?? "";
|
|
1321
|
+
const [firstStage = ""] = splitOutsideQuotes(firstWorkStep, PIPE_SEPARATORS2);
|
|
1322
|
+
const simple = simpleCommandOf(firstStage);
|
|
1323
|
+
const invocation = parseGitInvocation(simple);
|
|
1324
|
+
return invocation ? { subcommand: invocation.subcommand } : { tool: shellWords(simple)[0]?.word ?? null };
|
|
1325
|
+
}
|
|
1326
|
+
var READ_ONLY_CATEGORY, PIPE_SEPARATORS2, ALWAYS_READS, GIT_READS, GIT_UNSAFE_OPTIONS, FIND_UNSAFE_ACTION, SED_ADDRESS, SED_PRINT_COMMAND, SED_SHORT_OPTIONS, SED_UNSAFE_SHORT_OPTION, OUTPUT_REDIRECTION, HARMLESS_TARGET, PROCESS_SUBSTITUTION, READERS, PIPE_FILTERS;
|
|
1327
|
+
var init_read_only = __esm({
|
|
1328
|
+
"src/normalize/read-only.ts"() {
|
|
1329
|
+
"use strict";
|
|
1330
|
+
init_command_steps();
|
|
1331
|
+
init_shell_syntax();
|
|
1332
|
+
init_git_invocation();
|
|
1333
|
+
READ_ONLY_CATEGORY = "exploring";
|
|
1334
|
+
PIPE_SEPARATORS2 = ["|"];
|
|
1335
|
+
ALWAYS_READS = () => true;
|
|
1336
|
+
GIT_READS = /* @__PURE__ */ new Set(["diff", "log", "show", "status"]);
|
|
1337
|
+
GIT_UNSAFE_OPTIONS = ["--output", "--ext-diff", "--textconv"];
|
|
1338
|
+
FIND_UNSAFE_ACTION = /^-(?:delete|exec|execdir|ok|okdir|fls|fprint\w*)$/;
|
|
1339
|
+
SED_ADDRESS = String.raw`(?:\d+|\$|/(?:[^/\\]|\\.)*/)`;
|
|
1340
|
+
SED_PRINT_COMMAND = new RegExp(String.raw`^(?:${SED_ADDRESS}(?:,${SED_ADDRESS})?)?\s*!?\s*[p=]$`);
|
|
1341
|
+
SED_SHORT_OPTIONS = /^-[a-zA-Z]+$/;
|
|
1342
|
+
SED_UNSAFE_SHORT_OPTION = /[iIf]/;
|
|
1343
|
+
OUTPUT_REDIRECTION = /(?:&>>?|\d*>>?\|?)\s*(&?[^\s;|&()]*)/g;
|
|
1344
|
+
HARMLESS_TARGET = /^(?:&\d+|\/dev\/null)$/;
|
|
1345
|
+
PROCESS_SUBSTITUTION = /[<>]\(/;
|
|
1346
|
+
READERS = {
|
|
1347
|
+
grep: ALWAYS_READS,
|
|
1348
|
+
egrep: ALWAYS_READS,
|
|
1349
|
+
fgrep: ALWAYS_READS,
|
|
1350
|
+
ag: ALWAYS_READS,
|
|
1351
|
+
ack: ALWAYS_READS,
|
|
1352
|
+
cat: ALWAYS_READS,
|
|
1353
|
+
bat: ALWAYS_READS,
|
|
1354
|
+
less: ALWAYS_READS,
|
|
1355
|
+
more: ALWAYS_READS,
|
|
1356
|
+
head: ALWAYS_READS,
|
|
1357
|
+
tail: ALWAYS_READS,
|
|
1358
|
+
ls: ALWAYS_READS,
|
|
1359
|
+
wc: ALWAYS_READS,
|
|
1360
|
+
// `--pre` runs a command on every file
|
|
1361
|
+
rg: (args) => hasNoOption(args, ["--pre"]),
|
|
1362
|
+
ripgrep: (args) => hasNoOption(args, ["--pre"]),
|
|
1363
|
+
find: (args) => !args.some((arg) => FIND_UNSAFE_ACTION.test(arg)),
|
|
1364
|
+
sed: sedOnlyPrints,
|
|
1365
|
+
python: (args) => runsInlineCode(args, ["-c", "-"]),
|
|
1366
|
+
python3: (args) => runsInlineCode(args, ["-c", "-"]),
|
|
1367
|
+
node: (args) => runsInlineCode(args, ["-e", "--eval"])
|
|
1368
|
+
};
|
|
1369
|
+
PIPE_FILTERS = {
|
|
1370
|
+
sort: (args) => hasNoOption(args, ["-o", "--output"]) && !args.some((arg) => /^-[a-zA-Z]*o/.test(arg)),
|
|
1371
|
+
// `uniq in out` writes to `out`
|
|
1372
|
+
uniq: (args) => args.filter((arg) => !arg.startsWith("-")).length <= 1,
|
|
1373
|
+
cut: ALWAYS_READS,
|
|
1374
|
+
tr: ALWAYS_READS,
|
|
1375
|
+
jq: ALWAYS_READS
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
// src/normalize/compound.ts
|
|
1381
|
+
function rankOf(category) {
|
|
1382
|
+
const index = STEP_CATEGORY_PRIORITY.indexOf(category);
|
|
1383
|
+
if (index !== -1) return index;
|
|
1384
|
+
if (category === UNCLASSIFIED.category) return UNCLASSIFIED_RANK;
|
|
1385
|
+
if (category === READ_ONLY_CATEGORY) return READ_ONLY_RANK;
|
|
1386
|
+
if (DEFAULT_EXCLUDED_CATEGORIES.has(category)) return EXCLUDED_CATEGORY_RANK;
|
|
1387
|
+
return OTHER_CATEGORY_RANK;
|
|
1388
|
+
}
|
|
1389
|
+
function primaryStepEvent(stepEvents) {
|
|
1390
|
+
return stepEvents.reduce((best, event) => rankOf(event.category) < rankOf(best.category) ? event : best);
|
|
1391
|
+
}
|
|
1392
|
+
function classifyCompound(command, matchCommand) {
|
|
1393
|
+
const workSteps = commandSteps(command).filter((step2) => !isGlueStep(step2));
|
|
1394
|
+
const wholeCommandEvent = {
|
|
1395
|
+
...matchCommand(command, command),
|
|
1396
|
+
matchedStep: workSteps.length === 1 ? workSteps[0] : command
|
|
1397
|
+
};
|
|
1398
|
+
const isRecognisedSingleCommand = workSteps.length <= 1 && wholeCommandEvent.category !== UNCLASSIFIED.category;
|
|
1399
|
+
if (isRecognisedSingleCommand || workSteps.length === 0) return wholeCommandEvent;
|
|
1400
|
+
const stepEvents = workSteps.map((step2) => ({ ...matchCommand(simpleCommandOf(step2), step2), matchedStep: step2 }));
|
|
1401
|
+
const primaryEvent = primaryStepEvent(stepEvents);
|
|
1402
|
+
if (primaryEvent.category !== UNCLASSIFIED.category) return primaryEvent;
|
|
1403
|
+
const isAnyStepRecognised = stepEvents.some((event) => event.category !== UNCLASSIFIED.category);
|
|
1404
|
+
return isAnyStepRecognised ? { ...UNCLASSIFIED, matchedStep: command } : wholeCommandEvent;
|
|
1405
|
+
}
|
|
1406
|
+
var UNCLASSIFIED, STEP_CATEGORY_PRIORITY, OTHER_CATEGORY_RANK, EXCLUDED_CATEGORY_RANK, UNCLASSIFIED_RANK, READ_ONLY_RANK;
|
|
1407
|
+
var init_compound = __esm({
|
|
1408
|
+
"src/normalize/compound.ts"() {
|
|
1409
|
+
"use strict";
|
|
1410
|
+
init_command_steps();
|
|
1411
|
+
init_exclusions();
|
|
1412
|
+
init_read_only();
|
|
1413
|
+
UNCLASSIFIED = { category: "other", metadata: {} };
|
|
1414
|
+
STEP_CATEGORY_PRIORITY = [
|
|
1415
|
+
"testing",
|
|
1416
|
+
"building",
|
|
1417
|
+
"releasing",
|
|
1418
|
+
"shipping",
|
|
1419
|
+
"dependencies",
|
|
1420
|
+
"database",
|
|
1421
|
+
"infrastructure",
|
|
1422
|
+
"running",
|
|
1423
|
+
"files",
|
|
1424
|
+
"versioning"
|
|
1425
|
+
];
|
|
1426
|
+
OTHER_CATEGORY_RANK = STEP_CATEGORY_PRIORITY.length;
|
|
1427
|
+
EXCLUDED_CATEGORY_RANK = OTHER_CATEGORY_RANK + 1;
|
|
1428
|
+
UNCLASSIFIED_RANK = EXCLUDED_CATEGORY_RANK + 1;
|
|
1429
|
+
READ_ONLY_RANK = UNCLASSIFIED_RANK + 1;
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1285
1433
|
// src/normalize/commit-message.ts
|
|
1286
1434
|
function firstLineOf(text) {
|
|
1287
1435
|
const line = text?.split("\n").map((part) => part.trim()).find((part) => part !== "");
|
|
@@ -1435,7 +1583,7 @@ function matchRules(command, context) {
|
|
|
1435
1583
|
function normalizeCommand(command) {
|
|
1436
1584
|
return classifyCompound(command, (simple, raw) => matchRules(simple, { raw, original: command }));
|
|
1437
1585
|
}
|
|
1438
|
-
var NX_TARGET_CATEGORIES, NX_COMMAND, nxRule, BASE_RULES, activeRules;
|
|
1586
|
+
var NX_TARGET_CATEGORIES, NX_COMMAND, nxRule, readOnlyRule, BASE_RULES, activeRules;
|
|
1439
1587
|
var init_patterns = __esm({
|
|
1440
1588
|
"src/normalize/patterns.ts"() {
|
|
1441
1589
|
"use strict";
|
|
@@ -1446,6 +1594,7 @@ var init_patterns = __esm({
|
|
|
1446
1594
|
init_commit_message();
|
|
1447
1595
|
init_dependency_metadata();
|
|
1448
1596
|
init_git_invocation();
|
|
1597
|
+
init_read_only();
|
|
1449
1598
|
NX_TARGET_CATEGORIES = {
|
|
1450
1599
|
build: "building",
|
|
1451
1600
|
test: "testing",
|
|
@@ -1463,7 +1612,13 @@ var init_patterns = __esm({
|
|
|
1463
1612
|
return category ? { category, metadata: { tool: "nx", target } } : null;
|
|
1464
1613
|
}
|
|
1465
1614
|
};
|
|
1615
|
+
readOnlyRule = {
|
|
1616
|
+
match(_cmd, { raw }) {
|
|
1617
|
+
return isReadOnlyCommand(raw) ? { category: READ_ONLY_CATEGORY, metadata: readOnlyMetadata(raw) } : null;
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1466
1620
|
BASE_RULES = [
|
|
1621
|
+
readOnlyRule,
|
|
1467
1622
|
// ─── Git operations ───────────────────────────────────────────────────────
|
|
1468
1623
|
makeRule(/^git\s+commit\b/, "shipping", (_m, _cmd, { raw, original }) => {
|
|
1469
1624
|
const commitMessage = commitMessageOf(raw, original);
|
|
@@ -1648,13 +1803,6 @@ var init_patterns = __esm({
|
|
|
1648
1803
|
tool: "bun",
|
|
1649
1804
|
script: cmd.split(/\s+/)[2]
|
|
1650
1805
|
})),
|
|
1651
|
-
// ─── Code search ─────────────────────────────────────────────────────────
|
|
1652
|
-
makeRule(/^(grep|egrep|fgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1653
|
-
makeRule(/^(rg|ag|ack|ripgrep)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1654
|
-
makeRule(/^find\s/, "exploring", () => ({ tool: "find" })),
|
|
1655
|
-
// ─── Code read ────────────────────────────────────────────────────────────
|
|
1656
|
-
makeRule(/^(cat|bat|less|more)\s+\S+/, "exploring", (match) => ({ tool: match[1] })),
|
|
1657
|
-
makeRule(/^(head|tail)\b/, "exploring", (match) => ({ tool: match[1] })),
|
|
1658
1806
|
// ─── File operations ──────────────────────────────────────────────────────
|
|
1659
1807
|
makeRule(/^(cp|rsync)\b/, "files", (match) => ({ tool: match[1] })),
|
|
1660
1808
|
makeRule(/^(mv)\b/, "files", () => ({ tool: "mv" })),
|
|
@@ -3021,7 +3169,7 @@ var init_aider = __esm({
|
|
|
3021
3169
|
// src/capture/workscribe-command.ts
|
|
3022
3170
|
function isWorkscribeStep(step2) {
|
|
3023
3171
|
if (containsCommandSubstitution(step2)) return false;
|
|
3024
|
-
const stages = splitOutsideQuotes(step2,
|
|
3172
|
+
const stages = splitOutsideQuotes(step2, PIPE_SEPARATORS3);
|
|
3025
3173
|
if (startsWithWord(stages[0] ?? "", "workscribe")) return true;
|
|
3026
3174
|
const isTextOrWorkscribe = (stage) => startsWithWord(stage, "workscribe") || TEXT_SOURCES.some((source) => startsWithWord(stage, source));
|
|
3027
3175
|
return stages.every(isTextOrWorkscribe) && stages.some((stage) => startsWithWord(stage, "workscribe"));
|
|
@@ -3031,13 +3179,13 @@ function isWorkscribeOnlyCommand(rawCommand) {
|
|
|
3031
3179
|
const workSteps = steps.filter((step2) => !isGlueStep(step2));
|
|
3032
3180
|
return workSteps.length > 0 && workSteps.every(isWorkscribeStep);
|
|
3033
3181
|
}
|
|
3034
|
-
var
|
|
3182
|
+
var PIPE_SEPARATORS3;
|
|
3035
3183
|
var init_workscribe_command = __esm({
|
|
3036
3184
|
"src/capture/workscribe-command.ts"() {
|
|
3037
3185
|
"use strict";
|
|
3038
3186
|
init_shell_syntax();
|
|
3039
3187
|
init_command_steps();
|
|
3040
|
-
|
|
3188
|
+
PIPE_SEPARATORS3 = ["|"];
|
|
3041
3189
|
}
|
|
3042
3190
|
});
|
|
3043
3191
|
|
|
@@ -4517,10 +4665,10 @@ function installIntoFolder(options) {
|
|
|
4517
4665
|
const outcome = installSkill({
|
|
4518
4666
|
sourceDir: options.sourceDir,
|
|
4519
4667
|
skillsFolder: target.skillsFolder,
|
|
4520
|
-
version: "3.
|
|
4668
|
+
version: "3.2.0",
|
|
4521
4669
|
force: options.force
|
|
4522
4670
|
});
|
|
4523
|
-
return describeInstallOutcome({ ...target, outcome, version: "3.
|
|
4671
|
+
return describeInstallOutcome({ ...target, outcome, version: "3.2.0" });
|
|
4524
4672
|
} catch (error) {
|
|
4525
4673
|
return describeInstallFailure({ ...target, error });
|
|
4526
4674
|
}
|
|
@@ -4539,8 +4687,8 @@ function removeSkills(agents, options) {
|
|
|
4539
4687
|
}
|
|
4540
4688
|
function formatAgentSkillLine(agent, homeDir = import_os6.default.homedir()) {
|
|
4541
4689
|
const location = displayPath(import_path19.default.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir);
|
|
4542
|
-
const installState = readInstallState(import_path19.default.join(homeDir, agent.skillsFolder), "3.
|
|
4543
|
-
const status = describeSkillStatus(installState, "3.
|
|
4690
|
+
const installState = readInstallState(import_path19.default.join(homeDir, agent.skillsFolder), "3.2.0");
|
|
4691
|
+
const status = describeSkillStatus(installState, "3.2.0");
|
|
4544
4692
|
return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${import_chalk2.default.dim(location)}`;
|
|
4545
4693
|
}
|
|
4546
4694
|
function formatUndetectedAgentLine(agent) {
|
|
@@ -4832,10 +4980,10 @@ async function runAIToolsStep() {
|
|
|
4832
4980
|
}
|
|
4833
4981
|
async function offerSkillInstall(group, sourceDir) {
|
|
4834
4982
|
const target = describeTarget(group);
|
|
4835
|
-
const state = readInstallState(target.skillsFolder, "3.
|
|
4983
|
+
const state = readInstallState(target.skillsFolder, "3.2.0");
|
|
4836
4984
|
const knownOutcome = outcomeWithoutPrompt(state);
|
|
4837
4985
|
if (knownOutcome) {
|
|
4838
|
-
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "3.
|
|
4986
|
+
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "3.2.0" }));
|
|
4839
4987
|
return;
|
|
4840
4988
|
}
|
|
4841
4989
|
const { installSkill: shouldInstall } = await (0, import_prompts2.default)({
|
|
@@ -5923,6 +6071,73 @@ var init_scrub_secrets = __esm({
|
|
|
5923
6071
|
}
|
|
5924
6072
|
});
|
|
5925
6073
|
|
|
6074
|
+
// src/db/jobs/read-only-commands.ts
|
|
6075
|
+
function parsedObject(metadata) {
|
|
6076
|
+
try {
|
|
6077
|
+
const parsed = JSON.parse(metadata);
|
|
6078
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6079
|
+
} catch {
|
|
6080
|
+
return null;
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
function movedMetadata({ command, metadata }) {
|
|
6084
|
+
if (metadata === null) return JSON.stringify(readOnlyMetadata(command));
|
|
6085
|
+
const parsed = parsedObject(metadata);
|
|
6086
|
+
if (parsed === null) return metadata;
|
|
6087
|
+
const { hint: _hint, tool: _tool, ...kept } = parsed;
|
|
6088
|
+
return JSON.stringify({ ...kept, ...readOnlyMetadata(command) });
|
|
6089
|
+
}
|
|
6090
|
+
function readCandidates(db, afterId, limit) {
|
|
6091
|
+
return db.prepare(
|
|
6092
|
+
`SELECT id, command, metadata FROM events
|
|
6093
|
+
WHERE id > ? AND category IN (${CATEGORY_PLACEHOLDERS})
|
|
6094
|
+
ORDER BY id LIMIT ?`
|
|
6095
|
+
).all(afterId, ...MOVABLE_CATEGORIES, limit);
|
|
6096
|
+
}
|
|
6097
|
+
function runBatch6(db, afterId, limit) {
|
|
6098
|
+
const events = readCandidates(db, afterId, limit);
|
|
6099
|
+
if (events.length === 0) return { lastId: null, issues: [] };
|
|
6100
|
+
const update = db.prepare("UPDATE events SET category = ?, metadata = ? WHERE id = ?");
|
|
6101
|
+
for (const event of events) {
|
|
6102
|
+
if (isReadOnlyCommand(event.command)) update.run(READ_ONLY_CATEGORY, movedMetadata(event), event.id);
|
|
6103
|
+
}
|
|
6104
|
+
return { lastId: events[events.length - 1].id, issues: [] };
|
|
6105
|
+
}
|
|
6106
|
+
function verify5(db) {
|
|
6107
|
+
let checked = 0;
|
|
6108
|
+
let unresolved = 0;
|
|
6109
|
+
let afterId = 0;
|
|
6110
|
+
for (; ; ) {
|
|
6111
|
+
const events = readCandidates(db, afterId, VERIFY_PAGE_SIZE2);
|
|
6112
|
+
if (events.length === 0) break;
|
|
6113
|
+
checked += events.length;
|
|
6114
|
+
unresolved += events.filter((event) => isReadOnlyCommand(event.command)).length;
|
|
6115
|
+
afterId = events[events.length - 1].id;
|
|
6116
|
+
}
|
|
6117
|
+
const notes = unresolved > 0 ? [`read-only events not moved: ${unresolved}`] : [];
|
|
6118
|
+
return { checked, resolved: checked - unresolved, unresolved, hasPassed: true, notes };
|
|
6119
|
+
}
|
|
6120
|
+
var JOB_NAME2, VERIFY_PAGE_SIZE2, MOVABLE_CATEGORIES, CATEGORY_PLACEHOLDERS, readOnlyCommandsJob;
|
|
6121
|
+
var init_read_only_commands = __esm({
|
|
6122
|
+
"src/db/jobs/read-only-commands.ts"() {
|
|
6123
|
+
"use strict";
|
|
6124
|
+
init_read_only();
|
|
6125
|
+
JOB_NAME2 = "read-only-commands";
|
|
6126
|
+
VERIFY_PAGE_SIZE2 = 500;
|
|
6127
|
+
MOVABLE_CATEGORIES = ["versioning", "other", "files", "running"];
|
|
6128
|
+
CATEGORY_PLACEHOLDERS = MOVABLE_CATEGORIES.map(() => "?").join(", ");
|
|
6129
|
+
readOnlyCommandsJob = {
|
|
6130
|
+
name: JOB_NAME2,
|
|
6131
|
+
version: 1,
|
|
6132
|
+
description: "Move stored read-only commands out of summaries",
|
|
6133
|
+
// Moved rows are simply not candidates any more, so a rerun has nothing to clear first
|
|
6134
|
+
prepare: () => void 0,
|
|
6135
|
+
runBatch: runBatch6,
|
|
6136
|
+
verify: verify5
|
|
6137
|
+
};
|
|
6138
|
+
}
|
|
6139
|
+
});
|
|
6140
|
+
|
|
5926
6141
|
// src/db/jobs/index.ts
|
|
5927
6142
|
var JOBS;
|
|
5928
6143
|
var init_jobs = __esm({
|
|
@@ -5934,11 +6149,13 @@ var init_jobs = __esm({
|
|
|
5934
6149
|
init_drop_old_sessions();
|
|
5935
6150
|
init_ticket_refs();
|
|
5936
6151
|
init_scrub_secrets();
|
|
6152
|
+
init_read_only_commands();
|
|
5937
6153
|
JOBS = [
|
|
5938
6154
|
createProjectDimensionsJob({ probeDirectory }),
|
|
5939
6155
|
summaryAnchorsJob,
|
|
5940
6156
|
ticketRefsJob,
|
|
5941
6157
|
scrubSecretsJob,
|
|
6158
|
+
readOnlyCommandsJob,
|
|
5942
6159
|
dropOldSessionsJob
|
|
5943
6160
|
];
|
|
5944
6161
|
}
|
|
@@ -6836,6 +7053,7 @@ function eventRowToPayload(row) {
|
|
|
6836
7053
|
branch: row.branch,
|
|
6837
7054
|
capturedAt: row.captured_at,
|
|
6838
7055
|
...row.source ? { source: row.source } : {},
|
|
7056
|
+
...row.agent_session_id == null ? {} : { agentSessionId: row.agent_session_id },
|
|
6839
7057
|
...gitMovesField(row)
|
|
6840
7058
|
};
|
|
6841
7059
|
if (row.category !== "other" || typeof metadata.hint !== "string") return payload;
|
|
@@ -7649,8 +7867,52 @@ var init_sessions = __esm({
|
|
|
7649
7867
|
}
|
|
7650
7868
|
});
|
|
7651
7869
|
|
|
7870
|
+
// src/ai/engines/template/utils.ts
|
|
7871
|
+
function pluralize(count, noun) {
|
|
7872
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
7873
|
+
}
|
|
7874
|
+
function stringsIn(value) {
|
|
7875
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
7876
|
+
}
|
|
7877
|
+
function unique(values) {
|
|
7878
|
+
return [...new Set(values)];
|
|
7879
|
+
}
|
|
7880
|
+
function eventsOf(events, category) {
|
|
7881
|
+
return events.filter((event) => event.category === category);
|
|
7882
|
+
}
|
|
7883
|
+
function toBulletList(bullets) {
|
|
7884
|
+
return bullets.map((bullet) => `- ${bullet}`).join("\n");
|
|
7885
|
+
}
|
|
7886
|
+
var init_utils3 = __esm({
|
|
7887
|
+
"src/ai/engines/template/utils.ts"() {
|
|
7888
|
+
"use strict";
|
|
7889
|
+
}
|
|
7890
|
+
});
|
|
7891
|
+
|
|
7892
|
+
// src/ai/engines/template/agents.ts
|
|
7893
|
+
function joinNames(names) {
|
|
7894
|
+
if (names.length <= 1) return names.join("");
|
|
7895
|
+
return `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
7896
|
+
}
|
|
7897
|
+
function describeAgents(events) {
|
|
7898
|
+
const agentEvents = events.filter((event) => event.source !== void 0 && AGENT_NAMES.has(event.source));
|
|
7899
|
+
if (agentEvents.length === 0) return null;
|
|
7900
|
+
const names = [...new Set(agentEvents.map((event) => AGENT_NAMES.get(event.source)))];
|
|
7901
|
+
const sessionIds = new Set(agentEvents.flatMap((event) => event.agentSessionId ?? []));
|
|
7902
|
+
return sessionIds.size > 0 ? `${joinNames(names)} (${pluralize(sessionIds.size, "session")})` : joinNames(names);
|
|
7903
|
+
}
|
|
7904
|
+
var AGENT_NAMES;
|
|
7905
|
+
var init_agents2 = __esm({
|
|
7906
|
+
"src/ai/engines/template/agents.ts"() {
|
|
7907
|
+
"use strict";
|
|
7908
|
+
init_ai_tools();
|
|
7909
|
+
init_utils3();
|
|
7910
|
+
AGENT_NAMES = new Map(ai_tools_default.flatMap((tool) => tool.hook ? [[tool.hook, tool.displayName]] : []));
|
|
7911
|
+
}
|
|
7912
|
+
});
|
|
7913
|
+
|
|
7652
7914
|
// src/ai/engines/template/constants.ts
|
|
7653
|
-
var MAX_SESSION_BULLETS, MAX_COMMIT_BULLETS, MAX_DAY_BULLETS, MAX_LISTED_PACKAGES,
|
|
7915
|
+
var MAX_SESSION_BULLETS, MAX_COMMIT_BULLETS, MAX_DAY_BULLETS, MAX_LISTED_PACKAGES, TEST_WORDING, BUILD_WORDING, STORY_TEST_WORDING, STORY_BUILD_WORDING;
|
|
7654
7916
|
var init_constants2 = __esm({
|
|
7655
7917
|
"src/ai/engines/template/constants.ts"() {
|
|
7656
7918
|
"use strict";
|
|
@@ -7658,7 +7920,6 @@ var init_constants2 = __esm({
|
|
|
7658
7920
|
MAX_COMMIT_BULLETS = 3;
|
|
7659
7921
|
MAX_DAY_BULLETS = 5;
|
|
7660
7922
|
MAX_LISTED_PACKAGES = 6;
|
|
7661
|
-
AGENT_SOURCES = ["claude-code"];
|
|
7662
7923
|
TEST_WORDING = {
|
|
7663
7924
|
category: "testing",
|
|
7664
7925
|
resolved: "Resolved failing tests",
|
|
@@ -7687,28 +7948,6 @@ var init_constants2 = __esm({
|
|
|
7687
7948
|
}
|
|
7688
7949
|
});
|
|
7689
7950
|
|
|
7690
|
-
// src/ai/engines/template/utils.ts
|
|
7691
|
-
function pluralize(count, noun) {
|
|
7692
|
-
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
7693
|
-
}
|
|
7694
|
-
function stringsIn(value) {
|
|
7695
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
7696
|
-
}
|
|
7697
|
-
function unique(values) {
|
|
7698
|
-
return [...new Set(values)];
|
|
7699
|
-
}
|
|
7700
|
-
function eventsOf(events, category) {
|
|
7701
|
-
return events.filter((event) => event.category === category);
|
|
7702
|
-
}
|
|
7703
|
-
function toBulletList(bullets) {
|
|
7704
|
-
return bullets.map((bullet) => `- ${bullet}`).join("\n");
|
|
7705
|
-
}
|
|
7706
|
-
var init_utils3 = __esm({
|
|
7707
|
-
"src/ai/engines/template/utils.ts"() {
|
|
7708
|
-
"use strict";
|
|
7709
|
-
}
|
|
7710
|
-
});
|
|
7711
|
-
|
|
7712
7951
|
// src/ai/engines/template/describe.ts
|
|
7713
7952
|
function describeNotes(events) {
|
|
7714
7953
|
return eventsOf(events, "note").flatMap((event) => stringsIn([event.metadata.text]).map((text) => `Note: ${text}`));
|
|
@@ -7780,8 +8019,8 @@ function describePushes(events) {
|
|
|
7780
8019
|
return targets.length > 0 ? `Pushed to ${targets.join(", ")}` : null;
|
|
7781
8020
|
}
|
|
7782
8021
|
function describeAgentWork(events) {
|
|
7783
|
-
const
|
|
7784
|
-
return
|
|
8022
|
+
const agents = describeAgents(events);
|
|
8023
|
+
return agents === null ? null : `Worked with ${agents}`;
|
|
7785
8024
|
}
|
|
7786
8025
|
function describeEdits(events) {
|
|
7787
8026
|
const saves = eventsOf(events, "code_edit").length;
|
|
@@ -7813,6 +8052,7 @@ var init_describe = __esm({
|
|
|
7813
8052
|
"use strict";
|
|
7814
8053
|
init_commits();
|
|
7815
8054
|
init_exclusions();
|
|
8055
|
+
init_agents2();
|
|
7816
8056
|
init_constants2();
|
|
7817
8057
|
init_utils3();
|
|
7818
8058
|
}
|
|
@@ -7952,12 +8192,10 @@ function branchBullets(group) {
|
|
|
7952
8192
|
].filter((bullet) => bullet !== null);
|
|
7953
8193
|
}
|
|
7954
8194
|
function describeActivityLine(allEvents, events) {
|
|
7955
|
-
const
|
|
7956
|
-
(event) => event.source !== void 0 && AGENT_SOURCES.includes(event.source)
|
|
7957
|
-
).length;
|
|
8195
|
+
const agents = describeAgents(allEvents);
|
|
7958
8196
|
const saves = eventsOf(events, "code_edit").length;
|
|
7959
8197
|
const parts = [
|
|
7960
|
-
|
|
8198
|
+
agents === null ? null : `With ${agents}`,
|
|
7961
8199
|
saves > 0 ? `${pluralize(saves, "file save")} in the editor` : null
|
|
7962
8200
|
].filter((part) => part !== null);
|
|
7963
8201
|
return parts.length > 0 ? parts.join(" \xB7 ") : null;
|
|
@@ -8002,6 +8240,7 @@ var init_story = __esm({
|
|
|
8002
8240
|
"src/ai/engines/template/story.ts"() {
|
|
8003
8241
|
"use strict";
|
|
8004
8242
|
init_heading();
|
|
8243
|
+
init_agents2();
|
|
8005
8244
|
init_constants2();
|
|
8006
8245
|
init_describe();
|
|
8007
8246
|
init_branch_moves();
|
|
@@ -9349,7 +9588,7 @@ function registerBugReport(program) {
|
|
|
9349
9588
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9350
9589
|
const reportPath = import_path30.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
9351
9590
|
const systemInfo = [
|
|
9352
|
-
`Workscribe version : ${"3.
|
|
9591
|
+
`Workscribe version : ${"3.2.0"}`,
|
|
9353
9592
|
`Node.js version : ${process.version}`,
|
|
9354
9593
|
`Platform : ${process.platform}`,
|
|
9355
9594
|
`Architecture : ${process.arch}`,
|
|
@@ -9380,7 +9619,7 @@ function registerBugReport(program) {
|
|
|
9380
9619
|
process.exit(1);
|
|
9381
9620
|
}
|
|
9382
9621
|
const params = new URLSearchParams({
|
|
9383
|
-
title: `Bug report \u2014 workscribe ${"3.
|
|
9622
|
+
title: `Bug report \u2014 workscribe ${"3.2.0"}`,
|
|
9384
9623
|
body: `<!-- Attach ${import_path30.default.basename(reportPath)} or paste the relevant sections below -->
|
|
9385
9624
|
|
|
9386
9625
|
**Describe the issue:**
|
|
@@ -9471,7 +9710,7 @@ function reportRun(result) {
|
|
|
9471
9710
|
function registerUpgrade(program) {
|
|
9472
9711
|
program.command("upgrade").description("Bring your stored data up to date after updating Workscribe (backs it up first)").action(() => {
|
|
9473
9712
|
const announced = /* @__PURE__ */ new Set();
|
|
9474
|
-
const result = runDataJobs("3.
|
|
9713
|
+
const result = runDataJobs("3.2.0", {
|
|
9475
9714
|
onProgress: ({ job }) => {
|
|
9476
9715
|
if (announced.has(job.name)) return;
|
|
9477
9716
|
announced.add(job.name);
|
|
@@ -9961,7 +10200,7 @@ function isNewer(latest, current) {
|
|
|
9961
10200
|
return latestPatch > currentPatch;
|
|
9962
10201
|
}
|
|
9963
10202
|
function checkForUpdate() {
|
|
9964
|
-
const current = "3.
|
|
10203
|
+
const current = "3.2.0";
|
|
9965
10204
|
const cache = readCache();
|
|
9966
10205
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
9967
10206
|
process.stderr.write(
|
|
@@ -9984,7 +10223,7 @@ function checkForUpdate() {
|
|
|
9984
10223
|
function buildTelemetryPayload(config) {
|
|
9985
10224
|
return {
|
|
9986
10225
|
installId: config.telemetry.installId,
|
|
9987
|
-
cliVersion: "3.
|
|
10226
|
+
cliVersion: "3.2.0",
|
|
9988
10227
|
platform: import_os12.default.platform(),
|
|
9989
10228
|
nodeVersion: process.version
|
|
9990
10229
|
};
|
|
@@ -10062,10 +10301,10 @@ var require_main = __commonJS({
|
|
|
10062
10301
|
pingTelemetry();
|
|
10063
10302
|
}
|
|
10064
10303
|
if (shouldRunDataJobs(process.argv.slice(2))) {
|
|
10065
|
-
runDataJobsBeforeCommand(invokedCommand, "3.
|
|
10304
|
+
runDataJobsBeforeCommand(invokedCommand, "3.2.0");
|
|
10066
10305
|
}
|
|
10067
10306
|
var program = new import_commander.Command();
|
|
10068
|
-
program.name("workscribe").description("Automatic developer work journal").version("3.
|
|
10307
|
+
program.name("workscribe").description("Automatic developer work journal").version("3.2.0");
|
|
10069
10308
|
registerCapture(program);
|
|
10070
10309
|
registerHook(program);
|
|
10071
10310
|
registerInit(program);
|