@mthanhlm/autodev 0.4.2 → 0.4.4
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/.claude-plugin/plugin.json +1 -1
- package/README.md +16 -1
- package/autodev/bin/autodev-tools.cjs +314 -69
- package/autodev/templates/project-state.md +2 -0
- package/autodev/templates/state.md +2 -0
- package/autodev/templates/track-state.md +2 -0
- package/autodev/templates/track.md +8 -1
- package/autodev/workflows/autodev-auto.md +62 -0
- package/autodev/workflows/autodev.md +15 -2
- package/autodev/workflows/execute-phase.md +32 -15
- package/autodev/workflows/explore-codebase.md +17 -8
- package/autodev/workflows/help.md +4 -1
- package/autodev/workflows/new-project.md +26 -11
- package/autodev/workflows/plan-phase.md +11 -3
- package/autodev/workflows/review-phase.md +9 -2
- package/autodev/workflows/verify-work.md +17 -3
- package/bin/install.js +113 -23
- package/commands/autodev/auto.md +27 -0
- package/hooks/autodev-phase-boundary.sh +1 -1
- package/hooks/autodev-prompt-guard.js +26 -3
- package/hooks/autodev-read-guard.js +2 -2
- package/hooks/autodev-statusline.js +9 -4
- package/hooks/autodev-workflow-guard.js +1 -1
- package/hooks/hooks.json +4 -4
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -10,7 +10,6 @@ const yellow = '\x1b[33m';
|
|
|
10
10
|
const cyan = '\x1b[36m';
|
|
11
11
|
const reset = '\x1b[0m';
|
|
12
12
|
|
|
13
|
-
const MANAGED_PREFIX = 'autodev-';
|
|
14
13
|
const ROOT_COMMAND = 'autodev';
|
|
15
14
|
const HOOK_FILES = [
|
|
16
15
|
'hooks.json',
|
|
@@ -223,9 +222,12 @@ function copyCommandsAsLocal(srcDir, destDir, transform) {
|
|
|
223
222
|
|
|
224
223
|
function copyCommandsAsGlobalSkills(srcDir, skillsDir, prefix, transform) {
|
|
225
224
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
225
|
+
const managedSkillNames = fs.readdirSync(srcDir, { withFileTypes: true })
|
|
226
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.md'))
|
|
227
|
+
.map(entry => skillNameFromSourceFile(entry.name, prefix));
|
|
226
228
|
|
|
227
229
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
228
|
-
if (entry.isDirectory() && (entry.name
|
|
230
|
+
if (entry.isDirectory() && managedSkillNames.includes(entry.name)) {
|
|
229
231
|
fs.rmSync(path.join(skillsDir, entry.name), { recursive: true, force: true });
|
|
230
232
|
}
|
|
231
233
|
}
|
|
@@ -265,8 +267,91 @@ function copyAgents(srcDir, destDir, transform) {
|
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
+
function managedAgents(srcRoot = path.resolve(__dirname, '..')) {
|
|
271
|
+
const agentsDir = path.join(srcRoot, 'agents');
|
|
272
|
+
if (!fs.existsSync(agentsDir)) {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
277
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.md'))
|
|
278
|
+
.map(entry => entry.name);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function managedCommandNames(srcRoot = path.resolve(__dirname, '..')) {
|
|
282
|
+
const commandsDir = path.join(srcRoot, 'commands', 'autodev');
|
|
283
|
+
if (!fs.existsSync(commandsDir)) {
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return fs.readdirSync(commandsDir, { withFileTypes: true })
|
|
288
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.md'))
|
|
289
|
+
.map(entry => commandNameFromSourceFile(entry.name));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function managedSkillNames(srcRoot = path.resolve(__dirname, '..')) {
|
|
293
|
+
const commandsDir = path.join(srcRoot, 'commands', 'autodev');
|
|
294
|
+
if (!fs.existsSync(commandsDir)) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return fs.readdirSync(commandsDir, { withFileTypes: true })
|
|
299
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.md'))
|
|
300
|
+
.map(entry => skillNameFromSourceFile(entry.name, ROOT_COMMAND));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function managedCommandStrings(targetDir, isGlobal) {
|
|
304
|
+
return {
|
|
305
|
+
statusLine: isGlobal
|
|
306
|
+
? buildGlobalCommand(targetDir, 'autodev-statusline.js')
|
|
307
|
+
: buildLocalCommand('autodev-statusline.js'),
|
|
308
|
+
hooks: [
|
|
309
|
+
isGlobal
|
|
310
|
+
? buildGlobalCommand(targetDir, 'autodev-session-state.sh', 'bash')
|
|
311
|
+
: buildLocalCommand('autodev-session-state.sh', 'bash'),
|
|
312
|
+
isGlobal
|
|
313
|
+
? buildGlobalCommand(targetDir, 'autodev-prompt-guard.js')
|
|
314
|
+
: buildLocalCommand('autodev-prompt-guard.js'),
|
|
315
|
+
isGlobal
|
|
316
|
+
? buildGlobalCommand(targetDir, 'autodev-read-guard.js')
|
|
317
|
+
: buildLocalCommand('autodev-read-guard.js'),
|
|
318
|
+
isGlobal
|
|
319
|
+
? buildGlobalCommand(targetDir, 'autodev-workflow-guard.js')
|
|
320
|
+
: buildLocalCommand('autodev-workflow-guard.js'),
|
|
321
|
+
isGlobal
|
|
322
|
+
? buildGlobalCommand(targetDir, 'autodev-git-guard.js')
|
|
323
|
+
: buildLocalCommand('autodev-git-guard.js'),
|
|
324
|
+
isGlobal
|
|
325
|
+
? buildGlobalCommand(targetDir, 'autodev-auto-format.js')
|
|
326
|
+
: buildLocalCommand('autodev-auto-format.js'),
|
|
327
|
+
isGlobal
|
|
328
|
+
? buildGlobalCommand(targetDir, 'autodev-context-monitor.js')
|
|
329
|
+
: buildLocalCommand('autodev-context-monitor.js'),
|
|
330
|
+
isGlobal
|
|
331
|
+
? buildGlobalCommand(targetDir, 'autodev-phase-boundary.sh', 'bash')
|
|
332
|
+
: buildLocalCommand('autodev-phase-boundary.sh', 'bash')
|
|
333
|
+
]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isManagedCommand(command, managedCommands = null) {
|
|
338
|
+
if (typeof command !== 'string') {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (managedCommands && managedCommands.includes(command)) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return /(?:^|[/"\s])autodev-(?:auto-format|context-monitor|git-guard|phase-boundary|prompt-guard|read-guard|session-state|statusline|workflow-guard)\.(?:js|sh)(?:"|$)/.test(command);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function removeManagedSettings(settings, options = {}) {
|
|
350
|
+
const managedStrings = options.targetDir
|
|
351
|
+
? managedCommandStrings(options.targetDir, Boolean(options.isGlobal))
|
|
352
|
+
: null;
|
|
353
|
+
|
|
354
|
+
if (settings.statusLine?.command && isManagedCommand(settings.statusLine.command, managedStrings ? [managedStrings.statusLine] : null)) {
|
|
270
355
|
delete settings.statusLine;
|
|
271
356
|
}
|
|
272
357
|
|
|
@@ -285,7 +370,7 @@ function removeManagedSettings(settings) {
|
|
|
285
370
|
const entries = Array.isArray(settings.hooks[eventName]) ? settings.hooks[eventName] : [];
|
|
286
371
|
const filtered = entries.filter(entry => {
|
|
287
372
|
const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
|
|
288
|
-
return !hooks.some(hook =>
|
|
373
|
+
return !hooks.some(hook => isManagedCommand(hook?.command, managedStrings?.hooks || null));
|
|
289
374
|
});
|
|
290
375
|
|
|
291
376
|
if (filtered.length > 0) {
|
|
@@ -367,7 +452,7 @@ function configureSettings(targetDir, isGlobal, options = {}) {
|
|
|
367
452
|
return null;
|
|
368
453
|
}
|
|
369
454
|
|
|
370
|
-
removeManagedSettings(settings);
|
|
455
|
+
removeManagedSettings(settings, { targetDir, isGlobal });
|
|
371
456
|
|
|
372
457
|
const preToolEvent = 'PreToolUse';
|
|
373
458
|
const postToolEvent = 'PostToolUse';
|
|
@@ -402,13 +487,13 @@ function configureSettings(targetDir, isGlobal, options = {}) {
|
|
|
402
487
|
: buildLocalCommand('autodev-statusline.js');
|
|
403
488
|
|
|
404
489
|
ensureHook(settings, sessionStartEvent, null, sessionStateCommand);
|
|
405
|
-
ensureHook(settings, preToolEvent, 'Write|Edit', promptGuardCommand, 5);
|
|
406
|
-
ensureHook(settings, preToolEvent, 'Write|Edit', readGuardCommand, 5);
|
|
407
|
-
ensureHook(settings, preToolEvent, 'Write|Edit', workflowGuardCommand, 5);
|
|
490
|
+
ensureHook(settings, preToolEvent, 'Write|Edit|MultiEdit', promptGuardCommand, 5);
|
|
491
|
+
ensureHook(settings, preToolEvent, 'Write|Edit|MultiEdit', readGuardCommand, 5);
|
|
492
|
+
ensureHook(settings, preToolEvent, 'Write|Edit|MultiEdit', workflowGuardCommand, 5);
|
|
408
493
|
ensureHook(settings, preToolEvent, 'Bash', gitGuardCommand, 5);
|
|
409
494
|
ensureHook(settings, postToolEvent, 'Edit|Write|MultiEdit', autoFormatCommand, 10);
|
|
410
495
|
ensureHook(settings, postToolEvent, 'Bash|Edit|Write|MultiEdit|Agent|Task', contextMonitorCommand, 10);
|
|
411
|
-
ensureHook(settings, postToolEvent, 'Write|Edit', phaseBoundaryCommand, 5);
|
|
496
|
+
ensureHook(settings, postToolEvent, 'Write|Edit|MultiEdit', phaseBoundaryCommand, 5);
|
|
412
497
|
|
|
413
498
|
if (options.installStatusLine !== false) {
|
|
414
499
|
settings.statusLine = {
|
|
@@ -426,6 +511,7 @@ function configureSettings(targetDir, isGlobal, options = {}) {
|
|
|
426
511
|
}
|
|
427
512
|
|
|
428
513
|
function removeInstalledFiles(targetDir, isGlobal) {
|
|
514
|
+
const srcRoot = path.resolve(__dirname, '..');
|
|
429
515
|
const supportDir = path.join(targetDir, 'autodev');
|
|
430
516
|
if (fs.existsSync(supportDir)) {
|
|
431
517
|
fs.rmSync(supportDir, { recursive: true, force: true });
|
|
@@ -433,9 +519,10 @@ function removeInstalledFiles(targetDir, isGlobal) {
|
|
|
433
519
|
|
|
434
520
|
const agentsDir = path.join(targetDir, 'agents');
|
|
435
521
|
if (fs.existsSync(agentsDir)) {
|
|
436
|
-
for (const
|
|
437
|
-
|
|
438
|
-
|
|
522
|
+
for (const agentFile of managedAgents(srcRoot)) {
|
|
523
|
+
const agentPath = path.join(agentsDir, agentFile);
|
|
524
|
+
if (fs.existsSync(agentPath)) {
|
|
525
|
+
fs.rmSync(agentPath, { force: true });
|
|
439
526
|
}
|
|
440
527
|
}
|
|
441
528
|
}
|
|
@@ -451,18 +538,20 @@ function removeInstalledFiles(targetDir, isGlobal) {
|
|
|
451
538
|
if (isGlobal) {
|
|
452
539
|
const skillsDir = path.join(targetDir, 'skills');
|
|
453
540
|
if (fs.existsSync(skillsDir)) {
|
|
454
|
-
for (const
|
|
455
|
-
|
|
456
|
-
|
|
541
|
+
for (const skillName of managedSkillNames(srcRoot)) {
|
|
542
|
+
const skillPath = path.join(skillsDir, skillName);
|
|
543
|
+
if (fs.existsSync(skillPath)) {
|
|
544
|
+
fs.rmSync(skillPath, { recursive: true, force: true });
|
|
457
545
|
}
|
|
458
546
|
}
|
|
459
547
|
}
|
|
460
548
|
} else {
|
|
461
549
|
const commandsDir = path.join(targetDir, 'commands');
|
|
462
550
|
if (fs.existsSync(commandsDir)) {
|
|
463
|
-
for (const
|
|
464
|
-
|
|
465
|
-
|
|
551
|
+
for (const commandName of managedCommandNames(srcRoot)) {
|
|
552
|
+
const commandPath = path.join(commandsDir, `${commandName}.md`);
|
|
553
|
+
if (fs.existsSync(commandPath)) {
|
|
554
|
+
fs.rmSync(commandPath, { force: true });
|
|
466
555
|
}
|
|
467
556
|
}
|
|
468
557
|
}
|
|
@@ -472,9 +561,10 @@ function removeInstalledFiles(targetDir, isGlobal) {
|
|
|
472
561
|
}
|
|
473
562
|
const skillsDir = path.join(targetDir, 'skills');
|
|
474
563
|
if (fs.existsSync(skillsDir)) {
|
|
475
|
-
for (const
|
|
476
|
-
|
|
477
|
-
|
|
564
|
+
for (const skillName of managedSkillNames(srcRoot)) {
|
|
565
|
+
const skillPath = path.join(skillsDir, skillName);
|
|
566
|
+
if (fs.existsSync(skillPath)) {
|
|
567
|
+
fs.rmSync(skillPath, { recursive: true, force: true });
|
|
478
568
|
}
|
|
479
569
|
}
|
|
480
570
|
}
|
|
@@ -553,7 +643,7 @@ function uninstall(options = {}) {
|
|
|
553
643
|
const settingsPath = path.join(targetDir, 'settings.json');
|
|
554
644
|
const settings = readSettings(settingsPath);
|
|
555
645
|
if (settings) {
|
|
556
|
-
removeManagedSettings(settings);
|
|
646
|
+
removeManagedSettings(settings, { targetDir, isGlobal });
|
|
557
647
|
writeSettings(settingsPath, settings);
|
|
558
648
|
}
|
|
559
649
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autodev:auto
|
|
3
|
+
description: Run autodev continuously until blocked, manual verification is required, or the active track is done
|
|
4
|
+
argument-hint: "[goal, track, or question]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- Grep
|
|
11
|
+
- Glob
|
|
12
|
+
- TodoWrite
|
|
13
|
+
- AskUserQuestion
|
|
14
|
+
- Agent
|
|
15
|
+
- WebFetch
|
|
16
|
+
---
|
|
17
|
+
<objective>
|
|
18
|
+
Use `/autodev-auto` as the explicit opt-in continuous runner. Keep going until the work is done or a real stop condition appears.
|
|
19
|
+
</objective>
|
|
20
|
+
|
|
21
|
+
<execution_context>
|
|
22
|
+
@~/.claude/autodev/workflows/autodev-auto.md
|
|
23
|
+
</execution_context>
|
|
24
|
+
|
|
25
|
+
<process>
|
|
26
|
+
Execute the auto router workflow in @~/.claude/autodev/workflows/autodev-auto.md end-to-end.
|
|
27
|
+
</process>
|
|
@@ -41,7 +41,7 @@ if [ "$ENABLED" != "1" ]; then
|
|
|
41
41
|
fi
|
|
42
42
|
|
|
43
43
|
INPUT=$(cat)
|
|
44
|
-
FILE=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{
|
|
44
|
+
FILE=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const t=JSON.parse(d).tool_input||{};process.stdout.write(t.file_path||t.path||'')}catch{}})" 2>/dev/null)
|
|
45
45
|
|
|
46
46
|
if [[ "$FILE" == *.autodev/* ]] || [[ "$FILE" == .autodev/* ]]; then
|
|
47
47
|
echo ".autodev file updated: $FILE"
|
|
@@ -11,6 +11,29 @@ const PATTERNS = [
|
|
|
11
11
|
/<\/?(?:system|assistant|human)>/i
|
|
12
12
|
];
|
|
13
13
|
|
|
14
|
+
function extractWrittenContent(toolInput) {
|
|
15
|
+
if (!toolInput || typeof toolInput !== 'object') {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof toolInput.content === 'string' && toolInput.content) {
|
|
20
|
+
return toolInput.content;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (typeof toolInput.new_string === 'string' && toolInput.new_string) {
|
|
24
|
+
return toolInput.new_string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(toolInput.edits)) {
|
|
28
|
+
return toolInput.edits
|
|
29
|
+
.map(edit => edit?.new_string || '')
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
|
|
14
37
|
let input = '';
|
|
15
38
|
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
16
39
|
process.stdin.setEncoding('utf8');
|
|
@@ -22,16 +45,16 @@ process.stdin.on('end', () => {
|
|
|
22
45
|
|
|
23
46
|
try {
|
|
24
47
|
const data = JSON.parse(input);
|
|
25
|
-
if (!['Write', 'Edit'].includes(data.tool_name)) {
|
|
48
|
+
if (!['Write', 'Edit', 'MultiEdit'].includes(data.tool_name)) {
|
|
26
49
|
process.exit(0);
|
|
27
50
|
}
|
|
28
51
|
|
|
29
|
-
const filePath = data.tool_input?.file_path || '';
|
|
52
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
30
53
|
if (!filePath.includes('.autodev/')) {
|
|
31
54
|
process.exit(0);
|
|
32
55
|
}
|
|
33
56
|
|
|
34
|
-
const content = data.tool_input
|
|
57
|
+
const content = extractWrittenContent(data.tool_input);
|
|
35
58
|
if (!content) {
|
|
36
59
|
process.exit(0);
|
|
37
60
|
}
|
|
@@ -15,7 +15,7 @@ process.stdin.on('end', () => {
|
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
17
|
const data = JSON.parse(input);
|
|
18
|
-
if (!['Write', 'Edit'].includes(data.tool_name)) {
|
|
18
|
+
if (!['Write', 'Edit', 'MultiEdit'].includes(data.tool_name)) {
|
|
19
19
|
process.exit(0);
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ process.stdin.on('end', () => {
|
|
|
24
24
|
process.exit(0);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const filePath = data.tool_input?.file_path || '';
|
|
27
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
28
28
|
if (!filePath || !fs.existsSync(filePath)) {
|
|
29
29
|
process.exit(0);
|
|
30
30
|
}
|
|
@@ -13,21 +13,25 @@ function readStateFields(cwd) {
|
|
|
13
13
|
if (!statePath) {
|
|
14
14
|
return {
|
|
15
15
|
currentTask: '',
|
|
16
|
-
currentTaskStatus: ''
|
|
16
|
+
currentTaskStatus: '',
|
|
17
|
+
runMode: ''
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const content = fs.readFileSync(statePath, 'utf8');
|
|
21
22
|
const currentTask = content.match(/^Current Task:\s*(.+)$/mi)?.[1]?.trim() || '';
|
|
22
23
|
const currentTaskStatus = content.match(/^Current Task Status:\s*(.+)$/mi)?.[1]?.trim() || '';
|
|
24
|
+
const runMode = content.match(/^Run Mode:\s*(.+)$/mi)?.[1]?.trim() || '';
|
|
23
25
|
return {
|
|
24
26
|
currentTask: currentTask && currentTask !== 'none' ? currentTask : '',
|
|
25
|
-
currentTaskStatus: currentTaskStatus && currentTaskStatus !== 'idle' ? currentTaskStatus : ''
|
|
27
|
+
currentTaskStatus: currentTaskStatus && currentTaskStatus !== 'idle' ? currentTaskStatus : '',
|
|
28
|
+
runMode: runMode === 'auto' ? 'auto' : ''
|
|
26
29
|
};
|
|
27
30
|
} catch {
|
|
28
31
|
return {
|
|
29
32
|
currentTask: '',
|
|
30
|
-
currentTaskStatus: ''
|
|
33
|
+
currentTaskStatus: '',
|
|
34
|
+
runMode: ''
|
|
31
35
|
};
|
|
32
36
|
}
|
|
33
37
|
}
|
|
@@ -67,11 +71,12 @@ process.stdin.on('end', () => {
|
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
const modeLabel = taskState.runMode ? ` | ${taskState.runMode}` : '';
|
|
70
75
|
const taskLabel = taskState.currentTask
|
|
71
76
|
? ` | task ${taskState.currentTask}${taskState.currentTaskStatus ? ` (${taskState.currentTaskStatus})` : ''}`
|
|
72
77
|
: '';
|
|
73
78
|
|
|
74
|
-
process.stdout.write(`${model} | ${path.basename(currentDir)}${taskLabel}${contextLabel}`);
|
|
79
|
+
process.stdout.write(`${model} | ${path.basename(currentDir)}${modeLabel}${taskLabel}${contextLabel}`);
|
|
75
80
|
} catch {
|
|
76
81
|
process.exit(0);
|
|
77
82
|
}
|
package/hooks/hooks.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"PreToolUse": [
|
|
15
15
|
{
|
|
16
|
-
"matcher": "Write|Edit",
|
|
16
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
17
17
|
"hooks": [
|
|
18
18
|
{
|
|
19
19
|
"type": "command",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
"matcher": "Write|Edit",
|
|
26
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
|
-
"matcher": "Write|Edit",
|
|
36
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
37
37
|
"hooks": [
|
|
38
38
|
{
|
|
39
39
|
"type": "command",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
]
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
"matcher": "Write|Edit",
|
|
78
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
79
79
|
"hooks": [
|
|
80
80
|
{
|
|
81
81
|
"type": "command",
|
package/package.json
CHANGED