agileflow 2.91.0 → 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 +5 -0
- package/README.md +3 -3
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +31 -23
- 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.js +116 -52
- package/package.json +1 -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 +80 -1248
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +23 -10
- package/scripts/query-codebase.js +122 -14
- 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 +20 -15
- package/scripts/tui/blessed/index.js +2 -2
- package/scripts/tui/blessed/panels/output.js +14 -8
- package/scripts/tui/blessed/panels/sessions.js +22 -15
- package/scripts/tui/blessed/panels/trace.js +14 -8
- package/scripts/tui/blessed/ui/help.js +3 -3
- package/scripts/tui/blessed/ui/screen.js +4 -4
- package/scripts/tui/blessed/ui/statusbar.js +5 -9
- package/scripts/tui/blessed/ui/tabbar.js +11 -11
- package/scripts/validators/component-validator.js +41 -14
- package/scripts/validators/json-schema-validator.js +11 -4
- package/scripts/validators/markdown-validator.js +1 -2
- package/scripts/validators/migration-validator.js +17 -5
- package/scripts/validators/security-validator.js +137 -33
- package/scripts/validators/story-format-validator.js +31 -10
- package/scripts/validators/test-result-validator.js +19 -4
- package/scripts/validators/workflow-validator.js +12 -5
- package/src/core/agents/codebase-query.md +24 -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/babysit.md +32 -5
- 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 +113 -0
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +75 -0
- package/src/core/commands/pr.md +96 -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 +74 -0
- package/src/core/commands/story.md +143 -4
- 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
|
@@ -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',
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* content-transformer.js - Reusable content transformation utilities
|
|
3
|
+
*
|
|
4
|
+
* Extracts common content transformation patterns from IDE installers:
|
|
5
|
+
* - replaceReferences: Generic string replacement with pattern support
|
|
6
|
+
* - stripFrontmatter: Remove YAML frontmatter from content
|
|
7
|
+
* - convertFrontmatter: Transform frontmatter keys/values between formats
|
|
8
|
+
* - injectContent: Delegate to existing content-injector
|
|
9
|
+
*
|
|
10
|
+
* Created as part of US-0177: Extract content transformation into reusable helper module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { parseFrontmatter, extractBody } = require('../../../scripts/lib/frontmatter-parser');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Replace multiple string patterns in content
|
|
17
|
+
*
|
|
18
|
+
* @param {string} content - The content to transform
|
|
19
|
+
* @param {Object|Array} replacements - Either an object of {pattern: replacement} pairs,
|
|
20
|
+
* or an array of {pattern, replacement, flags} objects
|
|
21
|
+
* @returns {string} Content with all replacements applied
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Object form (simple string replacement)
|
|
25
|
+
* replaceReferences(content, {
|
|
26
|
+
* 'Claude Code': 'Codex CLI',
|
|
27
|
+
* '.claude/': '.codex/',
|
|
28
|
+
* 'CLAUDE.md': 'AGENTS.md'
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Array form (with regex flags)
|
|
33
|
+
* replaceReferences(content, [
|
|
34
|
+
* { pattern: 'Claude Code', replacement: 'Codex CLI', flags: 'gi' },
|
|
35
|
+
* { pattern: /\.claude\//g, replacement: '.codex/' }
|
|
36
|
+
* ]);
|
|
37
|
+
*/
|
|
38
|
+
function replaceReferences(content, replacements) {
|
|
39
|
+
if (!content || typeof content !== 'string') {
|
|
40
|
+
return content || '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let result = content;
|
|
44
|
+
|
|
45
|
+
if (Array.isArray(replacements)) {
|
|
46
|
+
// Array form: [{pattern, replacement, flags?}]
|
|
47
|
+
for (const item of replacements) {
|
|
48
|
+
if (!item || !item.pattern) continue;
|
|
49
|
+
|
|
50
|
+
let regex;
|
|
51
|
+
if (item.pattern instanceof RegExp) {
|
|
52
|
+
regex = item.pattern;
|
|
53
|
+
} else {
|
|
54
|
+
const flags = item.flags || 'g';
|
|
55
|
+
regex = new RegExp(escapeRegex(item.pattern), flags);
|
|
56
|
+
}
|
|
57
|
+
result = result.replace(regex, item.replacement || '');
|
|
58
|
+
}
|
|
59
|
+
} else if (typeof replacements === 'object' && replacements !== null) {
|
|
60
|
+
// Object form: {pattern: replacement}
|
|
61
|
+
for (const [pattern, replacement] of Object.entries(replacements)) {
|
|
62
|
+
result = result.replace(new RegExp(escapeRegex(pattern), 'g'), replacement);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Escape special regex characters in a string
|
|
71
|
+
* @param {string} str - String to escape
|
|
72
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
73
|
+
*/
|
|
74
|
+
function escapeRegex(str) {
|
|
75
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Remove YAML frontmatter from content, returning only the body
|
|
80
|
+
*
|
|
81
|
+
* @param {string} content - Content with optional YAML frontmatter
|
|
82
|
+
* @returns {string} Content body without frontmatter
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const body = stripFrontmatter(`---
|
|
86
|
+
* title: My Document
|
|
87
|
+
* ---
|
|
88
|
+
*
|
|
89
|
+
* # Heading
|
|
90
|
+
* Content here`);
|
|
91
|
+
* // Returns: "# Heading\nContent here"
|
|
92
|
+
*/
|
|
93
|
+
function stripFrontmatter(content) {
|
|
94
|
+
return extractBody(content);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Convert frontmatter between formats using a mapping configuration
|
|
99
|
+
*
|
|
100
|
+
* @param {Object} frontmatter - Parsed frontmatter object
|
|
101
|
+
* @param {Object} config - Conversion configuration
|
|
102
|
+
* @param {Object} [config.keyMap] - Map of source keys to target keys
|
|
103
|
+
* @param {Object} [config.valueMap] - Map of key names to value transformation functions
|
|
104
|
+
* @param {Array} [config.include] - Only include these keys (whitelist)
|
|
105
|
+
* @param {Array} [config.exclude] - Exclude these keys (blacklist)
|
|
106
|
+
* @param {Object} [config.defaults] - Default values to add if not present
|
|
107
|
+
* @returns {Object} Transformed frontmatter object
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const converted = convertFrontmatter(
|
|
111
|
+
* { name: 'security', description: 'Security agent', tools: ['Read', 'Write'] },
|
|
112
|
+
* {
|
|
113
|
+
* keyMap: { name: 'skill_name', tools: 'allowed_tools' },
|
|
114
|
+
* valueMap: { description: (v) => v.replace('agent', 'skill') },
|
|
115
|
+
* exclude: ['internal_only'],
|
|
116
|
+
* defaults: { version: '1.0' }
|
|
117
|
+
* }
|
|
118
|
+
* );
|
|
119
|
+
*/
|
|
120
|
+
function convertFrontmatter(frontmatter, config = {}) {
|
|
121
|
+
if (!frontmatter || typeof frontmatter !== 'object') {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const { keyMap = {}, valueMap = {}, include, exclude = [], defaults = {} } = config;
|
|
126
|
+
|
|
127
|
+
const result = { ...defaults };
|
|
128
|
+
|
|
129
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
130
|
+
// Skip excluded keys
|
|
131
|
+
if (exclude.includes(key)) continue;
|
|
132
|
+
|
|
133
|
+
// Skip if not in include list (when include is specified)
|
|
134
|
+
if (include && !include.includes(key)) continue;
|
|
135
|
+
|
|
136
|
+
// Map key name if mapping exists
|
|
137
|
+
const targetKey = keyMap[key] || key;
|
|
138
|
+
|
|
139
|
+
// Transform value if transformation exists
|
|
140
|
+
const targetValue = valueMap[key] ? valueMap[key](value) : value;
|
|
141
|
+
|
|
142
|
+
result[targetKey] = targetValue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Inject dynamic content into a template using content-injector
|
|
150
|
+
*
|
|
151
|
+
* @param {string} content - Content with placeholders
|
|
152
|
+
* @param {Object} options - Injection options
|
|
153
|
+
* @param {string} options.coreDir - Path to AgileFlow core directory
|
|
154
|
+
* @param {string} [options.agileflowFolder] - Target AgileFlow folder name (default: '.agileflow')
|
|
155
|
+
* @param {string} [options.version] - Version string to inject
|
|
156
|
+
* @returns {string} Content with placeholders replaced
|
|
157
|
+
*/
|
|
158
|
+
function injectContent(content, options) {
|
|
159
|
+
const { injectContent: inject } = require('./content-injector');
|
|
160
|
+
return inject(content, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Parse frontmatter from content
|
|
165
|
+
* Re-exported from frontmatter-parser for convenience
|
|
166
|
+
*
|
|
167
|
+
* @param {string} content - Content with YAML frontmatter
|
|
168
|
+
* @returns {Object} Parsed frontmatter as object
|
|
169
|
+
*/
|
|
170
|
+
function getFrontmatter(content) {
|
|
171
|
+
return parseFrontmatter(content);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Common replacement patterns for IDE conversions
|
|
176
|
+
*/
|
|
177
|
+
const IDE_REPLACEMENTS = {
|
|
178
|
+
/**
|
|
179
|
+
* Claude Code to Codex CLI conversions
|
|
180
|
+
*/
|
|
181
|
+
codex: {
|
|
182
|
+
'Claude Code': 'Codex CLI',
|
|
183
|
+
'claude code': 'Codex CLI',
|
|
184
|
+
CLAUDE_CODE: 'CODEX_CLI',
|
|
185
|
+
'CLAUDE.md': 'AGENTS.md',
|
|
186
|
+
'.claude/': '.codex/',
|
|
187
|
+
'.claude\\': '.codex\\',
|
|
188
|
+
'Task tool': 'skill invocation',
|
|
189
|
+
'Task agent': 'skill invocation',
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Claude Code to Cursor conversions
|
|
194
|
+
*/
|
|
195
|
+
cursor: {
|
|
196
|
+
'Claude Code': 'Cursor',
|
|
197
|
+
'claude code': 'Cursor',
|
|
198
|
+
'.claude/': '.cursor/',
|
|
199
|
+
'.claude\\': '.cursor\\',
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Claude Code to Windsurf conversions
|
|
204
|
+
*/
|
|
205
|
+
windsurf: {
|
|
206
|
+
'Claude Code': 'Windsurf',
|
|
207
|
+
'claude code': 'Windsurf',
|
|
208
|
+
'.claude/': '.windsurf/',
|
|
209
|
+
'.claude\\': '.windsurf\\',
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Create docs folder reference replacements
|
|
215
|
+
*
|
|
216
|
+
* @param {string} targetFolder - Target docs folder name (e.g., 'project-docs')
|
|
217
|
+
* @returns {Object} Replacement patterns for docs references
|
|
218
|
+
*/
|
|
219
|
+
function createDocsReplacements(targetFolder) {
|
|
220
|
+
if (targetFolder === 'docs') {
|
|
221
|
+
return {}; // No changes needed
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
'docs/': `${targetFolder}/`,
|
|
226
|
+
'`docs/': `\`${targetFolder}/`,
|
|
227
|
+
'"docs/': `"${targetFolder}/`,
|
|
228
|
+
"'docs/": `'${targetFolder}/`,
|
|
229
|
+
'(docs/': `(${targetFolder}/`,
|
|
230
|
+
'[docs/': `[${targetFolder}/`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Transform content for a specific IDE target
|
|
236
|
+
*
|
|
237
|
+
* @param {string} content - Source content
|
|
238
|
+
* @param {string} targetIde - Target IDE: 'codex', 'cursor', 'windsurf'
|
|
239
|
+
* @param {Object} [options] - Additional options
|
|
240
|
+
* @param {string} [options.docsFolder] - Custom docs folder name
|
|
241
|
+
* @param {Object} [options.additionalReplacements] - Extra replacements to apply
|
|
242
|
+
* @returns {string} Transformed content
|
|
243
|
+
*/
|
|
244
|
+
function transformForIde(content, targetIde, options = {}) {
|
|
245
|
+
const { docsFolder, additionalReplacements = {} } = options;
|
|
246
|
+
|
|
247
|
+
// Start with IDE-specific replacements
|
|
248
|
+
const replacements = { ...(IDE_REPLACEMENTS[targetIde] || {}) };
|
|
249
|
+
|
|
250
|
+
// Add docs folder replacements if needed
|
|
251
|
+
if (docsFolder && docsFolder !== 'docs') {
|
|
252
|
+
Object.assign(replacements, createDocsReplacements(docsFolder));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Add any additional custom replacements
|
|
256
|
+
Object.assign(replacements, additionalReplacements);
|
|
257
|
+
|
|
258
|
+
return replaceReferences(content, replacements);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = {
|
|
262
|
+
replaceReferences,
|
|
263
|
+
stripFrontmatter,
|
|
264
|
+
convertFrontmatter,
|
|
265
|
+
injectContent,
|
|
266
|
+
getFrontmatter,
|
|
267
|
+
escapeRegex,
|
|
268
|
+
IDE_REPLACEMENTS,
|
|
269
|
+
createDocsReplacements,
|
|
270
|
+
transformForIde,
|
|
271
|
+
};
|
|
@@ -7,9 +7,15 @@
|
|
|
7
7
|
* - CRITICAL: Severe errors (exit 1 + stack trace if DEBUG=1)
|
|
8
8
|
*
|
|
9
9
|
* Error output format: "X <problem> | Action: <what to do> | Run: <command>"
|
|
10
|
+
*
|
|
11
|
+
* Note: Formatting logic is extracted to lib/format-error.js for standalone use.
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
formatError: formatErrorHelper,
|
|
16
|
+
formatWarning: formatWarningHelper,
|
|
17
|
+
formatErrorWithStack,
|
|
18
|
+
} = require('../../../lib/format-error');
|
|
13
19
|
|
|
14
20
|
class ErrorHandler {
|
|
15
21
|
/**
|
|
@@ -30,14 +36,7 @@ class ErrorHandler {
|
|
|
30
36
|
* @returns {string} Formatted error string
|
|
31
37
|
*/
|
|
32
38
|
formatError(message, actionText, commandHint) {
|
|
33
|
-
|
|
34
|
-
if (actionText) {
|
|
35
|
-
output += ` ${c.dim}|${c.reset} ${c.cyan}Action:${c.reset} ${actionText}`;
|
|
36
|
-
}
|
|
37
|
-
if (commandHint) {
|
|
38
|
-
output += ` ${c.dim}|${c.reset} ${c.green}Run:${c.reset} ${c.bold}${commandHint}${c.reset}`;
|
|
39
|
-
}
|
|
40
|
-
return output;
|
|
39
|
+
return formatErrorHelper(message, actionText, commandHint);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/**
|
|
@@ -48,14 +47,7 @@ class ErrorHandler {
|
|
|
48
47
|
* @returns {string} Formatted warning string
|
|
49
48
|
*/
|
|
50
49
|
formatWarning(message, actionText, commandHint) {
|
|
51
|
-
|
|
52
|
-
if (actionText) {
|
|
53
|
-
output += ` ${c.dim}|${c.reset} ${c.cyan}Action:${c.reset} ${actionText}`;
|
|
54
|
-
}
|
|
55
|
-
if (commandHint) {
|
|
56
|
-
output += ` ${c.dim}|${c.reset} ${c.green}Run:${c.reset} ${c.bold}${commandHint}${c.reset}`;
|
|
57
|
-
}
|
|
58
|
-
return output;
|
|
50
|
+
return formatWarningHelper(message, actionText, commandHint);
|
|
59
51
|
}
|
|
60
52
|
|
|
61
53
|
/**
|
|
@@ -94,11 +86,11 @@ class ErrorHandler {
|
|
|
94
86
|
* @param {Error} [error] - Original error object for stack trace
|
|
95
87
|
*/
|
|
96
88
|
critical(message, actionText, commandHint, error) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
const formatted = formatErrorWithStack(message, error, {
|
|
90
|
+
action: actionText,
|
|
91
|
+
command: commandHint,
|
|
92
|
+
});
|
|
93
|
+
console.error(formatted);
|
|
102
94
|
process.exit(1);
|
|
103
95
|
}
|
|
104
96
|
|