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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +818 -0
- package/lib/colors.js +190 -12
- package/lib/consent.js +232 -0
- package/lib/correlation.js +277 -0
- package/lib/error-codes.js +46 -0
- package/lib/errors.js +48 -6
- package/lib/file-cache.js +182 -0
- package/lib/format-error.js +156 -0
- package/lib/path-resolver.js +155 -7
- package/lib/paths.js +212 -20
- package/lib/placeholder-registry.js +205 -0
- package/lib/registry-di.js +358 -0
- package/lib/result-schema.js +363 -0
- package/lib/result.js +210 -0
- package/lib/session-registry.js +13 -0
- package/lib/session-state-machine.js +465 -0
- package/lib/validate-commands.js +308 -0
- package/lib/validate-names.js +3 -3
- package/lib/validate.js +116 -52
- package/package.json +4 -1
- package/scripts/af +34 -0
- package/scripts/agent-loop.js +63 -9
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +435 -23
- package/scripts/archive-completed-stories.sh +57 -11
- package/scripts/claude-tmux.sh +102 -0
- package/scripts/damage-control-bash.js +3 -70
- package/scripts/damage-control-edit.js +3 -20
- package/scripts/damage-control-write.js +3 -20
- package/scripts/dependency-check.js +310 -0
- package/scripts/get-env.js +11 -4
- package/scripts/lib/configure-detect.js +23 -1
- package/scripts/lib/configure-features.js +43 -2
- package/scripts/lib/context-formatter.js +771 -0
- package/scripts/lib/context-loader.js +699 -0
- package/scripts/lib/damage-control-utils.js +107 -0
- package/scripts/lib/json-utils.sh +162 -0
- package/scripts/lib/state-migrator.js +353 -0
- package/scripts/lib/story-state-machine.js +437 -0
- package/scripts/obtain-context.js +118 -1048
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +36 -11
- package/scripts/query-codebase.js +538 -0
- package/scripts/ralph-loop.js +5 -5
- package/scripts/session-manager.js +220 -42
- package/scripts/spawn-parallel.js +651 -0
- package/scripts/tui/blessed/data/watcher.js +180 -0
- package/scripts/tui/blessed/index.js +244 -0
- package/scripts/tui/blessed/panels/output.js +101 -0
- package/scripts/tui/blessed/panels/sessions.js +150 -0
- package/scripts/tui/blessed/panels/trace.js +97 -0
- package/scripts/tui/blessed/ui/help.js +77 -0
- package/scripts/tui/blessed/ui/screen.js +52 -0
- package/scripts/tui/blessed/ui/statusbar.js +47 -0
- package/scripts/tui/blessed/ui/tabbar.js +99 -0
- package/scripts/tui/index.js +38 -30
- package/scripts/validators/README.md +143 -0
- package/scripts/validators/component-validator.js +239 -0
- package/scripts/validators/json-schema-validator.js +186 -0
- package/scripts/validators/markdown-validator.js +152 -0
- package/scripts/validators/migration-validator.js +129 -0
- package/scripts/validators/security-validator.js +380 -0
- package/scripts/validators/story-format-validator.js +197 -0
- package/scripts/validators/test-result-validator.js +114 -0
- package/scripts/validators/workflow-validator.js +247 -0
- package/src/core/agents/accessibility.md +6 -0
- package/src/core/agents/adr-writer.md +6 -0
- package/src/core/agents/analytics.md +6 -0
- package/src/core/agents/api.md +6 -0
- package/src/core/agents/ci.md +6 -0
- package/src/core/agents/codebase-query.md +261 -0
- package/src/core/agents/compliance.md +6 -0
- package/src/core/agents/configuration-damage-control.md +6 -0
- package/src/core/agents/configuration-visual-e2e.md +6 -0
- package/src/core/agents/database.md +10 -0
- package/src/core/agents/datamigration.md +6 -0
- package/src/core/agents/design.md +6 -0
- package/src/core/agents/devops.md +6 -0
- package/src/core/agents/documentation.md +6 -0
- package/src/core/agents/epic-planner.md +6 -0
- package/src/core/agents/integrations.md +6 -0
- package/src/core/agents/mentor.md +6 -0
- package/src/core/agents/mobile.md +6 -0
- package/src/core/agents/monitoring.md +6 -0
- package/src/core/agents/multi-expert.md +6 -0
- package/src/core/agents/performance.md +6 -0
- package/src/core/agents/product.md +6 -0
- package/src/core/agents/qa.md +6 -0
- package/src/core/agents/readme-updater.md +6 -0
- package/src/core/agents/refactor.md +6 -0
- package/src/core/agents/research.md +6 -0
- package/src/core/agents/security.md +6 -0
- package/src/core/agents/testing.md +10 -0
- package/src/core/agents/ui.md +6 -0
- package/src/core/commands/adr.md +114 -0
- package/src/core/commands/agent.md +120 -0
- package/src/core/commands/assign.md +145 -0
- package/src/core/commands/audit.md +401 -0
- package/src/core/commands/babysit.md +32 -5
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +118 -0
- package/src/core/commands/configure.md +42 -6
- package/src/core/commands/diagnose.md +114 -0
- package/src/core/commands/epic.md +205 -1
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +76 -0
- package/src/core/commands/metrics.md +1 -0
- package/src/core/commands/pr.md +96 -0
- package/src/core/commands/research/analyze.md +1 -0
- package/src/core/commands/research/ask.md +2 -0
- package/src/core/commands/research/import.md +1 -0
- package/src/core/commands/research/list.md +2 -0
- package/src/core/commands/research/synthesize.md +584 -0
- package/src/core/commands/research/view.md +2 -0
- package/src/core/commands/roadmap/analyze.md +400 -0
- package/src/core/commands/session/new.md +113 -6
- package/src/core/commands/session/spawn.md +197 -0
- package/src/core/commands/sprint.md +22 -0
- package/src/core/commands/status.md +200 -1
- package/src/core/commands/story/list.md +9 -9
- package/src/core/commands/story/view.md +1 -0
- package/src/core/commands/story.md +143 -4
- package/src/core/experts/codebase-query/expertise.yaml +190 -0
- package/src/core/experts/codebase-query/question.md +73 -0
- package/src/core/experts/codebase-query/self-improve.md +105 -0
- package/src/core/templates/agileflow-metadata.json +55 -2
- package/src/core/templates/plan-template.md +125 -0
- package/src/core/templates/story-lifecycle.md +213 -0
- package/src/core/templates/story-template.md +4 -0
- package/src/core/templates/tdd-test-template.js +241 -0
- package/tools/cli/commands/setup.js +86 -0
- package/tools/cli/installers/core/installer.js +94 -0
- package/tools/cli/installers/ide/_base-ide.js +20 -11
- package/tools/cli/installers/ide/codex.js +29 -47
- package/tools/cli/lib/config-manager.js +17 -2
- package/tools/cli/lib/content-transformer.js +271 -0
- package/tools/cli/lib/error-handler.js +14 -22
- package/tools/cli/lib/ide-error-factory.js +421 -0
- package/tools/cli/lib/ide-health-monitor.js +364 -0
- package/tools/cli/lib/ide-registry.js +114 -1
- 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
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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 {
|
|
17
|
+
const { yaml } = require('../../../../lib/yaml-utils');
|
|
18
18
|
const { BaseIdeSetup } = require('./_base-ide');
|
|
19
|
-
const {
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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',
|