agileflow 2.79.0 → 2.80.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.79.0",
3
+ "version": "2.80.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -23,7 +23,7 @@
23
23
  * --detect Show current status
24
24
  * --help Show help
25
25
  *
26
- * Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate, damagecontrol
26
+ * Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate, damagecontrol, askuserquestion
27
27
  */
28
28
 
29
29
  const fs = require('fs');
@@ -81,6 +81,7 @@ const FEATURES = {
81
81
  scripts: ['damage-control-bash.js', 'damage-control-edit.js', 'damage-control-write.js'],
82
82
  patternsFile: 'damage-control-patterns.yaml',
83
83
  },
84
+ askuserquestion: { metadataOnly: true }, // Stored in metadata.features.askUserQuestion
84
85
  };
85
86
 
86
87
  // Complete registry of all scripts that may need repair
@@ -131,24 +132,40 @@ const STATUSLINE_COMPONENTS = [
131
132
  const PROFILES = {
132
133
  full: {
133
134
  description: 'All features enabled (including experimental Stop hooks)',
134
- enable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
135
+ enable: [
136
+ 'sessionstart',
137
+ 'precompact',
138
+ 'archival',
139
+ 'statusline',
140
+ 'ralphloop',
141
+ 'selfimprove',
142
+ 'askuserquestion',
143
+ ],
135
144
  archivalDays: 30,
136
145
  },
137
146
  basic: {
138
147
  description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
139
- enable: ['sessionstart', 'precompact', 'archival'],
148
+ enable: ['sessionstart', 'precompact', 'archival', 'askuserquestion'],
140
149
  disable: ['statusline', 'ralphloop', 'selfimprove'],
141
150
  archivalDays: 30,
142
151
  },
143
152
  minimal: {
144
153
  description: 'SessionStart + archival only',
145
154
  enable: ['sessionstart', 'archival'],
146
- disable: ['precompact', 'statusline', 'ralphloop', 'selfimprove'],
155
+ disable: ['precompact', 'statusline', 'ralphloop', 'selfimprove', 'askuserquestion'],
147
156
  archivalDays: 30,
148
157
  },
149
158
  none: {
150
159
  description: 'Disable all AgileFlow features',
151
- disable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
160
+ disable: [
161
+ 'sessionstart',
162
+ 'precompact',
163
+ 'archival',
164
+ 'statusline',
165
+ 'ralphloop',
166
+ 'selfimprove',
167
+ 'askuserquestion',
168
+ ],
152
169
  },
153
170
  };
154
171
 
@@ -223,7 +240,23 @@ function detectConfig() {
223
240
  selfimprove: { enabled: false, valid: true, issues: [], version: null, outdated: false },
224
241
  archival: { enabled: false, threshold: null, version: null, outdated: false },
225
242
  statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
226
- damagecontrol: { enabled: false, valid: true, issues: [], version: null, outdated: false, level: null, patternCount: 0 },
243
+ damagecontrol: {
244
+ enabled: false,
245
+ valid: true,
246
+ issues: [],
247
+ version: null,
248
+ outdated: false,
249
+ level: null,
250
+ patternCount: 0,
251
+ },
252
+ askuserquestion: {
253
+ enabled: false,
254
+ valid: true,
255
+ issues: [],
256
+ version: null,
257
+ outdated: false,
258
+ mode: null,
259
+ },
227
260
  },
228
261
  metadata: { exists: false, version: null },
229
262
  currentVersion: VERSION,
@@ -313,13 +346,16 @@ function detectConfig() {
313
346
  if (Array.isArray(settings.hooks.PreToolUse) && settings.hooks.PreToolUse.length > 0) {
314
347
  // Check for damage-control hooks by looking for damage-control scripts
315
348
  const hasBashHook = settings.hooks.PreToolUse.some(
316
- h => h.matcher === 'Bash' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
349
+ h =>
350
+ h.matcher === 'Bash' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
317
351
  );
318
352
  const hasEditHook = settings.hooks.PreToolUse.some(
319
- h => h.matcher === 'Edit' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
353
+ h =>
354
+ h.matcher === 'Edit' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
320
355
  );
321
356
  const hasWriteHook = settings.hooks.PreToolUse.some(
322
- h => h.matcher === 'Write' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
357
+ h =>
358
+ h.matcher === 'Write' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
323
359
  );
324
360
 
325
361
  if (hasBashHook || hasEditHook || hasWriteHook) {
@@ -363,17 +399,30 @@ function detectConfig() {
363
399
 
364
400
  // Damage control metadata
365
401
  if (meta.features?.damagecontrol?.enabled) {
366
- status.features.damagecontrol.level = meta.features.damagecontrol.protectionLevel || 'standard';
402
+ status.features.damagecontrol.level =
403
+ meta.features.damagecontrol.protectionLevel || 'standard';
404
+ }
405
+
406
+ // AskUserQuestion metadata
407
+ if (meta.features?.askUserQuestion?.enabled) {
408
+ status.features.askuserquestion.enabled = true;
409
+ status.features.askuserquestion.mode = meta.features.askUserQuestion.mode || 'all';
367
410
  }
368
411
 
369
412
  // Read feature versions from metadata and check if outdated
370
413
  if (meta.features) {
414
+ // Map metadata keys to status keys (handle camelCase differences)
415
+ const featureKeyMap = {
416
+ askUserQuestion: 'askuserquestion',
417
+ };
371
418
  Object.entries(meta.features).forEach(([feature, data]) => {
372
- if (status.features[feature] && data.version) {
373
- status.features[feature].version = data.version;
419
+ // Use mapped key if exists, otherwise lowercase
420
+ const statusKey = featureKeyMap[feature] || feature.toLowerCase();
421
+ if (status.features[statusKey] && data.version) {
422
+ status.features[statusKey].version = data.version;
374
423
  // Check if feature version differs from current VERSION
375
- if (data.version !== VERSION && status.features[feature].enabled) {
376
- status.features[feature].outdated = true;
424
+ if (data.version !== VERSION && status.features[statusKey].enabled) {
425
+ status.features[statusKey].outdated = true;
377
426
  status.hasOutdated = true;
378
427
  }
379
428
  }
@@ -458,6 +507,16 @@ function printStatus(status) {
458
507
  log(` ❌ Damage Control: disabled`, c.dim);
459
508
  }
460
509
 
510
+ // AskUserQuestion
511
+ const auq = status.features.askuserquestion;
512
+ if (auq.enabled) {
513
+ let auqStatusText = 'enabled';
514
+ if (auq.mode) auqStatusText += ` (mode: ${auq.mode})`;
515
+ log(` 💬 AskUserQuestion: ${auqStatusText}`, c.green);
516
+ } else {
517
+ log(` ❌ AskUserQuestion: disabled`, c.dim);
518
+ }
519
+
461
520
  // Metadata
462
521
  if (status.metadata.exists) {
463
522
  log(`\nMetadata: v${status.metadata.version}`, c.dim);
@@ -741,12 +800,34 @@ function enableFeature(feature, options = {}) {
741
800
  return true; // Skip settings.json write for this feature
742
801
  }
743
802
 
803
+ // Handle askuserquestion (metadata only, no hooks needed)
804
+ if (feature === 'askuserquestion') {
805
+ const mode = options.mode || 'all';
806
+ updateMetadata({
807
+ features: {
808
+ askUserQuestion: {
809
+ enabled: true,
810
+ mode: mode,
811
+ version: VERSION,
812
+ at: new Date().toISOString(),
813
+ },
814
+ },
815
+ });
816
+ success(`AskUserQuestion enabled (mode: ${mode})`);
817
+ info('All commands will end with AskUserQuestion tool for guided interaction');
818
+ return true; // Skip settings.json write for this feature
819
+ }
820
+
744
821
  // Handle damage control (PreToolUse hooks)
745
822
  if (feature === 'damagecontrol') {
746
823
  const level = options.protectionLevel || 'standard';
747
824
 
748
825
  // Verify all required scripts exist
749
- const requiredScripts = ['damage-control-bash.js', 'damage-control-edit.js', 'damage-control-write.js'];
826
+ const requiredScripts = [
827
+ 'damage-control-bash.js',
828
+ 'damage-control-edit.js',
829
+ 'damage-control-write.js',
830
+ ];
750
831
  for (const script of requiredScripts) {
751
832
  if (!scriptExists(script)) {
752
833
  error(`Script not found: ${getScriptPath(script)}`);
@@ -761,7 +842,12 @@ function enableFeature(feature, options = {}) {
761
842
  if (!fs.existsSync(patternsDest)) {
762
843
  ensureDir(patternsDir);
763
844
  // Try to copy from templates
764
- const templatePath = path.join(process.cwd(), '.agileflow', 'templates', 'damage-control-patterns.yaml');
845
+ const templatePath = path.join(
846
+ process.cwd(),
847
+ '.agileflow',
848
+ 'templates',
849
+ 'damage-control-patterns.yaml'
850
+ );
765
851
  if (fs.existsSync(templatePath)) {
766
852
  fs.copyFileSync(templatePath, patternsDest);
767
853
  success('Deployed damage control patterns');
@@ -893,6 +979,22 @@ function disableFeature(feature) {
893
979
  return true; // Skip settings.json write for this feature
894
980
  }
895
981
 
982
+ // Disable askuserquestion
983
+ if (feature === 'askuserquestion') {
984
+ updateMetadata({
985
+ features: {
986
+ askUserQuestion: {
987
+ enabled: false,
988
+ version: VERSION,
989
+ at: new Date().toISOString(),
990
+ },
991
+ },
992
+ });
993
+ success('AskUserQuestion disabled');
994
+ info('Commands will end with natural text questions instead of AskUserQuestion tool');
995
+ return true; // Skip settings.json write for this feature
996
+ }
997
+
896
998
  // Disable damage control (PreToolUse hooks)
897
999
  if (feature === 'damagecontrol') {
898
1000
  if (settings.hooks?.PreToolUse && Array.isArray(settings.hooks.PreToolUse)) {
@@ -1396,10 +1498,11 @@ ${c.cyan}Feature Control:${c.reset}
1396
1498
  --enable=<list> Enable features (comma-separated)
1397
1499
  --disable=<list> Disable features (comma-separated)
1398
1500
 
1399
- Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, damagecontrol
1501
+ Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, damagecontrol, askuserquestion
1400
1502
 
1401
1503
  Stop hooks (ralphloop, selfimprove) run when Claude completes/pauses
1402
1504
  Damage control (damagecontrol) uses PreToolUse hooks to block dangerous commands
1505
+ AskUserQuestion (askuserquestion) makes all commands end with guided options
1403
1506
 
1404
1507
  ${c.cyan}Statusline Components:${c.reset}
1405
1508
  --show=<list> Show statusline components (comma-separated)
@@ -1465,6 +1568,12 @@ ${c.cyan}Examples:${c.reset}
1465
1568
 
1466
1569
  # Enable damage control (PreToolUse hooks to block dangerous commands)
1467
1570
  node .agileflow/scripts/agileflow-configure.js --enable=damagecontrol
1571
+
1572
+ # Enable AskUserQuestion (all commands end with guided options)
1573
+ node .agileflow/scripts/agileflow-configure.js --enable=askuserquestion
1574
+
1575
+ # Disable AskUserQuestion (commands end with natural text questions)
1576
+ node .agileflow/scripts/agileflow-configure.js --disable=askuserquestion
1468
1577
  `);
1469
1578
  }
1470
1579
 
@@ -171,6 +171,7 @@ function clearActiveCommands(rootDir) {
171
171
  const state = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
172
172
  result.ran = true;
173
173
 
174
+ // Handle new array format (active_commands)
174
175
  if (state.active_commands && state.active_commands.length > 0) {
175
176
  result.cleared = state.active_commands.length;
176
177
  // Capture command names before clearing
@@ -179,11 +180,14 @@ function clearActiveCommands(rootDir) {
179
180
  }
180
181
  state.active_commands = [];
181
182
  }
183
+
184
+ // Handle legacy singular format (active_command) - only capture if not already in array
182
185
  if (state.active_command !== undefined) {
183
- result.cleared++;
184
- // Capture single command name
185
- if (state.active_command.name) {
186
- result.commandNames.push(state.active_command.name);
186
+ const legacyName = state.active_command.name;
187
+ // Only add to count/names if not already captured from array (avoid duplicates)
188
+ if (legacyName && !result.commandNames.includes(legacyName)) {
189
+ result.cleared++;
190
+ result.commandNames.push(legacyName);
187
191
  }
188
192
  delete state.active_command;
189
193
  }
@@ -298,8 +302,8 @@ function checkDamageControl(rootDir) {
298
302
  const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
299
303
  if (settings.hooks?.PreToolUse && Array.isArray(settings.hooks.PreToolUse)) {
300
304
  // Check for damage-control hooks
301
- const hasDamageControlHooks = settings.hooks.PreToolUse.some(
302
- h => h.hooks?.some(hk => hk.command?.includes('damage-control'))
305
+ const hasDamageControlHooks = settings.hooks.PreToolUse.some(h =>
306
+ h.hooks?.some(hk => hk.command?.includes('damage-control'))
303
307
  );
304
308
  if (hasDamageControlHooks) {
305
309
  result.configured = true;
@@ -311,8 +315,8 @@ function checkDamageControl(rootDir) {
311
315
  result.hooksCount = dcHooks.length;
312
316
 
313
317
  // Check for enhanced mode (has prompt hook)
314
- const hasPromptHook = settings.hooks.PreToolUse.some(
315
- h => h.hooks?.some(hk => hk.type === 'prompt')
318
+ const hasPromptHook = settings.hooks.PreToolUse.some(h =>
319
+ h.hooks?.some(hk => hk.type === 'prompt')
316
320
  );
317
321
  if (hasPromptHook) {
318
322
  result.level = 'enhanced';
@@ -680,14 +684,24 @@ function formatTable(
680
684
  // Show update available notification (using vibrant colors)
681
685
  if (updateInfo.available && updateInfo.latest && !updateInfo.justUpdated) {
682
686
  lines.push(fullDivider());
683
- lines.push(fullRow(`${c.amber}↑${c.reset} Update available: ${c.softGold}v${updateInfo.latest}${c.reset}`, ''));
687
+ lines.push(
688
+ fullRow(
689
+ `${c.amber}↑${c.reset} Update available: ${c.softGold}v${updateInfo.latest}${c.reset}`,
690
+ ''
691
+ )
692
+ );
684
693
  lines.push(fullRow(` Run: ${c.skyBlue}npx agileflow update${c.reset}`, ''));
685
694
  }
686
695
 
687
696
  // Show "just updated" changelog
688
697
  if (updateInfo.justUpdated && updateInfo.changelog && updateInfo.changelog.length > 0) {
689
698
  lines.push(fullDivider());
690
- lines.push(fullRow(`${c.mintGreen}✨${c.reset} What's new in ${c.softGold}v${info.version}${c.reset}:`, ''));
699
+ lines.push(
700
+ fullRow(
701
+ `${c.mintGreen}✨${c.reset} What's new in ${c.softGold}v${info.version}${c.reset}:`,
702
+ ''
703
+ )
704
+ );
691
705
  for (const entry of updateInfo.changelog.slice(0, 2)) {
692
706
  lines.push(fullRow(` ${c.teal}•${c.reset} ${truncate(entry, W - 6)}`, ''));
693
707
  }
@@ -746,7 +760,9 @@ function formatTable(
746
760
 
747
761
  // Session cleanup
748
762
  const sessionStatus = session.cleared > 0 ? `cleared ${session.cleared} command(s)` : `clean`;
749
- lines.push(row('Session state', sessionStatus, c.lavender, session.cleared > 0 ? c.mintGreen : c.dim));
763
+ lines.push(
764
+ row('Session state', sessionStatus, c.lavender, session.cleared > 0 ? c.mintGreen : c.dim)
765
+ );
750
766
 
751
767
  // PreCompact status with version check
752
768
  if (precompact.configured && precompact.scriptExists) {
@@ -798,7 +814,8 @@ function formatTable(
798
814
  lines.push(row('Damage control', '⚠️ scripts missing', c.coral, c.coral));
799
815
  } else {
800
816
  const levelStr = damageControl.level || 'standard';
801
- const patternStr = damageControl.patternCount > 0 ? `${damageControl.patternCount} patterns` : '';
817
+ const patternStr =
818
+ damageControl.patternCount > 0 ? `${damageControl.patternCount} patterns` : '';
802
819
  const dcStatus = `🛡️ ${levelStr}${patternStr ? ` (${patternStr})` : ''}`;
803
820
  lines.push(row('Damage control', dcStatus, c.lavender, c.mintGreen));
804
821
  }
@@ -823,7 +840,9 @@ function formatTable(
823
840
  }
824
841
 
825
842
  // Last commit (colorful like obtain-context)
826
- lines.push(row('Last commit', `${c.peach}${info.commit}${c.reset} ${info.lastCommit}`, c.lavender, ''));
843
+ lines.push(
844
+ row('Last commit', `${c.peach}${info.commit}${c.reset} ${info.lastCommit}`, c.lavender, '')
845
+ );
827
846
 
828
847
  lines.push(bottomBorder);
829
848
 
@@ -864,15 +883,28 @@ async function main() {
864
883
  }
865
884
 
866
885
  console.log(
867
- formatTable(info, archival, session, precompact, parallelSessions, updateInfo, expertise, damageControl)
886
+ formatTable(
887
+ info,
888
+ archival,
889
+ session,
890
+ precompact,
891
+ parallelSessions,
892
+ updateInfo,
893
+ expertise,
894
+ damageControl
895
+ )
868
896
  );
869
897
 
870
898
  // Show warning and tip if other sessions are active (vibrant colors)
871
899
  if (parallelSessions.otherActive > 0) {
872
900
  console.log('');
873
901
  console.log(`${c.amber}⚠️ Other Claude session(s) active in this repo.${c.reset}`);
874
- console.log(`${c.slate} Run ${c.skyBlue}/agileflow:session:status${c.reset}${c.slate} to see all sessions.${c.reset}`);
875
- console.log(`${c.slate} Run ${c.skyBlue}/agileflow:session:new${c.reset}${c.slate} to create isolated workspace.${c.reset}`);
902
+ console.log(
903
+ `${c.slate} Run ${c.skyBlue}/agileflow:session:status${c.reset}${c.slate} to see all sessions.${c.reset}`
904
+ );
905
+ console.log(
906
+ `${c.slate} Run ${c.skyBlue}/agileflow:session:new${c.reset}${c.slate} to create isolated workspace.${c.reset}`
907
+ );
876
908
  }
877
909
  }
878
910
 
@@ -139,7 +139,11 @@ function getDefaultPatterns() {
139
139
  { pattern: 'DROP\\s+(TABLE|DATABASE)', reason: 'DROP commands are destructive' },
140
140
  { pattern: 'DELETE\\s+FROM\\s+\\w+\\s*;', reason: 'DELETE without WHERE clause' },
141
141
  { pattern: 'TRUNCATE\\s+(TABLE\\s+)?\\w+', reason: 'TRUNCATE removes all data' },
142
- { pattern: 'git\\s+push\\s+.*--force', reason: 'Force push can overwrite history', ask: true },
142
+ {
143
+ pattern: 'git\\s+push\\s+.*--force',
144
+ reason: 'Force push can overwrite history',
145
+ ask: true,
146
+ },
143
147
  { pattern: 'git\\s+reset\\s+--hard', reason: 'Hard reset discards changes', ask: true },
144
148
  ],
145
149
  askPatterns: [
@@ -159,10 +163,7 @@ function getDefaultPatterns() {
159
163
  */
160
164
  function checkCommand(command, patterns) {
161
165
  // Combine all pattern sources
162
- const allPatterns = [
163
- ...(patterns.bashToolPatterns || []),
164
- ...(patterns.agileflowPatterns || []),
165
- ];
166
+ const allPatterns = [...(patterns.bashToolPatterns || []), ...(patterns.agileflowPatterns || [])];
166
167
 
167
168
  // Check block/ask patterns
168
169
  for (const p of allPatterns) {
@@ -181,7 +182,7 @@ function checkCommand(command, patterns) {
181
182
  }
182
183
 
183
184
  // Check ask-only patterns
184
- for (const p of (patterns.askPatterns || [])) {
185
+ for (const p of patterns.askPatterns || []) {
185
186
  try {
186
187
  const regex = new RegExp(p.pattern, 'i');
187
188
  if (regex.test(command)) {
@@ -114,27 +114,9 @@ function parsePathRules(content) {
114
114
  */
115
115
  function getDefaultPathRules() {
116
116
  return {
117
- zeroAccessPaths: [
118
- '~/.ssh/',
119
- '~/.aws/credentials',
120
- '.env',
121
- '.env.local',
122
- '.env.production',
123
- ],
124
- readOnlyPaths: [
125
- '/etc/',
126
- '~/.bashrc',
127
- '~/.zshrc',
128
- 'package-lock.json',
129
- 'yarn.lock',
130
- '.git/',
131
- ],
132
- noDeletePaths: [
133
- '.agileflow/',
134
- '.claude/',
135
- 'docs/09-agents/status.json',
136
- 'CLAUDE.md',
137
- ],
117
+ zeroAccessPaths: ['~/.ssh/', '~/.aws/credentials', '.env', '.env.local', '.env.production'],
118
+ readOnlyPaths: ['/etc/', '~/.bashrc', '~/.zshrc', 'package-lock.json', 'yarn.lock', '.git/'],
119
+ noDeletePaths: ['.agileflow/', '.claude/', 'docs/09-agents/status.json', 'CLAUDE.md'],
138
120
  };
139
121
  }
140
122
 
@@ -252,9 +234,7 @@ function main() {
252
234
  }
253
235
 
254
236
  // Resolve to absolute path
255
- const absolutePath = path.isAbsolute(filePath)
256
- ? filePath
257
- : path.join(projectDir, filePath);
237
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
258
238
 
259
239
  // Load rules
260
240
  const rules = loadPathRules(projectDir);
@@ -20,87 +20,87 @@
20
20
  bashToolPatterns:
21
21
  # Recursive/force deletion
22
22
  - pattern: '\brm\s+-[rRf]'
23
- reason: "rm with recursive or force flags can destroy entire directories"
23
+ reason: 'rm with recursive or force flags can destroy entire directories'
24
24
 
25
25
  - pattern: '\brm\s+.*--no-preserve-root'
26
- reason: "rm with --no-preserve-root is catastrophically dangerous"
26
+ reason: 'rm with --no-preserve-root is catastrophically dangerous'
27
27
 
28
28
  - pattern: '\brm\s+-rf\s+/'
29
- reason: "rm -rf on root directory would destroy the entire system"
29
+ reason: 'rm -rf on root directory would destroy the entire system'
30
30
 
31
31
  # SQL destructive commands without WHERE clause
32
32
  - pattern: 'DELETE\s+FROM\s+\w+\s*;'
33
- reason: "DELETE without WHERE clause would delete all records"
33
+ reason: 'DELETE without WHERE clause would delete all records'
34
34
 
35
35
  - pattern: 'TRUNCATE\s+(TABLE\s+)?\w+'
36
- reason: "TRUNCATE removes all data from table"
36
+ reason: 'TRUNCATE removes all data from table'
37
37
 
38
38
  - pattern: 'DROP\s+(TABLE|DATABASE|SCHEMA|INDEX)'
39
- reason: "DROP commands permanently destroy database objects"
39
+ reason: 'DROP commands permanently destroy database objects'
40
40
 
41
41
  # Git force operations
42
42
  - pattern: 'git\s+push\s+.*--force'
43
- reason: "Force push can overwrite remote history"
43
+ reason: 'Force push can overwrite remote history'
44
44
  ask: true
45
45
 
46
46
  - pattern: 'git\s+push\s+.*-f\b'
47
- reason: "Force push can overwrite remote history"
47
+ reason: 'Force push can overwrite remote history'
48
48
  ask: true
49
49
 
50
50
  - pattern: 'git\s+reset\s+--hard'
51
- reason: "Hard reset discards uncommitted changes"
51
+ reason: 'Hard reset discards uncommitted changes'
52
52
  ask: true
53
53
 
54
54
  # Format/wipe operations
55
55
  - pattern: '\bmkfs\b'
56
- reason: "mkfs formats filesystems, destroying all data"
56
+ reason: 'mkfs formats filesystems, destroying all data'
57
57
 
58
58
  - pattern: '\bdd\s+.*of=/dev/'
59
- reason: "dd writing to device can destroy disk data"
59
+ reason: 'dd writing to device can destroy disk data'
60
60
 
61
61
  - pattern: '\bshred\b'
62
- reason: "shred permanently destroys file data"
62
+ reason: 'shred permanently destroys file data'
63
63
 
64
64
  # Credential/secret exposure
65
65
  - pattern: 'cat\s+.*\.env'
66
- reason: "Displaying .env may expose secrets"
66
+ reason: 'Displaying .env may expose secrets'
67
67
  ask: true
68
68
 
69
69
  - pattern: 'cat\s+.*/\.ssh/'
70
- reason: "Displaying SSH keys is a security risk"
70
+ reason: 'Displaying SSH keys is a security risk'
71
71
 
72
72
  - pattern: 'cat\s+.*/credentials'
73
- reason: "Displaying credentials files is a security risk"
73
+ reason: 'Displaying credentials files is a security risk'
74
74
 
75
75
  # Cloud CLI destructive operations
76
76
  - pattern: 'aws\s+s3\s+rm\s+--recursive'
77
- reason: "Recursive S3 delete can destroy entire buckets"
77
+ reason: 'Recursive S3 delete can destroy entire buckets'
78
78
  ask: true
79
79
 
80
80
  - pattern: 'aws\s+ec2\s+terminate-instances'
81
- reason: "Terminating EC2 instances is irreversible"
81
+ reason: 'Terminating EC2 instances is irreversible'
82
82
  ask: true
83
83
 
84
84
  - pattern: 'gcloud\s+.*delete'
85
- reason: "GCloud delete operations may be destructive"
85
+ reason: 'GCloud delete operations may be destructive'
86
86
  ask: true
87
87
 
88
88
  # Docker cleanup commands
89
89
  - pattern: 'docker\s+system\s+prune\s+-a'
90
- reason: "Docker prune -a removes all unused images"
90
+ reason: 'Docker prune -a removes all unused images'
91
91
  ask: true
92
92
 
93
93
  - pattern: 'docker\s+volume\s+rm'
94
- reason: "Docker volume removal may delete persistent data"
94
+ reason: 'Docker volume removal may delete persistent data'
95
95
  ask: true
96
96
 
97
97
  # npm/package manager dangerous commands
98
98
  - pattern: 'npm\s+unpublish'
99
- reason: "npm unpublish can break dependent packages"
99
+ reason: 'npm unpublish can break dependent packages'
100
100
  ask: true
101
101
 
102
102
  - pattern: 'npm\s+deprecate'
103
- reason: "npm deprecate affects package visibility"
103
+ reason: 'npm deprecate affects package visibility'
104
104
  ask: true
105
105
 
106
106
  # ============================================================================
@@ -110,19 +110,19 @@ bashToolPatterns:
110
110
 
111
111
  askPatterns:
112
112
  - pattern: 'DELETE\s+FROM\s+\w+\s+WHERE'
113
- reason: "Deleting specific records - confirm data is correct"
113
+ reason: 'Deleting specific records - confirm data is correct'
114
114
 
115
115
  - pattern: 'UPDATE\s+\w+\s+SET'
116
- reason: "Updating records - confirm scope is correct"
116
+ reason: 'Updating records - confirm scope is correct'
117
117
 
118
118
  - pattern: 'npm\s+publish'
119
- reason: "Publishing to npm is permanent"
119
+ reason: 'Publishing to npm is permanent'
120
120
 
121
121
  - pattern: 'git\s+tag\s+-d'
122
- reason: "Deleting git tags"
122
+ reason: 'Deleting git tags'
123
123
 
124
124
  - pattern: 'kubectl\s+delete'
125
- reason: "Kubernetes delete operations"
125
+ reason: 'Kubernetes delete operations'
126
126
 
127
127
  # ============================================================================
128
128
  # PATH PROTECTION
@@ -193,17 +193,17 @@ noDeletePaths:
193
193
  agileflowPatterns:
194
194
  # Protect AgileFlow infrastructure
195
195
  - pattern: 'rm.*\.agileflow'
196
- reason: "Deleting .agileflow would break AgileFlow installation"
196
+ reason: 'Deleting .agileflow would break AgileFlow installation'
197
197
 
198
198
  - pattern: 'rm.*\.claude'
199
- reason: "Deleting .claude would break Claude Code configuration"
199
+ reason: 'Deleting .claude would break Claude Code configuration'
200
200
 
201
201
  - pattern: 'rm.*status\.json'
202
- reason: "Deleting status.json would lose story tracking data"
202
+ reason: 'Deleting status.json would lose story tracking data'
203
203
 
204
204
  # Dangerous npm operations in AgileFlow context
205
205
  - pattern: 'npm\s+uninstall\s+agileflow'
206
- reason: "Uninstalling AgileFlow - confirm this is intentional"
206
+ reason: 'Uninstalling AgileFlow - confirm this is intentional'
207
207
  ask: true
208
208
 
209
209
  # ============================================================================
@@ -224,4 +224,4 @@ config:
224
224
 
225
225
  # Enable/disable prompt hooks (AI-based evaluation)
226
226
  promptHooksEnabled: false
227
- promptHookMessage: "Evaluate if this command could cause irreversible damage. Block if dangerous."
227
+ promptHookMessage: 'Evaluate if this command could cause irreversible damage. Block if dangerous.'