agileflow 2.50.0 → 2.55.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/README.md +82 -460
- package/package.json +18 -3
- package/scripts/agileflow-configure.js +134 -63
- package/scripts/agileflow-welcome.js +161 -31
- package/scripts/generators/agent-registry.js +2 -2
- package/scripts/generators/command-registry.js +6 -6
- package/scripts/generators/index.js +2 -6
- package/scripts/generators/inject-babysit.js +9 -2
- package/scripts/generators/inject-help.js +3 -1
- package/scripts/generators/inject-readme.js +7 -3
- package/scripts/generators/skill-registry.js +5 -5
- package/scripts/get-env.js +13 -12
- package/scripts/obtain-context.js +396 -185
- package/scripts/session-coordinator.sh +232 -0
- package/scripts/session-manager.js +512 -0
- package/src/core/agents/orchestrator.md +275 -0
- package/src/core/commands/adr.md +38 -16
- package/src/core/commands/agent.md +39 -22
- package/src/core/commands/assign.md +17 -0
- package/src/core/commands/auto.md +60 -46
- package/src/core/commands/babysit.md +302 -637
- package/src/core/commands/baseline.md +20 -0
- package/src/core/commands/blockers.md +33 -48
- package/src/core/commands/board.md +19 -0
- package/src/core/commands/changelog.md +20 -0
- package/src/core/commands/ci.md +17 -0
- package/src/core/commands/context.md +43 -40
- package/src/core/commands/debt.md +76 -45
- package/src/core/commands/deploy.md +20 -0
- package/src/core/commands/deps.md +40 -46
- package/src/core/commands/diagnose.md +24 -18
- package/src/core/commands/docs.md +18 -0
- package/src/core/commands/epic.md +31 -0
- package/src/core/commands/feedback.md +33 -21
- package/src/core/commands/handoff.md +29 -0
- package/src/core/commands/help.md +16 -7
- package/src/core/commands/impact.md +31 -61
- package/src/core/commands/metrics.md +17 -35
- package/src/core/commands/packages.md +21 -0
- package/src/core/commands/pr.md +15 -0
- package/src/core/commands/readme-sync.md +42 -9
- package/src/core/commands/research.md +58 -11
- package/src/core/commands/retro.md +42 -50
- package/src/core/commands/review.md +22 -27
- package/src/core/commands/session/end.md +53 -297
- package/src/core/commands/session/history.md +38 -257
- package/src/core/commands/session/init.md +44 -446
- package/src/core/commands/session/new.md +152 -0
- package/src/core/commands/session/resume.md +51 -447
- package/src/core/commands/session/status.md +32 -244
- package/src/core/commands/sprint.md +33 -0
- package/src/core/commands/status.md +18 -0
- package/src/core/commands/story-validate.md +32 -0
- package/src/core/commands/story.md +21 -6
- package/src/core/commands/template.md +18 -0
- package/src/core/commands/tests.md +22 -0
- package/src/core/commands/update.md +72 -58
- package/src/core/commands/validate-expertise.md +25 -37
- package/src/core/commands/velocity.md +33 -74
- package/src/core/commands/verify.md +16 -0
- package/src/core/experts/documentation/expertise.yaml +16 -2
- package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
- package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
- package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
- package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
- package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
- package/src/core/skills/writing-skills/SKILL.md +352 -0
- package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
- package/tools/cli/agileflow-cli.js +4 -2
- package/tools/cli/commands/config.js +20 -13
- package/tools/cli/commands/doctor.js +25 -9
- package/tools/cli/commands/list.js +10 -6
- package/tools/cli/commands/setup.js +54 -3
- package/tools/cli/commands/status.js +6 -8
- package/tools/cli/commands/uninstall.js +5 -5
- package/tools/cli/commands/update.js +51 -7
- package/tools/cli/installers/core/installer.js +8 -4
- package/tools/cli/installers/ide/_base-ide.js +3 -1
- package/tools/cli/installers/ide/claude-code.js +3 -7
- package/tools/cli/installers/ide/codex.js +440 -0
- package/tools/cli/installers/ide/manager.js +2 -6
- package/tools/cli/lib/content-injector.js +3 -3
- package/tools/cli/lib/docs-setup.js +3 -2
- package/tools/cli/lib/npm-utils.js +3 -3
- package/tools/cli/lib/ui.js +7 -7
- package/tools/cli/lib/version-checker.js +3 -3
- package/tools/postinstall.js +2 -3
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
* AgileFlow CLI - Update Command
|
|
3
3
|
*
|
|
4
4
|
* Updates an existing AgileFlow installation.
|
|
5
|
+
* Includes self-update capability to always use the latest CLI.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
const chalk = require('chalk');
|
|
8
9
|
const path = require('node:path');
|
|
10
|
+
const { spawnSync } = require('node:child_process');
|
|
9
11
|
const semver = require('semver');
|
|
10
12
|
const { Installer } = require('../installers/core/installer');
|
|
11
13
|
const { IdeManager } = require('../installers/ide/manager');
|
|
12
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
displayLogo,
|
|
16
|
+
displaySection,
|
|
17
|
+
success,
|
|
18
|
+
warning,
|
|
19
|
+
error,
|
|
20
|
+
info,
|
|
21
|
+
confirm,
|
|
22
|
+
} = require('../lib/ui');
|
|
13
23
|
const { createDocsStructure, getDocsFolderName } = require('../lib/docs-setup');
|
|
14
24
|
const { getLatestVersion } = require('../lib/npm-utils');
|
|
15
25
|
|
|
@@ -22,8 +32,10 @@ module.exports = {
|
|
|
22
32
|
options: [
|
|
23
33
|
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
24
34
|
['--force', 'Force reinstall (skip prompts; overwrites local changes)'],
|
|
35
|
+
['--no-self-update', 'Skip automatic CLI self-update check'],
|
|
36
|
+
['--self-updated', 'Internal flag: indicates CLI was already self-updated'],
|
|
25
37
|
],
|
|
26
|
-
action: async
|
|
38
|
+
action: async options => {
|
|
27
39
|
try {
|
|
28
40
|
const directory = path.resolve(options.directory || '.');
|
|
29
41
|
|
|
@@ -60,14 +72,44 @@ module.exports = {
|
|
|
60
72
|
console.log(chalk.bold('Latest (npm):'), npmLatestVersion);
|
|
61
73
|
}
|
|
62
74
|
|
|
63
|
-
//
|
|
64
|
-
|
|
75
|
+
// Self-update: if CLI is outdated and we haven't already self-updated, re-run with latest
|
|
76
|
+
const shouldSelfUpdate = options.selfUpdate !== false && !options.selfUpdated;
|
|
77
|
+
if (npmLatestVersion && semver.lt(localCliVersion, npmLatestVersion) && shouldSelfUpdate) {
|
|
78
|
+
console.log();
|
|
79
|
+
info(`Updating CLI from v${localCliVersion} to v${npmLatestVersion}...`);
|
|
80
|
+
console.log(chalk.dim(' Fetching latest version from npm...\n'));
|
|
81
|
+
|
|
82
|
+
// Build the command with all current options forwarded
|
|
83
|
+
const args = ['agileflow@latest', 'update', '--self-updated'];
|
|
84
|
+
if (options.directory) args.push('-d', options.directory);
|
|
85
|
+
if (options.force) args.push('--force');
|
|
86
|
+
|
|
87
|
+
const result = spawnSync('npx', args, {
|
|
88
|
+
stdio: 'inherit',
|
|
89
|
+
cwd: process.cwd(),
|
|
90
|
+
shell: process.platform === 'win32',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Exit with the same code as the spawned process
|
|
94
|
+
process.exit(result.status ?? 0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If we self-updated, show confirmation
|
|
98
|
+
if (options.selfUpdated) {
|
|
99
|
+
success(`CLI updated to v${localCliVersion}`);
|
|
100
|
+
console.log();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if CLI itself is still outdated (only if self-update was disabled)
|
|
104
|
+
if (npmLatestVersion && semver.lt(localCliVersion, npmLatestVersion) && !shouldSelfUpdate) {
|
|
65
105
|
console.log();
|
|
66
106
|
warning('Your CLI is outdated!');
|
|
67
107
|
console.log(chalk.dim(` To update your installation, run:\n`));
|
|
68
108
|
console.log(chalk.cyan(` npx agileflow@latest update\n`));
|
|
69
109
|
|
|
70
|
-
const useOutdated = options.force
|
|
110
|
+
const useOutdated = options.force
|
|
111
|
+
? true
|
|
112
|
+
: await confirm('Continue with outdated CLI anyway?');
|
|
71
113
|
if (!useOutdated) {
|
|
72
114
|
console.log(chalk.dim('\nUpdate cancelled\n'));
|
|
73
115
|
process.exit(0);
|
|
@@ -137,12 +179,14 @@ module.exports = {
|
|
|
137
179
|
|
|
138
180
|
// Create/update docs structure (idempotent - only creates missing files)
|
|
139
181
|
displaySection('Updating Documentation Structure', `Folder: ${config.docsFolder}/`);
|
|
140
|
-
const docsResult = await createDocsStructure(directory, config.docsFolder, {
|
|
182
|
+
const docsResult = await createDocsStructure(directory, config.docsFolder, {
|
|
183
|
+
updateGitignore: false,
|
|
184
|
+
});
|
|
141
185
|
|
|
142
186
|
if (!docsResult.success) {
|
|
143
187
|
warning('Failed to update docs structure');
|
|
144
188
|
if (docsResult.errors.length > 0) {
|
|
145
|
-
docsResult.errors.forEach(
|
|
189
|
+
docsResult.errors.forEach(err => error(` ${err}`));
|
|
146
190
|
}
|
|
147
191
|
}
|
|
148
192
|
|
|
@@ -161,7 +161,11 @@ class Installer {
|
|
|
161
161
|
|
|
162
162
|
// Create manifest
|
|
163
163
|
spinner.text = 'Creating manifest...';
|
|
164
|
-
await this.createManifest(
|
|
164
|
+
await this.createManifest(
|
|
165
|
+
cfgDir,
|
|
166
|
+
{ ides, userName, agileflowFolder, docsFolder },
|
|
167
|
+
{ force: effectiveForce }
|
|
168
|
+
);
|
|
165
169
|
|
|
166
170
|
// Persist file index (used for safe future updates)
|
|
167
171
|
spinner.text = 'Writing file index...';
|
|
@@ -578,21 +582,21 @@ class Installer {
|
|
|
578
582
|
const agentsDir = path.join(agileflowDir, 'agents');
|
|
579
583
|
if (await fs.pathExists(agentsDir)) {
|
|
580
584
|
const files = await fs.readdir(agentsDir);
|
|
581
|
-
counts.agents = files.filter(
|
|
585
|
+
counts.agents = files.filter(f => f.endsWith('.md')).length;
|
|
582
586
|
}
|
|
583
587
|
|
|
584
588
|
// Count commands
|
|
585
589
|
const commandsDir = path.join(agileflowDir, 'commands');
|
|
586
590
|
if (await fs.pathExists(commandsDir)) {
|
|
587
591
|
const files = await fs.readdir(commandsDir);
|
|
588
|
-
counts.commands = files.filter(
|
|
592
|
+
counts.commands = files.filter(f => f.endsWith('.md')).length;
|
|
589
593
|
}
|
|
590
594
|
|
|
591
595
|
// Count skills
|
|
592
596
|
const skillsDir = path.join(agileflowDir, 'skills');
|
|
593
597
|
if (await fs.pathExists(skillsDir)) {
|
|
594
598
|
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
595
|
-
counts.skills = entries.filter(
|
|
599
|
+
counts.skills = entries.filter(e => e.isDirectory()).length;
|
|
596
600
|
}
|
|
597
601
|
|
|
598
602
|
return counts;
|
|
@@ -96,7 +96,9 @@ class BaseIdeSetup {
|
|
|
96
96
|
const agileflowPath = path.join(projectDir, this.configDir, 'commands', folderName);
|
|
97
97
|
if (await fs.pathExists(agileflowPath)) {
|
|
98
98
|
await fs.remove(agileflowPath);
|
|
99
|
-
console.log(
|
|
99
|
+
console.log(
|
|
100
|
+
chalk.dim(` Removed old ${folderName} configuration from ${this.displayName}`)
|
|
101
|
+
);
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
}
|
|
@@ -114,12 +114,7 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
|
|
114
114
|
// ALSO install agents as spawnable subagents (.claude/agents/agileflow/)
|
|
115
115
|
// This allows Task tool to spawn them with subagent_type: "agileflow-ui"
|
|
116
116
|
const spawnableAgentsDir = path.join(claudeDir, 'agents', 'agileflow');
|
|
117
|
-
await this.installCommandsRecursive(
|
|
118
|
-
agentsSource,
|
|
119
|
-
spawnableAgentsDir,
|
|
120
|
-
agileflowDir,
|
|
121
|
-
false
|
|
122
|
-
);
|
|
117
|
+
await this.installCommandsRecursive(agentsSource, spawnableAgentsDir, agileflowDir, false);
|
|
123
118
|
console.log(chalk.dim(` - Spawnable agents: .claude/agents/agileflow/`));
|
|
124
119
|
|
|
125
120
|
// Install skills (.claude/skills/)
|
|
@@ -140,7 +135,8 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
|
|
140
135
|
}
|
|
141
136
|
|
|
142
137
|
const totalCommands = commandResult.commands + agentResult.commands;
|
|
143
|
-
const totalSubdirs =
|
|
138
|
+
const totalSubdirs =
|
|
139
|
+
commandResult.subdirs + (agentResult.commands > 0 ? 1 : 0) + agentResult.subdirs;
|
|
144
140
|
|
|
145
141
|
console.log(chalk.green(` ✓ ${this.displayName} configured:`));
|
|
146
142
|
console.log(chalk.dim(` - ${totalCommands} commands installed`));
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgileFlow CLI - OpenAI Codex CLI Installer
|
|
3
|
+
*
|
|
4
|
+
* Installs AgileFlow for OpenAI Codex CLI:
|
|
5
|
+
* - Agents become Codex Skills (.codex/skills/agileflow-NAME/)
|
|
6
|
+
* - Commands become Codex Prompts (~/.codex/prompts/agileflow-NAME.md)
|
|
7
|
+
* - AGENTS.md provides project instructions at repo root
|
|
8
|
+
*
|
|
9
|
+
* @see https://developers.openai.com/codex/
|
|
10
|
+
* @see ADR-0002: Codex CLI Integration Strategy
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('node:path');
|
|
14
|
+
const os = require('node:os');
|
|
15
|
+
const fs = require('fs-extra');
|
|
16
|
+
const chalk = require('chalk');
|
|
17
|
+
const yaml = require('js-yaml');
|
|
18
|
+
const { BaseIdeSetup } = require('./_base-ide');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* OpenAI Codex CLI setup handler
|
|
22
|
+
*/
|
|
23
|
+
class CodexSetup extends BaseIdeSetup {
|
|
24
|
+
constructor() {
|
|
25
|
+
super('codex', 'OpenAI Codex CLI', false);
|
|
26
|
+
// Per-repo config directory
|
|
27
|
+
this.configDir = '.codex';
|
|
28
|
+
// User-level Codex home (can be overridden by $CODEX_HOME)
|
|
29
|
+
this.codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the Codex home directory
|
|
34
|
+
* @returns {string} Path to ~/.codex or $CODEX_HOME
|
|
35
|
+
*/
|
|
36
|
+
getCodexHome() {
|
|
37
|
+
return this.codexHome;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Detect if Codex CLI is installed/configured
|
|
42
|
+
* @param {string} projectDir - Project directory
|
|
43
|
+
* @returns {Promise<boolean>}
|
|
44
|
+
*/
|
|
45
|
+
async detect(projectDir) {
|
|
46
|
+
// Check if Codex home exists (user has Codex CLI)
|
|
47
|
+
const codexHomeExists = await this.exists(this.codexHome);
|
|
48
|
+
// Check if project has .codex/ or AGENTS.md
|
|
49
|
+
const projectCodexExists = await this.exists(path.join(projectDir, this.configDir));
|
|
50
|
+
const agentsMdExists = await this.exists(path.join(projectDir, 'AGENTS.md'));
|
|
51
|
+
|
|
52
|
+
return codexHomeExists || projectCodexExists || agentsMdExists;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Convert an AgileFlow agent markdown file to Codex SKILL.md format
|
|
57
|
+
* @param {string} content - Original agent markdown content
|
|
58
|
+
* @param {string} agentName - Agent name (e.g., 'database')
|
|
59
|
+
* @returns {string} Codex SKILL.md content
|
|
60
|
+
*/
|
|
61
|
+
convertAgentToSkill(content, agentName) {
|
|
62
|
+
// Extract frontmatter if present
|
|
63
|
+
let description = `AgileFlow ${agentName} agent`;
|
|
64
|
+
let model = 'default';
|
|
65
|
+
|
|
66
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
67
|
+
if (frontmatterMatch) {
|
|
68
|
+
try {
|
|
69
|
+
const frontmatter = yaml.load(frontmatterMatch[1]);
|
|
70
|
+
if (frontmatter.description) {
|
|
71
|
+
description = frontmatter.description;
|
|
72
|
+
}
|
|
73
|
+
if (frontmatter.model) {
|
|
74
|
+
model = frontmatter.model;
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {
|
|
77
|
+
// Ignore YAML parse errors
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create SKILL.md with YAML frontmatter
|
|
82
|
+
const skillFrontmatter = yaml
|
|
83
|
+
.dump({
|
|
84
|
+
name: `agileflow-${agentName}`,
|
|
85
|
+
description: description,
|
|
86
|
+
version: '1.0.0',
|
|
87
|
+
})
|
|
88
|
+
.trim();
|
|
89
|
+
|
|
90
|
+
// Remove original frontmatter from content
|
|
91
|
+
let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
|
92
|
+
|
|
93
|
+
// Add Codex-specific header
|
|
94
|
+
const codexHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Agent
|
|
95
|
+
|
|
96
|
+
> Invoke with \`$agileflow-${agentName}\` or via \`/skills\`
|
|
97
|
+
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
// Replace Claude-specific references
|
|
101
|
+
bodyContent = bodyContent
|
|
102
|
+
.replace(/Claude Code/gi, 'Codex CLI')
|
|
103
|
+
.replace(/CLAUDE\.md/g, 'AGENTS.md')
|
|
104
|
+
.replace(/\.claude\//g, '.codex/')
|
|
105
|
+
.replace(/Task tool/gi, 'skill invocation');
|
|
106
|
+
|
|
107
|
+
return `---
|
|
108
|
+
${skillFrontmatter}
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
${codexHeader}${bodyContent}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Convert an AgileFlow command markdown file to Codex prompt format
|
|
116
|
+
* @param {string} content - Original command markdown content
|
|
117
|
+
* @param {string} commandName - Command name (e.g., 'board')
|
|
118
|
+
* @returns {string} Codex prompt content
|
|
119
|
+
*/
|
|
120
|
+
convertCommandToPrompt(content, commandName) {
|
|
121
|
+
// Extract description from frontmatter if present
|
|
122
|
+
let description = `AgileFlow ${commandName} command`;
|
|
123
|
+
|
|
124
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
125
|
+
if (frontmatterMatch) {
|
|
126
|
+
try {
|
|
127
|
+
const frontmatter = yaml.load(frontmatterMatch[1]);
|
|
128
|
+
if (frontmatter.description) {
|
|
129
|
+
description = frontmatter.description;
|
|
130
|
+
}
|
|
131
|
+
} catch (e) {
|
|
132
|
+
// Ignore YAML parse errors
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Remove original frontmatter from content
|
|
137
|
+
let bodyContent = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
|
138
|
+
|
|
139
|
+
// Replace Claude-specific references
|
|
140
|
+
bodyContent = bodyContent
|
|
141
|
+
.replace(/Claude Code/gi, 'Codex CLI')
|
|
142
|
+
.replace(/CLAUDE\.md/g, 'AGENTS.md')
|
|
143
|
+
.replace(/\.claude\//g, '.codex/')
|
|
144
|
+
.replace(/\/agileflow:/g, '$agileflow-');
|
|
145
|
+
|
|
146
|
+
// Add Codex prompt header
|
|
147
|
+
const header = `# AgileFlow: ${commandName}
|
|
148
|
+
|
|
149
|
+
> ${description}
|
|
150
|
+
|
|
151
|
+
## Instructions
|
|
152
|
+
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
// Add input placeholder at the end
|
|
156
|
+
const footer = `
|
|
157
|
+
|
|
158
|
+
## Context
|
|
159
|
+
|
|
160
|
+
{{input}}
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
return `${header}${bodyContent}${footer}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Install AgileFlow agents as Codex skills
|
|
168
|
+
* @param {string} projectDir - Project directory
|
|
169
|
+
* @param {string} agileflowDir - AgileFlow installation directory
|
|
170
|
+
* @returns {Promise<number>} Number of skills installed
|
|
171
|
+
*/
|
|
172
|
+
async installSkills(projectDir, agileflowDir) {
|
|
173
|
+
const agentsSource = path.join(agileflowDir, 'agents');
|
|
174
|
+
const skillsTarget = path.join(projectDir, this.configDir, 'skills');
|
|
175
|
+
|
|
176
|
+
if (!(await this.exists(agentsSource))) {
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let skillCount = 0;
|
|
181
|
+
const agents = await this.scanDirectory(agentsSource, '.md');
|
|
182
|
+
|
|
183
|
+
for (const agent of agents) {
|
|
184
|
+
const content = await this.readFile(agent.path);
|
|
185
|
+
const skillContent = this.convertAgentToSkill(content, agent.name);
|
|
186
|
+
|
|
187
|
+
// Create skill directory: .codex/skills/agileflow-{name}/
|
|
188
|
+
const skillDir = path.join(skillsTarget, `agileflow-${agent.name}`);
|
|
189
|
+
await this.ensureDir(skillDir);
|
|
190
|
+
|
|
191
|
+
// Write SKILL.md
|
|
192
|
+
await this.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
|
193
|
+
skillCount++;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return skillCount;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Install AgileFlow commands as Codex prompts (user-level)
|
|
201
|
+
* @param {string} agileflowDir - AgileFlow installation directory
|
|
202
|
+
* @returns {Promise<number>} Number of prompts installed
|
|
203
|
+
*/
|
|
204
|
+
async installPrompts(agileflowDir) {
|
|
205
|
+
const commandsSource = path.join(agileflowDir, 'commands');
|
|
206
|
+
const promptsTarget = path.join(this.codexHome, 'prompts');
|
|
207
|
+
|
|
208
|
+
if (!(await this.exists(commandsSource))) {
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await this.ensureDir(promptsTarget);
|
|
213
|
+
|
|
214
|
+
let promptCount = 0;
|
|
215
|
+
const commands = await this.scanDirectory(commandsSource, '.md');
|
|
216
|
+
|
|
217
|
+
for (const command of commands) {
|
|
218
|
+
const content = await this.readFile(command.path);
|
|
219
|
+
const promptContent = this.convertCommandToPrompt(content, command.name);
|
|
220
|
+
|
|
221
|
+
// Write prompt: ~/.codex/prompts/agileflow-{name}.md
|
|
222
|
+
const promptPath = path.join(promptsTarget, `agileflow-${command.name}.md`);
|
|
223
|
+
await this.writeFile(promptPath, promptContent);
|
|
224
|
+
promptCount++;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return promptCount;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Generate AGENTS.md scaffolding for the project
|
|
232
|
+
* @param {string} projectDir - Project directory
|
|
233
|
+
* @returns {Promise<boolean>} Whether AGENTS.md was created
|
|
234
|
+
*/
|
|
235
|
+
async generateAgentsMd(projectDir) {
|
|
236
|
+
const agentsMdPath = path.join(projectDir, 'AGENTS.md');
|
|
237
|
+
|
|
238
|
+
// Don't overwrite existing AGENTS.md
|
|
239
|
+
if (await this.exists(agentsMdPath)) {
|
|
240
|
+
console.log(chalk.dim(` - AGENTS.md already exists (preserved)`));
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const content = `# AGENTS.md
|
|
245
|
+
|
|
246
|
+
> Project instructions for Codex CLI with AgileFlow integration
|
|
247
|
+
|
|
248
|
+
## Project Commands
|
|
249
|
+
|
|
250
|
+
\`\`\`bash
|
|
251
|
+
# Run tests
|
|
252
|
+
npm test
|
|
253
|
+
|
|
254
|
+
# Lint code
|
|
255
|
+
npm run lint
|
|
256
|
+
|
|
257
|
+
# Build project
|
|
258
|
+
npm run build
|
|
259
|
+
\`\`\`
|
|
260
|
+
|
|
261
|
+
## Conventions
|
|
262
|
+
|
|
263
|
+
- Prefer running tests before claiming success
|
|
264
|
+
- Keep changes small and reviewable
|
|
265
|
+
- Use AgileFlow skills when relevant (see below)
|
|
266
|
+
- Check \`${this.docsFolder}/09-agents/status.json\` for current work status
|
|
267
|
+
|
|
268
|
+
## When to Use AgileFlow Skills
|
|
269
|
+
|
|
270
|
+
Invoke skills with \`$skill-name\` or list available skills with \`/skills\`.
|
|
271
|
+
|
|
272
|
+
| Task Type | Skill to Use |
|
|
273
|
+
|-----------|--------------|
|
|
274
|
+
| Architecture decisions | \`$agileflow-adr-writer\` |
|
|
275
|
+
| Database work | \`$agileflow-database\` |
|
|
276
|
+
| API endpoints | \`$agileflow-api\` |
|
|
277
|
+
| UI components | \`$agileflow-ui\` |
|
|
278
|
+
| Testing | \`$agileflow-testing\` |
|
|
279
|
+
| CI/CD setup | \`$agileflow-ci\` |
|
|
280
|
+
| Security review | \`$agileflow-security\` |
|
|
281
|
+
| Performance | \`$agileflow-performance\` |
|
|
282
|
+
| Documentation | \`$agileflow-documentation\` |
|
|
283
|
+
|
|
284
|
+
## AgileFlow Status
|
|
285
|
+
|
|
286
|
+
- **Stories**: \`${this.docsFolder}/09-agents/status.json\`
|
|
287
|
+
- **Research**: \`${this.docsFolder}/10-research/\`
|
|
288
|
+
- **Decisions**: \`${this.docsFolder}/03-decisions/\`
|
|
289
|
+
- **Epics**: \`${this.docsFolder}/05-epics/\`
|
|
290
|
+
|
|
291
|
+
## Security Recommendations
|
|
292
|
+
|
|
293
|
+
For safe operation, configure Codex with:
|
|
294
|
+
|
|
295
|
+
\`\`\`toml
|
|
296
|
+
# ~/.codex/config.toml
|
|
297
|
+
approval_policy = "on-request"
|
|
298
|
+
sandbox_mode = "workspace-write"
|
|
299
|
+
\`\`\`
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
*Generated by AgileFlow - https://github.com/projectquestorg/AgileFlow*
|
|
304
|
+
`;
|
|
305
|
+
|
|
306
|
+
await this.writeFile(agentsMdPath, content);
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generate AGENTS.override.md template for subdirectories
|
|
312
|
+
* @param {string} targetDir - Directory to create override in
|
|
313
|
+
* @returns {Promise<boolean>} Whether file was created
|
|
314
|
+
*/
|
|
315
|
+
async generateAgentsOverride(targetDir) {
|
|
316
|
+
const overridePath = path.join(targetDir, 'AGENTS.override.md');
|
|
317
|
+
|
|
318
|
+
// Don't overwrite existing override
|
|
319
|
+
if (await this.exists(overridePath)) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const content = `# AGENTS.override.md
|
|
324
|
+
|
|
325
|
+
> Override instructions for this subdirectory
|
|
326
|
+
|
|
327
|
+
## Directory-Specific Context
|
|
328
|
+
|
|
329
|
+
This directory contains: [describe purpose]
|
|
330
|
+
|
|
331
|
+
## Additional Commands
|
|
332
|
+
|
|
333
|
+
\`\`\`bash
|
|
334
|
+
# Directory-specific commands here
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
## Directory Conventions
|
|
338
|
+
|
|
339
|
+
- [List any patterns specific to this directory]
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
*This file overrides AGENTS.md for this subdirectory*
|
|
344
|
+
`;
|
|
345
|
+
|
|
346
|
+
await this.writeFile(overridePath, content);
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Setup Codex CLI configuration
|
|
352
|
+
* @param {string} projectDir - Project directory
|
|
353
|
+
* @param {string} agileflowDir - AgileFlow installation directory
|
|
354
|
+
* @param {Object} options - Setup options
|
|
355
|
+
*/
|
|
356
|
+
async setup(projectDir, agileflowDir, options = {}) {
|
|
357
|
+
console.log(chalk.hex('#e8683a')(` Setting up ${this.displayName}...`));
|
|
358
|
+
|
|
359
|
+
// Clean up old installation first
|
|
360
|
+
await this.cleanup(projectDir);
|
|
361
|
+
|
|
362
|
+
// Ensure .codex directory exists
|
|
363
|
+
await this.ensureDir(path.join(projectDir, this.configDir));
|
|
364
|
+
|
|
365
|
+
// 1. Install agents as skills (per-repo)
|
|
366
|
+
const skillCount = await this.installSkills(projectDir, agileflowDir);
|
|
367
|
+
if (skillCount > 0) {
|
|
368
|
+
console.log(chalk.dim(` - ${skillCount} skills installed to .codex/skills/`));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 2. Install commands as prompts (user-level)
|
|
372
|
+
const promptCount = await this.installPrompts(agileflowDir);
|
|
373
|
+
if (promptCount > 0) {
|
|
374
|
+
console.log(chalk.dim(` - ${promptCount} prompts installed to ~/.codex/prompts/`));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 3. Generate AGENTS.md if it doesn't exist
|
|
378
|
+
const agentsMdCreated = await this.generateAgentsMd(projectDir);
|
|
379
|
+
if (agentsMdCreated) {
|
|
380
|
+
console.log(chalk.dim(` - AGENTS.md created at project root`));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
console.log(chalk.green(` ✓ ${this.displayName} configured`));
|
|
384
|
+
console.log(chalk.dim(` - Skills: .codex/skills/agileflow-*/`));
|
|
385
|
+
console.log(chalk.dim(` - Prompts: ~/.codex/prompts/agileflow-*.md`));
|
|
386
|
+
console.log(chalk.dim(` - Instructions: AGENTS.md`));
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
success: true,
|
|
390
|
+
skills: skillCount,
|
|
391
|
+
prompts: promptCount,
|
|
392
|
+
agentsMd: agentsMdCreated,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Cleanup Codex configuration
|
|
398
|
+
* @param {string} projectDir - Project directory
|
|
399
|
+
* @param {Object} options - Cleanup options
|
|
400
|
+
* @param {boolean} options.removeAgentsMd - Also remove AGENTS.md (requires confirmation)
|
|
401
|
+
*/
|
|
402
|
+
async cleanup(projectDir, options = {}) {
|
|
403
|
+
// Clean up per-repo skills
|
|
404
|
+
const skillsDir = path.join(projectDir, this.configDir, 'skills');
|
|
405
|
+
if (await this.exists(skillsDir)) {
|
|
406
|
+
const entries = await fs.readdir(skillsDir);
|
|
407
|
+
for (const entry of entries) {
|
|
408
|
+
if (entry.startsWith('agileflow-')) {
|
|
409
|
+
await fs.remove(path.join(skillsDir, entry));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
console.log(chalk.dim(` Cleaned up AgileFlow skills from .codex/skills/`));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Clean up user-level prompts
|
|
416
|
+
const promptsDir = path.join(this.codexHome, 'prompts');
|
|
417
|
+
if (await this.exists(promptsDir)) {
|
|
418
|
+
const entries = await fs.readdir(promptsDir);
|
|
419
|
+
for (const entry of entries) {
|
|
420
|
+
if (entry.startsWith('agileflow-')) {
|
|
421
|
+
await fs.remove(path.join(promptsDir, entry));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
console.log(chalk.dim(` Cleaned up AgileFlow prompts from ~/.codex/prompts/`));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Optionally remove AGENTS.md if explicitly requested
|
|
428
|
+
if (options.removeAgentsMd) {
|
|
429
|
+
const agentsMdPath = path.join(projectDir, 'AGENTS.md');
|
|
430
|
+
if (await this.exists(agentsMdPath)) {
|
|
431
|
+
await fs.remove(agentsMdPath);
|
|
432
|
+
console.log(chalk.dim(` Removed AGENTS.md`));
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
console.log(chalk.dim(` Note: AGENTS.md preserved (use removeAgentsMd option to delete)`));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
module.exports = { CodexSetup };
|
|
@@ -52,12 +52,8 @@ class IdeManager {
|
|
|
52
52
|
const ideDir = __dirname;
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
|
-
const files = fs.readdirSync(ideDir).filter(
|
|
56
|
-
return (
|
|
57
|
-
file.endsWith('.js') &&
|
|
58
|
-
!file.startsWith('_') &&
|
|
59
|
-
file !== 'manager.js'
|
|
60
|
-
);
|
|
55
|
+
const files = fs.readdirSync(ideDir).filter(file => {
|
|
56
|
+
return file.endsWith('.js') && !file.startsWith('_') && file !== 'manager.js';
|
|
61
57
|
});
|
|
62
58
|
|
|
63
59
|
files.sort();
|
|
@@ -44,7 +44,7 @@ function generateAgentList(agentsDir) {
|
|
|
44
44
|
name: frontmatter.name || path.basename(file, '.md'),
|
|
45
45
|
description: frontmatter.description || '',
|
|
46
46
|
tools: tools,
|
|
47
|
-
model: frontmatter.model || 'haiku'
|
|
47
|
+
model: frontmatter.model || 'haiku',
|
|
48
48
|
});
|
|
49
49
|
} catch (err) {
|
|
50
50
|
// Silently skip files with parsing errors
|
|
@@ -98,7 +98,7 @@ function generateCommandList(commandsDir) {
|
|
|
98
98
|
commands.push({
|
|
99
99
|
name: cmdName,
|
|
100
100
|
description: frontmatter.description || '',
|
|
101
|
-
argumentHint: frontmatter['argument-hint'] || ''
|
|
101
|
+
argumentHint: frontmatter['argument-hint'] || '',
|
|
102
102
|
});
|
|
103
103
|
} catch (err) {
|
|
104
104
|
// Silently skip files with parsing errors - they might be generated files
|
|
@@ -149,5 +149,5 @@ function injectContent(templateContent, agentsDir, commandsDir) {
|
|
|
149
149
|
module.exports = {
|
|
150
150
|
generateAgentList,
|
|
151
151
|
generateCommandList,
|
|
152
|
-
injectContent
|
|
152
|
+
injectContent,
|
|
153
153
|
};
|
|
@@ -25,7 +25,8 @@ try {
|
|
|
25
25
|
);
|
|
26
26
|
} catch (e) {
|
|
27
27
|
// Fallback if README not found
|
|
28
|
-
packageReadme =
|
|
28
|
+
packageReadme =
|
|
29
|
+
'# AgileFlow\n\nSee https://github.com/projectquestorg/AgileFlow for documentation.';
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -386,7 +387,7 @@ Document your CI/CD workflows and configuration here.
|
|
|
386
387
|
|
|
387
388
|
if (fs.existsSync(gitignorePath)) {
|
|
388
389
|
const existingGitignore = await fs.readFile(gitignorePath, 'utf8');
|
|
389
|
-
const newEntries = gitignoreEntries.filter(
|
|
390
|
+
const newEntries = gitignoreEntries.filter(entry => !existingGitignore.includes(entry));
|
|
390
391
|
if (newEntries.length > 0) {
|
|
391
392
|
await fs.appendFile(gitignorePath, '\n' + newEntries.join('\n') + '\n', 'utf8');
|
|
392
393
|
console.log(chalk.yellow(` ↻ Updated .gitignore with ${newEntries.length} entries`));
|