agileflow 2.94.1 → 2.95.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +3 -3
- package/lib/colors.generated.js +117 -0
- package/lib/colors.js +59 -109
- package/lib/generator-factory.js +333 -0
- package/lib/path-utils.js +49 -0
- package/lib/session-registry.js +25 -15
- package/lib/smart-json-file.js +40 -32
- package/lib/state-machine.js +286 -0
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +7 -6
- package/scripts/archive-completed-stories.sh +86 -11
- package/scripts/babysit-context-restore.js +89 -0
- package/scripts/claude-tmux.sh +111 -5
- package/scripts/damage-control/bash-tool-damage-control.js +11 -247
- package/scripts/damage-control/edit-tool-damage-control.js +9 -249
- package/scripts/damage-control/write-tool-damage-control.js +9 -244
- package/scripts/generate-colors.js +314 -0
- package/scripts/lib/colors.generated.sh +82 -0
- package/scripts/lib/colors.sh +10 -70
- package/scripts/lib/configure-features.js +401 -0
- package/scripts/lib/context-loader.js +181 -52
- package/scripts/precompact-context.sh +54 -17
- package/scripts/session-coordinator.sh +2 -2
- package/scripts/session-manager.js +653 -10
- package/src/core/commands/audit.md +93 -0
- package/src/core/commands/auto.md +73 -0
- package/src/core/commands/babysit.md +169 -13
- package/src/core/commands/baseline.md +73 -0
- package/src/core/commands/batch.md +64 -0
- package/src/core/commands/blockers.md +60 -0
- package/src/core/commands/board.md +66 -0
- package/src/core/commands/choose.md +77 -0
- package/src/core/commands/ci.md +77 -0
- package/src/core/commands/compress.md +27 -1
- package/src/core/commands/configure.md +126 -10
- package/src/core/commands/council.md +74 -0
- package/src/core/commands/debt.md +72 -0
- package/src/core/commands/deploy.md +73 -0
- package/src/core/commands/deps.md +68 -0
- package/src/core/commands/docs.md +60 -0
- package/src/core/commands/feedback.md +68 -0
- package/src/core/commands/ideate.md +74 -0
- package/src/core/commands/impact.md +74 -0
- package/src/core/commands/install.md +529 -0
- package/src/core/commands/maintain.md +558 -0
- package/src/core/commands/metrics.md +75 -0
- package/src/core/commands/multi-expert.md +74 -0
- package/src/core/commands/packages.md +69 -0
- package/src/core/commands/readme-sync.md +64 -0
- package/src/core/commands/research/analyze.md +285 -121
- package/src/core/commands/research/import.md +281 -109
- package/src/core/commands/retro.md +76 -0
- package/src/core/commands/review.md +72 -0
- package/src/core/commands/rlm.md +83 -0
- package/src/core/commands/rpi.md +90 -0
- package/src/core/commands/session/cleanup.md +214 -12
- package/src/core/commands/session/end.md +155 -17
- package/src/core/commands/sprint.md +72 -0
- package/src/core/commands/story-validate.md +68 -0
- package/src/core/commands/template.md +69 -0
- package/src/core/commands/tests.md +83 -0
- package/src/core/commands/update.md +59 -0
- package/src/core/commands/validate-expertise.md +76 -0
- package/src/core/commands/velocity.md +74 -0
- package/src/core/commands/verify.md +91 -0
- package/src/core/commands/whats-new.md +69 -0
- package/src/core/commands/workflow.md +88 -0
- package/src/core/templates/command-documentation.md +187 -0
- package/tools/cli/commands/session.js +1171 -0
- package/tools/cli/commands/setup.js +2 -81
- package/tools/cli/installers/core/installer.js +0 -5
- package/tools/cli/installers/ide/claude-code.js +6 -0
- package/tools/cli/lib/config-manager.js +42 -5
|
@@ -20,7 +20,7 @@ const fs = require('fs');
|
|
|
20
20
|
const fsPromises = require('fs').promises;
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const os = require('os');
|
|
23
|
-
const {
|
|
23
|
+
const { spawnSync, spawn } = require('child_process');
|
|
24
24
|
|
|
25
25
|
// Try to use cached reads if available
|
|
26
26
|
let readJSONCached, readFileCached;
|
|
@@ -39,25 +39,26 @@ try {
|
|
|
39
39
|
// =============================================================================
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Whitelisted
|
|
43
|
-
* Only
|
|
42
|
+
* Whitelisted git subcommands with allowed arguments (US-0187)
|
|
43
|
+
* Only these specific read-only git operations are permitted.
|
|
44
|
+
*
|
|
45
|
+
* Format: { subcommand: true } allows any args (read-only commands)
|
|
46
|
+
* { subcommand: ['--flag1', '--flag2'] } allows only listed first args
|
|
44
47
|
*/
|
|
45
|
-
const
|
|
46
|
-
//
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
'
|
|
50
|
-
|
|
51
|
-
'git
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
|
|
59
|
-
'node ',
|
|
60
|
-
];
|
|
48
|
+
const SAFEEXEC_ALLOWED_GIT_SUBCOMMANDS = {
|
|
49
|
+
// Read-only git operations
|
|
50
|
+
branch: ['--show-current', '-a', '--list', '-r', '--all'],
|
|
51
|
+
log: true, // All log flags are read-only
|
|
52
|
+
status: ['--short', '--porcelain', '-s', '--ignored'],
|
|
53
|
+
diff: true, // All diff flags are read-only
|
|
54
|
+
'rev-parse': ['HEAD', '--git-dir', '--show-toplevel', '--abbrev-ref', '--is-inside-work-tree'],
|
|
55
|
+
describe: true, // Read-only
|
|
56
|
+
show: true, // Read-only
|
|
57
|
+
config: ['--get', '--list', '-l', '--get-all'], // Read-only config operations only
|
|
58
|
+
remote: ['-v', '--verbose', 'get-url'],
|
|
59
|
+
tag: ['--list', '-l'],
|
|
60
|
+
'ls-files': true, // Read-only listing
|
|
61
|
+
};
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
64
|
* Dangerous patterns that should never be executed
|
|
@@ -107,32 +108,86 @@ function logSafeExec(level, message, details = {}) {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param {string} cmd - Command
|
|
112
|
-
* @returns {{
|
|
111
|
+
* Parse a git command string into executable and arguments (US-0187)
|
|
112
|
+
* @param {string} cmd - Command string (e.g., "git branch --show-current")
|
|
113
|
+
* @returns {{ ok: boolean, data?: { executable: string, subcommand: string, args: string[], fullArgs: string[] }, error?: string }}
|
|
113
114
|
*/
|
|
114
|
-
function
|
|
115
|
+
function parseGitCommand(cmd) {
|
|
115
116
|
if (!cmd || typeof cmd !== 'string') {
|
|
116
|
-
return {
|
|
117
|
+
return { ok: false, error: 'Invalid command' };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const parts = cmd.trim().split(/\s+/);
|
|
121
|
+
if (parts.length < 1 || parts[0] !== 'git') {
|
|
122
|
+
return { ok: false, error: 'Only git commands are supported' };
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
|
|
125
|
+
// Handle bare 'git' command
|
|
126
|
+
if (parts.length < 2) {
|
|
127
|
+
return { ok: false, error: 'Git subcommand required' };
|
|
128
|
+
}
|
|
120
129
|
|
|
121
|
-
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
data: {
|
|
133
|
+
executable: 'git',
|
|
134
|
+
subcommand: parts[1],
|
|
135
|
+
args: parts.slice(2),
|
|
136
|
+
fullArgs: parts.slice(1), // ['branch', '--show-current']
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a git subcommand with args is allowed (US-0187)
|
|
143
|
+
* @param {string} subcommand - Git subcommand (e.g., 'branch')
|
|
144
|
+
* @param {string[]} args - Arguments to subcommand
|
|
145
|
+
* @returns {{ allowed: boolean, reason?: string }}
|
|
146
|
+
*/
|
|
147
|
+
function isGitCommandAllowed(subcommand, args) {
|
|
148
|
+
// First check blocked patterns in arguments
|
|
149
|
+
const fullCmd = `git ${subcommand} ${args.join(' ')}`;
|
|
122
150
|
for (const pattern of SAFEEXEC_BLOCKED_PATTERNS) {
|
|
123
|
-
if (pattern.test(
|
|
151
|
+
if (pattern.test(fullCmd)) {
|
|
124
152
|
return { allowed: false, reason: `Blocked pattern: ${pattern}` };
|
|
125
153
|
}
|
|
126
154
|
}
|
|
127
155
|
|
|
128
|
-
// Check against
|
|
129
|
-
const
|
|
156
|
+
// Check against allowed subcommands
|
|
157
|
+
const allowedArgs = SAFEEXEC_ALLOWED_GIT_SUBCOMMANDS[subcommand];
|
|
158
|
+
if (!allowedArgs) {
|
|
159
|
+
return { allowed: false, reason: `Git subcommand '${subcommand}' not in whitelist` };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// If allowedArgs is true, any args are allowed for this subcommand (read-only)
|
|
163
|
+
if (allowedArgs === true) {
|
|
164
|
+
return { allowed: true };
|
|
165
|
+
}
|
|
130
166
|
|
|
131
|
-
|
|
132
|
-
|
|
167
|
+
// If allowedArgs is an array, first arg must match one of the allowed values
|
|
168
|
+
// (or args can be empty for commands like 'git status')
|
|
169
|
+
if (args.length === 0) {
|
|
170
|
+
return { allowed: true };
|
|
133
171
|
}
|
|
134
172
|
|
|
135
|
-
|
|
173
|
+
if (allowedArgs.includes(args[0])) {
|
|
174
|
+
return { allowed: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { allowed: false, reason: `Argument '${args[0]}' not allowed for 'git ${subcommand}'` };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if a command is allowed (legacy wrapper for backwards compatibility)
|
|
182
|
+
* @param {string} cmd - Command to check
|
|
183
|
+
* @returns {{allowed: boolean, reason?: string}}
|
|
184
|
+
*/
|
|
185
|
+
function isCommandAllowed(cmd) {
|
|
186
|
+
const parsed = parseGitCommand(cmd);
|
|
187
|
+
if (!parsed.ok) {
|
|
188
|
+
return { allowed: false, reason: parsed.error };
|
|
189
|
+
}
|
|
190
|
+
return isGitCommandAllowed(parsed.data.subcommand, parsed.data.args);
|
|
136
191
|
}
|
|
137
192
|
|
|
138
193
|
// =============================================================================
|
|
@@ -184,12 +239,13 @@ function safeLs(dirPath) {
|
|
|
184
239
|
}
|
|
185
240
|
|
|
186
241
|
/**
|
|
187
|
-
* Safely execute a
|
|
242
|
+
* Safely execute a git command with whitelist validation (US-0187).
|
|
188
243
|
*
|
|
189
|
-
*
|
|
244
|
+
* Uses spawnSync with shell: false to prevent shell injection.
|
|
245
|
+
* Only whitelisted read-only git commands are allowed.
|
|
190
246
|
* Dangerous patterns (pipes, redirects, etc.) are blocked.
|
|
191
247
|
*
|
|
192
|
-
* @param {string} cmd - Command to execute
|
|
248
|
+
* @param {string} cmd - Command to execute (must be a git command)
|
|
193
249
|
* @param {Object} [options] - Options
|
|
194
250
|
* @param {boolean} [options.bypassWhitelist=false] - Skip whitelist check (use with caution)
|
|
195
251
|
* @returns {string|null} Command output or null
|
|
@@ -197,9 +253,19 @@ function safeLs(dirPath) {
|
|
|
197
253
|
function safeExec(cmd, options = {}) {
|
|
198
254
|
const { bypassWhitelist = false } = options;
|
|
199
255
|
|
|
256
|
+
// Parse command into executable and arguments
|
|
257
|
+
const parsed = parseGitCommand(cmd);
|
|
258
|
+
if (!parsed.ok) {
|
|
259
|
+
logSafeExec('warn', 'Invalid command format', {
|
|
260
|
+
cmd: cmd?.substring(0, 100),
|
|
261
|
+
error: parsed.error,
|
|
262
|
+
});
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
200
266
|
// Validate command unless bypassed
|
|
201
267
|
if (!bypassWhitelist) {
|
|
202
|
-
const check =
|
|
268
|
+
const check = isGitCommandAllowed(parsed.data.subcommand, parsed.data.args);
|
|
203
269
|
if (!check.allowed) {
|
|
204
270
|
logSafeExec('warn', 'Command blocked by whitelist', {
|
|
205
271
|
cmd: cmd?.substring(0, 100),
|
|
@@ -215,14 +281,38 @@ function safeExec(cmd, options = {}) {
|
|
|
215
281
|
});
|
|
216
282
|
|
|
217
283
|
try {
|
|
218
|
-
|
|
284
|
+
// Use spawnSync with array arguments - NO SHELL INTERPRETATION (US-0187)
|
|
285
|
+
const result = spawnSync(parsed.data.executable, parsed.data.fullArgs, {
|
|
286
|
+
encoding: 'utf8',
|
|
287
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
288
|
+
shell: false, // CRITICAL: Prevents shell injection
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (result.error) {
|
|
292
|
+
logSafeExec('error', 'Command spawn failed', {
|
|
293
|
+
cmd: cmd?.substring(0, 50),
|
|
294
|
+
error: result.error.message,
|
|
295
|
+
});
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (result.status !== 0) {
|
|
300
|
+
logSafeExec('debug', 'Command exited non-zero', {
|
|
301
|
+
cmd: cmd?.substring(0, 50),
|
|
302
|
+
status: result.status,
|
|
303
|
+
stderr: result.stderr?.substring(0, 100),
|
|
304
|
+
});
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const output = (result.stdout || '').trim();
|
|
219
309
|
logSafeExec('debug', 'Command succeeded', {
|
|
220
310
|
cmd: cmd?.substring(0, 50),
|
|
221
|
-
outputLength:
|
|
311
|
+
outputLength: output.length,
|
|
222
312
|
});
|
|
223
|
-
return
|
|
313
|
+
return output;
|
|
224
314
|
} catch (error) {
|
|
225
|
-
logSafeExec('
|
|
315
|
+
logSafeExec('error', 'Command execution error', {
|
|
226
316
|
cmd: cmd?.substring(0, 50),
|
|
227
317
|
error: error?.message?.substring(0, 100),
|
|
228
318
|
});
|
|
@@ -275,12 +365,13 @@ async function safeLsAsync(dirPath) {
|
|
|
275
365
|
}
|
|
276
366
|
|
|
277
367
|
/**
|
|
278
|
-
* Execute a command asynchronously with whitelist validation.
|
|
368
|
+
* Execute a git command asynchronously with whitelist validation (US-0187).
|
|
279
369
|
*
|
|
280
|
-
*
|
|
370
|
+
* Uses spawn with shell: false to prevent shell injection.
|
|
371
|
+
* Only whitelisted read-only git commands are allowed.
|
|
281
372
|
* Dangerous patterns (pipes, redirects, etc.) are blocked.
|
|
282
373
|
*
|
|
283
|
-
* @param {string} cmd - Command to execute
|
|
374
|
+
* @param {string} cmd - Command to execute (must be a git command)
|
|
284
375
|
* @param {Object} [options] - Options
|
|
285
376
|
* @param {boolean} [options.bypassWhitelist=false] - Skip whitelist check (use with caution)
|
|
286
377
|
* @returns {Promise<string|null>} Command output or null
|
|
@@ -288,9 +379,19 @@ async function safeLsAsync(dirPath) {
|
|
|
288
379
|
async function safeExecAsync(cmd, options = {}) {
|
|
289
380
|
const { bypassWhitelist = false } = options;
|
|
290
381
|
|
|
382
|
+
// Parse command into executable and arguments
|
|
383
|
+
const parsed = parseGitCommand(cmd);
|
|
384
|
+
if (!parsed.ok) {
|
|
385
|
+
logSafeExec('warn', 'Invalid async command format', {
|
|
386
|
+
cmd: cmd?.substring(0, 100),
|
|
387
|
+
error: parsed.error,
|
|
388
|
+
});
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
291
392
|
// Validate command unless bypassed
|
|
292
393
|
if (!bypassWhitelist) {
|
|
293
|
-
const check =
|
|
394
|
+
const check = isGitCommandAllowed(parsed.data.subcommand, parsed.data.args);
|
|
294
395
|
if (!check.allowed) {
|
|
295
396
|
logSafeExec('warn', 'Async command blocked by whitelist', {
|
|
296
397
|
cmd: cmd?.substring(0, 100),
|
|
@@ -306,18 +407,44 @@ async function safeExecAsync(cmd, options = {}) {
|
|
|
306
407
|
});
|
|
307
408
|
|
|
308
409
|
return new Promise(resolve => {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
410
|
+
// Use spawn with array arguments - NO SHELL INTERPRETATION (US-0187)
|
|
411
|
+
const proc = spawn(parsed.data.executable, parsed.data.fullArgs, {
|
|
412
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
413
|
+
shell: false, // CRITICAL: Prevents shell injection
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
let stdout = '';
|
|
417
|
+
let stderr = '';
|
|
418
|
+
|
|
419
|
+
proc.stdout.on('data', data => {
|
|
420
|
+
stdout += data;
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
proc.stderr.on('data', data => {
|
|
424
|
+
stderr += data;
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
proc.on('error', error => {
|
|
428
|
+
logSafeExec('error', 'Async spawn error', {
|
|
429
|
+
cmd: cmd?.substring(0, 50),
|
|
430
|
+
error: error.message,
|
|
431
|
+
});
|
|
432
|
+
resolve(null);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
proc.on('close', code => {
|
|
436
|
+
if (code !== 0) {
|
|
437
|
+
logSafeExec('debug', 'Async command exited non-zero', {
|
|
312
438
|
cmd: cmd?.substring(0, 50),
|
|
313
|
-
|
|
439
|
+
code,
|
|
440
|
+
stderr: stderr?.substring(0, 100),
|
|
314
441
|
});
|
|
315
442
|
resolve(null);
|
|
316
443
|
} else {
|
|
317
444
|
const result = stdout.trim();
|
|
318
445
|
logSafeExec('debug', 'Async command succeeded', {
|
|
319
446
|
cmd: cmd?.substring(0, 50),
|
|
320
|
-
outputLength: result
|
|
447
|
+
outputLength: result.length,
|
|
321
448
|
});
|
|
322
449
|
resolve(result);
|
|
323
450
|
}
|
|
@@ -676,11 +803,13 @@ module.exports = {
|
|
|
676
803
|
safeLsAsync,
|
|
677
804
|
safeExecAsync,
|
|
678
805
|
|
|
679
|
-
// Command whitelist (US-0120)
|
|
680
|
-
|
|
806
|
+
// Command whitelist (US-0120, US-0187)
|
|
807
|
+
SAFEEXEC_ALLOWED_GIT_SUBCOMMANDS,
|
|
681
808
|
SAFEEXEC_BLOCKED_PATTERNS,
|
|
682
809
|
configureSafeExecLogger,
|
|
683
|
-
|
|
810
|
+
parseGitCommand,
|
|
811
|
+
isGitCommandAllowed,
|
|
812
|
+
isCommandAllowed, // Legacy wrapper for backward compatibility
|
|
684
813
|
|
|
685
814
|
// Context tracking
|
|
686
815
|
getContextPercentage,
|
|
@@ -3,10 +3,22 @@
|
|
|
3
3
|
# AgileFlow PreCompact Hook
|
|
4
4
|
# Outputs critical context that should survive conversation compaction.
|
|
5
5
|
#
|
|
6
|
+
# Supports two modes:
|
|
7
|
+
# 1. Default: Extract COMPACT_SUMMARY sections from active command files
|
|
8
|
+
# 2. Experimental (fullFileInjection): Inject entire command files (more context, may be more reliable)
|
|
9
|
+
#
|
|
6
10
|
|
|
7
11
|
# Get current version from package.json
|
|
8
12
|
VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "unknown")
|
|
9
13
|
|
|
14
|
+
# Check if experimental full-file injection mode is enabled
|
|
15
|
+
FULL_FILE_INJECTION=$(node -p "
|
|
16
|
+
try {
|
|
17
|
+
const meta = require('./docs/00-meta/agileflow-metadata.json');
|
|
18
|
+
meta.features?.experimental?.fullFileInjection === true ? 'true' : 'false';
|
|
19
|
+
} catch { 'false'; }
|
|
20
|
+
" 2>/dev/null || echo "false")
|
|
21
|
+
|
|
10
22
|
# Get current git branch
|
|
11
23
|
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
12
24
|
|
|
@@ -77,25 +89,50 @@ if [ -f "docs/09-agents/session-state.json" ]; then
|
|
|
77
89
|
# Security: Validate COMMAND_FILE contains only safe characters (alphanumeric, /, -, _, .)
|
|
78
90
|
# and doesn't contain path traversal sequences
|
|
79
91
|
if [[ "$COMMAND_FILE" =~ ^[a-zA-Z0-9/_.-]+$ ]] && [[ ! "$COMMAND_FILE" =~ \.\. ]]; then
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.log('##
|
|
92
|
+
if [ "$FULL_FILE_INJECTION" = "true" ]; then
|
|
93
|
+
# EXPERIMENTAL: Inject the entire command file content
|
|
94
|
+
SUMMARY=$(COMMAND_FILE_PATH="$COMMAND_FILE" ACTIVE_CMD="$ACTIVE_COMMAND" node -e "
|
|
95
|
+
const fs = require('fs');
|
|
96
|
+
const filePath = process.env.COMMAND_FILE_PATH;
|
|
97
|
+
const activeCmd = process.env.ACTIVE_CMD;
|
|
98
|
+
// Double-check: only allow paths within expected directories
|
|
99
|
+
const allowedPrefixes = ['packages/cli/src/core/commands/', '.agileflow/commands/', '.claude/commands/agileflow/'];
|
|
100
|
+
if (!allowedPrefixes.some(p => filePath.startsWith(p))) {
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
105
|
+
console.log('## ⚠️ FULL COMMAND FILE (EXPERIMENTAL MODE): /agileflow:' + activeCmd);
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log('The following is the COMPLETE command file. Follow ALL instructions below.');
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log('---');
|
|
94
110
|
console.log('');
|
|
95
|
-
console.log(
|
|
111
|
+
console.log(content);
|
|
112
|
+
} catch (e) {}
|
|
113
|
+
" 2>/dev/null || echo "")
|
|
114
|
+
else
|
|
115
|
+
# Default: Extract only the compact summary section
|
|
116
|
+
SUMMARY=$(COMMAND_FILE_PATH="$COMMAND_FILE" ACTIVE_CMD="$ACTIVE_COMMAND" node -e "
|
|
117
|
+
const fs = require('fs');
|
|
118
|
+
const filePath = process.env.COMMAND_FILE_PATH;
|
|
119
|
+
const activeCmd = process.env.ACTIVE_CMD;
|
|
120
|
+
// Double-check: only allow paths within expected directories
|
|
121
|
+
const allowedPrefixes = ['packages/cli/src/core/commands/', '.agileflow/commands/', '.claude/commands/agileflow/'];
|
|
122
|
+
if (!allowedPrefixes.some(p => filePath.startsWith(p))) {
|
|
123
|
+
process.exit(1);
|
|
96
124
|
}
|
|
97
|
-
|
|
98
|
-
|
|
125
|
+
try {
|
|
126
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
127
|
+
const match = content.match(/<!-- COMPACT_SUMMARY_START[\\s\\S]*?-->([\\s\\S]*?)<!-- COMPACT_SUMMARY_END -->/);
|
|
128
|
+
if (match) {
|
|
129
|
+
console.log('## ACTIVE COMMAND: /agileflow:' + activeCmd);
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(match[1].trim());
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {}
|
|
134
|
+
" 2>/dev/null || echo "")
|
|
135
|
+
fi
|
|
99
136
|
fi
|
|
100
137
|
|
|
101
138
|
if [ ! -z "$SUMMARY" ]; then
|
|
@@ -36,7 +36,7 @@ is_pid_alive() {
|
|
|
36
36
|
cleanup_stale_sessions() {
|
|
37
37
|
local now=$(date +%s)
|
|
38
38
|
|
|
39
|
-
for lockfile in "$SESSIONS_DIR"/session-*.lock
|
|
39
|
+
for lockfile in "$SESSIONS_DIR"/session-*.lock; do
|
|
40
40
|
[[ -f "$lockfile" ]] || continue
|
|
41
41
|
|
|
42
42
|
# Read lock file
|
|
@@ -63,7 +63,7 @@ cleanup_stale_sessions() {
|
|
|
63
63
|
get_active_sessions() {
|
|
64
64
|
local sessions=()
|
|
65
65
|
|
|
66
|
-
for lockfile in "$SESSIONS_DIR"/session-*.lock
|
|
66
|
+
for lockfile in "$SESSIONS_DIR"/session-*.lock; do
|
|
67
67
|
[[ -f "$lockfile" ]] || continue
|
|
68
68
|
|
|
69
69
|
local lock_pid=$(grep "^pid=" "$lockfile" 2>/dev/null | cut -d= -f2)
|