prjct-cli 0.6.0 → 0.7.1
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 +67 -6
- package/CLAUDE.md +442 -36
- package/README.md +47 -54
- package/bin/prjct +174 -240
- package/core/agentic/command-executor.js +113 -0
- package/core/agentic/context-builder.js +85 -0
- package/core/agentic/prompt-builder.js +86 -0
- package/core/agentic/template-loader.js +104 -0
- package/core/agentic/tool-registry.js +117 -0
- package/core/command-registry.js +109 -65
- package/core/commands.js +2213 -2173
- package/core/domain/agent-generator.js +118 -0
- package/core/domain/analyzer.js +211 -0
- package/core/domain/architect-session.js +300 -0
- package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
- package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
- package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
- package/core/{command-installer.js → infrastructure/command-installer.js} +5 -3
- package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
- package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
- package/core/{migrator.js → infrastructure/migrator.js} +34 -19
- package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
- package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
- package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
- package/core/{animations-simple.js → utils/animations.js} +3 -23
- package/core/utils/date-helper.js +238 -0
- package/core/utils/file-helper.js +327 -0
- package/core/utils/jsonl-helper.js +206 -0
- package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
- package/core/utils/session-helper.js +277 -0
- package/core/{version.js → utils/version.js} +1 -1
- package/package.json +4 -12
- package/templates/agents/AGENTS.md +101 -27
- package/templates/analysis/analyze.md +84 -0
- package/templates/commands/analyze.md +9 -2
- package/templates/commands/bug.md +79 -0
- package/templates/commands/build.md +5 -2
- package/templates/commands/cleanup.md +5 -2
- package/templates/commands/design.md +5 -2
- package/templates/commands/done.md +4 -2
- package/templates/commands/feature.md +113 -0
- package/templates/commands/fix.md +41 -10
- package/templates/commands/git.md +7 -2
- package/templates/commands/help.md +2 -2
- package/templates/commands/idea.md +14 -5
- package/templates/commands/init.md +62 -7
- package/templates/commands/next.md +4 -2
- package/templates/commands/now.md +4 -2
- package/templates/commands/progress.md +27 -5
- package/templates/commands/recap.md +39 -10
- package/templates/commands/roadmap.md +19 -5
- package/templates/commands/ship.md +118 -16
- package/templates/commands/status.md +4 -2
- package/templates/commands/sync.md +19 -15
- package/templates/commands/task.md +4 -2
- package/templates/commands/test.md +5 -2
- package/templates/commands/workflow.md +4 -2
- package/core/agent-generator.js +0 -525
- package/core/analyzer.js +0 -600
- package/core/animations.js +0 -277
- package/core/ascii-graphics.js +0 -433
- package/core/git-integration.js +0 -401
- package/core/task-schema.js +0 -342
- package/core/workflow-engine.js +0 -213
- package/core/workflow-prompts.js +0 -192
- package/core/workflow-rules.js +0 -147
- package/scripts/post-install.js +0 -121
- package/scripts/preuninstall.js +0 -94
- package/scripts/verify-installation.sh +0 -158
- package/templates/agents/be.template.md +0 -27
- package/templates/agents/coordinator.template.md +0 -34
- package/templates/agents/data.template.md +0 -27
- package/templates/agents/devops.template.md +0 -27
- package/templates/agents/fe.template.md +0 -27
- package/templates/agents/mobile.template.md +0 -27
- package/templates/agents/qa.template.md +0 -27
- package/templates/agents/scribe.template.md +0 -29
- package/templates/agents/security.template.md +0 -27
- package/templates/agents/ux.template.md +0 -27
- package/templates/commands/context.md +0 -36
- package/templates/commands/stuck.md +0 -36
- package/templates/examples/natural-language-examples.md +0 -532
- /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
package/bin/prjct
CHANGED
|
@@ -1,247 +1,181 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* prjct CLI - 100% Agentic Entry Point
|
|
5
|
+
*
|
|
6
|
+
* Uses command-registry as single source of truth.
|
|
7
|
+
* Zero hardcoded logic - all commands resolved dynamically.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const PrjctCommands = require('../core/commands')
|
|
11
|
+
const registry = require('../core/command-registry')
|
|
5
12
|
|
|
6
13
|
async function main() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!designTarget) {
|
|
90
|
-
result = {
|
|
91
|
-
success: false,
|
|
92
|
-
message: 'Please specify what to design: prjct design "authentication system"'
|
|
93
|
-
};
|
|
94
|
-
} else {
|
|
95
|
-
result = await commands.design(designTarget, designOptions);
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
case 'migrate-all':
|
|
99
|
-
const migrateOptions = {};
|
|
100
|
-
const migrateArgs = args.slice(1);
|
|
101
|
-
|
|
102
|
-
for (let i = 0; i < migrateArgs.length; i++) {
|
|
103
|
-
if (migrateArgs[i] === '--deep-scan') {
|
|
104
|
-
migrateOptions.deepScan = true;
|
|
105
|
-
} else if (migrateArgs[i] === '--remove-legacy') {
|
|
106
|
-
migrateOptions.removeLegacy = true;
|
|
107
|
-
} else if (migrateArgs[i] === '--dry-run') {
|
|
108
|
-
migrateOptions.dryRun = true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
result = await commands.migrateAll(migrateOptions);
|
|
113
|
-
break;
|
|
114
|
-
case 'setup':
|
|
115
|
-
const setupOptions = {};
|
|
116
|
-
const setupArgs = args.slice(1);
|
|
117
|
-
|
|
118
|
-
for (let i = 0; i < setupArgs.length; i++) {
|
|
119
|
-
if (setupArgs[i] === '--force') {
|
|
120
|
-
setupOptions.force = true;
|
|
121
|
-
} else if (setupArgs[i] === '--editor') {
|
|
122
|
-
setupOptions.editor = setupArgs[++i];
|
|
123
|
-
} else if (setupArgs[i] === '--create-templates') {
|
|
124
|
-
setupOptions.createTemplates = true;
|
|
125
|
-
} else if (setupArgs[i] === '--no-interactive') {
|
|
126
|
-
setupOptions.interactive = false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
result = await commands.setup(setupOptions);
|
|
131
|
-
break;
|
|
132
|
-
case 'analyze':
|
|
133
|
-
const analyzeOptions = {};
|
|
134
|
-
const analyzeArgs = args.slice(1);
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < analyzeArgs.length; i++) {
|
|
137
|
-
if (analyzeArgs[i] === '--sync') {
|
|
138
|
-
analyzeOptions.sync = true;
|
|
139
|
-
} else if (analyzeArgs[i] === '--report-only') {
|
|
140
|
-
analyzeOptions.reportOnly = true;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
result = await commands.analyze(analyzeOptions);
|
|
145
|
-
break;
|
|
146
|
-
case 'sync':
|
|
147
|
-
result = await commands.sync();
|
|
148
|
-
break;
|
|
149
|
-
case 'workflow':
|
|
150
|
-
result = await commands.workflow();
|
|
151
|
-
break;
|
|
152
|
-
case '--version':
|
|
153
|
-
case '-v':
|
|
154
|
-
case 'version':
|
|
155
|
-
const packageJson = require('../package.json');
|
|
156
|
-
result = {
|
|
157
|
-
success: true,
|
|
158
|
-
message: `prjct-cli v${packageJson.version}`
|
|
159
|
-
};
|
|
160
|
-
break;
|
|
161
|
-
case '--help':
|
|
162
|
-
case '-h':
|
|
163
|
-
case 'help':
|
|
164
|
-
case undefined:
|
|
165
|
-
// Generate help message from registry
|
|
166
|
-
const categories = registry.getCategories();
|
|
167
|
-
const categorizedCommands = {};
|
|
168
|
-
|
|
169
|
-
// Group commands by category
|
|
170
|
-
registry.getTerminalCommands().forEach(cmd => {
|
|
171
|
-
if (!categorizedCommands[cmd.category]) {
|
|
172
|
-
categorizedCommands[cmd.category] = [];
|
|
173
|
-
}
|
|
174
|
-
categorizedCommands[cmd.category].push(cmd);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
let helpMessage = 'Available commands:\n';
|
|
178
|
-
|
|
179
|
-
// Display commands by category
|
|
180
|
-
Object.entries(categorizedCommands).forEach(([categoryKey, cmds]) => {
|
|
181
|
-
const categoryInfo = categories[categoryKey];
|
|
182
|
-
helpMessage += `\n ${categoryInfo.title}:\n`;
|
|
183
|
-
|
|
184
|
-
cmds.forEach(cmd => {
|
|
185
|
-
const params = cmd.params ? ` ${cmd.params}` : '';
|
|
186
|
-
const spacing = ' '.repeat(Math.max(18 - cmd.name.length - params.length, 1));
|
|
187
|
-
helpMessage += ` ${cmd.name}${params}${spacing}${cmd.description}\n`;
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const stats = registry.getStats();
|
|
192
|
-
helpMessage += `\nTotal: ${stats.implemented} implemented / ${stats.total} total commands`;
|
|
193
|
-
|
|
194
|
-
result = {
|
|
195
|
-
success: true,
|
|
196
|
-
message: helpMessage
|
|
197
|
-
};
|
|
198
|
-
break;
|
|
199
|
-
default:
|
|
200
|
-
// Check if command exists in registry but not implemented
|
|
201
|
-
const registryCmd = registry.getByName(command);
|
|
202
|
-
|
|
203
|
-
if (registryCmd && !registryCmd.implemented) {
|
|
204
|
-
result = {
|
|
205
|
-
success: false,
|
|
206
|
-
message: `Command '${command}' exists but is not yet implemented.
|
|
207
|
-
Check the roadmap or contribute: https://github.com/your-org/prjct-cli
|
|
208
|
-
|
|
209
|
-
Use 'prjct --help' to see all available commands.`
|
|
210
|
-
};
|
|
211
|
-
} else {
|
|
212
|
-
// Generate error message with available commands from registry
|
|
213
|
-
const categories = registry.getCategories();
|
|
214
|
-
const categorizedCommands = {};
|
|
215
|
-
|
|
216
|
-
registry.getTerminalCommands().forEach(cmd => {
|
|
217
|
-
if (!categorizedCommands[cmd.category]) {
|
|
218
|
-
categorizedCommands[cmd.category] = [];
|
|
219
|
-
}
|
|
220
|
-
categorizedCommands[cmd.category].push(cmd);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
let errorMessage = `Unknown command: ${command}\n\nAvailable commands:\n`;
|
|
224
|
-
|
|
225
|
-
Object.entries(categorizedCommands).forEach(([categoryKey, cmds]) => {
|
|
226
|
-
const categoryInfo = categories[categoryKey];
|
|
227
|
-
errorMessage += `\n ${categoryInfo.title}:\n`;
|
|
228
|
-
|
|
229
|
-
cmds.forEach(cmd => {
|
|
230
|
-
const params = cmd.params ? ` ${cmd.params}` : '';
|
|
231
|
-
const spacing = ' '.repeat(Math.max(18 - cmd.name.length - params.length, 1));
|
|
232
|
-
errorMessage += ` ${cmd.name}${params}${spacing}${cmd.description}\n`;
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
result = {
|
|
237
|
-
success: false,
|
|
238
|
-
message: errorMessage
|
|
239
|
-
};
|
|
240
|
-
}
|
|
14
|
+
const [commandName, ...rawArgs] = process.argv.slice(2)
|
|
15
|
+
|
|
16
|
+
// === SPECIAL COMMANDS (version, help) ===
|
|
17
|
+
|
|
18
|
+
if (['-v', '--version', 'version'].includes(commandName)) {
|
|
19
|
+
const packageJson = require('../package.json')
|
|
20
|
+
console.log(`prjct-cli v${packageJson.version}`)
|
|
21
|
+
process.exit(0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (['-h', '--help', 'help', undefined].includes(commandName)) {
|
|
25
|
+
displayHelp()
|
|
26
|
+
process.exit(0)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// === DYNAMIC COMMAND EXECUTION ===
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// 1. Find command in registry
|
|
33
|
+
const cmd = registry.getByName(commandName)
|
|
34
|
+
|
|
35
|
+
if (!cmd) {
|
|
36
|
+
console.error(`Unknown command: ${commandName}`)
|
|
37
|
+
console.error(`\nUse 'prjct --help' to see available commands.`)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Check if deprecated (before checking implemented)
|
|
42
|
+
if (cmd.deprecated) {
|
|
43
|
+
console.error(`Command '${commandName}' is deprecated.`)
|
|
44
|
+
if (cmd.replacedBy) {
|
|
45
|
+
console.error(`Use 'prjct ${cmd.replacedBy}' instead.`)
|
|
46
|
+
}
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Check if implemented
|
|
51
|
+
if (!cmd.implemented) {
|
|
52
|
+
console.error(`Command '${commandName}' exists but is not yet implemented.`)
|
|
53
|
+
console.error(`Check the roadmap or contribute: https://github.com/jlopezlira/prjct-cli`)
|
|
54
|
+
console.error(`\nUse 'prjct --help' to see available commands.`)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 4. Parse arguments based on command definition
|
|
59
|
+
const { parsedArgs, options } = parseCommandArgs(cmd, rawArgs)
|
|
60
|
+
|
|
61
|
+
// 5. Instantiate commands handler
|
|
62
|
+
const commands = new PrjctCommands()
|
|
63
|
+
|
|
64
|
+
// 6. Execute command
|
|
65
|
+
let result
|
|
66
|
+
|
|
67
|
+
// Commands with special option handling
|
|
68
|
+
if (commandName === 'design') {
|
|
69
|
+
const target = parsedArgs.join(' ')
|
|
70
|
+
result = await commands.design(target, options)
|
|
71
|
+
} else if (commandName === 'analyze') {
|
|
72
|
+
result = await commands.analyze(options)
|
|
73
|
+
} else if (commandName === 'cleanup') {
|
|
74
|
+
result = await commands.cleanup(options)
|
|
75
|
+
} else if (commandName === 'setup') {
|
|
76
|
+
result = await commands.setup(options)
|
|
77
|
+
} else if (commandName === 'migrate-all') {
|
|
78
|
+
result = await commands.migrateAll(options)
|
|
79
|
+
} else if (commandName === 'progress') {
|
|
80
|
+
const period = parsedArgs[0] || 'week'
|
|
81
|
+
result = await commands.progress(period)
|
|
82
|
+
} else if (commandName === 'build') {
|
|
83
|
+
const taskOrNumber = parsedArgs.join(' ')
|
|
84
|
+
result = await commands.build(taskOrNumber)
|
|
85
|
+
} else {
|
|
86
|
+
// Standard commands (most common case)
|
|
87
|
+
const param = parsedArgs.join(' ') || null
|
|
88
|
+
result = await commands[commandName](param)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 7. Display result
|
|
92
|
+
if (result && result.message) {
|
|
93
|
+
console.log(result.message)
|
|
241
94
|
}
|
|
242
95
|
|
|
243
|
-
|
|
244
|
-
|
|
96
|
+
process.exit(result && result.success ? 0 : 1)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('Error:', error.message)
|
|
99
|
+
if (process.env.DEBUG) {
|
|
100
|
+
console.error(error.stack)
|
|
101
|
+
}
|
|
102
|
+
process.exit(1)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse command arguments dynamically
|
|
108
|
+
*/
|
|
109
|
+
function parseCommandArgs(cmd, rawArgs) {
|
|
110
|
+
const parsedArgs = []
|
|
111
|
+
const options = {}
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
114
|
+
const arg = rawArgs[i]
|
|
115
|
+
|
|
116
|
+
if (arg.startsWith('--')) {
|
|
117
|
+
// Handle flags
|
|
118
|
+
const flagName = arg.slice(2)
|
|
119
|
+
|
|
120
|
+
// Check if next arg is a value (doesn't start with --)
|
|
121
|
+
if (i + 1 < rawArgs.length && !rawArgs[i + 1].startsWith('--')) {
|
|
122
|
+
options[flagName] = rawArgs[++i]
|
|
123
|
+
} else {
|
|
124
|
+
options[flagName] = true
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
parsedArgs.push(arg)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { parsedArgs, options }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Display help using registry
|
|
136
|
+
*/
|
|
137
|
+
function displayHelp() {
|
|
138
|
+
const categories = registry.getCategories()
|
|
139
|
+
const categorizedCommands = {}
|
|
140
|
+
|
|
141
|
+
// Group commands by category (exclude deprecated)
|
|
142
|
+
registry.getTerminalCommands().forEach((cmd) => {
|
|
143
|
+
if (cmd.deprecated) return
|
|
144
|
+
|
|
145
|
+
if (!categorizedCommands[cmd.category]) {
|
|
146
|
+
categorizedCommands[cmd.category] = []
|
|
147
|
+
}
|
|
148
|
+
categorizedCommands[cmd.category].push(cmd)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
console.log('prjct - Developer momentum tool for solo builders')
|
|
152
|
+
console.log('\nAvailable commands:\n')
|
|
153
|
+
|
|
154
|
+
// Display commands by category
|
|
155
|
+
Object.entries(categorizedCommands).forEach(([categoryKey, cmds]) => {
|
|
156
|
+
const categoryInfo = categories[categoryKey]
|
|
157
|
+
console.log(` ${categoryInfo.title}:`)
|
|
158
|
+
|
|
159
|
+
cmds.forEach((cmd) => {
|
|
160
|
+
const params = cmd.params ? ` ${cmd.params}` : ''
|
|
161
|
+
const spacing = ' '.repeat(Math.max(20 - cmd.name.length - params.length, 1))
|
|
162
|
+
const impl = cmd.implemented ? '' : ' (not implemented)'
|
|
163
|
+
console.log(` ${cmd.name}${params}${spacing}${cmd.description}${impl}`)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
console.log('')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const stats = registry.getStats()
|
|
170
|
+
console.log(`Total: ${stats.implemented} implemented / ${stats.total} commands`)
|
|
171
|
+
console.log('\nFor more info: https://prjct.app')
|
|
245
172
|
}
|
|
246
173
|
|
|
247
|
-
|
|
174
|
+
// Run CLI
|
|
175
|
+
main().catch((error) => {
|
|
176
|
+
console.error('Fatal error:', error.message)
|
|
177
|
+
if (process.env.DEBUG) {
|
|
178
|
+
console.error(error.stack)
|
|
179
|
+
}
|
|
180
|
+
process.exit(1)
|
|
181
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Executor
|
|
3
|
+
* Executes commands using templates - Claude decides everything
|
|
4
|
+
* ZERO if/else business logic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const templateLoader = require('./template-loader')
|
|
8
|
+
const contextBuilder = require('./context-builder')
|
|
9
|
+
const promptBuilder = require('./prompt-builder')
|
|
10
|
+
const toolRegistry = require('./tool-registry')
|
|
11
|
+
|
|
12
|
+
class CommandExecutor {
|
|
13
|
+
/**
|
|
14
|
+
* Execute a command agentically
|
|
15
|
+
* @param {string} commandName - Command name (e.g., 'now', 'done')
|
|
16
|
+
* @param {Object} params - Command parameters
|
|
17
|
+
* @param {string} projectPath - Project path
|
|
18
|
+
* @returns {Promise<Object>} Execution result
|
|
19
|
+
*/
|
|
20
|
+
async execute(commandName, params, projectPath) {
|
|
21
|
+
try {
|
|
22
|
+
// 1. Load template
|
|
23
|
+
const template = await templateLoader.load(commandName)
|
|
24
|
+
|
|
25
|
+
// 2. Build context
|
|
26
|
+
const context = await contextBuilder.build(projectPath, params)
|
|
27
|
+
|
|
28
|
+
// 3. Load current state
|
|
29
|
+
const state = await contextBuilder.loadState(context)
|
|
30
|
+
|
|
31
|
+
// 4. Build prompt for Claude
|
|
32
|
+
const prompt = promptBuilder.build(template, context, state)
|
|
33
|
+
|
|
34
|
+
// 5. Execute (in real implementation, this would call Claude)
|
|
35
|
+
// For now, we return structured data that Claude can work with
|
|
36
|
+
return {
|
|
37
|
+
success: true,
|
|
38
|
+
template,
|
|
39
|
+
context,
|
|
40
|
+
state,
|
|
41
|
+
prompt,
|
|
42
|
+
// In production: result from Claude's execution
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: error.message,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Execute tool with permission check
|
|
54
|
+
* @param {string} toolName - Tool name
|
|
55
|
+
* @param {Array} args - Tool arguments
|
|
56
|
+
* @param {string[]} allowedTools - Allowed tools for this command
|
|
57
|
+
* @returns {Promise<any>}
|
|
58
|
+
*/
|
|
59
|
+
async executeTool(toolName, args, allowedTools) {
|
|
60
|
+
// Check if tool is allowed
|
|
61
|
+
if (!toolRegistry.isAllowed(toolName, allowedTools)) {
|
|
62
|
+
throw new Error(`Tool ${toolName} not allowed for this command`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get tool function
|
|
66
|
+
const tool = toolRegistry.get(toolName)
|
|
67
|
+
|
|
68
|
+
// Execute tool
|
|
69
|
+
return await tool(...args)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Simple execution for direct tool access
|
|
74
|
+
* Used by legacy commands during migration
|
|
75
|
+
* @param {string} commandName - Command name
|
|
76
|
+
* @param {Function} executionFn - Function that uses tools
|
|
77
|
+
* @param {string} projectPath - Project path
|
|
78
|
+
* @returns {Promise<Object>}
|
|
79
|
+
*/
|
|
80
|
+
async executeSimple(commandName, executionFn, projectPath) {
|
|
81
|
+
try {
|
|
82
|
+
// Load template to get allowed tools
|
|
83
|
+
const template = await templateLoader.load(commandName)
|
|
84
|
+
const allowedTools = template.frontmatter['allowed-tools'] || []
|
|
85
|
+
|
|
86
|
+
// Build context
|
|
87
|
+
const context = await contextBuilder.build(projectPath)
|
|
88
|
+
|
|
89
|
+
// Create tools proxy that checks permissions
|
|
90
|
+
const tools = {
|
|
91
|
+
read: async (filePath) => this.executeTool('Read', [filePath], allowedTools),
|
|
92
|
+
write: async (filePath, content) =>
|
|
93
|
+
this.executeTool('Write', [filePath, content], allowedTools),
|
|
94
|
+
bash: async (command) => this.executeTool('Bash', [command], allowedTools),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Execute user function with tools
|
|
98
|
+
const result = await executionFn(tools, context)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
result,
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: error.message,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = new CommandExecutor()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Builder
|
|
3
|
+
* Builds project context for Claude to make decisions
|
|
4
|
+
* NO if/else logic - just data collection
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs').promises
|
|
8
|
+
const pathManager = require('../infrastructure/path-manager')
|
|
9
|
+
const configManager = require('../infrastructure/config-manager')
|
|
10
|
+
|
|
11
|
+
class ContextBuilder {
|
|
12
|
+
/**
|
|
13
|
+
* Build full project context for Claude
|
|
14
|
+
* @param {string} projectPath - Local project path
|
|
15
|
+
* @param {Object} commandParams - Command-specific parameters
|
|
16
|
+
* @returns {Promise<Object>} Context object
|
|
17
|
+
*/
|
|
18
|
+
async build(projectPath, commandParams = {}) {
|
|
19
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
20
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
// Project identification
|
|
24
|
+
projectId,
|
|
25
|
+
projectPath,
|
|
26
|
+
globalPath,
|
|
27
|
+
|
|
28
|
+
// File paths
|
|
29
|
+
paths: {
|
|
30
|
+
now: pathManager.getFilePath(projectId, 'core', 'now.md'),
|
|
31
|
+
next: pathManager.getFilePath(projectId, 'core', 'next.md'),
|
|
32
|
+
context: pathManager.getFilePath(projectId, 'core', 'context.md'),
|
|
33
|
+
shipped: pathManager.getFilePath(projectId, 'progress', 'shipped.md'),
|
|
34
|
+
metrics: pathManager.getFilePath(projectId, 'progress', 'metrics.md'),
|
|
35
|
+
ideas: pathManager.getFilePath(projectId, 'planning', 'ideas.md'),
|
|
36
|
+
roadmap: pathManager.getFilePath(projectId, 'planning', 'roadmap.md'),
|
|
37
|
+
memory: pathManager.getFilePath(projectId, 'memory', 'context.jsonl'),
|
|
38
|
+
analysis: pathManager.getFilePath(projectId, 'analysis', 'repo-summary.md'),
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Command parameters
|
|
42
|
+
params: commandParams,
|
|
43
|
+
|
|
44
|
+
// Timestamps
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
date: new Date().toLocaleString(),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load current project state
|
|
52
|
+
* @param {Object} context - Context from build()
|
|
53
|
+
* @returns {Promise<Object>} Current state
|
|
54
|
+
*/
|
|
55
|
+
async loadState(context) {
|
|
56
|
+
const state = {}
|
|
57
|
+
|
|
58
|
+
// Read all core files
|
|
59
|
+
for (const [key, filePath] of Object.entries(context.paths)) {
|
|
60
|
+
try {
|
|
61
|
+
state[key] = await fs.readFile(filePath, 'utf-8')
|
|
62
|
+
} catch {
|
|
63
|
+
state[key] = null
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return state
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check file existence
|
|
72
|
+
* @param {string} filePath - File path
|
|
73
|
+
* @returns {Promise<boolean>}
|
|
74
|
+
*/
|
|
75
|
+
async fileExists(filePath) {
|
|
76
|
+
try {
|
|
77
|
+
await fs.access(filePath)
|
|
78
|
+
return true
|
|
79
|
+
} catch {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = new ContextBuilder()
|