@staff0rd/assist 0.189.3 → 0.191.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +294 -264
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.191.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -87,7 +87,7 @@ var package_default = {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
// src/commands/backlog/next.ts
|
|
90
|
-
import
|
|
90
|
+
import chalk9 from "chalk";
|
|
91
91
|
import enquirer2 from "enquirer";
|
|
92
92
|
|
|
93
93
|
// src/shared/exitOnCancel.ts
|
|
@@ -763,7 +763,7 @@ function findUnblockedTodos(items) {
|
|
|
763
763
|
}
|
|
764
764
|
|
|
765
765
|
// src/commands/backlog/run.ts
|
|
766
|
-
import
|
|
766
|
+
import chalk8 from "chalk";
|
|
767
767
|
|
|
768
768
|
// src/commands/backlog/buildCommentLines.ts
|
|
769
769
|
function buildCommentLines(comments2) {
|
|
@@ -879,7 +879,7 @@ function buildReviewPhase() {
|
|
|
879
879
|
}
|
|
880
880
|
|
|
881
881
|
// src/commands/backlog/executePhase.ts
|
|
882
|
-
import
|
|
882
|
+
import chalk6 from "chalk";
|
|
883
883
|
|
|
884
884
|
// src/commands/backlog/resolvePhaseResult.ts
|
|
885
885
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
@@ -965,8 +965,220 @@ Phase ${phaseNumber} completed.`));
|
|
|
965
965
|
|
|
966
966
|
// src/commands/backlog/spawnClaude.ts
|
|
967
967
|
import { spawn } from "child_process";
|
|
968
|
+
|
|
969
|
+
// src/shared/loadConfig.ts
|
|
970
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
971
|
+
import { homedir } from "os";
|
|
972
|
+
import { dirname, join as join7 } from "path";
|
|
973
|
+
import chalk5 from "chalk";
|
|
974
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
975
|
+
|
|
976
|
+
// src/shared/loadRawYaml.ts
|
|
977
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
978
|
+
import { parse as parseYaml2 } from "yaml";
|
|
979
|
+
function loadRawYaml(path50) {
|
|
980
|
+
if (!existsSync7(path50)) return {};
|
|
981
|
+
try {
|
|
982
|
+
const content = readFileSync6(path50, "utf-8");
|
|
983
|
+
return parseYaml2(content) || {};
|
|
984
|
+
} catch {
|
|
985
|
+
return {};
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/shared/types.ts
|
|
990
|
+
import { z as z2 } from "zod";
|
|
991
|
+
var runParamSchema = z2.strictObject({
|
|
992
|
+
name: z2.string(),
|
|
993
|
+
required: z2.boolean().optional(),
|
|
994
|
+
default: z2.string().optional(),
|
|
995
|
+
description: z2.string().optional()
|
|
996
|
+
});
|
|
997
|
+
var runConfigSchema = z2.strictObject({
|
|
998
|
+
name: z2.string(),
|
|
999
|
+
command: z2.string(),
|
|
1000
|
+
args: z2.array(z2.string()).optional(),
|
|
1001
|
+
params: z2.array(runParamSchema).optional(),
|
|
1002
|
+
env: z2.record(z2.string(), z2.string()).optional(),
|
|
1003
|
+
filter: z2.string().optional(),
|
|
1004
|
+
pre: z2.array(z2.string()).optional(),
|
|
1005
|
+
cwd: z2.string().optional()
|
|
1006
|
+
});
|
|
1007
|
+
var runLinkSchema = z2.strictObject({
|
|
1008
|
+
link: z2.string(),
|
|
1009
|
+
prefix: z2.string()
|
|
1010
|
+
});
|
|
1011
|
+
var transcriptConfigSchema = z2.strictObject({
|
|
1012
|
+
vttDir: z2.string(),
|
|
1013
|
+
transcriptsDir: z2.string(),
|
|
1014
|
+
summaryDir: z2.string()
|
|
1015
|
+
});
|
|
1016
|
+
var DEFAULT_WAKE_WORDS = ["computer"];
|
|
1017
|
+
var DEFAULT_MODELS_DIR = "~/.assist/voice/models";
|
|
1018
|
+
var assistConfigSchema = z2.strictObject({
|
|
1019
|
+
commit: z2.strictObject({
|
|
1020
|
+
conventional: z2.boolean().default(false),
|
|
1021
|
+
pull: z2.boolean().default(false),
|
|
1022
|
+
push: z2.boolean().default(false)
|
|
1023
|
+
}).default({ conventional: false, pull: false, push: false }),
|
|
1024
|
+
devlog: z2.strictObject({
|
|
1025
|
+
name: z2.string().optional(),
|
|
1026
|
+
ignore: z2.array(z2.string()).optional(),
|
|
1027
|
+
skip: z2.record(z2.string(), z2.array(z2.string())).optional()
|
|
1028
|
+
}).optional(),
|
|
1029
|
+
notify: z2.strictObject({
|
|
1030
|
+
enabled: z2.boolean().default(true)
|
|
1031
|
+
}).default({ enabled: true }),
|
|
1032
|
+
complexity: z2.strictObject({
|
|
1033
|
+
ignore: z2.array(z2.string()).default(["**/*test.ts*"])
|
|
1034
|
+
}).default({ ignore: ["**/*test.ts*"] }),
|
|
1035
|
+
hardcodedColors: z2.strictObject({
|
|
1036
|
+
ignore: z2.array(z2.string()).default([])
|
|
1037
|
+
}).optional(),
|
|
1038
|
+
restructure: z2.strictObject({
|
|
1039
|
+
ignore: z2.array(z2.string()).default([])
|
|
1040
|
+
}).optional(),
|
|
1041
|
+
jira: z2.strictObject({
|
|
1042
|
+
acField: z2.string().default("customfield_11937")
|
|
1043
|
+
}).optional(),
|
|
1044
|
+
roam: z2.strictObject({
|
|
1045
|
+
clientId: z2.string(),
|
|
1046
|
+
clientSecret: z2.string(),
|
|
1047
|
+
accessToken: z2.string().optional(),
|
|
1048
|
+
refreshToken: z2.string().optional(),
|
|
1049
|
+
tokenExpiresAt: z2.number().optional()
|
|
1050
|
+
}).optional(),
|
|
1051
|
+
run: z2.array(z2.union([runConfigSchema, runLinkSchema])).optional(),
|
|
1052
|
+
transcript: transcriptConfigSchema.optional(),
|
|
1053
|
+
cliReadVerbs: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
1054
|
+
news: z2.strictObject({
|
|
1055
|
+
feeds: z2.array(z2.string()).default([])
|
|
1056
|
+
}).default({ feeds: [] }),
|
|
1057
|
+
dotnet: z2.strictObject({
|
|
1058
|
+
inspect: z2.strictObject({
|
|
1059
|
+
suppress: z2.array(z2.string()).default([])
|
|
1060
|
+
}).default({ suppress: [] })
|
|
1061
|
+
}).optional(),
|
|
1062
|
+
ravendb: z2.strictObject({
|
|
1063
|
+
connections: z2.array(
|
|
1064
|
+
z2.strictObject({
|
|
1065
|
+
name: z2.string(),
|
|
1066
|
+
url: z2.string(),
|
|
1067
|
+
database: z2.string(),
|
|
1068
|
+
apiKeyRef: z2.string()
|
|
1069
|
+
})
|
|
1070
|
+
).default([]),
|
|
1071
|
+
defaultConnection: z2.string().optional()
|
|
1072
|
+
}).optional(),
|
|
1073
|
+
seq: z2.strictObject({
|
|
1074
|
+
connections: z2.array(
|
|
1075
|
+
z2.strictObject({
|
|
1076
|
+
name: z2.string(),
|
|
1077
|
+
url: z2.string(),
|
|
1078
|
+
apiToken: z2.string()
|
|
1079
|
+
})
|
|
1080
|
+
).default([]),
|
|
1081
|
+
defaultConnection: z2.string().optional()
|
|
1082
|
+
}).optional(),
|
|
1083
|
+
screenshot: z2.strictObject({
|
|
1084
|
+
outputDir: z2.string().default("./screenshots")
|
|
1085
|
+
}).default({ outputDir: "./screenshots" }),
|
|
1086
|
+
backlog: z2.strictObject({
|
|
1087
|
+
autoCommit: z2.boolean().default(false)
|
|
1088
|
+
}).default({ autoCommit: false }),
|
|
1089
|
+
deny: z2.array(
|
|
1090
|
+
z2.strictObject({
|
|
1091
|
+
pattern: z2.string(),
|
|
1092
|
+
message: z2.string()
|
|
1093
|
+
})
|
|
1094
|
+
).optional(),
|
|
1095
|
+
sync: z2.strictObject({
|
|
1096
|
+
autoConfirm: z2.boolean().default(false)
|
|
1097
|
+
}).default({ autoConfirm: false }),
|
|
1098
|
+
caveman: z2.boolean().default(true),
|
|
1099
|
+
voice: z2.strictObject({
|
|
1100
|
+
wakeWords: z2.array(z2.string()).default(DEFAULT_WAKE_WORDS),
|
|
1101
|
+
mic: z2.string().optional(),
|
|
1102
|
+
cwd: z2.string().optional(),
|
|
1103
|
+
modelsDir: z2.string().default(DEFAULT_MODELS_DIR),
|
|
1104
|
+
lockDir: z2.string().optional(),
|
|
1105
|
+
submitWindows: z2.array(z2.string()).optional(),
|
|
1106
|
+
models: z2.strictObject({
|
|
1107
|
+
vad: z2.string().optional(),
|
|
1108
|
+
smartTurn: z2.string().optional()
|
|
1109
|
+
}).default({})
|
|
1110
|
+
}).default({
|
|
1111
|
+
wakeWords: DEFAULT_WAKE_WORDS,
|
|
1112
|
+
modelsDir: DEFAULT_MODELS_DIR,
|
|
1113
|
+
models: {}
|
|
1114
|
+
})
|
|
1115
|
+
});
|
|
1116
|
+
function isRunLink(entry) {
|
|
1117
|
+
return "link" in entry;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// src/shared/loadConfig.ts
|
|
1121
|
+
function findConfigUp(startDir) {
|
|
1122
|
+
let current = startDir;
|
|
1123
|
+
while (current !== dirname(current)) {
|
|
1124
|
+
const claudePath = join7(current, ".claude", "assist.yml");
|
|
1125
|
+
if (existsSync8(claudePath)) return claudePath;
|
|
1126
|
+
const rootPath = join7(current, "assist.yml");
|
|
1127
|
+
if (existsSync8(rootPath)) return rootPath;
|
|
1128
|
+
current = dirname(current);
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
function getConfigPath() {
|
|
1133
|
+
const found = findConfigUp(process.cwd());
|
|
1134
|
+
if (found) return found;
|
|
1135
|
+
return join7(process.cwd(), "assist.yml");
|
|
1136
|
+
}
|
|
1137
|
+
function getGlobalConfigPath() {
|
|
1138
|
+
return join7(homedir(), ".assist.yml");
|
|
1139
|
+
}
|
|
1140
|
+
function getConfigDir() {
|
|
1141
|
+
return dirname(getConfigPath());
|
|
1142
|
+
}
|
|
1143
|
+
function loadConfig() {
|
|
1144
|
+
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
1145
|
+
const projectRaw = loadRawYaml(getConfigPath());
|
|
1146
|
+
const merged = { ...globalRaw, ...projectRaw };
|
|
1147
|
+
return assistConfigSchema.parse(merged);
|
|
1148
|
+
}
|
|
1149
|
+
function loadProjectConfig() {
|
|
1150
|
+
return loadRawYaml(getConfigPath());
|
|
1151
|
+
}
|
|
1152
|
+
function loadGlobalConfigRaw() {
|
|
1153
|
+
return loadRawYaml(getGlobalConfigPath());
|
|
1154
|
+
}
|
|
1155
|
+
function saveGlobalConfig(config) {
|
|
1156
|
+
writeFileSync5(getGlobalConfigPath(), stringifyYaml(config, { lineWidth: 0 }));
|
|
1157
|
+
}
|
|
1158
|
+
function saveConfig(config) {
|
|
1159
|
+
const configPath = getConfigPath();
|
|
1160
|
+
writeFileSync5(configPath, stringifyYaml(config, { lineWidth: 0 }));
|
|
1161
|
+
}
|
|
1162
|
+
function getTranscriptConfig() {
|
|
1163
|
+
const config = loadConfig();
|
|
1164
|
+
if (!config.transcript) {
|
|
1165
|
+
console.error(
|
|
1166
|
+
chalk5.red(
|
|
1167
|
+
"Transcript directories not configured. Run 'assist transcript configure' first."
|
|
1168
|
+
)
|
|
1169
|
+
);
|
|
1170
|
+
process.exit(1);
|
|
1171
|
+
}
|
|
1172
|
+
return config.transcript;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/commands/backlog/spawnClaude.ts
|
|
968
1176
|
function spawnClaude(prompt, options2 = {}) {
|
|
969
|
-
const
|
|
1177
|
+
const config = loadConfig();
|
|
1178
|
+
const finalPrompt = config.caveman ? `/caveman
|
|
1179
|
+
|
|
1180
|
+
${prompt}` : prompt;
|
|
1181
|
+
const args = [finalPrompt];
|
|
970
1182
|
if (options2.allowEdits) {
|
|
971
1183
|
args.push("--permission-mode", "acceptEdits");
|
|
972
1184
|
}
|
|
@@ -981,11 +1193,11 @@ function spawnClaude(prompt, options2 = {}) {
|
|
|
981
1193
|
}
|
|
982
1194
|
|
|
983
1195
|
// src/commands/backlog/watchForMarker.ts
|
|
984
|
-
import { existsSync as
|
|
1196
|
+
import { existsSync as existsSync9, unwatchFile, watchFile } from "fs";
|
|
985
1197
|
function watchForMarker(child) {
|
|
986
1198
|
const statusPath = getSignalPath();
|
|
987
1199
|
watchFile(statusPath, { interval: 1e3 }, () => {
|
|
988
|
-
if (!
|
|
1200
|
+
if (!existsSync9(statusPath)) return;
|
|
989
1201
|
const signal = readSignal();
|
|
990
1202
|
if (signal) {
|
|
991
1203
|
unwatchFile(statusPath);
|
|
@@ -1002,7 +1214,7 @@ async function executePhase(item, phaseIndex, phases, spawnOptions) {
|
|
|
1002
1214
|
const phase = phases[phaseIndex];
|
|
1003
1215
|
const phaseNumber = phaseIndex + 1;
|
|
1004
1216
|
console.log(
|
|
1005
|
-
|
|
1217
|
+
chalk6.bold(
|
|
1006
1218
|
`
|
|
1007
1219
|
--- Phase ${phaseNumber}/${phases.length}: ${phase.name} ---
|
|
1008
1220
|
`
|
|
@@ -1020,7 +1232,7 @@ async function executePhase(item, phaseIndex, phases, spawnOptions) {
|
|
|
1020
1232
|
}
|
|
1021
1233
|
|
|
1022
1234
|
// src/commands/backlog/prepareRun.ts
|
|
1023
|
-
import
|
|
1235
|
+
import chalk7 from "chalk";
|
|
1024
1236
|
|
|
1025
1237
|
// src/commands/backlog/resolvePlan.ts
|
|
1026
1238
|
function resolvePlan(item) {
|
|
@@ -1043,13 +1255,13 @@ function prepareRun(id) {
|
|
|
1043
1255
|
const plan2 = resolvePlan(item);
|
|
1044
1256
|
const startPhase = (item.currentPhase ?? 1) - 1;
|
|
1045
1257
|
if (item.status === "done") {
|
|
1046
|
-
console.log(
|
|
1258
|
+
console.log(chalk7.green(`Already done: #${id}: ${item.name}`));
|
|
1047
1259
|
return void 0;
|
|
1048
1260
|
}
|
|
1049
1261
|
if (startPhase > plan2.length) {
|
|
1050
1262
|
setStatus(id, "done");
|
|
1051
1263
|
console.log(
|
|
1052
|
-
|
|
1264
|
+
chalk7.green(`All phases already complete for #${id}: ${item.name}`)
|
|
1053
1265
|
);
|
|
1054
1266
|
return void 0;
|
|
1055
1267
|
}
|
|
@@ -1074,13 +1286,13 @@ async function run(id, spawnOptions) {
|
|
|
1074
1286
|
}
|
|
1075
1287
|
}
|
|
1076
1288
|
function logProgress(id, name, startPhase, total) {
|
|
1077
|
-
console.log(
|
|
1289
|
+
console.log(chalk8.bold(`Running plan for #${id}: ${name}`));
|
|
1078
1290
|
if (startPhase > 0) {
|
|
1079
1291
|
const phaseNumber = startPhase + 1;
|
|
1080
|
-
console.log(
|
|
1292
|
+
console.log(chalk8.dim(`Resuming from phase ${phaseNumber}/${total}
|
|
1081
1293
|
`));
|
|
1082
1294
|
} else {
|
|
1083
|
-
console.log(
|
|
1295
|
+
console.log(chalk8.dim(`${total} phase(s)
|
|
1084
1296
|
`));
|
|
1085
1297
|
}
|
|
1086
1298
|
}
|
|
@@ -1113,7 +1325,7 @@ async function runReview(item, plan2, spawnOptions) {
|
|
|
1113
1325
|
// src/commands/backlog/next.ts
|
|
1114
1326
|
function toChoice(item, items) {
|
|
1115
1327
|
const name = `${typeLabel(item.type)} #${item.id}: ${item.name}`;
|
|
1116
|
-
return isBlocked(item, items) ? { name, disabled:
|
|
1328
|
+
return isBlocked(item, items) ? { name, disabled: chalk9.red("[blocked]") } : { name };
|
|
1117
1329
|
}
|
|
1118
1330
|
async function selectItem(todo, items) {
|
|
1119
1331
|
const { selected } = await exitOnCancel(
|
|
@@ -1130,7 +1342,7 @@ async function pickItem(items, firstPick = false) {
|
|
|
1130
1342
|
const resumable = findResumable(items);
|
|
1131
1343
|
if (resumable) {
|
|
1132
1344
|
console.log(
|
|
1133
|
-
|
|
1345
|
+
chalk9.bold(
|
|
1134
1346
|
`Resuming in-progress item #${resumable.id}: ${resumable.name}`
|
|
1135
1347
|
)
|
|
1136
1348
|
);
|
|
@@ -1140,7 +1352,7 @@ async function pickItem(items, firstPick = false) {
|
|
|
1140
1352
|
if (!unblocked) return void 0;
|
|
1141
1353
|
if (firstPick && unblocked.length === 1) {
|
|
1142
1354
|
const item = unblocked[0];
|
|
1143
|
-
console.log(
|
|
1355
|
+
console.log(chalk9.bold(`Auto-selecting item #${item.id}: ${item.name}`));
|
|
1144
1356
|
return String(item.id);
|
|
1145
1357
|
}
|
|
1146
1358
|
const todo = items.filter((i) => i.status === "todo");
|
|
@@ -1158,7 +1370,7 @@ async function next(options2) {
|
|
|
1158
1370
|
}
|
|
1159
1371
|
|
|
1160
1372
|
// src/commands/backlog/phaseDone.ts
|
|
1161
|
-
import
|
|
1373
|
+
import chalk10 from "chalk";
|
|
1162
1374
|
|
|
1163
1375
|
// src/commands/backlog/addComment.ts
|
|
1164
1376
|
function addComment(item, text, phase) {
|
|
@@ -1193,7 +1405,7 @@ function phaseDone(id, phase, summary) {
|
|
|
1193
1405
|
});
|
|
1194
1406
|
const result = loadAndFindItem(id);
|
|
1195
1407
|
if (result?.item.status === "done") {
|
|
1196
|
-
console.log(
|
|
1408
|
+
console.log(chalk10.dim(`Item #${id} already done, skipping phase advance.`));
|
|
1197
1409
|
return;
|
|
1198
1410
|
}
|
|
1199
1411
|
if (result) {
|
|
@@ -1202,24 +1414,24 @@ function phaseDone(id, phase, summary) {
|
|
|
1202
1414
|
}
|
|
1203
1415
|
setCurrentPhase(id, phaseNumber + 1);
|
|
1204
1416
|
console.log(
|
|
1205
|
-
|
|
1417
|
+
chalk10.green(`Phase ${phaseNumber} of item #${id} marked as complete.`)
|
|
1206
1418
|
);
|
|
1207
1419
|
}
|
|
1208
1420
|
|
|
1209
1421
|
// src/commands/backlog/plan.ts
|
|
1210
|
-
import
|
|
1422
|
+
import chalk11 from "chalk";
|
|
1211
1423
|
function plan(id) {
|
|
1212
1424
|
const result = loadAndFindItem(id);
|
|
1213
1425
|
if (!result) return;
|
|
1214
1426
|
const { item } = result;
|
|
1215
1427
|
if (!item.plan || item.plan.length === 0) {
|
|
1216
|
-
console.log(
|
|
1428
|
+
console.log(chalk11.dim("No plan defined for this item."));
|
|
1217
1429
|
return;
|
|
1218
1430
|
}
|
|
1219
|
-
console.log(
|
|
1431
|
+
console.log(chalk11.bold(item.name));
|
|
1220
1432
|
console.log();
|
|
1221
1433
|
for (const [i, phase] of item.plan.entries()) {
|
|
1222
|
-
console.log(`${
|
|
1434
|
+
console.log(`${chalk11.bold(`Phase ${i + 1}:`)} ${phase.name}`);
|
|
1223
1435
|
for (const task of phase.tasks) {
|
|
1224
1436
|
console.log(` - ${task.task}`);
|
|
1225
1437
|
}
|
|
@@ -1228,35 +1440,35 @@ function plan(id) {
|
|
|
1228
1440
|
}
|
|
1229
1441
|
|
|
1230
1442
|
// src/commands/backlog/show/index.ts
|
|
1231
|
-
import
|
|
1443
|
+
import chalk15 from "chalk";
|
|
1232
1444
|
|
|
1233
1445
|
// src/commands/backlog/formatComment.ts
|
|
1234
|
-
import
|
|
1446
|
+
import chalk12 from "chalk";
|
|
1235
1447
|
function formatComment(entry) {
|
|
1236
|
-
const id = entry.id !== void 0 ?
|
|
1237
|
-
const tag = entry.type === "summary" ?
|
|
1238
|
-
const phase = entry.phase !== void 0 ?
|
|
1239
|
-
const time =
|
|
1448
|
+
const id = entry.id !== void 0 ? chalk12.dim(`#${entry.id} `) : "";
|
|
1449
|
+
const tag = entry.type === "summary" ? chalk12.magenta("[summary]") : chalk12.cyan("[comment]");
|
|
1450
|
+
const phase = entry.phase !== void 0 ? chalk12.dim(` (phase ${entry.phase})`) : "";
|
|
1451
|
+
const time = chalk12.dim(entry.timestamp);
|
|
1240
1452
|
return `${id}${tag}${phase} ${time}
|
|
1241
1453
|
${entry.text}`;
|
|
1242
1454
|
}
|
|
1243
1455
|
|
|
1244
1456
|
// src/commands/backlog/show/printLinks.ts
|
|
1245
|
-
import
|
|
1457
|
+
import chalk13 from "chalk";
|
|
1246
1458
|
function printLinks(item, items) {
|
|
1247
1459
|
const links = item.links ?? [];
|
|
1248
1460
|
if (links.length === 0) return;
|
|
1249
|
-
console.log(
|
|
1461
|
+
console.log(chalk13.bold("Links"));
|
|
1250
1462
|
for (const link3 of links) {
|
|
1251
1463
|
const target = items.find((i) => i.id === link3.targetId);
|
|
1252
|
-
const typeLabel2 = link3.type === "depends-on" ?
|
|
1464
|
+
const typeLabel2 = link3.type === "depends-on" ? chalk13.red("depends-on") : chalk13.blue("relates-to");
|
|
1253
1465
|
if (target) {
|
|
1254
1466
|
console.log(
|
|
1255
|
-
` ${typeLabel2} #${target.id} ${target.name} ${
|
|
1467
|
+
` ${typeLabel2} #${target.id} ${target.name} ${chalk13.dim(`(${target.status})`)}`
|
|
1256
1468
|
);
|
|
1257
1469
|
} else {
|
|
1258
1470
|
console.log(
|
|
1259
|
-
` ${typeLabel2} #${link3.targetId} ${
|
|
1471
|
+
` ${typeLabel2} #${link3.targetId} ${chalk13.dim("(not found)")}`
|
|
1260
1472
|
);
|
|
1261
1473
|
}
|
|
1262
1474
|
}
|
|
@@ -1264,15 +1476,15 @@ function printLinks(item, items) {
|
|
|
1264
1476
|
}
|
|
1265
1477
|
|
|
1266
1478
|
// src/commands/backlog/show/printPhaseTasks.ts
|
|
1267
|
-
import
|
|
1479
|
+
import chalk14 from "chalk";
|
|
1268
1480
|
function printPhaseTasks(phase) {
|
|
1269
1481
|
for (const task of phase.tasks) {
|
|
1270
1482
|
console.log(` - ${task.task}`);
|
|
1271
1483
|
}
|
|
1272
1484
|
if (phase.manualChecks && phase.manualChecks.length > 0) {
|
|
1273
|
-
console.log(` ${
|
|
1485
|
+
console.log(` ${chalk14.dim("Manual checks:")}`);
|
|
1274
1486
|
for (const check2 of phase.manualChecks) {
|
|
1275
|
-
console.log(` ${
|
|
1487
|
+
console.log(` ${chalk14.dim(`- ${check2}`)}`);
|
|
1276
1488
|
}
|
|
1277
1489
|
}
|
|
1278
1490
|
}
|
|
@@ -1280,7 +1492,7 @@ function printPhaseTasks(phase) {
|
|
|
1280
1492
|
// src/commands/backlog/show/index.ts
|
|
1281
1493
|
function printPlan(item) {
|
|
1282
1494
|
if (!item.plan || item.plan.length === 0) return;
|
|
1283
|
-
console.log(
|
|
1495
|
+
console.log(chalk15.bold("Plan"));
|
|
1284
1496
|
for (const [i, phase] of item.plan.entries()) {
|
|
1285
1497
|
const isCurrent = item.currentPhase === i + 1;
|
|
1286
1498
|
printPhase(phase, i, isCurrent);
|
|
@@ -1289,8 +1501,8 @@ function printPlan(item) {
|
|
|
1289
1501
|
}
|
|
1290
1502
|
function phaseHeader(index, name, isCurrent) {
|
|
1291
1503
|
const phaseNumber = index + 1;
|
|
1292
|
-
const marker = isCurrent ?
|
|
1293
|
-
const label2 = isCurrent ?
|
|
1504
|
+
const marker = isCurrent ? chalk15.green("\u25B6 ") : " ";
|
|
1505
|
+
const label2 = isCurrent ? chalk15.green.bold(`Phase ${phaseNumber}: ${name}`) : `${chalk15.bold(`Phase ${phaseNumber}:`)} ${name}`;
|
|
1294
1506
|
return `${marker}${label2}`;
|
|
1295
1507
|
}
|
|
1296
1508
|
function printPhase(phase, index, isCurrent) {
|
|
@@ -1298,15 +1510,15 @@ function printPhase(phase, index, isCurrent) {
|
|
|
1298
1510
|
printPhaseTasks(phase);
|
|
1299
1511
|
}
|
|
1300
1512
|
function printHeader(item) {
|
|
1301
|
-
console.log(
|
|
1513
|
+
console.log(chalk15.bold(`#${item.id} ${item.name}`));
|
|
1302
1514
|
console.log(
|
|
1303
|
-
`${
|
|
1515
|
+
`${chalk15.dim("Type:")} ${item.type} ${chalk15.dim("Status:")} ${item.status}`
|
|
1304
1516
|
);
|
|
1305
1517
|
console.log();
|
|
1306
1518
|
}
|
|
1307
1519
|
function printAcceptanceCriteria(criteria) {
|
|
1308
1520
|
if (criteria.length === 0) return;
|
|
1309
|
-
console.log(
|
|
1521
|
+
console.log(chalk15.bold("Acceptance Criteria"));
|
|
1310
1522
|
for (const [i, ac] of criteria.entries()) {
|
|
1311
1523
|
console.log(` ${i + 1}. ${ac}`);
|
|
1312
1524
|
}
|
|
@@ -1318,7 +1530,7 @@ function show(id) {
|
|
|
1318
1530
|
const { item, items } = result;
|
|
1319
1531
|
printHeader(item);
|
|
1320
1532
|
if (item.description) {
|
|
1321
|
-
console.log(
|
|
1533
|
+
console.log(chalk15.bold("Description"));
|
|
1322
1534
|
console.log(item.description);
|
|
1323
1535
|
console.log();
|
|
1324
1536
|
}
|
|
@@ -1330,7 +1542,7 @@ function show(id) {
|
|
|
1330
1542
|
function printComments(item) {
|
|
1331
1543
|
const entries = item.comments ?? [];
|
|
1332
1544
|
if (entries.length === 0) return;
|
|
1333
|
-
console.log(
|
|
1545
|
+
console.log(chalk15.bold("Comments"));
|
|
1334
1546
|
for (const entry of entries) {
|
|
1335
1547
|
console.log(` ${formatComment(entry)}`);
|
|
1336
1548
|
}
|
|
@@ -1339,23 +1551,23 @@ function printComments(item) {
|
|
|
1339
1551
|
|
|
1340
1552
|
// src/shared/web.ts
|
|
1341
1553
|
import { exec } from "child_process";
|
|
1342
|
-
import { readFileSync as
|
|
1554
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
1343
1555
|
import {
|
|
1344
1556
|
createServer
|
|
1345
1557
|
} from "http";
|
|
1346
|
-
import { dirname, join as
|
|
1558
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
1347
1559
|
import { fileURLToPath } from "url";
|
|
1348
|
-
import
|
|
1560
|
+
import chalk16 from "chalk";
|
|
1349
1561
|
function respondJson(res, status2, data) {
|
|
1350
1562
|
res.writeHead(status2, { "Content-Type": "application/json" });
|
|
1351
1563
|
res.end(JSON.stringify(data));
|
|
1352
1564
|
}
|
|
1353
1565
|
function createBundleHandler(importMetaUrl, bundlePath) {
|
|
1354
|
-
const dir =
|
|
1566
|
+
const dir = dirname2(fileURLToPath(importMetaUrl));
|
|
1355
1567
|
let cache;
|
|
1356
1568
|
return (_req, res) => {
|
|
1357
1569
|
if (!cache) {
|
|
1358
|
-
cache =
|
|
1570
|
+
cache = readFileSync7(join8(dir, bundlePath), "utf-8");
|
|
1359
1571
|
}
|
|
1360
1572
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
1361
1573
|
res.end(cache);
|
|
@@ -1389,8 +1601,8 @@ function startWebServer(label2, port, handler) {
|
|
|
1389
1601
|
handler(req, res, port);
|
|
1390
1602
|
});
|
|
1391
1603
|
server.listen(port, () => {
|
|
1392
|
-
console.log(
|
|
1393
|
-
console.log(
|
|
1604
|
+
console.log(chalk16.green(`${label2}: ${url}`));
|
|
1605
|
+
console.log(chalk16.dim("Press Ctrl+C to stop"));
|
|
1394
1606
|
exec(`open ${url}`);
|
|
1395
1607
|
});
|
|
1396
1608
|
}
|
|
@@ -1550,7 +1762,7 @@ async function web(options2) {
|
|
|
1550
1762
|
}
|
|
1551
1763
|
|
|
1552
1764
|
// src/commands/backlog/launchMode.ts
|
|
1553
|
-
import
|
|
1765
|
+
import chalk17 from "chalk";
|
|
1554
1766
|
async function launchMode(slashCommand) {
|
|
1555
1767
|
process.env.ASSIST_SESSION_ID = String(process.pid);
|
|
1556
1768
|
const { child, done: done2 } = spawnClaude(`/${slashCommand}`, { allowEdits: true });
|
|
@@ -1560,13 +1772,13 @@ async function launchMode(slashCommand) {
|
|
|
1560
1772
|
const signal = readSignal();
|
|
1561
1773
|
cleanupSignal();
|
|
1562
1774
|
if (signal?.event === "next") {
|
|
1563
|
-
console.log(
|
|
1775
|
+
console.log(chalk17.bold("\nChaining into assist next...\n"));
|
|
1564
1776
|
await next({ allowEdits: true });
|
|
1565
1777
|
}
|
|
1566
1778
|
}
|
|
1567
1779
|
|
|
1568
1780
|
// src/commands/backlog/refine.ts
|
|
1569
|
-
import
|
|
1781
|
+
import chalk18 from "chalk";
|
|
1570
1782
|
import enquirer3 from "enquirer";
|
|
1571
1783
|
async function pickItemForRefine() {
|
|
1572
1784
|
const items = loadBacklog();
|
|
@@ -1574,12 +1786,12 @@ async function pickItemForRefine() {
|
|
|
1574
1786
|
(i) => i.status === "todo" || i.status === "in-progress"
|
|
1575
1787
|
);
|
|
1576
1788
|
if (active.length === 0) {
|
|
1577
|
-
console.log(
|
|
1789
|
+
console.log(chalk18.yellow("No active backlog items to refine."));
|
|
1578
1790
|
return void 0;
|
|
1579
1791
|
}
|
|
1580
1792
|
if (active.length === 1) {
|
|
1581
1793
|
const item = active[0];
|
|
1582
|
-
console.log(
|
|
1794
|
+
console.log(chalk18.bold(`Auto-selecting item #${item.id}: ${item.name}`));
|
|
1583
1795
|
return String(item.id);
|
|
1584
1796
|
}
|
|
1585
1797
|
const { selected } = await exitOnCancel(
|
|
@@ -1603,211 +1815,6 @@ async function refine(id) {
|
|
|
1603
1815
|
// src/commands/commit.ts
|
|
1604
1816
|
import { execSync } from "child_process";
|
|
1605
1817
|
|
|
1606
|
-
// src/shared/loadConfig.ts
|
|
1607
|
-
import { existsSync as existsSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
1608
|
-
import { homedir } from "os";
|
|
1609
|
-
import { dirname as dirname2, join as join8 } from "path";
|
|
1610
|
-
import chalk18 from "chalk";
|
|
1611
|
-
import { stringify as stringifyYaml } from "yaml";
|
|
1612
|
-
|
|
1613
|
-
// src/shared/loadRawYaml.ts
|
|
1614
|
-
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
1615
|
-
import { parse as parseYaml2 } from "yaml";
|
|
1616
|
-
function loadRawYaml(path50) {
|
|
1617
|
-
if (!existsSync8(path50)) return {};
|
|
1618
|
-
try {
|
|
1619
|
-
const content = readFileSync7(path50, "utf-8");
|
|
1620
|
-
return parseYaml2(content) || {};
|
|
1621
|
-
} catch {
|
|
1622
|
-
return {};
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
// src/shared/types.ts
|
|
1627
|
-
import { z as z2 } from "zod";
|
|
1628
|
-
var runParamSchema = z2.strictObject({
|
|
1629
|
-
name: z2.string(),
|
|
1630
|
-
required: z2.boolean().optional(),
|
|
1631
|
-
default: z2.string().optional(),
|
|
1632
|
-
description: z2.string().optional()
|
|
1633
|
-
});
|
|
1634
|
-
var runConfigSchema = z2.strictObject({
|
|
1635
|
-
name: z2.string(),
|
|
1636
|
-
command: z2.string(),
|
|
1637
|
-
args: z2.array(z2.string()).optional(),
|
|
1638
|
-
params: z2.array(runParamSchema).optional(),
|
|
1639
|
-
env: z2.record(z2.string(), z2.string()).optional(),
|
|
1640
|
-
filter: z2.string().optional(),
|
|
1641
|
-
pre: z2.array(z2.string()).optional(),
|
|
1642
|
-
cwd: z2.string().optional()
|
|
1643
|
-
});
|
|
1644
|
-
var runLinkSchema = z2.strictObject({
|
|
1645
|
-
link: z2.string(),
|
|
1646
|
-
prefix: z2.string()
|
|
1647
|
-
});
|
|
1648
|
-
var transcriptConfigSchema = z2.strictObject({
|
|
1649
|
-
vttDir: z2.string(),
|
|
1650
|
-
transcriptsDir: z2.string(),
|
|
1651
|
-
summaryDir: z2.string()
|
|
1652
|
-
});
|
|
1653
|
-
var DEFAULT_WAKE_WORDS = ["computer"];
|
|
1654
|
-
var DEFAULT_MODELS_DIR = "~/.assist/voice/models";
|
|
1655
|
-
var assistConfigSchema = z2.strictObject({
|
|
1656
|
-
commit: z2.strictObject({
|
|
1657
|
-
conventional: z2.boolean().default(false),
|
|
1658
|
-
pull: z2.boolean().default(false),
|
|
1659
|
-
push: z2.boolean().default(false)
|
|
1660
|
-
}).default({ conventional: false, pull: false, push: false }),
|
|
1661
|
-
devlog: z2.strictObject({
|
|
1662
|
-
name: z2.string().optional(),
|
|
1663
|
-
ignore: z2.array(z2.string()).optional(),
|
|
1664
|
-
skip: z2.record(z2.string(), z2.array(z2.string())).optional()
|
|
1665
|
-
}).optional(),
|
|
1666
|
-
notify: z2.strictObject({
|
|
1667
|
-
enabled: z2.boolean().default(true)
|
|
1668
|
-
}).default({ enabled: true }),
|
|
1669
|
-
complexity: z2.strictObject({
|
|
1670
|
-
ignore: z2.array(z2.string()).default(["**/*test.ts*"])
|
|
1671
|
-
}).default({ ignore: ["**/*test.ts*"] }),
|
|
1672
|
-
hardcodedColors: z2.strictObject({
|
|
1673
|
-
ignore: z2.array(z2.string()).default([])
|
|
1674
|
-
}).optional(),
|
|
1675
|
-
restructure: z2.strictObject({
|
|
1676
|
-
ignore: z2.array(z2.string()).default([])
|
|
1677
|
-
}).optional(),
|
|
1678
|
-
jira: z2.strictObject({
|
|
1679
|
-
acField: z2.string().default("customfield_11937")
|
|
1680
|
-
}).optional(),
|
|
1681
|
-
roam: z2.strictObject({
|
|
1682
|
-
clientId: z2.string(),
|
|
1683
|
-
clientSecret: z2.string(),
|
|
1684
|
-
accessToken: z2.string().optional(),
|
|
1685
|
-
refreshToken: z2.string().optional(),
|
|
1686
|
-
tokenExpiresAt: z2.number().optional()
|
|
1687
|
-
}).optional(),
|
|
1688
|
-
run: z2.array(z2.union([runConfigSchema, runLinkSchema])).optional(),
|
|
1689
|
-
transcript: transcriptConfigSchema.optional(),
|
|
1690
|
-
cliReadVerbs: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
1691
|
-
news: z2.strictObject({
|
|
1692
|
-
feeds: z2.array(z2.string()).default([])
|
|
1693
|
-
}).default({ feeds: [] }),
|
|
1694
|
-
dotnet: z2.strictObject({
|
|
1695
|
-
inspect: z2.strictObject({
|
|
1696
|
-
suppress: z2.array(z2.string()).default([])
|
|
1697
|
-
}).default({ suppress: [] })
|
|
1698
|
-
}).optional(),
|
|
1699
|
-
ravendb: z2.strictObject({
|
|
1700
|
-
connections: z2.array(
|
|
1701
|
-
z2.strictObject({
|
|
1702
|
-
name: z2.string(),
|
|
1703
|
-
url: z2.string(),
|
|
1704
|
-
database: z2.string(),
|
|
1705
|
-
apiKeyRef: z2.string()
|
|
1706
|
-
})
|
|
1707
|
-
).default([]),
|
|
1708
|
-
defaultConnection: z2.string().optional()
|
|
1709
|
-
}).optional(),
|
|
1710
|
-
seq: z2.strictObject({
|
|
1711
|
-
connections: z2.array(
|
|
1712
|
-
z2.strictObject({
|
|
1713
|
-
name: z2.string(),
|
|
1714
|
-
url: z2.string(),
|
|
1715
|
-
apiToken: z2.string()
|
|
1716
|
-
})
|
|
1717
|
-
).default([]),
|
|
1718
|
-
defaultConnection: z2.string().optional()
|
|
1719
|
-
}).optional(),
|
|
1720
|
-
screenshot: z2.strictObject({
|
|
1721
|
-
outputDir: z2.string().default("./screenshots")
|
|
1722
|
-
}).default({ outputDir: "./screenshots" }),
|
|
1723
|
-
backlog: z2.strictObject({
|
|
1724
|
-
autoCommit: z2.boolean().default(false)
|
|
1725
|
-
}).default({ autoCommit: false }),
|
|
1726
|
-
deny: z2.array(
|
|
1727
|
-
z2.strictObject({
|
|
1728
|
-
pattern: z2.string(),
|
|
1729
|
-
message: z2.string()
|
|
1730
|
-
})
|
|
1731
|
-
).optional(),
|
|
1732
|
-
sync: z2.strictObject({
|
|
1733
|
-
autoConfirm: z2.boolean().default(false)
|
|
1734
|
-
}).default({ autoConfirm: false }),
|
|
1735
|
-
voice: z2.strictObject({
|
|
1736
|
-
wakeWords: z2.array(z2.string()).default(DEFAULT_WAKE_WORDS),
|
|
1737
|
-
mic: z2.string().optional(),
|
|
1738
|
-
cwd: z2.string().optional(),
|
|
1739
|
-
modelsDir: z2.string().default(DEFAULT_MODELS_DIR),
|
|
1740
|
-
lockDir: z2.string().optional(),
|
|
1741
|
-
submitWindows: z2.array(z2.string()).optional(),
|
|
1742
|
-
models: z2.strictObject({
|
|
1743
|
-
vad: z2.string().optional(),
|
|
1744
|
-
smartTurn: z2.string().optional()
|
|
1745
|
-
}).default({})
|
|
1746
|
-
}).default({
|
|
1747
|
-
wakeWords: DEFAULT_WAKE_WORDS,
|
|
1748
|
-
modelsDir: DEFAULT_MODELS_DIR,
|
|
1749
|
-
models: {}
|
|
1750
|
-
})
|
|
1751
|
-
});
|
|
1752
|
-
function isRunLink(entry) {
|
|
1753
|
-
return "link" in entry;
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
// src/shared/loadConfig.ts
|
|
1757
|
-
function findConfigUp(startDir) {
|
|
1758
|
-
let current = startDir;
|
|
1759
|
-
while (current !== dirname2(current)) {
|
|
1760
|
-
const claudePath = join8(current, ".claude", "assist.yml");
|
|
1761
|
-
if (existsSync9(claudePath)) return claudePath;
|
|
1762
|
-
const rootPath = join8(current, "assist.yml");
|
|
1763
|
-
if (existsSync9(rootPath)) return rootPath;
|
|
1764
|
-
current = dirname2(current);
|
|
1765
|
-
}
|
|
1766
|
-
return null;
|
|
1767
|
-
}
|
|
1768
|
-
function getConfigPath() {
|
|
1769
|
-
const found = findConfigUp(process.cwd());
|
|
1770
|
-
if (found) return found;
|
|
1771
|
-
return join8(process.cwd(), "assist.yml");
|
|
1772
|
-
}
|
|
1773
|
-
function getGlobalConfigPath() {
|
|
1774
|
-
return join8(homedir(), ".assist.yml");
|
|
1775
|
-
}
|
|
1776
|
-
function getConfigDir() {
|
|
1777
|
-
return dirname2(getConfigPath());
|
|
1778
|
-
}
|
|
1779
|
-
function loadConfig() {
|
|
1780
|
-
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
1781
|
-
const projectRaw = loadRawYaml(getConfigPath());
|
|
1782
|
-
const merged = { ...globalRaw, ...projectRaw };
|
|
1783
|
-
return assistConfigSchema.parse(merged);
|
|
1784
|
-
}
|
|
1785
|
-
function loadProjectConfig() {
|
|
1786
|
-
return loadRawYaml(getConfigPath());
|
|
1787
|
-
}
|
|
1788
|
-
function loadGlobalConfigRaw() {
|
|
1789
|
-
return loadRawYaml(getGlobalConfigPath());
|
|
1790
|
-
}
|
|
1791
|
-
function saveGlobalConfig(config) {
|
|
1792
|
-
writeFileSync5(getGlobalConfigPath(), stringifyYaml(config, { lineWidth: 0 }));
|
|
1793
|
-
}
|
|
1794
|
-
function saveConfig(config) {
|
|
1795
|
-
const configPath = getConfigPath();
|
|
1796
|
-
writeFileSync5(configPath, stringifyYaml(config, { lineWidth: 0 }));
|
|
1797
|
-
}
|
|
1798
|
-
function getTranscriptConfig() {
|
|
1799
|
-
const config = loadConfig();
|
|
1800
|
-
if (!config.transcript) {
|
|
1801
|
-
console.error(
|
|
1802
|
-
chalk18.red(
|
|
1803
|
-
"Transcript directories not configured. Run 'assist transcript configure' first."
|
|
1804
|
-
)
|
|
1805
|
-
);
|
|
1806
|
-
process.exit(1);
|
|
1807
|
-
}
|
|
1808
|
-
return config.transcript;
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
1818
|
// src/shared/shellQuote.ts
|
|
1812
1819
|
function shellQuote(arg) {
|
|
1813
1820
|
if (/[^a-zA-Z0-9_./:=@%^+,-]/.test(arg)) {
|
|
@@ -4846,7 +4853,7 @@ function stripEnvPrefix(parts) {
|
|
|
4846
4853
|
}
|
|
4847
4854
|
|
|
4848
4855
|
// src/shared/isApprovedRead.ts
|
|
4849
|
-
import { resolve as resolve7 } from "path";
|
|
4856
|
+
import { resolve as resolve7, sep } from "path";
|
|
4850
4857
|
|
|
4851
4858
|
// src/shared/tokenize.ts
|
|
4852
4859
|
function tokenize(command) {
|
|
@@ -5060,8 +5067,11 @@ function parsePerms(entries) {
|
|
|
5060
5067
|
}
|
|
5061
5068
|
|
|
5062
5069
|
// src/shared/isApprovedRead.ts
|
|
5070
|
+
var READ_RE = /^Read\((.+)\)$/;
|
|
5063
5071
|
function isApprovedRead(command, toolName = "Bash") {
|
|
5064
5072
|
if (isCdToCwd(command)) return "cd to current directory";
|
|
5073
|
+
const cdRead = isCdToReadAllowedDir(command);
|
|
5074
|
+
if (cdRead) return cdRead;
|
|
5065
5075
|
const matchedRead = findCliRead(command);
|
|
5066
5076
|
if (matchedRead) return `Read-only CLI command: ${matchedRead}`;
|
|
5067
5077
|
const matchedWrite = findCliWrite(command);
|
|
@@ -5078,6 +5088,26 @@ function isCdToCwd(command) {
|
|
|
5078
5088
|
const resolved = resolve7(normalizeMsysPath(parts[1]));
|
|
5079
5089
|
return resolved === resolve7(process.cwd());
|
|
5080
5090
|
}
|
|
5091
|
+
function isCdToReadAllowedDir(command) {
|
|
5092
|
+
const parts = command.split(/\s+/);
|
|
5093
|
+
if (parts[0] !== "cd" || parts.length !== 2) return void 0;
|
|
5094
|
+
const target = resolve7(normalizeMsysPath(parts[1]));
|
|
5095
|
+
for (const entry of readSettingsPerms("allow")) {
|
|
5096
|
+
const m = entry.match(READ_RE);
|
|
5097
|
+
if (!m) continue;
|
|
5098
|
+
const base = globBaseDir(m[1]);
|
|
5099
|
+
if (!base) continue;
|
|
5100
|
+
const resolved = resolve7(normalizeMsysPath(base));
|
|
5101
|
+
if (target === resolved || target.startsWith(resolved + sep)) {
|
|
5102
|
+
return `cd to Read-allowed directory: ${entry}`;
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
5105
|
+
return void 0;
|
|
5106
|
+
}
|
|
5107
|
+
function globBaseDir(pattern2) {
|
|
5108
|
+
const base = pattern2.replace(/[/\\][^/\\]*[*?[].*$/, "");
|
|
5109
|
+
return /[*?[]/.test(base) ? "" : base;
|
|
5110
|
+
}
|
|
5081
5111
|
function normalizeMsysPath(p) {
|
|
5082
5112
|
const m = p.match(/^\/([a-zA-Z])(\/.*)/);
|
|
5083
5113
|
return m ? `${m[1].toUpperCase()}:${m[2]}` : p;
|