agileflow 2.90.7 → 2.92.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +818 -0
  5. package/lib/colors.js +190 -12
  6. package/lib/consent.js +232 -0
  7. package/lib/correlation.js +277 -0
  8. package/lib/error-codes.js +46 -0
  9. package/lib/errors.js +48 -6
  10. package/lib/file-cache.js +182 -0
  11. package/lib/format-error.js +156 -0
  12. package/lib/path-resolver.js +155 -7
  13. package/lib/paths.js +212 -20
  14. package/lib/placeholder-registry.js +205 -0
  15. package/lib/registry-di.js +358 -0
  16. package/lib/result-schema.js +363 -0
  17. package/lib/result.js +210 -0
  18. package/lib/session-registry.js +13 -0
  19. package/lib/session-state-machine.js +465 -0
  20. package/lib/validate-commands.js +308 -0
  21. package/lib/validate-names.js +3 -3
  22. package/lib/validate.js +116 -52
  23. package/package.json +4 -1
  24. package/scripts/af +34 -0
  25. package/scripts/agent-loop.js +63 -9
  26. package/scripts/agileflow-configure.js +2 -2
  27. package/scripts/agileflow-welcome.js +435 -23
  28. package/scripts/archive-completed-stories.sh +57 -11
  29. package/scripts/claude-tmux.sh +102 -0
  30. package/scripts/damage-control-bash.js +3 -70
  31. package/scripts/damage-control-edit.js +3 -20
  32. package/scripts/damage-control-write.js +3 -20
  33. package/scripts/dependency-check.js +310 -0
  34. package/scripts/get-env.js +11 -4
  35. package/scripts/lib/configure-detect.js +23 -1
  36. package/scripts/lib/configure-features.js +43 -2
  37. package/scripts/lib/context-formatter.js +771 -0
  38. package/scripts/lib/context-loader.js +699 -0
  39. package/scripts/lib/damage-control-utils.js +107 -0
  40. package/scripts/lib/json-utils.sh +162 -0
  41. package/scripts/lib/state-migrator.js +353 -0
  42. package/scripts/lib/story-state-machine.js +437 -0
  43. package/scripts/obtain-context.js +118 -1048
  44. package/scripts/pre-push-check.sh +46 -0
  45. package/scripts/precompact-context.sh +36 -11
  46. package/scripts/query-codebase.js +538 -0
  47. package/scripts/ralph-loop.js +5 -5
  48. package/scripts/session-manager.js +220 -42
  49. package/scripts/spawn-parallel.js +651 -0
  50. package/scripts/tui/blessed/data/watcher.js +180 -0
  51. package/scripts/tui/blessed/index.js +244 -0
  52. package/scripts/tui/blessed/panels/output.js +101 -0
  53. package/scripts/tui/blessed/panels/sessions.js +150 -0
  54. package/scripts/tui/blessed/panels/trace.js +97 -0
  55. package/scripts/tui/blessed/ui/help.js +77 -0
  56. package/scripts/tui/blessed/ui/screen.js +52 -0
  57. package/scripts/tui/blessed/ui/statusbar.js +47 -0
  58. package/scripts/tui/blessed/ui/tabbar.js +99 -0
  59. package/scripts/tui/index.js +38 -30
  60. package/scripts/validators/README.md +143 -0
  61. package/scripts/validators/component-validator.js +239 -0
  62. package/scripts/validators/json-schema-validator.js +186 -0
  63. package/scripts/validators/markdown-validator.js +152 -0
  64. package/scripts/validators/migration-validator.js +129 -0
  65. package/scripts/validators/security-validator.js +380 -0
  66. package/scripts/validators/story-format-validator.js +197 -0
  67. package/scripts/validators/test-result-validator.js +114 -0
  68. package/scripts/validators/workflow-validator.js +247 -0
  69. package/src/core/agents/accessibility.md +6 -0
  70. package/src/core/agents/adr-writer.md +6 -0
  71. package/src/core/agents/analytics.md +6 -0
  72. package/src/core/agents/api.md +6 -0
  73. package/src/core/agents/ci.md +6 -0
  74. package/src/core/agents/codebase-query.md +261 -0
  75. package/src/core/agents/compliance.md +6 -0
  76. package/src/core/agents/configuration-damage-control.md +6 -0
  77. package/src/core/agents/configuration-visual-e2e.md +6 -0
  78. package/src/core/agents/database.md +10 -0
  79. package/src/core/agents/datamigration.md +6 -0
  80. package/src/core/agents/design.md +6 -0
  81. package/src/core/agents/devops.md +6 -0
  82. package/src/core/agents/documentation.md +6 -0
  83. package/src/core/agents/epic-planner.md +6 -0
  84. package/src/core/agents/integrations.md +6 -0
  85. package/src/core/agents/mentor.md +6 -0
  86. package/src/core/agents/mobile.md +6 -0
  87. package/src/core/agents/monitoring.md +6 -0
  88. package/src/core/agents/multi-expert.md +6 -0
  89. package/src/core/agents/performance.md +6 -0
  90. package/src/core/agents/product.md +6 -0
  91. package/src/core/agents/qa.md +6 -0
  92. package/src/core/agents/readme-updater.md +6 -0
  93. package/src/core/agents/refactor.md +6 -0
  94. package/src/core/agents/research.md +6 -0
  95. package/src/core/agents/security.md +6 -0
  96. package/src/core/agents/testing.md +10 -0
  97. package/src/core/agents/ui.md +6 -0
  98. package/src/core/commands/adr.md +114 -0
  99. package/src/core/commands/agent.md +120 -0
  100. package/src/core/commands/assign.md +145 -0
  101. package/src/core/commands/audit.md +401 -0
  102. package/src/core/commands/babysit.md +32 -5
  103. package/src/core/commands/board.md +1 -0
  104. package/src/core/commands/changelog.md +118 -0
  105. package/src/core/commands/configure.md +42 -6
  106. package/src/core/commands/diagnose.md +114 -0
  107. package/src/core/commands/epic.md +205 -1
  108. package/src/core/commands/handoff.md +128 -0
  109. package/src/core/commands/help.md +76 -0
  110. package/src/core/commands/metrics.md +1 -0
  111. package/src/core/commands/pr.md +96 -0
  112. package/src/core/commands/research/analyze.md +1 -0
  113. package/src/core/commands/research/ask.md +2 -0
  114. package/src/core/commands/research/import.md +1 -0
  115. package/src/core/commands/research/list.md +2 -0
  116. package/src/core/commands/research/synthesize.md +584 -0
  117. package/src/core/commands/research/view.md +2 -0
  118. package/src/core/commands/roadmap/analyze.md +400 -0
  119. package/src/core/commands/session/new.md +113 -6
  120. package/src/core/commands/session/spawn.md +197 -0
  121. package/src/core/commands/sprint.md +22 -0
  122. package/src/core/commands/status.md +200 -1
  123. package/src/core/commands/story/list.md +9 -9
  124. package/src/core/commands/story/view.md +1 -0
  125. package/src/core/commands/story.md +143 -4
  126. package/src/core/experts/codebase-query/expertise.yaml +190 -0
  127. package/src/core/experts/codebase-query/question.md +73 -0
  128. package/src/core/experts/codebase-query/self-improve.md +105 -0
  129. package/src/core/templates/agileflow-metadata.json +55 -2
  130. package/src/core/templates/plan-template.md +125 -0
  131. package/src/core/templates/story-lifecycle.md +213 -0
  132. package/src/core/templates/story-template.md +4 -0
  133. package/src/core/templates/tdd-test-template.js +241 -0
  134. package/tools/cli/commands/setup.js +86 -0
  135. package/tools/cli/installers/core/installer.js +94 -0
  136. package/tools/cli/installers/ide/_base-ide.js +20 -11
  137. package/tools/cli/installers/ide/codex.js +29 -47
  138. package/tools/cli/lib/config-manager.js +17 -2
  139. package/tools/cli/lib/content-transformer.js +271 -0
  140. package/tools/cli/lib/error-handler.js +14 -22
  141. package/tools/cli/lib/ide-error-factory.js +421 -0
  142. package/tools/cli/lib/ide-health-monitor.js +364 -0
  143. package/tools/cli/lib/ide-registry.js +114 -1
  144. package/tools/cli/lib/ui.js +14 -25
@@ -7,6 +7,7 @@
7
7
 
8
8
  const chalk = require('chalk');
9
9
  const path = require('node:path');
10
+ const fs = require('node:fs');
10
11
  const { spawnSync } = require('node:child_process');
11
12
  const semver = require('semver');
12
13
  const { Installer } = require('../installers/core/installer');
@@ -90,6 +91,7 @@ module.exports = {
90
91
  agileflowFolder: '.agileflow',
91
92
  docsFolder: 'docs',
92
93
  updateGitignore: true,
94
+ claudeMdReinforcement: true,
93
95
  };
94
96
  } else {
95
97
  // Interactive prompts
@@ -110,6 +112,16 @@ module.exports = {
110
112
  success(`Installed ${coreResult.counts.commands} commands`);
111
113
  success(`Installed ${coreResult.counts.skills} skills`);
112
114
 
115
+ // Report shell alias setup
116
+ if (coreResult.shellAliases) {
117
+ if (coreResult.shellAliases.configured.length > 0) {
118
+ success(`Added 'af' alias to: ${coreResult.shellAliases.configured.join(', ')}`);
119
+ }
120
+ if (coreResult.shellAliases.skipped.length > 0) {
121
+ info(`Shell aliases skipped: ${coreResult.shellAliases.skipped.join(', ')}`);
122
+ }
123
+ }
124
+
113
125
  // Setup IDE configurations
114
126
  displaySection('Configuring IDEs');
115
127
 
@@ -133,6 +145,73 @@ module.exports = {
133
145
  }
134
146
  }
135
147
 
148
+ // CLAUDE.md reinforcement for /babysit AskUserQuestion rules
149
+ if (config.claudeMdReinforcement) {
150
+ const claudeMdPath = path.join(config.directory, 'CLAUDE.md');
151
+ const claudeMdMarker = '<!-- AGILEFLOW_BABYSIT_RULES -->';
152
+ const claudeMdContent = `
153
+
154
+ ${claudeMdMarker}
155
+ ## AgileFlow /babysit Context Preservation Rules
156
+
157
+ When \`/agileflow:babysit\` is active (check session-state.json), these rules are MANDATORY:
158
+
159
+ 1. **ALWAYS end responses with the AskUserQuestion tool** - Not text like "What next?" but the ACTUAL TOOL CALL
160
+ 2. **Use Plan Mode for non-trivial tasks** - Call \`EnterPlanMode\` before complex implementations
161
+ 3. **Delegate complex work to domain experts** - Use \`Task\` tool with appropriate \`subagent_type\`
162
+ 4. **Track progress with TodoWrite** - For any task with 3+ steps
163
+
164
+ These rules persist across conversation compaction. Check \`docs/09-agents/session-state.json\` for active commands.
165
+ ${claudeMdMarker}
166
+ `;
167
+
168
+ try {
169
+ let existingContent = '';
170
+ if (fs.existsSync(claudeMdPath)) {
171
+ existingContent = fs.readFileSync(claudeMdPath, 'utf8');
172
+ }
173
+
174
+ // Only append if marker doesn't exist
175
+ if (!existingContent.includes(claudeMdMarker)) {
176
+ fs.appendFileSync(claudeMdPath, claudeMdContent);
177
+ success('Added /babysit rules to CLAUDE.md');
178
+ } else {
179
+ info('CLAUDE.md already has /babysit rules');
180
+ }
181
+ } catch (err) {
182
+ warning(`Could not update CLAUDE.md: ${err.message}`);
183
+ }
184
+ }
185
+
186
+ // Update metadata with config tracking
187
+ try {
188
+ const metadataPath = path.join(config.directory, config.docsFolder, '00-meta', 'agileflow-metadata.json');
189
+ if (fs.existsSync(metadataPath)) {
190
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
191
+ const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
192
+
193
+ // Track config schema version and profile
194
+ metadata.config_schema_version = packageJson.version;
195
+ metadata.active_profile = options.yes ? 'default' : null; // null = custom
196
+
197
+ // Track config options that were configured
198
+ if (!metadata.agileflow) metadata.agileflow = {};
199
+ if (!metadata.agileflow.config_options) metadata.agileflow.config_options = {};
200
+
201
+ metadata.agileflow.config_options.claudeMdReinforcement = {
202
+ available_since: '2.92.0',
203
+ configured: true,
204
+ enabled: config.claudeMdReinforcement,
205
+ configured_at: new Date().toISOString(),
206
+ description: 'Add /babysit AskUserQuestion rules to CLAUDE.md',
207
+ };
208
+
209
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + '\n');
210
+ }
211
+ } catch (err) {
212
+ // Silently fail - metadata tracking is non-critical
213
+ }
214
+
136
215
  // Final summary
137
216
  console.log(chalk.green('\n✨ Setup complete!\n'));
138
217
 
@@ -141,6 +220,13 @@ module.exports = {
141
220
  info(`Run 'npx agileflow status' to check setup`);
142
221
  info(`Run 'npx agileflow update' to get updates`);
143
222
 
223
+ // Shell alias reload reminder
224
+ if (coreResult.shellAliases?.configured?.length > 0) {
225
+ console.log(chalk.bold('\nShell aliases:'));
226
+ info(`Reload shell to use: ${chalk.cyan('source ~/.bashrc')} or ${chalk.cyan('source ~/.zshrc')}`);
227
+ info(`Then run ${chalk.cyan('af')} to start Claude in tmux (or ${chalk.cyan('claude')} for normal)`);
228
+ }
229
+
144
230
  console.log(chalk.dim(`\nInstalled to: ${coreResult.path}\n`));
145
231
 
146
232
  process.exit(0);
@@ -164,6 +164,10 @@ class Installer {
164
164
  spinner.text = 'Installing changelog...';
165
165
  await this.installChangelog(agileflowDir, { force: effectiveForce });
166
166
 
167
+ // Set up shell aliases for claude command
168
+ spinner.text = 'Setting up shell aliases...';
169
+ const aliasResult = await this.setupShellAliases(directory, { force: effectiveForce });
170
+
167
171
  // Create config.yaml
168
172
  spinner.text = 'Creating configuration...';
169
173
  await this.createConfig(agileflowDir, userName, agileflowFolder, { force: effectiveForce });
@@ -191,6 +195,7 @@ class Installer {
191
195
  projectDir: directory,
192
196
  counts,
193
197
  fileOps,
198
+ shellAliases: aliasResult,
194
199
  };
195
200
  } catch (error) {
196
201
  spinner.fail('Installation failed');
@@ -813,6 +818,95 @@ class Installer {
813
818
  }
814
819
  }
815
820
 
821
+ /**
822
+ * Set up shell aliases for the claude command
823
+ * Adds aliases to ~/.bashrc and/or ~/.zshrc so 'claude' auto-spawns tmux
824
+ * @param {string} directory - Project directory (used for relative path in alias)
825
+ * @param {Object} options - Setup options
826
+ * @param {boolean} options.force - Overwrite existing aliases
827
+ * @returns {Promise<Object>} Result with shells configured
828
+ */
829
+ async setupShellAliases(directory, options = {}) {
830
+ const os = require('os');
831
+ const result = {
832
+ configured: [],
833
+ skipped: [],
834
+ error: null,
835
+ };
836
+
837
+ // Only set up aliases on Unix-like systems
838
+ if (process.platform === 'win32') {
839
+ result.skipped.push('Windows (not supported)');
840
+ return result;
841
+ }
842
+
843
+ const homeDir = os.homedir();
844
+ const aliasBlock = `
845
+ # AgileFlow tmux wrapper
846
+ # Use 'af' or 'agileflow' for tmux, 'claude' stays normal
847
+ alias af="bash .agileflow/scripts/af"
848
+ alias agileflow="bash .agileflow/scripts/af"
849
+ `;
850
+
851
+ const marker = '# AgileFlow tmux wrapper';
852
+ const rcFiles = [
853
+ { name: 'bash', path: path.join(homeDir, '.bashrc') },
854
+ { name: 'zsh', path: path.join(homeDir, '.zshrc') },
855
+ ];
856
+
857
+ for (const rc of rcFiles) {
858
+ try {
859
+ // Check if RC file exists
860
+ if (!(await fs.pathExists(rc.path))) {
861
+ result.skipped.push(`${rc.name} (no ${path.basename(rc.path)})`);
862
+ continue;
863
+ }
864
+
865
+ const content = await fs.readFile(rc.path, 'utf8');
866
+
867
+ // Check if aliases already exist
868
+ if (content.includes(marker)) {
869
+ if (options.force) {
870
+ // Remove existing block and re-add
871
+ const lines = content.split('\n');
872
+ const filteredLines = [];
873
+ let inBlock = false;
874
+
875
+ for (const line of lines) {
876
+ if (line.includes(marker)) {
877
+ inBlock = true;
878
+ continue;
879
+ }
880
+ if (inBlock && line.startsWith('alias ')) {
881
+ continue;
882
+ }
883
+ if (inBlock && line.trim() === '') {
884
+ inBlock = false;
885
+ continue;
886
+ }
887
+ inBlock = false;
888
+ filteredLines.push(line);
889
+ }
890
+
891
+ await fs.writeFile(rc.path, filteredLines.join('\n') + aliasBlock, 'utf8');
892
+ result.configured.push(rc.name);
893
+ } else {
894
+ result.skipped.push(`${rc.name} (already configured)`);
895
+ }
896
+ continue;
897
+ }
898
+
899
+ // Append aliases to RC file
900
+ await fs.appendFile(rc.path, aliasBlock);
901
+ result.configured.push(rc.name);
902
+ } catch (err) {
903
+ result.skipped.push(`${rc.name} (error: ${err.message})`);
904
+ }
905
+ }
906
+
907
+ return result;
908
+ }
909
+
816
910
  /**
817
911
  * Get installation status
818
912
  * @param {string} directory - Project directory
@@ -15,6 +15,11 @@ const {
15
15
  ContentInjectionError,
16
16
  withPermissionHandling,
17
17
  } = require('../../lib/ide-errors');
18
+ const {
19
+ replaceReferences,
20
+ createDocsReplacements,
21
+ injectContent: injectDynamicContentHelper,
22
+ } = require('../../lib/content-transformer');
18
23
 
19
24
  /**
20
25
  * Base class for IDE-specific setup
@@ -48,6 +53,7 @@ class BaseIdeSetup {
48
53
 
49
54
  /**
50
55
  * Replace docs/ references in content with custom folder name
56
+ * Uses content-transformer module for consistent replacements
51
57
  * @param {string} content - File content
52
58
  * @returns {string} Updated content
53
59
  */
@@ -56,28 +62,31 @@ class BaseIdeSetup {
56
62
  return content; // No replacement needed
57
63
  }
58
64
 
59
- // Replace all variations of docs/ references
60
- return content
61
- .replace(/docs\//g, `${this.docsFolder}/`)
62
- .replace(/`docs\//g, `\`${this.docsFolder}/`)
63
- .replace(/"docs\//g, `"${this.docsFolder}/`)
64
- .replace(/'docs\//g, `'${this.docsFolder}/`)
65
- .replace(/\(docs\//g, `(${this.docsFolder}/`)
66
- .replace(/docs\/\)/g, `${this.docsFolder}/)`)
67
- .replace(/\bdocs\b(?!\.)/g, this.docsFolder); // Replace standalone "docs" word
65
+ // Use content-transformer for standard replacements
66
+ let result = replaceReferences(content, createDocsReplacements(this.docsFolder));
67
+
68
+ // Additional patterns not covered by standard replacements
69
+ result = replaceReferences(result, {
70
+ 'docs/)': `${this.docsFolder}/)`,
71
+ });
72
+
73
+ // Replace standalone "docs" word (not followed by .)
74
+ result = result.replace(/\bdocs\b(?!\.)/g, this.docsFolder);
75
+
76
+ return result;
68
77
  }
69
78
 
70
79
  /**
71
80
  * Inject dynamic content into template (agent lists, command lists)
81
+ * Uses content-transformer module for consistent injection
72
82
  * @param {string} content - Template file content
73
83
  * @param {string} agileflowDir - AgileFlow installation directory
74
84
  * @returns {string} Content with placeholders replaced
75
85
  */
76
86
  injectDynamicContent(content, agileflowDir) {
77
- const { injectContent } = require('../../lib/content-injector');
78
87
  // agileflowDir is the user's .agileflow installation directory
79
88
  // which has agents/, commands/, skills/ at the root level
80
- return injectContent(content, {
89
+ return injectDynamicContentHelper(content, {
81
90
  coreDir: agileflowDir,
82
91
  agileflowFolder: this.agileflowFolder,
83
92
  version: this.getVersion(),
@@ -14,9 +14,14 @@ const path = require('node:path');
14
14
  const os = require('node:os');
15
15
  const fs = require('fs-extra');
16
16
  const chalk = require('chalk');
17
- const { safeLoad, yaml } = require('../../../../lib/yaml-utils');
17
+ const { yaml } = require('../../../../lib/yaml-utils');
18
18
  const { BaseIdeSetup } = require('./_base-ide');
19
- const { parseFrontmatter } = require('../../../../scripts/lib/frontmatter-parser');
19
+ const {
20
+ getFrontmatter,
21
+ stripFrontmatter,
22
+ replaceReferences,
23
+ IDE_REPLACEMENTS,
24
+ } = require('../../lib/content-transformer');
20
25
 
21
26
  /**
22
27
  * OpenAI Codex CLI setup handler
@@ -55,24 +60,15 @@ class CodexSetup extends BaseIdeSetup {
55
60
 
56
61
  /**
57
62
  * Convert an AgileFlow agent markdown file to Codex SKILL.md format
63
+ * Uses content-transformer module for consistent transformations
58
64
  * @param {string} content - Original agent markdown content
59
65
  * @param {string} agentName - Agent name (e.g., 'database')
60
66
  * @returns {string} Codex SKILL.md content
61
67
  */
62
68
  convertAgentToSkill(content, agentName) {
63
- // Extract frontmatter using shared parser
64
- let description = `AgileFlow ${agentName} agent`;
65
- let model = 'default';
66
-
67
- const frontmatter = parseFrontmatter(content);
68
- if (frontmatter && Object.keys(frontmatter).length > 0) {
69
- if (frontmatter.description) {
70
- description = frontmatter.description;
71
- }
72
- if (frontmatter.model) {
73
- model = frontmatter.model;
74
- }
75
- }
69
+ // Extract frontmatter using content-transformer
70
+ const frontmatter = getFrontmatter(content);
71
+ const description = frontmatter.description || `AgileFlow ${agentName} agent`;
76
72
 
77
73
  // Create SKILL.md with YAML frontmatter
78
74
  const skillFrontmatter = yaml
@@ -83,8 +79,8 @@ class CodexSetup extends BaseIdeSetup {
83
79
  })
84
80
  .trim();
85
81
 
86
- // Remove original frontmatter from content
87
- let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
82
+ // Remove original frontmatter from content using content-transformer
83
+ let bodyContent = stripFrontmatter(content);
88
84
 
89
85
  // Add Codex-specific header
90
86
  const codexHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Agent
@@ -93,12 +89,8 @@ class CodexSetup extends BaseIdeSetup {
93
89
 
94
90
  `;
95
91
 
96
- // Replace Claude-specific references
97
- bodyContent = bodyContent
98
- .replace(/Claude Code/gi, 'Codex CLI')
99
- .replace(/CLAUDE\.md/g, 'AGENTS.md')
100
- .replace(/\.claude\//g, '.codex/')
101
- .replace(/Task tool/gi, 'skill invocation');
92
+ // Replace Claude-specific references using content-transformer
93
+ bodyContent = replaceReferences(bodyContent, IDE_REPLACEMENTS.codex);
102
94
 
103
95
  return `---
104
96
  ${skillFrontmatter}
@@ -109,35 +101,25 @@ ${codexHeader}${bodyContent}`;
109
101
 
110
102
  /**
111
103
  * Convert an AgileFlow command markdown file to Codex prompt format
104
+ * Uses content-transformer module for consistent transformations
112
105
  * @param {string} content - Original command markdown content
113
106
  * @param {string} commandName - Command name (e.g., 'board')
114
107
  * @returns {string} Codex prompt content
115
108
  */
116
109
  convertCommandToPrompt(content, commandName) {
117
- // Extract description from frontmatter if present
118
- let description = `AgileFlow ${commandName} command`;
119
-
120
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
121
- if (frontmatterMatch) {
122
- try {
123
- const frontmatter = safeLoad(frontmatterMatch[1]);
124
- if (frontmatter.description) {
125
- description = frontmatter.description;
126
- }
127
- } catch (e) {
128
- // Ignore YAML parse errors
129
- }
130
- }
131
-
132
- // Remove original frontmatter from content
133
- let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
134
-
135
- // Replace Claude-specific references
136
- bodyContent = bodyContent
137
- .replace(/Claude Code/gi, 'Codex CLI')
138
- .replace(/CLAUDE\.md/g, 'AGENTS.md')
139
- .replace(/\.claude\//g, '.codex/')
140
- .replace(/\/agileflow:/g, '$agileflow-');
110
+ // Extract description from frontmatter using content-transformer
111
+ const frontmatter = getFrontmatter(content);
112
+ const description = frontmatter.description || `AgileFlow ${commandName} command`;
113
+
114
+ // Remove original frontmatter from content using content-transformer
115
+ let bodyContent = stripFrontmatter(content);
116
+
117
+ // Replace Claude-specific references using content-transformer
118
+ // Use codex replacements plus command-specific pattern
119
+ bodyContent = replaceReferences(bodyContent, {
120
+ ...IDE_REPLACEMENTS.codex,
121
+ '/agileflow:': '$agileflow-',
122
+ });
141
123
 
142
124
  // Add Codex prompt header
143
125
  const header = `# AgileFlow: ${commandName}
@@ -13,6 +13,7 @@
13
13
  const path = require('path');
14
14
  const fs = require('fs-extra');
15
15
  const { safeLoad, safeDump } = require('../../../lib/yaml-utils');
16
+ const { hasUnsafePathPatterns } = require('../../../lib/validate-paths');
16
17
 
17
18
  /**
18
19
  * Configuration schema definition
@@ -50,13 +51,27 @@ const CONFIG_SCHEMA = {
50
51
  type: 'string',
51
52
  default: '.agileflow',
52
53
  required: true,
53
- validate: v => typeof v === 'string' && v.length > 0 && !v.includes('..'),
54
+ // Security: Use proper path validation instead of simple string check
55
+ validate: v => {
56
+ if (typeof v !== 'string' || v.length === 0) return false;
57
+ // Must be a relative path without unsafe patterns
58
+ if (path.isAbsolute(v)) return false;
59
+ const check = hasUnsafePathPatterns(v);
60
+ return check.safe;
61
+ },
54
62
  },
55
63
  docsFolder: {
56
64
  type: 'string',
57
65
  default: 'docs',
58
66
  required: true,
59
- validate: v => typeof v === 'string' && v.length > 0 && !v.includes('..'),
67
+ // Security: Use proper path validation instead of simple string check
68
+ validate: v => {
69
+ if (typeof v !== 'string' || v.length === 0) return false;
70
+ // Must be a relative path without unsafe patterns
71
+ if (path.isAbsolute(v)) return false;
72
+ const check = hasUnsafePathPatterns(v);
73
+ return check.safe;
74
+ },
60
75
  },
61
76
  installedAt: {
62
77
  type: 'string',