devflow-agent-cli 0.1.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/LICENSE +21 -0
- package/README.md +313 -0
- package/bin/devflow.js +2 -0
- package/dist/cli.js +52 -0
- package/dist/commands/add.js +137 -0
- package/dist/commands/doctor.js +246 -0
- package/dist/commands/explain.js +204 -0
- package/dist/commands/init.js +91 -0
- package/dist/constants.js +120 -0
- package/dist/install.js +176 -0
- package/dist/plugins.js +184 -0
- package/dist/types.js +2 -0
- package/package.json +51 -0
- package/templates/adapters/claude/.claude/commands/build.md +15 -0
- package/templates/adapters/claude/.claude/commands/commit.md +74 -0
- package/templates/adapters/claude/.claude/commands/plan.md +15 -0
- package/templates/adapters/claude/.claude/commands/review.md +16 -0
- package/templates/adapters/claude/.claude/commands/tests.md +14 -0
- package/templates/adapters/claude/.claude/commands/verify.md +16 -0
- package/templates/adapters/codex/README.md +14 -0
- package/templates/adapters/cursor/.cursor/commands/build.md +15 -0
- package/templates/adapters/cursor/.cursor/commands/plan.md +15 -0
- package/templates/adapters/cursor/.cursor/commands/review.md +16 -0
- package/templates/adapters/cursor/.cursor/commands/tests.md +14 -0
- package/templates/adapters/cursor/.cursor/commands/verify.md +16 -0
- package/templates/adapters/cursor/.cursor/rules/typescript.md +15 -0
- package/templates/adapters/gemini/README.md +16 -0
- package/templates/adapters/generic/README.md +14 -0
- package/templates/core/.devflow/workflows.yml +21 -0
- package/templates/core/AGENTS.md +44 -0
- package/templates/core/DEVFLOW.md +138 -0
- package/templates/prompts/build.txt +13 -0
- package/templates/prompts/plan.txt +13 -0
- package/templates/prompts/review.txt +14 -0
- package/templates/prompts/tests.txt +18 -0
- package/templates/prompts/verify.txt +15 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.buildDoctorReport = buildDoctorReport;
|
|
37
|
+
exports.runDoctor = runDoctor;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const constants_1 = require("../constants");
|
|
41
|
+
const install_1 = require("../install");
|
|
42
|
+
const plugins_1 = require("../plugins");
|
|
43
|
+
const pkg = require('../../package.json');
|
|
44
|
+
function inspectCore(targetDir) {
|
|
45
|
+
const agentsMd = (0, install_1.exists)('AGENTS.md', targetDir);
|
|
46
|
+
const devflowMd = (0, install_1.exists)('DEVFLOW.md', targetDir);
|
|
47
|
+
const workflowsConfig = (0, install_1.exists)(path.join('.devflow', 'workflows.yml'), targetDir);
|
|
48
|
+
const promptsDir = (0, install_1.exists)(path.join('devflow', 'prompts'), targetDir);
|
|
49
|
+
const planPrompt = (0, install_1.exists)(path.join('devflow', 'prompts', 'plan.txt'), targetDir);
|
|
50
|
+
const promptFiles = Object.fromEntries(constants_1.PROMPT_WORKFLOWS.map((w) => [w, (0, install_1.exists)(path.join('devflow', 'prompts', `${w}.txt`), targetDir)]));
|
|
51
|
+
const prompts = promptsDir && planPrompt;
|
|
52
|
+
const missing = [];
|
|
53
|
+
if (!agentsMd)
|
|
54
|
+
missing.push('AGENTS.md');
|
|
55
|
+
if (!devflowMd)
|
|
56
|
+
missing.push('DEVFLOW.md');
|
|
57
|
+
if (!promptsDir)
|
|
58
|
+
missing.push(path.join('devflow', 'prompts'));
|
|
59
|
+
if (promptsDir && !planPrompt)
|
|
60
|
+
missing.push(path.join('devflow', 'prompts', 'plan.txt'));
|
|
61
|
+
return {
|
|
62
|
+
agentsMd, devflowMd, workflowsConfig, prompts, promptFiles, missing,
|
|
63
|
+
status: missing.length === 0 ? 'ok' : 'missing',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function inspectAdapter(name, targetDir) {
|
|
67
|
+
const expected = constants_1.DOCTOR_ADAPTERS[name].expected;
|
|
68
|
+
const missing = expected.filter((rel) => !(0, install_1.exists)(rel, targetDir));
|
|
69
|
+
const present = (0, install_1.hasAnyPath)(targetDir, expected);
|
|
70
|
+
return {
|
|
71
|
+
present, missing, expected,
|
|
72
|
+
status: !present ? 'absent' : missing.length === 0 ? 'ok' : 'partial',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function inspectInstallation(targetDir) {
|
|
76
|
+
const core = inspectCore(targetDir);
|
|
77
|
+
const adapters = {};
|
|
78
|
+
for (const adapter of Object.keys(constants_1.DOCTOR_ADAPTERS)) {
|
|
79
|
+
adapters[adapter] = inspectAdapter(adapter, targetDir);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
core,
|
|
83
|
+
adapters,
|
|
84
|
+
detectedAdapters: Object.entries(adapters)
|
|
85
|
+
.filter(([, details]) => details.present)
|
|
86
|
+
.map(([name]) => name),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function severityRank(severity) {
|
|
90
|
+
return severity === 'error' ? 0 : 1;
|
|
91
|
+
}
|
|
92
|
+
function buildRecommendationCommand(targetDir, cwd, adapters) {
|
|
93
|
+
const parts = ['devflow', 'init'];
|
|
94
|
+
if (adapters.length === 1) {
|
|
95
|
+
parts.push('--adapter', adapters[0]);
|
|
96
|
+
}
|
|
97
|
+
else if (adapters.length > 1) {
|
|
98
|
+
parts.push('--adapters', adapters.join(','));
|
|
99
|
+
}
|
|
100
|
+
parts.push('--merge');
|
|
101
|
+
if (path.resolve(targetDir) !== path.resolve(cwd)) {
|
|
102
|
+
parts.push('--target', JSON.stringify(targetDir));
|
|
103
|
+
}
|
|
104
|
+
return parts.join(' ');
|
|
105
|
+
}
|
|
106
|
+
function inspectPlugins(targetDir) {
|
|
107
|
+
const { plugins } = (0, plugins_1.readPluginsFile)(targetDir);
|
|
108
|
+
return plugins.map((p) => ({
|
|
109
|
+
name: p.name,
|
|
110
|
+
source: p.source,
|
|
111
|
+
version: p.version,
|
|
112
|
+
keyFile: p.keyFile,
|
|
113
|
+
status: fs.existsSync(path.join(targetDir, p.keyFile)) ? 'ok' : 'missing',
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function buildDoctorReport(targetDir, cwd) {
|
|
117
|
+
const inspection = inspectInstallation(targetDir);
|
|
118
|
+
const plugins = inspectPlugins(targetDir);
|
|
119
|
+
const issues = [];
|
|
120
|
+
const recommendations = [];
|
|
121
|
+
const { detectedAdapters } = inspection;
|
|
122
|
+
const partiallyInstalledAdapters = Object.entries(inspection.adapters)
|
|
123
|
+
.filter(([, details]) => details.status === 'partial')
|
|
124
|
+
.map(([name]) => name);
|
|
125
|
+
const anyDevflowPresence = inspection.core.agentsMd
|
|
126
|
+
|| inspection.core.devflowMd
|
|
127
|
+
|| inspection.core.prompts
|
|
128
|
+
|| detectedAdapters.length > 0;
|
|
129
|
+
if (!anyDevflowPresence) {
|
|
130
|
+
issues.push({
|
|
131
|
+
id: 'devflow-not-installed',
|
|
132
|
+
severity: 'error',
|
|
133
|
+
message: 'Devflow does not appear to be installed in the target.',
|
|
134
|
+
fix: buildRecommendationCommand(targetDir, cwd, []),
|
|
135
|
+
});
|
|
136
|
+
recommendations.push(buildRecommendationCommand(targetDir, cwd, []));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
if (inspection.core.missing.length > 0) {
|
|
140
|
+
const coreAdapters = detectedAdapters.length > 0 ? detectedAdapters : [];
|
|
141
|
+
issues.push({
|
|
142
|
+
id: 'core-missing',
|
|
143
|
+
severity: 'error',
|
|
144
|
+
message: `Core installation is incomplete. Missing: ${inspection.core.missing.join(', ')}`,
|
|
145
|
+
fix: buildRecommendationCommand(targetDir, cwd, coreAdapters),
|
|
146
|
+
});
|
|
147
|
+
recommendations.push(buildRecommendationCommand(targetDir, cwd, coreAdapters));
|
|
148
|
+
}
|
|
149
|
+
for (const adapter of partiallyInstalledAdapters) {
|
|
150
|
+
issues.push({
|
|
151
|
+
id: `${adapter}-partial`,
|
|
152
|
+
severity: 'warn',
|
|
153
|
+
message: `${adapter} adapter is partially installed. Missing: ${inspection.adapters[adapter].missing.join(', ')}`,
|
|
154
|
+
fix: buildRecommendationCommand(targetDir, cwd, [adapter]),
|
|
155
|
+
});
|
|
156
|
+
recommendations.push(buildRecommendationCommand(targetDir, cwd, [adapter]));
|
|
157
|
+
}
|
|
158
|
+
for (const plugin of plugins) {
|
|
159
|
+
if (plugin.status === 'missing') {
|
|
160
|
+
issues.push({
|
|
161
|
+
id: `plugin-${plugin.name}-missing`,
|
|
162
|
+
severity: 'warn',
|
|
163
|
+
message: `Plugin "${plugin.name}" key file is missing: ${plugin.keyFile}`,
|
|
164
|
+
fix: `devflow add ${plugin.source}${path.resolve(targetDir) !== path.resolve(cwd) ? ` --target ${JSON.stringify(targetDir)}` : ''}`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (issues.length === 0) {
|
|
170
|
+
recommendations.push('No action needed.');
|
|
171
|
+
}
|
|
172
|
+
issues.sort((a, b) => severityRank(a.severity) - severityRank(b.severity));
|
|
173
|
+
return {
|
|
174
|
+
version: pkg.version,
|
|
175
|
+
target: targetDir,
|
|
176
|
+
core: inspection.core,
|
|
177
|
+
adapters: inspection.adapters,
|
|
178
|
+
plugins,
|
|
179
|
+
issues,
|
|
180
|
+
recommendations: [...new Set(recommendations)],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function printDoctorHuman(report, options) {
|
|
184
|
+
console.log('Devflow doctor');
|
|
185
|
+
console.log(`Version: ${report.version}`);
|
|
186
|
+
console.log(`Target: ${report.target}`);
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log(`Core: ${report.core.status === 'ok' ? 'OK' : 'MISSING'}`);
|
|
189
|
+
if (report.core.missing.length > 0) {
|
|
190
|
+
console.log(`Missing core files: ${report.core.missing.join(', ')}`);
|
|
191
|
+
}
|
|
192
|
+
console.log(`Custom workflow config present: ${report.core.workflowsConfig ? 'yes' : 'no'}`);
|
|
193
|
+
const detectedAdapters = Object.entries(report.adapters)
|
|
194
|
+
.filter(([, details]) => details.present)
|
|
195
|
+
.map(([name]) => name);
|
|
196
|
+
console.log(`Adapters detected: ${detectedAdapters.length > 0 ? detectedAdapters.join(', ') : 'none'}`);
|
|
197
|
+
console.log(`Plugins installed: ${report.plugins.length > 0 ? report.plugins.map((p) => p.name).join(', ') : 'none'}`);
|
|
198
|
+
if (options.verbose) {
|
|
199
|
+
console.log('');
|
|
200
|
+
console.log('Adapter details:');
|
|
201
|
+
for (const [name, details] of Object.entries(report.adapters)) {
|
|
202
|
+
console.log(`- ${name}: ${details.status}`);
|
|
203
|
+
if (details.missing.length > 0) {
|
|
204
|
+
console.log(` missing: ${details.missing.join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (report.plugins.length > 0) {
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log('Plugin details:');
|
|
210
|
+
for (const plugin of report.plugins) {
|
|
211
|
+
console.log(`- ${plugin.name} (${plugin.version}): ${plugin.status}`);
|
|
212
|
+
if (plugin.status === 'missing') {
|
|
213
|
+
console.log(` missing key file: ${plugin.keyFile}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
console.log('');
|
|
219
|
+
console.log('Issues:');
|
|
220
|
+
if (report.issues.length === 0) {
|
|
221
|
+
console.log('- none');
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
for (const issue of report.issues) {
|
|
225
|
+
console.log(`- [${issue.severity}] ${issue.message}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
console.log('');
|
|
229
|
+
console.log('Recommended next command(s):');
|
|
230
|
+
for (const recommendation of report.recommendations) {
|
|
231
|
+
console.log(`- ${recommendation}`);
|
|
232
|
+
}
|
|
233
|
+
if (options.fix) {
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log('Fix mode: no automatic safe fixes are available. Use the recommended command manually.');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function runDoctor(options) {
|
|
239
|
+
const targetDir = (0, install_1.resolveTarget)(options.target ?? process.cwd());
|
|
240
|
+
const report = buildDoctorReport(targetDir, process.cwd());
|
|
241
|
+
if (options.json) {
|
|
242
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
printDoctorHuman(report, options);
|
|
246
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runExplain = runExplain;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const constants_1 = require("../constants");
|
|
39
|
+
const install_1 = require("../install");
|
|
40
|
+
const doctor_1 = require("./doctor");
|
|
41
|
+
const pkg = require('../../package.json');
|
|
42
|
+
const CURSOR_WORKFLOW_FILES = {
|
|
43
|
+
plan: path.join('.cursor', 'commands', 'plan.md'),
|
|
44
|
+
build: path.join('.cursor', 'commands', 'build.md'),
|
|
45
|
+
tests: path.join('.cursor', 'commands', 'tests.md'),
|
|
46
|
+
review: path.join('.cursor', 'commands', 'review.md'),
|
|
47
|
+
verify: path.join('.cursor', 'commands', 'verify.md'),
|
|
48
|
+
};
|
|
49
|
+
const CLAUDE_WORKFLOW_FILES = {
|
|
50
|
+
plan: path.join('.claude', 'commands', 'plan.md'),
|
|
51
|
+
build: path.join('.claude', 'commands', 'build.md'),
|
|
52
|
+
tests: path.join('.claude', 'commands', 'tests.md'),
|
|
53
|
+
review: path.join('.claude', 'commands', 'review.md'),
|
|
54
|
+
verify: path.join('.claude', 'commands', 'verify.md'),
|
|
55
|
+
};
|
|
56
|
+
function buildExplainWorkflows(inspection) {
|
|
57
|
+
const workflows = new Set();
|
|
58
|
+
for (const workflow of constants_1.PROMPT_WORKFLOWS) {
|
|
59
|
+
if (inspection.core.promptFiles[workflow]) {
|
|
60
|
+
workflows.add(workflow);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const [workflow, file] of Object.entries(CURSOR_WORKFLOW_FILES)) {
|
|
64
|
+
const cursorAdapter = inspection.adapters.cursor;
|
|
65
|
+
if (cursorAdapter?.expected.includes(file) && cursorAdapter.present && !cursorAdapter.missing.includes(file)) {
|
|
66
|
+
workflows.add(workflow);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const [workflow, file] of Object.entries(CLAUDE_WORKFLOW_FILES)) {
|
|
70
|
+
const claudeAdapter = inspection.adapters.claude;
|
|
71
|
+
if (claudeAdapter?.expected.includes(file) && claudeAdapter.present && !claudeAdapter.missing.includes(file)) {
|
|
72
|
+
workflows.add(workflow);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...workflows];
|
|
76
|
+
}
|
|
77
|
+
function buildExplainSources(targetDir, inspection) {
|
|
78
|
+
const sources = [];
|
|
79
|
+
if (inspection.core.agentsMd)
|
|
80
|
+
sources.push(path.join(targetDir, 'AGENTS.md'));
|
|
81
|
+
if (inspection.core.devflowMd)
|
|
82
|
+
sources.push(path.join(targetDir, 'DEVFLOW.md'));
|
|
83
|
+
if (inspection.core.workflowsConfig)
|
|
84
|
+
sources.push(path.join(targetDir, '.devflow', 'workflows.yml'));
|
|
85
|
+
for (const workflow of constants_1.PROMPT_WORKFLOWS) {
|
|
86
|
+
if (inspection.core.promptFiles[workflow]) {
|
|
87
|
+
sources.push(path.join(targetDir, 'devflow', 'prompts', `${workflow}.txt`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const [adapter, details] of Object.entries(inspection.adapters)) {
|
|
91
|
+
for (const expected of details.expected) {
|
|
92
|
+
if (!details.missing.includes(expected) && (0, install_1.exists)(expected, targetDir)) {
|
|
93
|
+
sources.push(path.join(targetDir, expected));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (adapter === 'generic' && details.present && !details.missing.includes(path.join('.devflow', 'README.md'))) {
|
|
97
|
+
sources.push(path.join(targetDir, '.devflow', 'README.md'));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return [...new Set(sources)];
|
|
101
|
+
}
|
|
102
|
+
function buildExplainRecommendations(targetDir, cwd, inspection) {
|
|
103
|
+
const recommendations = [];
|
|
104
|
+
const { detectedAdapters } = inspection;
|
|
105
|
+
const partialAdapters = Object.entries(inspection.adapters)
|
|
106
|
+
.filter(([, details]) => details.status === 'partial')
|
|
107
|
+
.map(([name]) => name);
|
|
108
|
+
const anyDevflowPresence = inspection.core.agentsMd
|
|
109
|
+
|| inspection.core.devflowMd
|
|
110
|
+
|| inspection.core.prompts
|
|
111
|
+
|| detectedAdapters.length > 0;
|
|
112
|
+
const buildCmd = (adapters) => {
|
|
113
|
+
const parts = ['devflow', 'init'];
|
|
114
|
+
if (adapters.length === 1)
|
|
115
|
+
parts.push('--adapter', adapters[0]);
|
|
116
|
+
else if (adapters.length > 1)
|
|
117
|
+
parts.push('--adapters', adapters.join(','));
|
|
118
|
+
parts.push('--merge');
|
|
119
|
+
if (path.resolve(targetDir) !== path.resolve(cwd))
|
|
120
|
+
parts.push('--target', JSON.stringify(targetDir));
|
|
121
|
+
return parts.join(' ');
|
|
122
|
+
};
|
|
123
|
+
if (!anyDevflowPresence) {
|
|
124
|
+
recommendations.push(buildCmd([]));
|
|
125
|
+
}
|
|
126
|
+
else if (inspection.core.missing.length > 0) {
|
|
127
|
+
recommendations.push(buildCmd(detectedAdapters));
|
|
128
|
+
}
|
|
129
|
+
for (const adapter of partialAdapters) {
|
|
130
|
+
recommendations.push(buildCmd([adapter]));
|
|
131
|
+
}
|
|
132
|
+
if (recommendations.length === 0) {
|
|
133
|
+
recommendations.push('No action needed.');
|
|
134
|
+
}
|
|
135
|
+
return [...new Set(recommendations)];
|
|
136
|
+
}
|
|
137
|
+
function buildExplainReport(targetDir, cwd) {
|
|
138
|
+
// Reuse inspectInstallation via doctor report internals
|
|
139
|
+
const doctorReport = (0, doctor_1.buildDoctorReport)(targetDir, cwd);
|
|
140
|
+
const inspection = {
|
|
141
|
+
core: doctorReport.core,
|
|
142
|
+
adapters: doctorReport.adapters,
|
|
143
|
+
detectedAdapters: Object.entries(doctorReport.adapters)
|
|
144
|
+
.filter(([, d]) => d.present)
|
|
145
|
+
.map(([name]) => name),
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
version: pkg.version,
|
|
149
|
+
target: targetDir,
|
|
150
|
+
core: inspection.core,
|
|
151
|
+
adapters: inspection.adapters,
|
|
152
|
+
workflows: buildExplainWorkflows(inspection),
|
|
153
|
+
sources: buildExplainSources(targetDir, inspection),
|
|
154
|
+
sourceOfCriteria: "Rules are defined by installed Devflow files in this project (AGENTS.md, DEVFLOW.md, prompts, and adapters). System instructions may also apply, but Devflow's intended source of behavior is the project files.",
|
|
155
|
+
recommendations: buildExplainRecommendations(targetDir, cwd, inspection),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function printExplainHuman(report, options) {
|
|
159
|
+
const detectedAdapters = Object.entries(report.adapters)
|
|
160
|
+
.filter(([, details]) => details.present)
|
|
161
|
+
.map(([name]) => name);
|
|
162
|
+
const missingCore = report.core.missing.length > 0 ? report.core.missing.join(', ') : 'none';
|
|
163
|
+
console.log('Devflow explain');
|
|
164
|
+
console.log(`Version: ${report.version}`);
|
|
165
|
+
console.log(`Target: ${report.target}`);
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(`Core: ${report.core.status === 'ok' ? 'OK' : 'MISSING'}`);
|
|
168
|
+
console.log(`Core missing: ${missingCore}`);
|
|
169
|
+
console.log(`Custom workflow config present: ${report.core.workflowsConfig ? 'yes' : 'no'}`);
|
|
170
|
+
console.log(`Adapters installed: ${detectedAdapters.length > 0 ? detectedAdapters.join(', ') : 'none'}`);
|
|
171
|
+
console.log(`Workflows available: ${report.workflows.length > 0 ? report.workflows.join(', ') : 'none'}`);
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log('Source of criteria:');
|
|
174
|
+
console.log(report.sourceOfCriteria);
|
|
175
|
+
if (options.verbose) {
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log('Sources:');
|
|
178
|
+
for (const source of report.sources) {
|
|
179
|
+
console.log(`- ${source}`);
|
|
180
|
+
}
|
|
181
|
+
console.log('');
|
|
182
|
+
console.log('Adapter checks:');
|
|
183
|
+
for (const [name, details] of Object.entries(report.adapters)) {
|
|
184
|
+
console.log(`- ${name}: ${details.status}`);
|
|
185
|
+
if (details.missing.length > 0) {
|
|
186
|
+
console.log(` missing: ${details.missing.join(', ')}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
console.log('');
|
|
191
|
+
console.log('Recommended next command(s):');
|
|
192
|
+
for (const recommendation of report.recommendations) {
|
|
193
|
+
console.log(`- ${recommendation}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function runExplain(options) {
|
|
197
|
+
const targetDir = (0, install_1.resolveTarget)(options.target ?? process.cwd());
|
|
198
|
+
const report = buildExplainReport(targetDir, process.cwd());
|
|
199
|
+
if (options.json) {
|
|
200
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
printExplainHuman(report, options);
|
|
204
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runInit = runInit;
|
|
4
|
+
const install_1 = require("../install");
|
|
5
|
+
function runInit(options) {
|
|
6
|
+
const targetDir = (0, install_1.resolveTarget)(options.target ?? process.cwd());
|
|
7
|
+
const dryRun = Boolean(options.dryRun);
|
|
8
|
+
const force = Boolean(options.force);
|
|
9
|
+
const merge = Boolean(options.merge) && !force; // force wins over merge
|
|
10
|
+
const adapters = (0, install_1.resolveAdapters)(options, targetDir);
|
|
11
|
+
let items;
|
|
12
|
+
try {
|
|
13
|
+
items = (0, install_1.buildInstallList)(adapters);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17
|
+
console.error(`Error: could not read templates.\n${msg}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (items.length === 0) {
|
|
21
|
+
console.error('Error: no template files found for the selected tools.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const conflicts = items.filter(({ dest }) => (0, install_1.exists)(dest, targetDir));
|
|
25
|
+
// Abort only when there are conflicts and neither --force nor --merge is set
|
|
26
|
+
if (conflicts.length > 0 && !force && !merge && !dryRun) {
|
|
27
|
+
console.error('Error: the following files already exist in the target:');
|
|
28
|
+
conflicts.forEach(({ dest }) => console.error(` ${dest}`));
|
|
29
|
+
console.error('\nUse --force to overwrite, --merge to skip existing, or --dry-run to preview.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
if (dryRun) {
|
|
33
|
+
const modeLabel = force ? ' --force' : merge ? ' --merge' : '';
|
|
34
|
+
const installLabel = adapters.length > 0 ? `core + ${adapters.join(', ')}` : 'core only';
|
|
35
|
+
console.log(`Devflow: dry run — target: ${targetDir}${modeLabel}`);
|
|
36
|
+
console.log(`Install set: ${installLabel}\n`);
|
|
37
|
+
for (const { dest } of items) {
|
|
38
|
+
const fileExists = (0, install_1.exists)(dest, targetDir);
|
|
39
|
+
let tag;
|
|
40
|
+
if (!fileExists)
|
|
41
|
+
tag = '[create] ';
|
|
42
|
+
else if (!(0, install_1.isManaged)(dest))
|
|
43
|
+
tag = '[skip] '; // never touch non-managed files
|
|
44
|
+
else if (force)
|
|
45
|
+
tag = '[overwrite]';
|
|
46
|
+
else if (merge)
|
|
47
|
+
tag = '[skip] ';
|
|
48
|
+
else
|
|
49
|
+
tag = '[conflict] ';
|
|
50
|
+
console.log(` ${tag} ${dest}`);
|
|
51
|
+
}
|
|
52
|
+
if (!force && !merge && conflicts.length > 0) {
|
|
53
|
+
console.log('\n Tip: use --force to overwrite or --merge to skip conflicts.');
|
|
54
|
+
}
|
|
55
|
+
console.log('\nDry run complete. Run without --dry-run to apply.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const modeLabel = force ? ' (force)' : merge ? ' (merge)' : '';
|
|
59
|
+
const installLabel = adapters.length > 0 ? `core + ${adapters.join(', ')}` : 'core only';
|
|
60
|
+
console.log(`Devflow: installing into ${targetDir}${modeLabel}`);
|
|
61
|
+
console.log(`Install set: ${installLabel}\n`);
|
|
62
|
+
for (const item of items) {
|
|
63
|
+
const existed = (0, install_1.exists)(item.dest, targetDir);
|
|
64
|
+
if (existed && merge) {
|
|
65
|
+
console.log(` – ${item.dest} (skipped)`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Safety: never overwrite files outside Devflow-managed paths, even with --force.
|
|
69
|
+
if (existed && !(0, install_1.isManaged)(item.dest)) {
|
|
70
|
+
console.log(` – ${item.dest} (skipped – not Devflow-managed)`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
(0, install_1.copyItem)(item, targetDir);
|
|
75
|
+
const suffix = existed ? ' (overwritten)' : '';
|
|
76
|
+
console.log(` ✓ ${item.dest}${suffix}`);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
80
|
+
console.error(` ✗ ${item.dest} → ${msg}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const failures = (0, install_1.validate)(adapters, targetDir);
|
|
85
|
+
if (failures.length > 0) {
|
|
86
|
+
console.error('\nValidation failed — these files are missing:');
|
|
87
|
+
failures.forEach((f) => console.error(` ${f}`));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
console.log('\nDone. Devflow installed successfully.');
|
|
91
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MANAGED_PREFIXES = exports.DOCTOR_ADAPTERS = exports.PROMPT_WORKFLOWS = exports.CORE_KEY_FILES = exports.DEFAULT_CLAUDE_MARKER = exports.DEFAULT_CURSOR_MARKER = exports.ALL_ADAPTERS = exports.ADAPTER_CONFIG = exports.PLUGINS_FILE = exports.TEMPLATES_ROOT = void 0;
|
|
37
|
+
exports.isManaged = isManaged;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
exports.TEMPLATES_ROOT = path.join(__dirname, '..', 'templates');
|
|
40
|
+
exports.PLUGINS_FILE = path.join('.devflow', 'plugins.yml');
|
|
41
|
+
exports.ADAPTER_CONFIG = {
|
|
42
|
+
cursor: {
|
|
43
|
+
srcDir: path.join('adapters', 'cursor'),
|
|
44
|
+
destDir: '',
|
|
45
|
+
keyFile: path.join('.cursor', 'commands', 'plan.md'),
|
|
46
|
+
},
|
|
47
|
+
claude: {
|
|
48
|
+
srcDir: path.join('adapters', 'claude'),
|
|
49
|
+
destDir: '',
|
|
50
|
+
keyFile: path.join('.claude', 'commands', 'plan.md'),
|
|
51
|
+
},
|
|
52
|
+
codex: {
|
|
53
|
+
srcDir: path.join('adapters', 'codex'),
|
|
54
|
+
destDir: path.join('.devflow', 'adapters', 'codex'),
|
|
55
|
+
keyFile: path.join('.devflow', 'adapters', 'codex', 'README.md'),
|
|
56
|
+
},
|
|
57
|
+
gemini: {
|
|
58
|
+
srcDir: path.join('adapters', 'gemini'),
|
|
59
|
+
destDir: path.join('.devflow', 'adapters', 'gemini'),
|
|
60
|
+
keyFile: path.join('.devflow', 'adapters', 'gemini', 'README.md'),
|
|
61
|
+
},
|
|
62
|
+
generic: {
|
|
63
|
+
srcDir: path.join('adapters', 'generic'),
|
|
64
|
+
destDir: '.devflow',
|
|
65
|
+
keyFile: path.join('.devflow', 'README.md'),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
exports.ALL_ADAPTERS = Object.keys(exports.ADAPTER_CONFIG);
|
|
69
|
+
exports.DEFAULT_CURSOR_MARKER = '.cursor';
|
|
70
|
+
exports.DEFAULT_CLAUDE_MARKER = '.claude';
|
|
71
|
+
exports.CORE_KEY_FILES = [
|
|
72
|
+
'AGENTS.md',
|
|
73
|
+
'DEVFLOW.md',
|
|
74
|
+
path.join('devflow', 'prompts', 'plan.txt'),
|
|
75
|
+
path.join('devflow', 'prompts', 'build.txt'),
|
|
76
|
+
path.join('devflow', 'prompts', 'tests.txt'),
|
|
77
|
+
path.join('devflow', 'prompts', 'review.txt'),
|
|
78
|
+
path.join('devflow', 'prompts', 'verify.txt'),
|
|
79
|
+
];
|
|
80
|
+
exports.PROMPT_WORKFLOWS = ['plan', 'build', 'tests', 'review', 'verify'];
|
|
81
|
+
exports.DOCTOR_ADAPTERS = {
|
|
82
|
+
cursor: {
|
|
83
|
+
expected: [
|
|
84
|
+
path.join('.cursor', 'commands', 'plan.md'),
|
|
85
|
+
path.join('.cursor', 'commands', 'build.md'),
|
|
86
|
+
path.join('.cursor', 'commands', 'tests.md'),
|
|
87
|
+
path.join('.cursor', 'commands', 'review.md'),
|
|
88
|
+
path.join('.cursor', 'commands', 'verify.md'),
|
|
89
|
+
path.join('.cursor', 'rules', 'typescript.md'),
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
claude: {
|
|
93
|
+
expected: [
|
|
94
|
+
path.join('.claude', 'commands', 'plan.md'),
|
|
95
|
+
path.join('.claude', 'commands', 'build.md'),
|
|
96
|
+
path.join('.claude', 'commands', 'tests.md'),
|
|
97
|
+
path.join('.claude', 'commands', 'review.md'),
|
|
98
|
+
path.join('.claude', 'commands', 'verify.md'),
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
codex: {
|
|
102
|
+
expected: [path.join('.devflow', 'adapters', 'codex', 'README.md')],
|
|
103
|
+
},
|
|
104
|
+
gemini: {
|
|
105
|
+
expected: [path.join('.devflow', 'adapters', 'gemini', 'README.md')],
|
|
106
|
+
},
|
|
107
|
+
generic: {
|
|
108
|
+
expected: [path.join('.devflow', 'README.md')],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
// Paths Devflow is allowed to create or overwrite.
|
|
112
|
+
// --force will never touch files outside these prefixes.
|
|
113
|
+
exports.MANAGED_PREFIXES = new Set([
|
|
114
|
+
'AGENTS.md', 'DEVFLOW.md',
|
|
115
|
+
'.claude', '.cursor', '.devflow',
|
|
116
|
+
'devflow',
|
|
117
|
+
]);
|
|
118
|
+
function isManaged(dest) {
|
|
119
|
+
return exports.MANAGED_PREFIXES.has(dest.split(path.sep)[0]);
|
|
120
|
+
}
|