@ai-coders/context 0.2.1 ā 0.3.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 +113 -580
- package/dist/generators/agents/agentGenerator.d.ts +6 -9
- package/dist/generators/agents/agentGenerator.d.ts.map +1 -1
- package/dist/generators/agents/agentGenerator.js +89 -32
- package/dist/generators/agents/agentGenerator.js.map +1 -1
- package/dist/generators/agents/index.d.ts +0 -2
- package/dist/generators/agents/index.d.ts.map +1 -1
- package/dist/generators/agents/index.js +1 -5
- package/dist/generators/agents/index.js.map +1 -1
- package/dist/generators/agents/templates/index.d.ts +4 -0
- package/dist/generators/agents/templates/index.d.ts.map +1 -0
- package/dist/generators/agents/templates/index.js +8 -0
- package/dist/generators/agents/templates/index.js.map +1 -0
- package/dist/generators/agents/templates/indexTemplate.d.ts +3 -0
- package/dist/generators/agents/templates/indexTemplate.d.ts.map +1 -0
- package/dist/generators/agents/templates/indexTemplate.js +35 -0
- package/dist/generators/agents/templates/indexTemplate.js.map +1 -0
- package/dist/generators/agents/templates/playbookTemplate.d.ts +4 -0
- package/dist/generators/agents/templates/playbookTemplate.d.ts.map +1 -0
- package/dist/generators/agents/templates/playbookTemplate.js +63 -0
- package/dist/generators/agents/templates/playbookTemplate.js.map +1 -0
- package/dist/generators/agents/templates/types.d.ts +14 -0
- package/dist/generators/agents/templates/types.d.ts.map +1 -0
- package/dist/generators/agents/templates/types.js +3 -0
- package/dist/generators/agents/templates/types.js.map +1 -0
- package/dist/generators/documentation/documentationGenerator.d.ts +9 -15
- package/dist/generators/documentation/documentationGenerator.d.ts.map +1 -1
- package/dist/generators/documentation/documentationGenerator.js +65 -77
- package/dist/generators/documentation/documentationGenerator.js.map +1 -1
- package/dist/generators/documentation/guideRegistry.d.ts +6 -0
- package/dist/generators/documentation/guideRegistry.d.ts.map +1 -0
- package/dist/generators/documentation/guideRegistry.js +82 -0
- package/dist/generators/documentation/guideRegistry.js.map +1 -0
- package/dist/generators/documentation/index.d.ts +0 -6
- package/dist/generators/documentation/index.d.ts.map +1 -1
- package/dist/generators/documentation/index.js +1 -17
- package/dist/generators/documentation/index.js.map +1 -1
- package/dist/generators/documentation/templates/architectureTemplate.d.ts +3 -0
- package/dist/generators/documentation/templates/architectureTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/architectureTemplate.js +66 -0
- package/dist/generators/documentation/templates/architectureTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/common.d.ts +7 -0
- package/dist/generators/documentation/templates/common.d.ts.map +1 -0
- package/dist/generators/documentation/templates/common.js +58 -0
- package/dist/generators/documentation/templates/common.js.map +1 -0
- package/dist/generators/documentation/templates/dataFlowTemplate.d.ts +3 -0
- package/dist/generators/documentation/templates/dataFlowTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/dataFlowTemplate.js +55 -0
- package/dist/generators/documentation/templates/dataFlowTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/developmentWorkflowTemplate.d.ts +2 -0
- package/dist/generators/documentation/templates/developmentWorkflowTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/developmentWorkflowTemplate.js +59 -0
- package/dist/generators/documentation/templates/developmentWorkflowTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/frontMatter.d.ts +11 -0
- package/dist/generators/documentation/templates/frontMatter.d.ts.map +1 -0
- package/dist/generators/documentation/templates/frontMatter.js +29 -0
- package/dist/generators/documentation/templates/frontMatter.js.map +1 -0
- package/dist/generators/documentation/templates/glossaryTemplate.d.ts +3 -0
- package/dist/generators/documentation/templates/glossaryTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/glossaryTemplate.js +55 -0
- package/dist/generators/documentation/templates/glossaryTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/index.d.ts +11 -0
- package/dist/generators/documentation/templates/index.d.ts.map +1 -0
- package/dist/generators/documentation/templates/index.js +22 -0
- package/dist/generators/documentation/templates/index.js.map +1 -0
- package/dist/generators/documentation/templates/indexTemplate.d.ts +3 -0
- package/dist/generators/documentation/templates/indexTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/indexTemplate.js +56 -0
- package/dist/generators/documentation/templates/indexTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/projectOverviewTemplate.d.ts +3 -0
- package/dist/generators/documentation/templates/projectOverviewTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/projectOverviewTemplate.js +68 -0
- package/dist/generators/documentation/templates/projectOverviewTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/securityTemplate.d.ts +2 -0
- package/dist/generators/documentation/templates/securityTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/securityTemplate.js +53 -0
- package/dist/generators/documentation/templates/securityTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/testingTemplate.d.ts +2 -0
- package/dist/generators/documentation/templates/testingTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/testingTemplate.js +59 -0
- package/dist/generators/documentation/templates/testingTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/toolingTemplate.d.ts +2 -0
- package/dist/generators/documentation/templates/toolingTemplate.d.ts.map +1 -0
- package/dist/generators/documentation/templates/toolingTemplate.js +56 -0
- package/dist/generators/documentation/templates/toolingTemplate.js.map +1 -0
- package/dist/generators/documentation/templates/types.d.ts +23 -0
- package/dist/generators/documentation/templates/types.d.ts.map +1 -0
- package/dist/generators/documentation/templates/types.js +3 -0
- package/dist/generators/documentation/templates/types.js.map +1 -0
- package/dist/generators/documentation/templates.d.ts +31 -0
- package/dist/generators/documentation/templates.d.ts.map +1 -0
- package/dist/generators/documentation/templates.js +566 -0
- package/dist/generators/documentation/templates.js.map +1 -0
- package/dist/generators/plans/index.d.ts +2 -0
- package/dist/generators/plans/index.d.ts.map +1 -0
- package/dist/generators/plans/index.js +6 -0
- package/dist/generators/plans/index.js.map +1 -0
- package/dist/generators/plans/planGenerator.d.ts +22 -0
- package/dist/generators/plans/planGenerator.d.ts.map +1 -0
- package/dist/generators/plans/planGenerator.js +109 -0
- package/dist/generators/plans/planGenerator.js.map +1 -0
- package/dist/generators/plans/templates/indexTemplate.d.ts +3 -0
- package/dist/generators/plans/templates/indexTemplate.d.ts.map +1 -0
- package/dist/generators/plans/templates/indexTemplate.js +36 -0
- package/dist/generators/plans/templates/indexTemplate.js.map +1 -0
- package/dist/generators/plans/templates/planTemplate.d.ts +3 -0
- package/dist/generators/plans/templates/planTemplate.d.ts.map +1 -0
- package/dist/generators/plans/templates/planTemplate.js +83 -0
- package/dist/generators/plans/templates/planTemplate.js.map +1 -0
- package/dist/generators/plans/templates/types.d.ts +19 -0
- package/dist/generators/plans/templates/types.d.ts.map +1 -0
- package/dist/generators/plans/templates/types.js +3 -0
- package/dist/generators/plans/templates/types.js.map +1 -0
- package/dist/generators/shared/contextGenerator.d.ts +2 -7
- package/dist/generators/shared/contextGenerator.d.ts.map +1 -1
- package/dist/generators/shared/contextGenerator.js +2 -98
- package/dist/generators/shared/contextGenerator.js.map +1 -1
- package/dist/generators/shared/directoryTemplateHelpers.d.ts +2 -0
- package/dist/generators/shared/directoryTemplateHelpers.d.ts.map +1 -0
- package/dist/generators/shared/directoryTemplateHelpers.js +12 -0
- package/dist/generators/shared/directoryTemplateHelpers.js.map +1 -0
- package/dist/generators/shared/index.d.ts +1 -0
- package/dist/generators/shared/index.d.ts.map +1 -1
- package/dist/generators/shared/index.js +3 -1
- package/dist/generators/shared/index.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1300 -609
- package/dist/index.js.map +1 -1
- package/dist/utils/cliUI.d.ts +6 -4
- package/dist/utils/cliUI.d.ts.map +1 -1
- package/dist/utils/cliUI.js +71 -56
- package/dist/utils/cliUI.js.map +1 -1
- package/dist/utils/i18n.d.ts +181 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +401 -0
- package/dist/utils/i18n.js.map +1 -0
- package/package.json +26 -23
- package/prompts/update_plan_prompt.md +42 -0
- package/prompts/update_scaffold_prompt.md +48 -0
- package/dist/generators/agentGenerator.d.ts +0 -23
- package/dist/generators/agentGenerator.d.ts.map +0 -1
- package/dist/generators/agentGenerator.js +0 -357
- package/dist/generators/agentGenerator.js.map +0 -1
- package/dist/generators/documentation/enhancedDocumentationGenerator.d.ts +0 -21
- package/dist/generators/documentation/enhancedDocumentationGenerator.d.ts.map +0 -1
- package/dist/generators/documentation/enhancedDocumentationGenerator.js +0 -216
- package/dist/generators/documentation/enhancedDocumentationGenerator.js.map +0 -1
- package/dist/generators/documentation/newDocumentationTemplates.d.ts +0 -19
- package/dist/generators/documentation/newDocumentationTemplates.d.ts.map +0 -1
- package/dist/generators/documentation/newDocumentationTemplates.js +0 -307
- package/dist/generators/documentation/newDocumentationTemplates.js.map +0 -1
- package/dist/generators/documentationGenerator.d.ts +0 -22
- package/dist/generators/documentationGenerator.d.ts.map +0 -1
- package/dist/generators/documentationGenerator.js +0 -235
- package/dist/generators/documentationGenerator.js.map +0 -1
- package/dist/generators/documentationTemplates.d.ts +0 -16
- package/dist/generators/documentationTemplates.d.ts.map +0 -1
- package/dist/generators/documentationTemplates.js +0 -326
- package/dist/generators/documentationTemplates.js.map +0 -1
- package/dist/generators/documentationUtils.d.ts +0 -7
- package/dist/generators/documentationUtils.d.ts.map +0 -1
- package/dist/generators/documentationUtils.js +0 -38
- package/dist/generators/documentationUtils.js.map +0 -1
- package/dist/generators/incrementalDocumentationGenerator.d.ts +0 -33
- package/dist/generators/incrementalDocumentationGenerator.d.ts.map +0 -1
- package/dist/generators/incrementalDocumentationGenerator.js +0 -400
- package/dist/generators/incrementalDocumentationGenerator.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -38,739 +38,1430 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.runGenerate = runGenerate;
|
|
41
|
-
exports.runGuidelines = runGuidelines;
|
|
42
41
|
exports.runAnalyze = runAnalyze;
|
|
43
42
|
exports.runUpdate = runUpdate;
|
|
44
43
|
exports.runPreview = runPreview;
|
|
44
|
+
exports.runGuidelines = runGuidelines;
|
|
45
|
+
exports.runInit = runInit;
|
|
45
46
|
const commander_1 = require("commander");
|
|
46
47
|
const path = __importStar(require("path"));
|
|
47
|
-
const
|
|
48
|
+
const fs = __importStar(require("fs-extra"));
|
|
49
|
+
const glob_1 = require("glob");
|
|
48
50
|
const dotenv = __importStar(require("dotenv"));
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
52
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
51
53
|
const fileMapper_1 = require("./utils/fileMapper");
|
|
52
54
|
const documentationGenerator_1 = require("./generators/documentation/documentationGenerator");
|
|
53
55
|
const agentGenerator_1 = require("./generators/agents/agentGenerator");
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
+
const planGenerator_1 = require("./generators/plans/planGenerator");
|
|
57
|
+
const shared_1 = require("./generators/shared");
|
|
56
58
|
const cliUI_1 = require("./utils/cliUI");
|
|
59
|
+
const i18n_1 = require("./utils/i18n");
|
|
57
60
|
const llmClientFactory_1 = require("./services/llmClientFactory");
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const
|
|
61
|
+
const guideRegistry_1 = require("./generators/documentation/guideRegistry");
|
|
62
|
+
const agentTypes_1 = require("./generators/agents/agentTypes");
|
|
63
|
+
dotenv.config();
|
|
64
|
+
const initialLocale = (0, i18n_1.detectLocale)(process.argv.slice(2), process.env.AI_CONTEXT_LANG);
|
|
65
|
+
let currentLocale = initialLocale;
|
|
66
|
+
let translateFn = (0, i18n_1.createTranslator)(initialLocale);
|
|
67
|
+
const t = (key, params) => translateFn(key, params);
|
|
68
|
+
const localeLabelKeys = {
|
|
69
|
+
en: 'prompts.language.option.en',
|
|
70
|
+
'pt-BR': 'prompts.language.option.pt-BR'
|
|
71
|
+
};
|
|
62
72
|
const program = new commander_1.Command();
|
|
63
|
-
const ui = new cliUI_1.CLIInterface();
|
|
73
|
+
const ui = new cliUI_1.CLIInterface(t);
|
|
74
|
+
const VERSION = '0.3.0';
|
|
75
|
+
const DEFAULT_MODEL = 'x-ai/grok-4-fast:free';
|
|
76
|
+
const DOC_CHOICES = guideRegistry_1.DOCUMENT_GUIDES.map(guide => ({
|
|
77
|
+
name: `${guide.title} (${guide.key})`,
|
|
78
|
+
value: guide.key
|
|
79
|
+
}));
|
|
80
|
+
const AGENT_CHOICES = agentTypes_1.AGENT_TYPES.map(agent => ({
|
|
81
|
+
name: formatAgentLabel(agent),
|
|
82
|
+
value: agent
|
|
83
|
+
}));
|
|
64
84
|
program
|
|
65
85
|
.name('ai-context')
|
|
66
|
-
.description('
|
|
67
|
-
.version(
|
|
86
|
+
.description(t('cli.description'))
|
|
87
|
+
.version(VERSION);
|
|
88
|
+
program.option('-l, --lang <locale>', t('global.options.lang'), initialLocale);
|
|
68
89
|
program
|
|
69
90
|
.command('init')
|
|
70
|
-
.description('
|
|
71
|
-
.argument('<repo-path>', '
|
|
72
|
-
.argument('[type]', '
|
|
73
|
-
.option('-o, --output <dir>', '
|
|
74
|
-
.option('
|
|
75
|
-
.option('
|
|
76
|
-
.option('
|
|
77
|
-
.option('--
|
|
78
|
-
.option('--
|
|
79
|
-
.option('-v, --verbose', 'Verbose output')
|
|
91
|
+
.description(t('commands.init.description'))
|
|
92
|
+
.argument('<repo-path>', t('commands.init.arguments.repoPath'))
|
|
93
|
+
.argument('[type]', t('commands.init.arguments.type'), 'both')
|
|
94
|
+
.option('-o, --output <dir>', t('commands.init.options.output'), './.context')
|
|
95
|
+
.option('--docs <keys...>', t('commands.init.options.docs'))
|
|
96
|
+
.option('--agents <keys...>', t('commands.init.options.agents'))
|
|
97
|
+
.option('--exclude <patterns...>', t('commands.init.options.exclude'))
|
|
98
|
+
.option('--include <patterns...>', t('commands.init.options.include'))
|
|
99
|
+
.option('-v, --verbose', t('commands.init.options.verbose'))
|
|
80
100
|
.action(async (repoPath, type, options) => {
|
|
81
101
|
try {
|
|
82
|
-
|
|
83
|
-
if (!['docs', 'agents', 'guidelines', 'both'].includes(type)) {
|
|
84
|
-
ui.displayError(`Invalid type "${type}". Must be "docs", "agents", "guidelines", or "both".`);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
// Set options based on type argument
|
|
88
|
-
if (type === 'docs') {
|
|
89
|
-
options.docsOnly = true;
|
|
90
|
-
}
|
|
91
|
-
else if (type === 'agents') {
|
|
92
|
-
options.agentsOnly = true;
|
|
93
|
-
}
|
|
94
|
-
else if (type === 'guidelines') {
|
|
95
|
-
options.guidelinesOnly = true;
|
|
96
|
-
}
|
|
97
|
-
// For 'both', neither flag is set
|
|
98
|
-
await runGenerate(repoPath, options);
|
|
102
|
+
await runInit(repoPath, type, options);
|
|
99
103
|
}
|
|
100
104
|
catch (error) {
|
|
101
|
-
ui.displayError('
|
|
105
|
+
ui.displayError(t('errors.init.scaffoldFailed'), error);
|
|
102
106
|
process.exit(1);
|
|
103
107
|
}
|
|
104
108
|
});
|
|
105
109
|
program
|
|
106
|
-
.command('
|
|
107
|
-
.description('
|
|
108
|
-
.argument('<repo-path>', '
|
|
109
|
-
.
|
|
110
|
-
.option('-
|
|
111
|
-
.option('
|
|
112
|
-
.option('
|
|
113
|
-
.option('--
|
|
114
|
-
.option('--
|
|
115
|
-
.option('--
|
|
116
|
-
.
|
|
117
|
-
.option('--include <patterns...>', 'Patterns to include in analysis')
|
|
118
|
-
.option('-v, --verbose', 'Verbose output')
|
|
119
|
-
.action(async (repoPath, options) => {
|
|
110
|
+
.command('scaffold')
|
|
111
|
+
.description(t('commands.scaffold.description'))
|
|
112
|
+
.argument('<repo-path>', t('commands.init.arguments.repoPath'))
|
|
113
|
+
.argument('[type]', t('commands.init.arguments.type'), 'both')
|
|
114
|
+
.option('-o, --output <dir>', t('commands.init.options.output'), './.context')
|
|
115
|
+
.option('--docs <keys...>', t('commands.init.options.docs'))
|
|
116
|
+
.option('--agents <keys...>', t('commands.init.options.agents'))
|
|
117
|
+
.option('--exclude <patterns...>', t('commands.init.options.exclude'))
|
|
118
|
+
.option('--include <patterns...>', t('commands.init.options.include'))
|
|
119
|
+
.option('-v, --verbose', t('commands.init.options.verbose'))
|
|
120
|
+
.action(async (repoPath, type, options) => {
|
|
120
121
|
try {
|
|
121
|
-
await
|
|
122
|
+
await runInit(repoPath, type, options);
|
|
122
123
|
}
|
|
123
124
|
catch (error) {
|
|
124
|
-
ui.displayError('
|
|
125
|
+
ui.displayError(t('errors.init.scaffoldFailed'), error);
|
|
125
126
|
process.exit(1);
|
|
126
127
|
}
|
|
127
128
|
});
|
|
128
129
|
program
|
|
129
|
-
.command('
|
|
130
|
-
.description('
|
|
131
|
-
.argument('<repo-path>', '
|
|
132
|
-
.option('--
|
|
133
|
-
.option('--
|
|
134
|
-
.option('
|
|
135
|
-
.option('
|
|
136
|
-
.option('-
|
|
130
|
+
.command('fill')
|
|
131
|
+
.description(t('commands.fill.description'))
|
|
132
|
+
.argument('<repo-path>', t('commands.fill.arguments.repoPath'))
|
|
133
|
+
.option('-o, --output <dir>', t('commands.fill.options.output'), './.context')
|
|
134
|
+
.option('-k, --api-key <key>', t('commands.fill.options.apiKey'))
|
|
135
|
+
.option('-m, --model <model>', t('commands.fill.options.model'), DEFAULT_MODEL)
|
|
136
|
+
.option('-p, --provider <provider>', t('commands.fill.options.provider'))
|
|
137
|
+
.option('--base-url <url>', t('commands.fill.options.baseUrl'))
|
|
138
|
+
.option('--prompt <file>', t('commands.fill.options.prompt'), path.join(__dirname, '../prompts/update_scaffold_prompt.md'))
|
|
139
|
+
.option('--dry-run', t('commands.fill.options.dryRun'), false)
|
|
140
|
+
.option('--all', t('commands.fill.options.all'), false)
|
|
141
|
+
.option('--limit <number>', t('commands.fill.options.limit'), (value) => parseInt(value, 10))
|
|
142
|
+
.option('--docs <keys...>', t('commands.fill.options.docs'))
|
|
143
|
+
.option('--agents <keys...>', t('commands.fill.options.agents'))
|
|
144
|
+
.option('--exclude <patterns...>', t('commands.fill.options.exclude'))
|
|
145
|
+
.option('--include <patterns...>', t('commands.fill.options.include'))
|
|
146
|
+
.option('-v, --verbose', t('commands.fill.options.verbose'))
|
|
137
147
|
.action(async (repoPath, options) => {
|
|
138
148
|
try {
|
|
139
|
-
await
|
|
149
|
+
await runLlmFill(repoPath, options);
|
|
140
150
|
}
|
|
141
151
|
catch (error) {
|
|
142
|
-
ui.displayError('
|
|
152
|
+
ui.displayError(t('errors.fill.failed'), error);
|
|
143
153
|
process.exit(1);
|
|
144
154
|
}
|
|
145
155
|
});
|
|
146
156
|
program
|
|
147
|
-
.command('
|
|
148
|
-
.description('
|
|
149
|
-
.argument('<
|
|
150
|
-
.option('--
|
|
151
|
-
.option('--
|
|
152
|
-
.option('--
|
|
153
|
-
.option('--
|
|
154
|
-
.option('--
|
|
155
|
-
.option('
|
|
156
|
-
.option('
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
.command('plan')
|
|
158
|
+
.description(t('commands.plan.description'))
|
|
159
|
+
.argument('<plan-name>', t('commands.plan.arguments.planName'))
|
|
160
|
+
.option('-o, --output <dir>', t('commands.plan.options.output'), './.context')
|
|
161
|
+
.option('--title <title>', t('commands.plan.options.title'))
|
|
162
|
+
.option('--summary <text>', t('commands.plan.options.summary'))
|
|
163
|
+
.option('--agents <types...>', t('commands.plan.options.agents'))
|
|
164
|
+
.option('--docs <keys...>', t('commands.plan.options.docs'))
|
|
165
|
+
.option('-f, --force', t('commands.plan.options.force'))
|
|
166
|
+
.option('--fill', t('commands.plan.options.fill'))
|
|
167
|
+
.option('-r, --repo <path>', t('commands.plan.options.repo'))
|
|
168
|
+
.option('-k, --api-key <key>', t('commands.plan.options.apiKey'))
|
|
169
|
+
.option('-m, --model <model>', t('commands.plan.options.model'), DEFAULT_MODEL)
|
|
170
|
+
.option('-p, --provider <provider>', t('commands.plan.options.provider'))
|
|
171
|
+
.option('--base-url <url>', t('commands.plan.options.baseUrl'))
|
|
172
|
+
.option('--prompt <file>', t('commands.plan.options.prompt'), path.join(__dirname, '../prompts/update_plan_prompt.md'))
|
|
173
|
+
.option('--dry-run', t('commands.plan.options.dryRun'), false)
|
|
174
|
+
.option('--include <patterns...>', t('commands.plan.options.include'))
|
|
175
|
+
.option('--exclude <patterns...>', t('commands.plan.options.exclude'))
|
|
176
|
+
.option('-v, --verbose', t('commands.plan.options.verbose'))
|
|
177
|
+
.action(async (planName, rawOptions) => {
|
|
178
|
+
const agentSelection = parseAgentSelection(rawOptions.agents);
|
|
179
|
+
if (agentSelection.invalid.length > 0) {
|
|
180
|
+
ui.displayWarning(t('warnings.agents.unknown', { values: agentSelection.invalid.join(', ') }));
|
|
167
181
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
const docSelection = parseDocSelection(rawOptions.docs);
|
|
183
|
+
if (docSelection.invalid.length > 0) {
|
|
184
|
+
ui.displayWarning(t('warnings.docs.unknown', { values: docSelection.invalid.join(', ') }));
|
|
171
185
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
$ ai-context guidelines ./ testing security # Generate only testing and security guidelines
|
|
194
|
-
$ ai-context guidelines ./ --project-type frontend # Generate guidelines for frontend project
|
|
195
|
-
$ ai-context guidelines ./ --include-examples # Include code examples in guidelines`)
|
|
196
|
-
.action(async (repoPath, categories, options) => {
|
|
186
|
+
const outputDir = path.resolve(rawOptions.output || './.context');
|
|
187
|
+
if (rawOptions.fill) {
|
|
188
|
+
try {
|
|
189
|
+
await scaffoldPlanIfNeeded(planName, outputDir, {
|
|
190
|
+
title: rawOptions.title,
|
|
191
|
+
summary: rawOptions.summary,
|
|
192
|
+
agentSelection,
|
|
193
|
+
docSelection,
|
|
194
|
+
force: Boolean(rawOptions.force),
|
|
195
|
+
verbose: Boolean(rawOptions.verbose)
|
|
196
|
+
});
|
|
197
|
+
await runPlanFill(planName, { ...rawOptions, output: outputDir });
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
ui.displayError(t('errors.plan.fillFailed'), error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const generator = new planGenerator_1.PlanGenerator();
|
|
206
|
+
ui.startSpinner(t('spinner.plan.creating'));
|
|
197
207
|
try {
|
|
198
|
-
await
|
|
208
|
+
const result = await generator.generatePlan({
|
|
209
|
+
planName,
|
|
210
|
+
outputDir,
|
|
211
|
+
title: rawOptions.title,
|
|
212
|
+
summary: rawOptions.summary,
|
|
213
|
+
selectedAgentTypes: agentSelection.explicitNone ? null : agentSelection.selected,
|
|
214
|
+
selectedDocKeys: docSelection.explicitNone ? null : docSelection.selected,
|
|
215
|
+
force: Boolean(rawOptions.force),
|
|
216
|
+
verbose: Boolean(rawOptions.verbose)
|
|
217
|
+
});
|
|
218
|
+
ui.updateSpinner(t('spinner.plan.created'), 'success');
|
|
219
|
+
ui.displaySuccess(t('success.plan.createdAt', { path: chalk_1.default.cyan(result.relativePath) }));
|
|
199
220
|
}
|
|
200
221
|
catch (error) {
|
|
201
|
-
ui.
|
|
222
|
+
ui.updateSpinner(t('spinner.plan.creationFailed'), 'fail');
|
|
223
|
+
ui.displayError(t('errors.plan.creationFailed'), error);
|
|
202
224
|
process.exit(1);
|
|
203
225
|
}
|
|
226
|
+
finally {
|
|
227
|
+
ui.stopSpinner();
|
|
228
|
+
}
|
|
204
229
|
});
|
|
205
|
-
async function
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
for (const envVar of envVars) {
|
|
212
|
-
apiKey = process.env[envVar];
|
|
213
|
-
if (apiKey)
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
230
|
+
async function runInit(repoPath, type, rawOptions) {
|
|
231
|
+
const resolvedType = resolveScaffoldType(type, rawOptions);
|
|
232
|
+
const docSelection = parseDocSelection(rawOptions.docs);
|
|
233
|
+
const agentSelection = parseAgentSelection(rawOptions.agents);
|
|
234
|
+
if (docSelection.invalid.length > 0) {
|
|
235
|
+
ui.displayWarning(t('warnings.docs.unknown', { values: docSelection.invalid.join(', ') }));
|
|
216
236
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
outputDir: path.resolve(options.output),
|
|
220
|
-
model: options.model,
|
|
221
|
-
apiKey,
|
|
222
|
-
provider,
|
|
223
|
-
exclude: options.exclude || [],
|
|
224
|
-
include: options.include,
|
|
225
|
-
verbose: options.verbose || false
|
|
226
|
-
};
|
|
227
|
-
if (!cliOptions.apiKey) {
|
|
228
|
-
const envVars = llmClientFactory_1.LLMClientFactory.getEnvironmentVariables()[provider];
|
|
229
|
-
ui.displayError(`${provider.toUpperCase()} API key is required. Set one of these environment variables: ${envVars.join(', ')} or use --api-key option.`);
|
|
230
|
-
process.exit(1);
|
|
237
|
+
if (agentSelection.invalid.length > 0) {
|
|
238
|
+
ui.displayWarning(t('warnings.agents.unknown', { values: agentSelection.invalid.join(', ') }));
|
|
231
239
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
apiKey: cliOptions.apiKey,
|
|
243
|
-
model: cliOptions.model || 'google/gemini-2.5-flash-preview-05-20',
|
|
244
|
-
provider: cliOptions.provider || 'openrouter'
|
|
240
|
+
const options = {
|
|
241
|
+
repoPath: path.resolve(repoPath),
|
|
242
|
+
outputDir: path.resolve(rawOptions.output || './.context'),
|
|
243
|
+
include: rawOptions.include,
|
|
244
|
+
exclude: rawOptions.exclude || [],
|
|
245
|
+
verbose: rawOptions.verbose || false,
|
|
246
|
+
scaffoldDocs: shouldGenerateDocs(resolvedType, docSelection),
|
|
247
|
+
scaffoldAgents: shouldGenerateAgents(resolvedType, agentSelection),
|
|
248
|
+
selectedDocKeys: docSelection.selected,
|
|
249
|
+
selectedAgentTypes: agentSelection.selected
|
|
245
250
|
};
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const guidelinesGenerator = new guidelinesGenerator_1.GuidelinesGenerator(fileMapper, llmClient);
|
|
250
|
-
// Step 1: Map repository structure
|
|
251
|
-
ui.displayStep(1, 4, 'Analyzing repository structure');
|
|
252
|
-
ui.startSpinner('Scanning files and directories...');
|
|
253
|
-
const repoStructure = await fileMapper.mapRepository(cliOptions.repoPath, cliOptions.include);
|
|
254
|
-
ui.updateSpinner(`Found ${repoStructure.totalFiles} files in ${repoStructure.directories.length} directories`, 'success');
|
|
255
|
-
// Display analysis results
|
|
256
|
-
if (cliOptions.verbose) {
|
|
257
|
-
ui.displayAnalysisResults(repoStructure.totalFiles, repoStructure.directories.length, ui.formatBytes(repoStructure.totalSize));
|
|
258
|
-
// Show file distribution
|
|
259
|
-
const extensions = new Map();
|
|
260
|
-
repoStructure.files.forEach(file => {
|
|
261
|
-
const ext = file.extension || 'no-extension';
|
|
262
|
-
extensions.set(ext, (extensions.get(ext) || 0) + 1);
|
|
263
|
-
});
|
|
264
|
-
ui.displayFileTypeDistribution(extensions, repoStructure.totalFiles);
|
|
251
|
+
if (!options.scaffoldDocs && !options.scaffoldAgents) {
|
|
252
|
+
ui.displayWarning(t('warnings.scaffold.noneSelected'));
|
|
253
|
+
return;
|
|
265
254
|
}
|
|
255
|
+
await ensurePaths(options);
|
|
256
|
+
ui.displayWelcome(VERSION);
|
|
257
|
+
ui.displayProjectInfo(options.repoPath, options.outputDir, resolvedType);
|
|
258
|
+
const fileMapper = new fileMapper_1.FileMapper(options.exclude);
|
|
259
|
+
ui.displayStep(1, 3, t('steps.init.analyze'));
|
|
260
|
+
ui.startSpinner(t('spinner.repo.scanning'));
|
|
261
|
+
const repoStructure = await fileMapper.mapRepository(options.repoPath, options.include);
|
|
262
|
+
ui.updateSpinner(t('spinner.repo.scanComplete', {
|
|
263
|
+
fileCount: repoStructure.totalFiles,
|
|
264
|
+
directoryCount: repoStructure.directories.length
|
|
265
|
+
}), 'success');
|
|
266
266
|
let docsGenerated = 0;
|
|
267
267
|
let agentsGenerated = 0;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
ui.displayStep(2,
|
|
272
|
-
ui.startSpinner('
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
268
|
+
const docGenerator = new documentationGenerator_1.DocumentationGenerator();
|
|
269
|
+
const agentGenerator = new agentGenerator_1.AgentGenerator();
|
|
270
|
+
if (options.scaffoldDocs) {
|
|
271
|
+
ui.displayStep(2, 3, t('steps.init.docs'));
|
|
272
|
+
ui.startSpinner(t('spinner.docs.creating'));
|
|
273
|
+
docsGenerated = await docGenerator.generateDocumentation(repoStructure, options.outputDir, { selectedDocs: options.selectedDocKeys }, options.verbose);
|
|
274
|
+
ui.updateSpinner(t('spinner.docs.created', { count: docsGenerated }), 'success');
|
|
275
|
+
}
|
|
276
|
+
if (options.scaffoldAgents) {
|
|
277
|
+
ui.displayStep(3, options.scaffoldDocs ? 3 : 2, t('steps.init.agents'));
|
|
278
|
+
ui.startSpinner(t('spinner.agents.creating'));
|
|
279
|
+
agentsGenerated = await agentGenerator.generateAgentPrompts(repoStructure, options.outputDir, options.selectedAgentTypes, options.verbose);
|
|
280
|
+
ui.updateSpinner(t('spinner.agents.created', { count: agentsGenerated }), 'success');
|
|
281
|
+
}
|
|
282
|
+
ui.displayGenerationSummary(docsGenerated, agentsGenerated);
|
|
283
|
+
ui.displaySuccess(t('success.scaffold.ready', { path: chalk_1.default.cyan(options.outputDir) }));
|
|
284
|
+
}
|
|
285
|
+
function resolveScaffoldType(type, rawOptions) {
|
|
286
|
+
const normalized = (type || 'both').toLowerCase();
|
|
287
|
+
const allowed = ['docs', 'agents', 'both'];
|
|
288
|
+
if (!allowed.includes(normalized)) {
|
|
289
|
+
throw new Error(t('errors.init.invalidType', { value: type, allowed: allowed.join(', ') }));
|
|
290
|
+
}
|
|
291
|
+
if (rawOptions.docsOnly) {
|
|
292
|
+
return 'docs';
|
|
293
|
+
}
|
|
294
|
+
if (rawOptions.agentsOnly) {
|
|
295
|
+
return 'agents';
|
|
296
|
+
}
|
|
297
|
+
return normalized;
|
|
298
|
+
}
|
|
299
|
+
async function ensurePaths(options) {
|
|
300
|
+
const exists = await fs.pathExists(options.repoPath);
|
|
301
|
+
if (!exists) {
|
|
302
|
+
throw new Error(t('errors.common.repoMissing', { path: options.repoPath }));
|
|
303
|
+
}
|
|
304
|
+
await fs.ensureDir(options.outputDir);
|
|
305
|
+
}
|
|
306
|
+
async function runGenerate(repoPath, options) {
|
|
307
|
+
const type = options?.docsOnly ? 'docs' : options?.agentsOnly ? 'agents' : (options?.type || 'both');
|
|
308
|
+
await runInit(repoPath, type, {
|
|
309
|
+
output: options?.output ?? options?.outputDir ?? './.context',
|
|
310
|
+
include: options?.include,
|
|
311
|
+
exclude: options?.exclude,
|
|
312
|
+
verbose: options?.verbose,
|
|
313
|
+
docs: options?.docs,
|
|
314
|
+
agents: options?.agents,
|
|
315
|
+
docsOnly: options?.docsOnly,
|
|
316
|
+
agentsOnly: options?.agentsOnly
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async function runAnalyze(..._args) {
|
|
320
|
+
throw new Error(t('errors.commands.analyzeRemoved'));
|
|
321
|
+
}
|
|
322
|
+
async function runUpdate(..._args) {
|
|
323
|
+
throw new Error(t('errors.commands.updateRemoved'));
|
|
324
|
+
}
|
|
325
|
+
async function runPreview(..._args) {
|
|
326
|
+
throw new Error(t('errors.commands.previewRemoved'));
|
|
327
|
+
}
|
|
328
|
+
async function runGuidelines(..._args) {
|
|
329
|
+
throw new Error(t('errors.commands.guidelinesRemoved'));
|
|
330
|
+
}
|
|
331
|
+
async function resolveLlmConfig(rawOptions, defaults) {
|
|
332
|
+
const promptPath = path.resolve(rawOptions.prompt || defaults.promptPath);
|
|
333
|
+
if (!(await fs.pathExists(promptPath))) {
|
|
334
|
+
throw new Error(t('errors.fill.promptMissing', { path: promptPath }));
|
|
335
|
+
}
|
|
336
|
+
const providerEnvMap = llmClientFactory_1.LLMClientFactory.getEnvironmentVariables();
|
|
337
|
+
const defaultModels = llmClientFactory_1.LLMClientFactory.getDefaultModels();
|
|
338
|
+
let provider = rawOptions.provider;
|
|
339
|
+
let model = rawOptions.model;
|
|
340
|
+
let apiKey = rawOptions.apiKey;
|
|
341
|
+
if (!apiKey) {
|
|
342
|
+
if (provider) {
|
|
343
|
+
for (const envVar of providerEnvMap[provider]) {
|
|
344
|
+
const value = process.env[envVar];
|
|
345
|
+
if (value) {
|
|
346
|
+
apiKey = value;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
279
350
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
351
|
+
else {
|
|
352
|
+
outer: for (const [prov, envVars] of Object.entries(providerEnvMap)) {
|
|
353
|
+
for (const envVar of envVars) {
|
|
354
|
+
const value = process.env[envVar];
|
|
355
|
+
if (value) {
|
|
356
|
+
apiKey = value;
|
|
357
|
+
provider = prov;
|
|
358
|
+
break outer;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
283
362
|
}
|
|
284
363
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
ui.startSpinner('Creating specialized agent prompts...');
|
|
289
|
-
try {
|
|
290
|
-
await agentGenerator.generateAgentPrompts(repoStructure, cliOptions.outputDir, false // We'll handle our own progress display
|
|
291
|
-
);
|
|
292
|
-
agentsGenerated = 9; // Number of agent files generated
|
|
293
|
-
ui.updateSpinner('Agent prompts generated successfully', 'success');
|
|
364
|
+
if (!provider) {
|
|
365
|
+
if (model) {
|
|
366
|
+
provider = llmClientFactory_1.LLMClientFactory.detectProviderFromModel(model);
|
|
294
367
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
throw error;
|
|
368
|
+
else if (apiKey) {
|
|
369
|
+
provider = llmClientFactory_1.LLMClientFactory.getProviderFromApiKey(apiKey);
|
|
298
370
|
}
|
|
299
371
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
ui.startSpinner('Creating comprehensive development guidelines...');
|
|
304
|
-
try {
|
|
305
|
-
await guidelinesGenerator.generateGuidelines(repoStructure, cliOptions.outputDir, {}, // Default config
|
|
306
|
-
false // We'll handle our own progress display
|
|
307
|
-
);
|
|
308
|
-
guidelinesGenerated = 12; // Number of guideline categories
|
|
309
|
-
ui.updateSpinner('Guidelines generated successfully', 'success');
|
|
372
|
+
if (!model) {
|
|
373
|
+
if (provider === 'openrouter' && process.env.OPENROUTER_MODEL) {
|
|
374
|
+
model = process.env.OPENROUTER_MODEL;
|
|
310
375
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
376
|
+
else if (provider && defaultModels[provider]?.length) {
|
|
377
|
+
model = defaultModels[provider][0];
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
model = defaults.fallbackModel;
|
|
381
|
+
provider = llmClientFactory_1.LLMClientFactory.detectProviderFromModel(model);
|
|
314
382
|
}
|
|
315
383
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// Get usage statistics from the LLM client
|
|
319
|
-
const usageStats = llmClient.getUsageStats();
|
|
320
|
-
ui.displayGenerationSummary(docsGenerated, agentsGenerated, usageStats);
|
|
321
|
-
if (options.guidelinesOnly && guidelinesGenerated > 0) {
|
|
322
|
-
ui.displaySuccess(`Guidelines generated! Output saved to: ${cliOptions.outputDir}`);
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
ui.displaySuccess(`Output saved to: ${cliOptions.outputDir}`);
|
|
384
|
+
if (!provider) {
|
|
385
|
+
provider = llmClientFactory_1.LLMClientFactory.detectProviderFromModel(model || defaults.fallbackModel);
|
|
326
386
|
}
|
|
327
|
-
}
|
|
328
|
-
async function runGuidelines(repoPath, categories, options) {
|
|
329
|
-
const provider = options.provider || llmClientFactory_1.LLMClientFactory.detectProviderFromModel(options.model);
|
|
330
|
-
// Get API key from options or environment variables
|
|
331
|
-
let apiKey = options.apiKey;
|
|
332
387
|
if (!apiKey) {
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
388
|
+
for (const envVar of providerEnvMap[provider]) {
|
|
389
|
+
const value = process.env[envVar];
|
|
390
|
+
if (value) {
|
|
391
|
+
apiKey = value;
|
|
337
392
|
break;
|
|
393
|
+
}
|
|
338
394
|
}
|
|
339
395
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
396
|
+
if (!apiKey) {
|
|
397
|
+
const envVars = providerEnvMap[provider];
|
|
398
|
+
throw new Error(t('errors.fill.apiKeyMissing', {
|
|
399
|
+
provider: provider.toUpperCase(),
|
|
400
|
+
envVars: envVars.join(', ')
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
345
404
|
provider,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
405
|
+
model: model || defaults.fallbackModel,
|
|
406
|
+
apiKey,
|
|
407
|
+
promptPath,
|
|
408
|
+
baseUrl: rawOptions.baseUrl
|
|
349
409
|
};
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
410
|
+
}
|
|
411
|
+
async function runLlmFill(repoPath, rawOptions) {
|
|
412
|
+
const resolvedRepo = path.resolve(repoPath);
|
|
413
|
+
const outputDir = path.resolve(rawOptions.output || './.context');
|
|
414
|
+
const docsDir = path.join(outputDir, 'docs');
|
|
415
|
+
const agentsDir = path.join(outputDir, 'agents');
|
|
416
|
+
await ensureDirectoryExists(docsDir, t('errors.fill.missingDocsScaffold'));
|
|
417
|
+
await ensureDirectoryExists(agentsDir, t('errors.fill.missingAgentsScaffold'));
|
|
418
|
+
const docSelection = parseDocSelection(rawOptions.docs);
|
|
419
|
+
const agentSelection = parseAgentSelection(rawOptions.agents);
|
|
420
|
+
if (docSelection.invalid.length > 0) {
|
|
421
|
+
ui.displayWarning(t('warnings.docs.unknown', { values: docSelection.invalid.join(', ') }));
|
|
422
|
+
}
|
|
423
|
+
if (agentSelection.invalid.length > 0) {
|
|
424
|
+
ui.displayWarning(t('warnings.agents.unknown', { values: agentSelection.invalid.join(', ') }));
|
|
354
425
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
426
|
+
const { provider, model, apiKey, promptPath, baseUrl } = await resolveLlmConfig(rawOptions, {
|
|
427
|
+
promptPath: path.join(__dirname, '../prompts/update_scaffold_prompt.md'),
|
|
428
|
+
fallbackModel: DEFAULT_MODEL
|
|
429
|
+
});
|
|
430
|
+
const docAllowlist = docSelection.explicitNone
|
|
431
|
+
? new Set()
|
|
432
|
+
: (0, guideRegistry_1.getDocFilesByKeys)(docSelection.selected);
|
|
433
|
+
const agentAllowlist = agentSelection.explicitNone
|
|
434
|
+
? new Set()
|
|
435
|
+
: getAgentFilesByTypes(agentSelection.selected);
|
|
436
|
+
const options = {
|
|
437
|
+
repoPath: resolvedRepo,
|
|
438
|
+
outputDir,
|
|
439
|
+
promptPath,
|
|
440
|
+
provider,
|
|
441
|
+
model,
|
|
442
|
+
apiKey,
|
|
443
|
+
baseUrl,
|
|
444
|
+
include: rawOptions.include,
|
|
445
|
+
exclude: rawOptions.exclude,
|
|
446
|
+
verbose: rawOptions.verbose || false,
|
|
447
|
+
dryRun: rawOptions.dryRun || false,
|
|
448
|
+
processAll: rawOptions.all || false,
|
|
449
|
+
limit: rawOptions.limit,
|
|
450
|
+
selectedDocKeys: docSelection.selected,
|
|
451
|
+
selectedAgentTypes: agentSelection.selected,
|
|
452
|
+
selectedDocFiles: docAllowlist,
|
|
453
|
+
selectedAgentFiles: agentAllowlist
|
|
368
454
|
};
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
ui.displayStep(1,
|
|
373
|
-
ui.startSpinner('
|
|
374
|
-
const repoStructure = await fileMapper.mapRepository(
|
|
375
|
-
ui.updateSpinner(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
455
|
+
ui.displayWelcome(VERSION);
|
|
456
|
+
ui.displayProjectInfo(options.repoPath, options.outputDir, `fill:${options.provider}`);
|
|
457
|
+
const fileMapper = new fileMapper_1.FileMapper(options.exclude);
|
|
458
|
+
ui.displayStep(1, 3, t('steps.fill.analyze'));
|
|
459
|
+
ui.startSpinner(t('spinner.repo.scanning'));
|
|
460
|
+
const repoStructure = await fileMapper.mapRepository(options.repoPath, options.include);
|
|
461
|
+
ui.updateSpinner(t('spinner.repo.scanComplete', {
|
|
462
|
+
fileCount: repoStructure.totalFiles,
|
|
463
|
+
directoryCount: repoStructure.directories.length
|
|
464
|
+
}), 'success');
|
|
465
|
+
const systemPrompt = await fs.readFile(options.promptPath, 'utf-8');
|
|
466
|
+
const llmClient = llmClientFactory_1.LLMClientFactory.createClient({
|
|
467
|
+
apiKey: options.apiKey,
|
|
468
|
+
model: options.model,
|
|
469
|
+
provider: options.provider,
|
|
470
|
+
baseUrl: options.baseUrl
|
|
471
|
+
});
|
|
472
|
+
const targets = await collectTargets(docsDir, agentsDir, options.processAll, options.limit, options.selectedDocFiles, options.selectedAgentFiles);
|
|
473
|
+
if (targets.length === 0) {
|
|
474
|
+
ui.displayWarning(t('warnings.fill.noTargets'));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const contextSummary = buildContextSummary(repoStructure);
|
|
478
|
+
const results = [];
|
|
479
|
+
ui.displayStep(2, 3, t('steps.fill.processFiles', { count: targets.length, model: options.model }));
|
|
480
|
+
for (const target of targets) {
|
|
481
|
+
const relativePath = path.relative(options.outputDir, target.fullPath);
|
|
482
|
+
ui.startSpinner(t('spinner.fill.processing', { path: relativePath }));
|
|
483
|
+
try {
|
|
484
|
+
const currentContent = await fs.readFile(target.fullPath, 'utf-8');
|
|
485
|
+
const userPrompt = buildUserPrompt(relativePath, currentContent, contextSummary, target.isAgent);
|
|
486
|
+
const updatedContent = await llmClient.generateText(userPrompt, systemPrompt);
|
|
487
|
+
if (!updatedContent || !updatedContent.trim()) {
|
|
488
|
+
ui.updateSpinner(t('spinner.fill.noContent', { path: relativePath }), 'warn');
|
|
489
|
+
results.push({ file: relativePath, status: 'skipped', message: t('messages.fill.emptyResponse') });
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (options.dryRun) {
|
|
493
|
+
ui.updateSpinner(t('spinner.fill.dryRunPreview', { path: relativePath }), 'info');
|
|
494
|
+
console.log(chalk_1.default.gray(`\n${t('messages.fill.previewStart')}`));
|
|
495
|
+
console.log(updatedContent.trim());
|
|
496
|
+
console.log(chalk_1.default.gray(`${t('messages.fill.previewEnd')}\n`));
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
await fs.writeFile(target.fullPath, ensureTrailingNewline(updatedContent));
|
|
500
|
+
ui.updateSpinner(t('spinner.fill.updated', { path: relativePath }), 'success');
|
|
501
|
+
}
|
|
502
|
+
results.push({ file: relativePath, status: options.dryRun ? 'skipped' : 'updated' });
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
ui.updateSpinner(t('spinner.fill.failed', { path: relativePath }), 'fail');
|
|
506
|
+
results.push({
|
|
507
|
+
file: relativePath,
|
|
508
|
+
status: 'failed',
|
|
509
|
+
message: error instanceof Error ? error.message : String(error)
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
ui.displayStep(3, 3, t('steps.fill.summary'));
|
|
514
|
+
printLlmSummary(llmClient.getUsageStats(), results, options.dryRun);
|
|
515
|
+
ui.displaySuccess(t('success.fill.completed'));
|
|
516
|
+
}
|
|
517
|
+
async function scaffoldPlanIfNeeded(planName, outputDir, options) {
|
|
518
|
+
const resolvedOutput = path.resolve(outputDir);
|
|
519
|
+
const plansDir = path.join(resolvedOutput, 'plans');
|
|
520
|
+
const normalizedInput = planName.replace(/\.md$/i, '');
|
|
521
|
+
const slug = shared_1.GeneratorUtils.slugify(normalizedInput);
|
|
522
|
+
if (!slug) {
|
|
523
|
+
throw new Error(t('errors.plan.invalidName'));
|
|
524
|
+
}
|
|
525
|
+
const planPath = path.join(plansDir, `${slug}.md`);
|
|
526
|
+
const planExists = await fs.pathExists(planPath);
|
|
527
|
+
if (planExists && !options.force) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const generator = new planGenerator_1.PlanGenerator();
|
|
531
|
+
const result = await generator.generatePlan({
|
|
532
|
+
planName,
|
|
533
|
+
outputDir: resolvedOutput,
|
|
534
|
+
title: options.title,
|
|
535
|
+
summary: options.summary,
|
|
536
|
+
selectedAgentTypes: options.agentSelection
|
|
537
|
+
? options.agentSelection.explicitNone
|
|
538
|
+
? null
|
|
539
|
+
: options.agentSelection.selected
|
|
540
|
+
: undefined,
|
|
541
|
+
selectedDocKeys: options.docSelection
|
|
542
|
+
? options.docSelection.explicitNone
|
|
543
|
+
? null
|
|
544
|
+
: options.docSelection.selected
|
|
545
|
+
: undefined,
|
|
546
|
+
force: Boolean(options.force),
|
|
547
|
+
verbose: Boolean(options.verbose)
|
|
548
|
+
});
|
|
549
|
+
const relativePath = result.relativePath;
|
|
550
|
+
const message = planExists && options.force
|
|
551
|
+
? t('messages.plan.regenerated', { path: relativePath })
|
|
552
|
+
: t('messages.plan.created', { path: relativePath });
|
|
553
|
+
ui.displayInfo(t('info.plan.scaffolded.title'), message);
|
|
554
|
+
}
|
|
555
|
+
async function runPlanFill(planName, rawOptions) {
|
|
556
|
+
const outputDir = path.resolve(rawOptions.output || './.context');
|
|
557
|
+
const plansDir = path.join(outputDir, 'plans');
|
|
558
|
+
await ensureDirectoryExists(plansDir, t('errors.plan.missingPlansDir'));
|
|
559
|
+
const normalizedInput = planName.replace(/\.md$/i, '');
|
|
560
|
+
const slug = shared_1.GeneratorUtils.slugify(normalizedInput);
|
|
561
|
+
if (!slug) {
|
|
562
|
+
throw new Error(t('errors.plan.invalidName'));
|
|
563
|
+
}
|
|
564
|
+
const candidateFiles = new Set();
|
|
565
|
+
candidateFiles.add(path.join(plansDir, `${slug}.md`));
|
|
566
|
+
if (planName.toLowerCase().endsWith('.md')) {
|
|
567
|
+
candidateFiles.add(path.join(plansDir, planName));
|
|
568
|
+
}
|
|
569
|
+
let planPath;
|
|
570
|
+
for (const candidate of candidateFiles) {
|
|
571
|
+
if (await fs.pathExists(candidate)) {
|
|
572
|
+
planPath = candidate;
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (!planPath) {
|
|
577
|
+
const expected = Array.from(candidateFiles).map(file => path.relative(process.cwd(), file)).join(' or ');
|
|
578
|
+
throw new Error(t('errors.plan.notFound', { expected }));
|
|
579
|
+
}
|
|
580
|
+
const docsDir = path.join(outputDir, 'docs');
|
|
581
|
+
const agentsDir = path.join(outputDir, 'agents');
|
|
582
|
+
await ensureDirectoryExists(docsDir, t('errors.fill.missingDocsScaffold'));
|
|
583
|
+
await ensureDirectoryExists(agentsDir, t('errors.fill.missingAgentsScaffold'));
|
|
584
|
+
const repoPath = path.resolve(rawOptions.repo || process.cwd());
|
|
585
|
+
if (!(await fs.pathExists(repoPath))) {
|
|
586
|
+
throw new Error(t('errors.common.repoMissing', { path: repoPath }));
|
|
587
|
+
}
|
|
588
|
+
const { provider, model, apiKey, promptPath, baseUrl } = await resolveLlmConfig(rawOptions, {
|
|
589
|
+
promptPath: path.join(__dirname, '../prompts/update_plan_prompt.md'),
|
|
590
|
+
fallbackModel: DEFAULT_MODEL
|
|
591
|
+
});
|
|
592
|
+
const planContent = await fs.readFile(planPath, 'utf-8');
|
|
593
|
+
const docsIndexPath = path.join(docsDir, 'README.md');
|
|
594
|
+
const agentsIndexPath = path.join(agentsDir, 'README.md');
|
|
595
|
+
const docsIndex = (await fs.pathExists(docsIndexPath)) ? await fs.readFile(docsIndexPath, 'utf-8') : undefined;
|
|
596
|
+
const agentsIndex = (await fs.pathExists(agentsIndexPath)) ? await fs.readFile(agentsIndexPath, 'utf-8') : undefined;
|
|
597
|
+
const referencedDocs = await loadReferencedMarkdown(docsDir, extractPlanReferences(planContent, 'docs'));
|
|
598
|
+
const referencedAgents = await loadReferencedMarkdown(agentsDir, extractPlanReferences(planContent, 'agents'));
|
|
599
|
+
ui.displayWelcome(VERSION);
|
|
600
|
+
ui.displayProjectInfo(repoPath, outputDir, `plan-fill:${provider}`);
|
|
601
|
+
const fileMapper = new fileMapper_1.FileMapper(rawOptions.exclude);
|
|
602
|
+
ui.displayStep(1, 3, t('steps.plan.summary'));
|
|
603
|
+
ui.startSpinner(t('spinner.planFill.analyzingRepo'));
|
|
604
|
+
const repoStructure = await fileMapper.mapRepository(repoPath, rawOptions.include);
|
|
605
|
+
const contextSummary = buildContextSummary(repoStructure);
|
|
606
|
+
ui.updateSpinner(t('spinner.planFill.summaryReady'), 'success');
|
|
607
|
+
const systemPrompt = await fs.readFile(promptPath, 'utf-8');
|
|
608
|
+
const llmClient = llmClientFactory_1.LLMClientFactory.createClient({
|
|
609
|
+
apiKey,
|
|
610
|
+
model,
|
|
611
|
+
provider,
|
|
612
|
+
baseUrl
|
|
613
|
+
});
|
|
614
|
+
const planRelativePath = path.relative(outputDir, planPath);
|
|
615
|
+
const results = [];
|
|
616
|
+
ui.displayStep(2, 3, t('steps.plan.update', { path: planRelativePath, model }));
|
|
617
|
+
ui.startSpinner(t('spinner.planFill.updating', { path: planRelativePath }));
|
|
383
618
|
try {
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
619
|
+
const userPrompt = buildPlanUserPrompt({
|
|
620
|
+
relativePath: planRelativePath,
|
|
621
|
+
planContent,
|
|
622
|
+
contextSummary,
|
|
623
|
+
docsIndex,
|
|
624
|
+
agentsIndex,
|
|
625
|
+
docs: referencedDocs,
|
|
626
|
+
agents: referencedAgents
|
|
627
|
+
});
|
|
628
|
+
const updatedContent = await llmClient.generateText(userPrompt, systemPrompt);
|
|
629
|
+
if (!updatedContent || !updatedContent.trim()) {
|
|
630
|
+
ui.updateSpinner(t('spinner.planFill.noContent'), 'warn');
|
|
631
|
+
results.push({ file: planRelativePath, status: 'skipped', message: t('messages.fill.emptyResponse') });
|
|
632
|
+
}
|
|
633
|
+
else if (rawOptions.dryRun) {
|
|
634
|
+
ui.updateSpinner(t('spinner.planFill.dryRun'), 'info');
|
|
635
|
+
console.log(chalk_1.default.gray(`\n${t('messages.fill.previewStart')}`));
|
|
636
|
+
console.log(updatedContent.trim());
|
|
637
|
+
console.log(chalk_1.default.gray(`${t('messages.fill.previewEnd')}\n`));
|
|
638
|
+
results.push({ file: planRelativePath, status: 'skipped', message: 'dry-run' });
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
await fs.writeFile(planPath, ensureTrailingNewline(updatedContent));
|
|
642
|
+
ui.updateSpinner(t('spinner.planFill.updated', { path: planRelativePath }), 'success');
|
|
643
|
+
results.push({ file: planRelativePath, status: 'updated' });
|
|
393
644
|
}
|
|
394
645
|
}
|
|
395
646
|
catch (error) {
|
|
396
|
-
ui.updateSpinner('
|
|
397
|
-
|
|
647
|
+
ui.updateSpinner(t('spinner.planFill.failed'), 'fail');
|
|
648
|
+
results.push({
|
|
649
|
+
file: planRelativePath,
|
|
650
|
+
status: 'failed',
|
|
651
|
+
message: error instanceof Error ? error.message : String(error)
|
|
652
|
+
});
|
|
398
653
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
654
|
+
finally {
|
|
655
|
+
ui.stopSpinner();
|
|
656
|
+
}
|
|
657
|
+
ui.displayStep(3, 3, t('steps.plan.summaryResults'));
|
|
658
|
+
printLlmSummary(llmClient.getUsageStats(), results, Boolean(rawOptions.dryRun));
|
|
659
|
+
ui.displaySuccess(t('success.plan.filled'));
|
|
660
|
+
}
|
|
661
|
+
async function ensureDirectoryExists(dir, message) {
|
|
662
|
+
const exists = await fs.pathExists(dir);
|
|
663
|
+
if (!exists) {
|
|
664
|
+
throw new Error(message);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async function collectTargets(docsDir, agentsDir, processAll, limit, docAllowlist, agentAllowlist) {
|
|
668
|
+
const docFiles = await (0, glob_1.glob)('**/*.md', { cwd: docsDir, absolute: true });
|
|
669
|
+
const agentFiles = await (0, glob_1.glob)('**/*.md', { cwd: agentsDir, absolute: true });
|
|
670
|
+
const candidates = [...docFiles, ...agentFiles];
|
|
671
|
+
const targets = [];
|
|
672
|
+
for (const fullPath of candidates) {
|
|
673
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
674
|
+
const hasMarkers = /<!--\s*ai-task:/.test(content) || /<!--\s*ai-slot:/.test(content) || /TODO/.test(content);
|
|
675
|
+
const isAgent = fullPath.includes(`${path.sep}agents${path.sep}`);
|
|
676
|
+
const fileName = path.basename(fullPath);
|
|
677
|
+
if (isAgent) {
|
|
678
|
+
if (agentAllowlist && !agentAllowlist.has(fileName)) {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
if (docAllowlist && !docAllowlist.has(fileName)) {
|
|
684
|
+
continue;
|
|
413
685
|
}
|
|
414
|
-
guidelineConfig.categories = categories;
|
|
415
686
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
687
|
+
const explicitSelection = isAgent ? !!agentAllowlist : !!docAllowlist;
|
|
688
|
+
const shouldInclude = processAll ||
|
|
689
|
+
hasMarkers ||
|
|
690
|
+
(explicitSelection && (isAgent ? agentAllowlist.has(fileName) : docAllowlist.has(fileName)));
|
|
691
|
+
if (!shouldInclude) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
targets.push({ fullPath, hasMarkers, isAgent });
|
|
695
|
+
if (limit && targets.length >= limit) {
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return targets;
|
|
700
|
+
}
|
|
701
|
+
function buildContextSummary(repoStructure) {
|
|
702
|
+
const directories = new Set();
|
|
703
|
+
repoStructure.directories.forEach(dir => {
|
|
704
|
+
const [first] = dir.relativePath.split(/[\\/]/).filter(Boolean);
|
|
705
|
+
if (first) {
|
|
706
|
+
directories.add(first);
|
|
419
707
|
}
|
|
420
|
-
|
|
421
|
-
|
|
708
|
+
});
|
|
709
|
+
const topDirs = Array.from(directories).sort().slice(0, 12);
|
|
710
|
+
const totalSizeMb = (repoStructure.totalSize / (1024 * 1024)).toFixed(2);
|
|
711
|
+
return [
|
|
712
|
+
`Top-level directories: ${topDirs.length ? topDirs.join(', ') : 'n/a'}`,
|
|
713
|
+
`Total files scanned: ${repoStructure.totalFiles}`,
|
|
714
|
+
`Repository size (approx.): ${totalSizeMb} MB`
|
|
715
|
+
].join('\n');
|
|
716
|
+
}
|
|
717
|
+
function buildUserPrompt(relativePath, currentContent, contextSummary, isAgent) {
|
|
718
|
+
const guidance = [
|
|
719
|
+
'- Preserve YAML front matter and existing `ai-task` sections.',
|
|
720
|
+
'- Replace TODOs and resolve `ai-slot` placeholders with concrete information.',
|
|
721
|
+
'- Ensure success criteria in the front matter are satisfied.',
|
|
722
|
+
'- Return only the full updated Markdown for this file.'
|
|
723
|
+
];
|
|
724
|
+
if (isAgent) {
|
|
725
|
+
guidance.push('- Keep agent responsibilities, best practices, and documentation touchpoints aligned with the latest docs.');
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
guidance.push('- Maintain accurate cross-links between docs and referenced resources.');
|
|
729
|
+
}
|
|
730
|
+
return [
|
|
731
|
+
`Target file: ${relativePath}`,
|
|
732
|
+
'Repository summary:',
|
|
733
|
+
contextSummary,
|
|
734
|
+
'',
|
|
735
|
+
'Guidance:',
|
|
736
|
+
...guidance,
|
|
737
|
+
'',
|
|
738
|
+
'Current content:',
|
|
739
|
+
'<file>',
|
|
740
|
+
currentContent,
|
|
741
|
+
'</file>'
|
|
742
|
+
].join('\n');
|
|
743
|
+
}
|
|
744
|
+
function buildPlanUserPrompt(context) {
|
|
745
|
+
const guidance = [
|
|
746
|
+
'- Preserve the YAML front matter and `ai-task` wrapper already in the plan.',
|
|
747
|
+
'- Replace TODOs with concrete steps that align with the provided documentation and agent playbooks.',
|
|
748
|
+
'- Keep the Agent Lineup and Documentation Touchpoints tables accurate and sorted.',
|
|
749
|
+
'- Ensure stages include owners, deliverables, and evidence expectations.',
|
|
750
|
+
'- Return only the full updated Markdown for this plan.'
|
|
751
|
+
];
|
|
752
|
+
const sections = [
|
|
753
|
+
`Target file: ${context.relativePath}`,
|
|
754
|
+
'Repository summary:',
|
|
755
|
+
context.contextSummary,
|
|
756
|
+
'',
|
|
757
|
+
'Guidance:',
|
|
758
|
+
...guidance,
|
|
759
|
+
'',
|
|
760
|
+
'Current plan:',
|
|
761
|
+
'<plan>',
|
|
762
|
+
context.planContent,
|
|
763
|
+
'</plan>'
|
|
764
|
+
];
|
|
765
|
+
if (context.docsIndex) {
|
|
766
|
+
sections.push('', 'Documentation index (docs/README.md):', '<docs-index>', context.docsIndex, '</docs-index>');
|
|
767
|
+
}
|
|
768
|
+
if (context.agentsIndex) {
|
|
769
|
+
sections.push('', 'Agent handbook (agents/README.md):', '<agents-index>', context.agentsIndex, '</agents-index>');
|
|
770
|
+
}
|
|
771
|
+
context.docs.forEach(doc => {
|
|
772
|
+
sections.push('', `Referenced documentation (${doc.path}):`, '<doc>', doc.content, '</doc>');
|
|
773
|
+
});
|
|
774
|
+
context.agents.forEach(agent => {
|
|
775
|
+
sections.push('', `Referenced agent playbook (${agent.path}):`, '<agent>', agent.content, '</agent>');
|
|
776
|
+
});
|
|
777
|
+
return sections.join('\n');
|
|
778
|
+
}
|
|
779
|
+
function extractPlanReferences(content, type) {
|
|
780
|
+
const regex = type === 'docs'
|
|
781
|
+
? /\]\(\.\.\/docs\/([^)#]+)(?:#[^)]*)?\)/g
|
|
782
|
+
: /\]\(\.\.\/agents\/([^)#]+)(?:#[^)]*)?\)/g;
|
|
783
|
+
const references = [];
|
|
784
|
+
let match;
|
|
785
|
+
while ((match = regex.exec(content)) !== null) {
|
|
786
|
+
const rawPath = match[1].trim();
|
|
787
|
+
if (!rawPath)
|
|
788
|
+
continue;
|
|
789
|
+
const normalized = rawPath.replace(/^\.\//, '').replace(/#.*$/, '');
|
|
790
|
+
if (!normalized || normalized.includes('..'))
|
|
791
|
+
continue;
|
|
792
|
+
if (!references.includes(normalized)) {
|
|
793
|
+
references.push(normalized);
|
|
422
794
|
}
|
|
423
|
-
|
|
424
|
-
|
|
795
|
+
}
|
|
796
|
+
return references;
|
|
797
|
+
}
|
|
798
|
+
async function loadReferencedMarkdown(baseDir, fileNames) {
|
|
799
|
+
const results = [];
|
|
800
|
+
const seen = new Set();
|
|
801
|
+
for (const name of fileNames) {
|
|
802
|
+
const cleanName = name.replace(/#.*$/, '');
|
|
803
|
+
if (!cleanName || seen.has(cleanName)) {
|
|
804
|
+
continue;
|
|
425
805
|
}
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
806
|
+
const normalized = path.normalize(cleanName).replace(/^\.\//, '');
|
|
807
|
+
if (normalized.includes('..')) {
|
|
808
|
+
continue;
|
|
429
809
|
}
|
|
430
|
-
|
|
431
|
-
|
|
810
|
+
const fullPath = path.join(baseDir, normalized);
|
|
811
|
+
if (!(await fs.pathExists(fullPath))) {
|
|
812
|
+
continue;
|
|
432
813
|
}
|
|
433
|
-
await
|
|
434
|
-
|
|
814
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
815
|
+
results.push({ path: normalized, content });
|
|
816
|
+
seen.add(cleanName);
|
|
435
817
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
ui.displaySuccess(`Guidelines generated! Output saved to: ${guidelinesPath}`);
|
|
447
|
-
// Display helpful next steps
|
|
448
|
-
console.log(chalk_1.default.bold('\nš” Next Steps:'));
|
|
818
|
+
return results;
|
|
819
|
+
}
|
|
820
|
+
function ensureTrailingNewline(content) {
|
|
821
|
+
return content.endsWith('\n') ? content : `${content}\n`;
|
|
822
|
+
}
|
|
823
|
+
function printLlmSummary(usage, results, dryRun) {
|
|
824
|
+
const updated = results.filter(r => r.status === 'updated').length;
|
|
825
|
+
const skipped = results.filter(r => r.status === 'skipped').length;
|
|
826
|
+
const failed = results.filter(r => r.status === 'failed');
|
|
827
|
+
console.log('\n' + chalk_1.default.bold('š LLM Fill Summary'));
|
|
449
828
|
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
450
|
-
console.log(`${chalk_1.default.blue('
|
|
451
|
-
console.log(`${chalk_1.default.blue('
|
|
452
|
-
console.log(`${chalk_1.default.blue('
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
async function runAnalyze(repoPath, options) {
|
|
456
|
-
const resolvedPath = path.resolve(repoPath);
|
|
457
|
-
// Display welcome
|
|
458
|
-
ui.displayWelcome('0.1.0');
|
|
459
|
-
ui.startSpinner('Analyzing repository structure...');
|
|
460
|
-
const fileMapper = new fileMapper_1.FileMapper(options.exclude || []);
|
|
461
|
-
const repoStructure = await fileMapper.mapRepository(resolvedPath, options.include);
|
|
462
|
-
ui.stopSpinner();
|
|
463
|
-
// Display analysis results
|
|
464
|
-
ui.displayAnalysisResults(repoStructure.totalFiles, repoStructure.directories.length, ui.formatBytes(repoStructure.totalSize));
|
|
465
|
-
// File type distribution
|
|
466
|
-
const extensions = new Map();
|
|
467
|
-
repoStructure.files.forEach(file => {
|
|
468
|
-
const ext = file.extension || 'no-extension';
|
|
469
|
-
extensions.set(ext, (extensions.get(ext) || 0) + 1);
|
|
470
|
-
});
|
|
471
|
-
ui.displayFileTypeDistribution(extensions, repoStructure.totalFiles);
|
|
472
|
-
// Directory structure (top level)
|
|
473
|
-
const topDirs = repoStructure.directories
|
|
474
|
-
.filter(dir => !dir.relativePath.includes('/'))
|
|
475
|
-
.slice(0, 10);
|
|
476
|
-
if (topDirs.length > 0) {
|
|
477
|
-
console.log(chalk_1.default.bold('\nš Top-level Directories:'));
|
|
829
|
+
console.log(`${chalk_1.default.blue('Updated files:')} ${chalk_1.default.white(updated.toString())}`);
|
|
830
|
+
console.log(`${chalk_1.default.blue('Skipped files:')} ${chalk_1.default.white(skipped.toString())}${dryRun ? chalk_1.default.gray(' (dry run)') : ''}`);
|
|
831
|
+
console.log(`${chalk_1.default.blue('Failures:')} ${failed.length}`);
|
|
832
|
+
if (usage.totalCalls > 0) {
|
|
478
833
|
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
479
|
-
|
|
480
|
-
|
|
834
|
+
console.log(`${chalk_1.default.blue('LLM calls:')} ${usage.totalCalls}`);
|
|
835
|
+
console.log(`${chalk_1.default.blue('Prompt tokens:')} ${usage.totalPromptTokens}`);
|
|
836
|
+
console.log(`${chalk_1.default.blue('Completion tokens:')} ${usage.totalCompletionTokens}`);
|
|
837
|
+
console.log(`${chalk_1.default.blue('Estimated cost:')} ${usage.estimatedCost.toFixed(4)}`);
|
|
838
|
+
console.log(`${chalk_1.default.blue('Model:')} ${usage.model}`);
|
|
839
|
+
}
|
|
840
|
+
if (failed.length > 0) {
|
|
841
|
+
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
842
|
+
failed.forEach(f => {
|
|
843
|
+
console.log(`${chalk_1.default.red('ā')} ${chalk_1.default.white(f.file)} ā ${chalk_1.default.gray(f.message || 'Unknown error')}`);
|
|
481
844
|
});
|
|
482
845
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (options.inputPrice !== undefined && options.outputPrice !== undefined) {
|
|
488
|
-
pricing = {
|
|
489
|
-
input: options.inputPrice,
|
|
490
|
-
output: options.outputPrice
|
|
491
|
-
};
|
|
846
|
+
}
|
|
847
|
+
function parseDocSelection(input) {
|
|
848
|
+
if (input === undefined) {
|
|
849
|
+
return { selected: undefined, invalid: [], provided: false, explicitNone: false };
|
|
492
850
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
process.exit(1);
|
|
851
|
+
if (Array.isArray(input) && input.length === 0) {
|
|
852
|
+
return { selected: [], invalid: [], provided: true, explicitNone: true };
|
|
496
853
|
}
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
854
|
+
const values = toStringArray(input);
|
|
855
|
+
const normalized = values.map(value => value.toLowerCase().replace(/\.md$/, ''));
|
|
856
|
+
const valid = Array.from(new Set(normalized.filter(key => guideRegistry_1.DOCUMENT_GUIDE_KEYS.includes(key))));
|
|
857
|
+
const invalid = normalized.filter(key => !guideRegistry_1.DOCUMENT_GUIDE_KEYS.includes(key));
|
|
858
|
+
if (values.length > 0 && valid.length === 0 && invalid.length > 0) {
|
|
859
|
+
return { selected: undefined, invalid, provided: true, explicitNone: false };
|
|
860
|
+
}
|
|
861
|
+
return { selected: valid.length > 0 ? valid : undefined, invalid, provided: true, explicitNone: false };
|
|
502
862
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
863
|
+
function parseAgentSelection(input) {
|
|
864
|
+
if (input === undefined) {
|
|
865
|
+
return { selected: undefined, invalid: [], provided: false, explicitNone: false };
|
|
866
|
+
}
|
|
867
|
+
if (Array.isArray(input) && input.length === 0) {
|
|
868
|
+
return { selected: [], invalid: [], provided: true, explicitNone: true };
|
|
869
|
+
}
|
|
870
|
+
const values = toStringArray(input);
|
|
871
|
+
const normalized = values.map(value => value.toLowerCase().replace(/\.md$/, ''));
|
|
872
|
+
const allowed = new Set(agentTypes_1.AGENT_TYPES);
|
|
873
|
+
const valid = Array.from(new Set(normalized.filter(value => allowed.has(value))));
|
|
874
|
+
const invalid = normalized.filter(value => !allowed.has(value));
|
|
875
|
+
if (values.length > 0 && valid.length === 0 && invalid.length > 0) {
|
|
876
|
+
return { selected: undefined, invalid, provided: true, explicitNone: false };
|
|
877
|
+
}
|
|
878
|
+
return { selected: valid.length > 0 ? valid : undefined, invalid, provided: true, explicitNone: false };
|
|
879
|
+
}
|
|
880
|
+
function shouldGenerateDocs(resolvedType, selection) {
|
|
881
|
+
if (selection.explicitNone) {
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
if (resolvedType === 'agents') {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
if (!selection.provided) {
|
|
888
|
+
return resolvedType === 'docs' || resolvedType === 'both';
|
|
889
|
+
}
|
|
890
|
+
if (selection.selected && selection.selected.length === 0) {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
return resolvedType === 'docs' || resolvedType === 'both';
|
|
894
|
+
}
|
|
895
|
+
function shouldGenerateAgents(resolvedType, selection) {
|
|
896
|
+
if (selection.explicitNone) {
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
if (resolvedType === 'docs') {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
if (!selection.provided) {
|
|
903
|
+
return resolvedType === 'agents' || resolvedType === 'both';
|
|
904
|
+
}
|
|
905
|
+
if (selection.selected && selection.selected.length === 0) {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
return resolvedType === 'agents' || resolvedType === 'both';
|
|
909
|
+
}
|
|
910
|
+
function toStringArray(input) {
|
|
911
|
+
if (Array.isArray(input)) {
|
|
912
|
+
return input.map(item => item.toString().trim()).filter(Boolean);
|
|
913
|
+
}
|
|
914
|
+
if (input === null || input === undefined) {
|
|
915
|
+
return [];
|
|
916
|
+
}
|
|
917
|
+
return input
|
|
918
|
+
.toString()
|
|
919
|
+
.split(',')
|
|
920
|
+
.map((part) => part.trim())
|
|
921
|
+
.filter(Boolean);
|
|
922
|
+
}
|
|
923
|
+
function formatAgentLabel(value) {
|
|
924
|
+
return value
|
|
925
|
+
.split('-')
|
|
926
|
+
.map(segment => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
927
|
+
.join(' ');
|
|
928
|
+
}
|
|
929
|
+
async function runInteractive() {
|
|
930
|
+
const { locale } = await inquirer_1.default.prompt([
|
|
931
|
+
{
|
|
932
|
+
type: 'list',
|
|
933
|
+
name: 'locale',
|
|
934
|
+
message: t('prompts.language.select'),
|
|
935
|
+
default: currentLocale,
|
|
936
|
+
choices: i18n_1.SUPPORTED_LOCALES.map(option => ({
|
|
937
|
+
value: option,
|
|
938
|
+
name: t(localeLabelKeys[option])
|
|
939
|
+
}))
|
|
513
940
|
}
|
|
941
|
+
]);
|
|
942
|
+
const normalizedLocale = (0, i18n_1.normalizeLocale)(locale);
|
|
943
|
+
currentLocale = normalizedLocale;
|
|
944
|
+
translateFn = (0, i18n_1.createTranslator)(normalizedLocale);
|
|
945
|
+
ui.displayWelcome(VERSION);
|
|
946
|
+
const { action } = await inquirer_1.default.prompt([
|
|
947
|
+
{
|
|
948
|
+
type: 'list',
|
|
949
|
+
name: 'action',
|
|
950
|
+
message: t('prompts.main.action'),
|
|
951
|
+
choices: [
|
|
952
|
+
{ name: t('prompts.main.choice.scaffold'), value: 'scaffold' },
|
|
953
|
+
{ name: t('prompts.main.choice.fill'), value: 'fill' },
|
|
954
|
+
{ name: t('prompts.main.choice.plan'), value: 'plan' }
|
|
955
|
+
]
|
|
956
|
+
}
|
|
957
|
+
]);
|
|
958
|
+
if (action === 'scaffold') {
|
|
959
|
+
await runInteractiveScaffold();
|
|
514
960
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
outputDir: path.resolve(options.output),
|
|
518
|
-
model: options.model,
|
|
519
|
-
apiKey,
|
|
520
|
-
provider,
|
|
521
|
-
exclude: options.exclude || [],
|
|
522
|
-
include: options.include,
|
|
523
|
-
verbose: options.verbose || false,
|
|
524
|
-
since: options.since,
|
|
525
|
-
staged: options.staged || false,
|
|
526
|
-
force: options.force || false
|
|
527
|
-
};
|
|
528
|
-
if (!cliOptions.apiKey) {
|
|
529
|
-
const envVars = llmClientFactory_1.LLMClientFactory.getEnvironmentVariables()[provider];
|
|
530
|
-
ui.displayError(`${provider.toUpperCase()} API key is required. Set one of these environment variables: ${envVars.join(', ')} or use --api-key option.`);
|
|
531
|
-
process.exit(1);
|
|
961
|
+
else if (action === 'fill') {
|
|
962
|
+
await runInteractiveLlmFill();
|
|
532
963
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (!gitService.isGitRepository()) {
|
|
536
|
-
ui.displayError('This command requires a Git repository. Initialize git first or use the generate command instead.');
|
|
537
|
-
process.exit(1);
|
|
964
|
+
else {
|
|
965
|
+
await runInteractivePlan();
|
|
538
966
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
967
|
+
}
|
|
968
|
+
async function runInteractiveScaffold() {
|
|
969
|
+
const { repoPath } = await inquirer_1.default.prompt([
|
|
970
|
+
{
|
|
971
|
+
type: 'input',
|
|
972
|
+
name: 'repoPath',
|
|
973
|
+
message: t('prompts.scaffold.repoPath'),
|
|
974
|
+
default: process.cwd()
|
|
975
|
+
}
|
|
976
|
+
]);
|
|
977
|
+
const resolvedRepo = path.resolve(repoPath.trim() || '.');
|
|
978
|
+
const defaultOutput = path.resolve(resolvedRepo, '.context');
|
|
979
|
+
const { outputDir } = await inquirer_1.default.prompt([
|
|
980
|
+
{
|
|
981
|
+
type: 'input',
|
|
982
|
+
name: 'outputDir',
|
|
983
|
+
message: t('commands.init.options.output'),
|
|
984
|
+
default: defaultOutput
|
|
985
|
+
}
|
|
986
|
+
]);
|
|
987
|
+
const { includeDocs } = await inquirer_1.default.prompt([
|
|
988
|
+
{
|
|
989
|
+
type: 'confirm',
|
|
990
|
+
name: 'includeDocs',
|
|
991
|
+
message: t('prompts.scaffold.includeDocs'),
|
|
992
|
+
default: true
|
|
993
|
+
}
|
|
994
|
+
]);
|
|
995
|
+
let selectedDocs;
|
|
996
|
+
if (includeDocs) {
|
|
997
|
+
const { docs } = await inquirer_1.default.prompt([
|
|
998
|
+
{
|
|
999
|
+
type: 'checkbox',
|
|
1000
|
+
name: 'docs',
|
|
1001
|
+
message: t('prompts.scaffold.selectDocs'),
|
|
1002
|
+
choices: DOC_CHOICES,
|
|
1003
|
+
default: DOC_CHOICES.map(choice => choice.value)
|
|
1004
|
+
}
|
|
1005
|
+
]);
|
|
1006
|
+
selectedDocs = docs;
|
|
552
1007
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
ui.startSpinner('Analyzing git changes...');
|
|
577
|
-
let changes;
|
|
578
|
-
if (cliOptions.staged) {
|
|
579
|
-
// For pre-commit hooks - analyze only staged files
|
|
580
|
-
changes = gitService.getStagedChanges();
|
|
581
|
-
}
|
|
582
|
-
else if (cliOptions.since) {
|
|
583
|
-
// Compare against specific commit
|
|
584
|
-
changes = gitService.getChangedFiles(cliOptions.since);
|
|
1008
|
+
else {
|
|
1009
|
+
selectedDocs = [];
|
|
1010
|
+
}
|
|
1011
|
+
const { includeAgents } = await inquirer_1.default.prompt([
|
|
1012
|
+
{
|
|
1013
|
+
type: 'confirm',
|
|
1014
|
+
name: 'includeAgents',
|
|
1015
|
+
message: t('prompts.scaffold.includeAgents'),
|
|
1016
|
+
default: true
|
|
1017
|
+
}
|
|
1018
|
+
]);
|
|
1019
|
+
let selectedAgents;
|
|
1020
|
+
if (includeAgents) {
|
|
1021
|
+
const { agents } = await inquirer_1.default.prompt([
|
|
1022
|
+
{
|
|
1023
|
+
type: 'checkbox',
|
|
1024
|
+
name: 'agents',
|
|
1025
|
+
message: t('prompts.scaffold.selectAgents'),
|
|
1026
|
+
choices: AGENT_CHOICES,
|
|
1027
|
+
default: AGENT_CHOICES.map(choice => choice.value)
|
|
1028
|
+
}
|
|
1029
|
+
]);
|
|
1030
|
+
selectedAgents = agents;
|
|
585
1031
|
}
|
|
586
1032
|
else {
|
|
587
|
-
|
|
588
|
-
changes = gitService.getChangedFiles();
|
|
1033
|
+
selectedAgents = [];
|
|
589
1034
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
ui.updateSpinner('No changes detected since last run', 'info');
|
|
593
|
-
ui.displaySuccess('Documentation is up to date!');
|
|
1035
|
+
if ((selectedDocs?.length ?? 0) === 0 && (selectedAgents?.length ?? 0) === 0) {
|
|
1036
|
+
ui.displayWarning(t('warnings.interactive.nothingSelected'));
|
|
594
1037
|
return;
|
|
595
1038
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
1039
|
+
const { verbose } = await inquirer_1.default.prompt([
|
|
1040
|
+
{
|
|
1041
|
+
type: 'confirm',
|
|
1042
|
+
name: 'verbose',
|
|
1043
|
+
message: t('prompts.common.verbose'),
|
|
1044
|
+
default: false
|
|
1045
|
+
}
|
|
1046
|
+
]);
|
|
1047
|
+
const scaffoldType = determineScaffoldType(selectedDocs, selectedAgents);
|
|
1048
|
+
await runInit(resolvedRepo, scaffoldType, {
|
|
1049
|
+
output: outputDir,
|
|
1050
|
+
docs: selectedDocs,
|
|
1051
|
+
agents: selectedAgents,
|
|
1052
|
+
verbose
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
function determineScaffoldType(docSelection, agentSelection) {
|
|
1056
|
+
const docsSelected = docSelection === undefined ? true : docSelection.length > 0;
|
|
1057
|
+
const agentsSelected = agentSelection === undefined ? true : agentSelection.length > 0;
|
|
1058
|
+
if (docsSelected && agentsSelected)
|
|
1059
|
+
return 'both';
|
|
1060
|
+
if (docsSelected)
|
|
1061
|
+
return 'docs';
|
|
1062
|
+
return 'agents';
|
|
1063
|
+
}
|
|
1064
|
+
async function runInteractiveLlmFill() {
|
|
1065
|
+
const { repoPath } = await inquirer_1.default.prompt([
|
|
1066
|
+
{
|
|
1067
|
+
type: 'input',
|
|
1068
|
+
name: 'repoPath',
|
|
1069
|
+
message: t('prompts.fill.repoPath'),
|
|
1070
|
+
default: process.cwd()
|
|
1071
|
+
}
|
|
1072
|
+
]);
|
|
1073
|
+
const resolvedRepo = path.resolve(repoPath.trim() || '.');
|
|
1074
|
+
const defaultOutput = path.resolve(resolvedRepo, '.context');
|
|
1075
|
+
const defaultPrompt = path.resolve(process.cwd(), 'prompts/update_scaffold_prompt.md');
|
|
1076
|
+
const { outputDir, promptPath } = await inquirer_1.default.prompt([
|
|
1077
|
+
{
|
|
1078
|
+
type: 'input',
|
|
1079
|
+
name: 'outputDir',
|
|
1080
|
+
message: t('commands.fill.options.output'),
|
|
1081
|
+
default: defaultOutput
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
type: 'input',
|
|
1085
|
+
name: 'promptPath',
|
|
1086
|
+
message: t('prompts.fill.promptPath'),
|
|
1087
|
+
default: defaultPrompt
|
|
1088
|
+
}
|
|
1089
|
+
]);
|
|
1090
|
+
const { dryRun, processAll } = await inquirer_1.default.prompt([
|
|
1091
|
+
{
|
|
1092
|
+
type: 'confirm',
|
|
1093
|
+
name: 'dryRun',
|
|
1094
|
+
message: t('prompts.fill.dryRun'),
|
|
1095
|
+
default: true
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
type: 'confirm',
|
|
1099
|
+
name: 'processAll',
|
|
1100
|
+
message: t('prompts.fill.processAll'),
|
|
1101
|
+
default: false
|
|
1102
|
+
}
|
|
1103
|
+
]);
|
|
1104
|
+
const { limit } = await inquirer_1.default.prompt([
|
|
1105
|
+
{
|
|
1106
|
+
type: 'input',
|
|
1107
|
+
name: 'limit',
|
|
1108
|
+
message: t('prompts.fill.limit'),
|
|
1109
|
+
filter: (value) => value.trim()
|
|
1110
|
+
}
|
|
1111
|
+
]);
|
|
1112
|
+
const limitValue = limit ? parseInt(limit, 10) : undefined;
|
|
1113
|
+
const parsedLimit = Number.isNaN(limitValue) ? undefined : limitValue;
|
|
1114
|
+
const { includeDocs } = await inquirer_1.default.prompt([
|
|
1115
|
+
{
|
|
1116
|
+
type: 'confirm',
|
|
1117
|
+
name: 'includeDocs',
|
|
1118
|
+
message: t('prompts.fill.includeDocs'),
|
|
1119
|
+
default: true
|
|
609
1120
|
}
|
|
1121
|
+
]);
|
|
1122
|
+
let selectedDocs;
|
|
1123
|
+
if (includeDocs) {
|
|
1124
|
+
const { docs } = await inquirer_1.default.prompt([
|
|
1125
|
+
{
|
|
1126
|
+
type: 'checkbox',
|
|
1127
|
+
name: 'docs',
|
|
1128
|
+
message: t('prompts.fill.selectDocs'),
|
|
1129
|
+
choices: DOC_CHOICES,
|
|
1130
|
+
default: DOC_CHOICES.map(choice => choice.value)
|
|
1131
|
+
}
|
|
1132
|
+
]);
|
|
1133
|
+
selectedDocs = docs;
|
|
610
1134
|
}
|
|
611
1135
|
else {
|
|
612
|
-
|
|
613
|
-
console.log(chalk_1.default.gray('No documentation changes made, state not updated'));
|
|
614
|
-
}
|
|
1136
|
+
selectedDocs = [];
|
|
615
1137
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
result.updatedFiles.forEach(file => {
|
|
623
|
-
console.log(` ${chalk_1.default.green('ā')} ${file}`);
|
|
624
|
-
});
|
|
1138
|
+
const { includeAgents } = await inquirer_1.default.prompt([
|
|
1139
|
+
{
|
|
1140
|
+
type: 'confirm',
|
|
1141
|
+
name: 'includeAgents',
|
|
1142
|
+
message: t('prompts.fill.includeAgents'),
|
|
1143
|
+
default: true
|
|
625
1144
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1145
|
+
]);
|
|
1146
|
+
let selectedAgents;
|
|
1147
|
+
if (includeAgents) {
|
|
1148
|
+
const { agents } = await inquirer_1.default.prompt([
|
|
1149
|
+
{
|
|
1150
|
+
type: 'checkbox',
|
|
1151
|
+
name: 'agents',
|
|
1152
|
+
message: t('prompts.fill.selectAgents'),
|
|
1153
|
+
choices: AGENT_CHOICES,
|
|
1154
|
+
default: AGENT_CHOICES.map(choice => choice.value)
|
|
1155
|
+
}
|
|
1156
|
+
]);
|
|
1157
|
+
selectedAgents = agents;
|
|
633
1158
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
ui.displayGenerationSummary(result.updated, 0, usageStats, true);
|
|
637
|
-
ui.displaySuccess(`Documentation updated! Processed ${result.updated} files.`);
|
|
638
|
-
}
|
|
639
|
-
async function runPreview(repoPath, options) {
|
|
640
|
-
const resolvedPath = path.resolve(repoPath);
|
|
641
|
-
// Display welcome
|
|
642
|
-
ui.displayWelcome('0.1.0');
|
|
643
|
-
ui.startSpinner('Initializing analysis...');
|
|
644
|
-
// Initialize services
|
|
645
|
-
const fileMapper = new fileMapper_1.FileMapper(options.exclude || []);
|
|
646
|
-
const gitService = new gitService_1.GitService(resolvedPath);
|
|
647
|
-
const changeAnalyzer = new changeAnalyzer_1.ChangeAnalyzer(gitService, fileMapper);
|
|
648
|
-
// Check if it's a git repository
|
|
649
|
-
if (!gitService.isGitRepository()) {
|
|
650
|
-
ui.updateSpinner('Not a git repository', 'fail');
|
|
651
|
-
ui.displayError('The specified path is not a git repository. Preview requires git tracking.');
|
|
652
|
-
process.exit(1);
|
|
1159
|
+
else {
|
|
1160
|
+
selectedAgents = [];
|
|
653
1161
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
ui.updateSpinner('Context not initialized', 'fail');
|
|
658
|
-
ui.displayError('No documentation context found. You should run analyze and init before previewing changes to update.');
|
|
659
|
-
console.log(chalk_1.default.bold('\nš” Getting Started:'));
|
|
660
|
-
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
661
|
-
console.log(`${chalk_1.default.blue('1. Analyze:')} ai-context analyze ${repoPath}`);
|
|
662
|
-
console.log(`${chalk_1.default.blue('2. Initialize:')} ai-context init ${repoPath}`);
|
|
663
|
-
console.log(`${chalk_1.default.blue('3. Preview:')} ai-context preview ${repoPath}`);
|
|
664
|
-
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
665
|
-
console.log(chalk_1.default.gray('The analyze command shows token estimates and costs.'));
|
|
666
|
-
console.log(chalk_1.default.gray('The init command creates the initial documentation.'));
|
|
667
|
-
console.log(chalk_1.default.gray('The preview command shows what would change in updates.'));
|
|
668
|
-
process.exit(1);
|
|
1162
|
+
if ((selectedDocs?.length ?? 0) === 0 && (selectedAgents?.length ?? 0) === 0) {
|
|
1163
|
+
ui.displayWarning(t('warnings.interactive.nothingSelected'));
|
|
1164
|
+
return;
|
|
669
1165
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1166
|
+
const { specifyModel } = await inquirer_1.default.prompt([
|
|
1167
|
+
{
|
|
1168
|
+
type: 'confirm',
|
|
1169
|
+
name: 'specifyModel',
|
|
1170
|
+
message: t('prompts.fill.overrideModel'),
|
|
1171
|
+
default: false
|
|
1172
|
+
}
|
|
1173
|
+
]);
|
|
1174
|
+
let provider;
|
|
1175
|
+
let model;
|
|
1176
|
+
if (specifyModel) {
|
|
1177
|
+
const providerAnswer = await inquirer_1.default.prompt([
|
|
1178
|
+
{
|
|
1179
|
+
type: 'list',
|
|
1180
|
+
name: 'provider',
|
|
1181
|
+
message: t('prompts.fill.provider'),
|
|
1182
|
+
choices: ['openrouter', 'openai', 'anthropic', 'gemini', 'grok']
|
|
1183
|
+
}
|
|
1184
|
+
]);
|
|
1185
|
+
provider = providerAnswer.provider;
|
|
1186
|
+
const modelAnswer = await inquirer_1.default.prompt([
|
|
1187
|
+
{
|
|
1188
|
+
type: 'input',
|
|
1189
|
+
name: 'model',
|
|
1190
|
+
message: t('prompts.fill.model'),
|
|
1191
|
+
default: DEFAULT_MODEL
|
|
1192
|
+
}
|
|
1193
|
+
]);
|
|
1194
|
+
model = modelAnswer.model.trim();
|
|
679
1195
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
1196
|
+
const { provideApiKey } = await inquirer_1.default.prompt([
|
|
1197
|
+
{
|
|
1198
|
+
type: 'confirm',
|
|
1199
|
+
name: 'provideApiKey',
|
|
1200
|
+
message: t('prompts.fill.provideApiKey'),
|
|
1201
|
+
default: false
|
|
1202
|
+
}
|
|
1203
|
+
]);
|
|
1204
|
+
let apiKey;
|
|
1205
|
+
if (provideApiKey) {
|
|
1206
|
+
const apiKeyAnswer = await inquirer_1.default.prompt([
|
|
1207
|
+
{
|
|
1208
|
+
type: 'password',
|
|
1209
|
+
name: 'apiKey',
|
|
1210
|
+
message: t('prompts.fill.apiKey'),
|
|
1211
|
+
mask: '*'
|
|
1212
|
+
}
|
|
1213
|
+
]);
|
|
1214
|
+
apiKey = apiKeyAnswer.apiKey.trim();
|
|
683
1215
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
1216
|
+
const { verbose } = await inquirer_1.default.prompt([
|
|
1217
|
+
{
|
|
1218
|
+
type: 'confirm',
|
|
1219
|
+
name: 'verbose',
|
|
1220
|
+
message: t('prompts.common.verbose'),
|
|
1221
|
+
default: false
|
|
1222
|
+
}
|
|
1223
|
+
]);
|
|
1224
|
+
await runLlmFill(resolvedRepo, {
|
|
1225
|
+
output: outputDir,
|
|
1226
|
+
prompt: promptPath,
|
|
1227
|
+
docs: selectedDocs,
|
|
1228
|
+
agents: selectedAgents,
|
|
1229
|
+
dryRun,
|
|
1230
|
+
all: processAll,
|
|
1231
|
+
limit: parsedLimit,
|
|
1232
|
+
model,
|
|
1233
|
+
provider,
|
|
1234
|
+
apiKey,
|
|
1235
|
+
verbose
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
async function runInteractivePlan() {
|
|
1239
|
+
const { planName } = await inquirer_1.default.prompt([
|
|
1240
|
+
{
|
|
1241
|
+
type: 'input',
|
|
1242
|
+
name: 'planName',
|
|
1243
|
+
message: t('prompts.plan.name'),
|
|
1244
|
+
default: 'new-plan'
|
|
1245
|
+
}
|
|
1246
|
+
]);
|
|
1247
|
+
const defaultOutput = path.resolve(process.cwd(), '.context');
|
|
1248
|
+
const { mode } = await inquirer_1.default.prompt([
|
|
1249
|
+
{
|
|
1250
|
+
type: 'list',
|
|
1251
|
+
name: 'mode',
|
|
1252
|
+
message: t('prompts.plan.mode'),
|
|
1253
|
+
choices: [
|
|
1254
|
+
{ name: t('prompts.plan.modeScaffold'), value: 'scaffold' },
|
|
1255
|
+
{ name: t('prompts.plan.modeFill'), value: 'fill' }
|
|
1256
|
+
],
|
|
1257
|
+
default: 'scaffold'
|
|
1258
|
+
}
|
|
1259
|
+
]);
|
|
1260
|
+
const { outputDir } = await inquirer_1.default.prompt([
|
|
1261
|
+
{
|
|
1262
|
+
type: 'input',
|
|
1263
|
+
name: 'outputDir',
|
|
1264
|
+
message: t('commands.plan.options.output'),
|
|
1265
|
+
default: defaultOutput
|
|
1266
|
+
}
|
|
1267
|
+
]);
|
|
1268
|
+
if (mode === 'fill') {
|
|
1269
|
+
const { summary } = await inquirer_1.default.prompt([
|
|
1270
|
+
{
|
|
1271
|
+
type: 'input',
|
|
1272
|
+
name: 'summary',
|
|
1273
|
+
message: t('prompts.plan.summary'),
|
|
1274
|
+
filter: (value) => value.trim()
|
|
1275
|
+
}
|
|
1276
|
+
]);
|
|
1277
|
+
const { includeAgents } = await inquirer_1.default.prompt([
|
|
1278
|
+
{
|
|
1279
|
+
type: 'confirm',
|
|
1280
|
+
name: 'includeAgents',
|
|
1281
|
+
message: t('prompts.plan.includeAgents'),
|
|
1282
|
+
default: true
|
|
1283
|
+
}
|
|
1284
|
+
]);
|
|
1285
|
+
let selectedAgents = [];
|
|
1286
|
+
if (includeAgents) {
|
|
1287
|
+
const { agents } = await inquirer_1.default.prompt([
|
|
1288
|
+
{
|
|
1289
|
+
type: 'checkbox',
|
|
1290
|
+
name: 'agents',
|
|
1291
|
+
message: t('prompts.plan.selectAgents'),
|
|
1292
|
+
choices: AGENT_CHOICES,
|
|
1293
|
+
default: AGENT_CHOICES.map(choice => choice.value)
|
|
705
1294
|
}
|
|
706
|
-
|
|
1295
|
+
]);
|
|
1296
|
+
selectedAgents = agents;
|
|
707
1297
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1298
|
+
const { includeDocs } = await inquirer_1.default.prompt([
|
|
1299
|
+
{
|
|
1300
|
+
type: 'confirm',
|
|
1301
|
+
name: 'includeDocs',
|
|
1302
|
+
message: t('prompts.plan.includeDocs'),
|
|
1303
|
+
default: true
|
|
713
1304
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
if (
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1305
|
+
]);
|
|
1306
|
+
let selectedDocs = [];
|
|
1307
|
+
if (includeDocs) {
|
|
1308
|
+
const { docs } = await inquirer_1.default.prompt([
|
|
1309
|
+
{
|
|
1310
|
+
type: 'checkbox',
|
|
1311
|
+
name: 'docs',
|
|
1312
|
+
message: t('prompts.plan.selectDocs'),
|
|
1313
|
+
choices: DOC_CHOICES,
|
|
1314
|
+
default: DOC_CHOICES.map(choice => choice.value)
|
|
1315
|
+
}
|
|
1316
|
+
]);
|
|
1317
|
+
selectedDocs = docs;
|
|
1318
|
+
}
|
|
1319
|
+
const agentSelection = parseAgentSelection(selectedAgents);
|
|
1320
|
+
const docSelection = parseDocSelection(selectedDocs);
|
|
1321
|
+
const { repoPath } = await inquirer_1.default.prompt([
|
|
1322
|
+
{
|
|
1323
|
+
type: 'input',
|
|
1324
|
+
name: 'repoPath',
|
|
1325
|
+
message: t('prompts.plan.repoPath'),
|
|
1326
|
+
default: process.cwd()
|
|
721
1327
|
}
|
|
722
|
-
|
|
723
|
-
|
|
1328
|
+
]);
|
|
1329
|
+
const { dryRun } = await inquirer_1.default.prompt([
|
|
1330
|
+
{
|
|
1331
|
+
type: 'confirm',
|
|
1332
|
+
name: 'dryRun',
|
|
1333
|
+
message: t('prompts.plan.dryRun'),
|
|
1334
|
+
default: true
|
|
724
1335
|
}
|
|
1336
|
+
]);
|
|
1337
|
+
try {
|
|
1338
|
+
const resolvedOutput = path.resolve(outputDir.trim() || defaultOutput);
|
|
1339
|
+
await scaffoldPlanIfNeeded(planName, resolvedOutput, {
|
|
1340
|
+
summary: summary || undefined,
|
|
1341
|
+
agentSelection,
|
|
1342
|
+
docSelection
|
|
1343
|
+
});
|
|
1344
|
+
await runPlanFill(planName, {
|
|
1345
|
+
output: resolvedOutput,
|
|
1346
|
+
repo: repoPath,
|
|
1347
|
+
dryRun
|
|
1348
|
+
});
|
|
725
1349
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
1350
|
+
catch (error) {
|
|
1351
|
+
ui.displayError(t('errors.plan.fillFailed'), error);
|
|
1352
|
+
}
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
const { summary } = await inquirer_1.default.prompt([
|
|
1356
|
+
{
|
|
1357
|
+
type: 'input',
|
|
1358
|
+
name: 'summary',
|
|
1359
|
+
message: t('prompts.plan.summary'),
|
|
1360
|
+
filter: (value) => value.trim()
|
|
1361
|
+
}
|
|
1362
|
+
]);
|
|
1363
|
+
const { includeAgents } = await inquirer_1.default.prompt([
|
|
1364
|
+
{
|
|
1365
|
+
type: 'confirm',
|
|
1366
|
+
name: 'includeAgents',
|
|
1367
|
+
message: t('prompts.plan.includeAgents'),
|
|
1368
|
+
default: true
|
|
1369
|
+
}
|
|
1370
|
+
]);
|
|
1371
|
+
let selectedAgents = null;
|
|
1372
|
+
if (includeAgents) {
|
|
1373
|
+
const { agents } = await inquirer_1.default.prompt([
|
|
1374
|
+
{
|
|
1375
|
+
type: 'checkbox',
|
|
1376
|
+
name: 'agents',
|
|
1377
|
+
message: t('prompts.plan.selectAgents'),
|
|
1378
|
+
choices: AGENT_CHOICES,
|
|
1379
|
+
default: AGENT_CHOICES.map(choice => choice.value)
|
|
1380
|
+
}
|
|
1381
|
+
]);
|
|
1382
|
+
selectedAgents = agents.length > 0 ? agents : null;
|
|
1383
|
+
}
|
|
1384
|
+
const { includeDocs } = await inquirer_1.default.prompt([
|
|
1385
|
+
{
|
|
1386
|
+
type: 'confirm',
|
|
1387
|
+
name: 'includeDocs',
|
|
1388
|
+
message: t('prompts.plan.includeDocs'),
|
|
1389
|
+
default: true
|
|
747
1390
|
}
|
|
748
|
-
|
|
749
|
-
|
|
1391
|
+
]);
|
|
1392
|
+
let selectedDocs = null;
|
|
1393
|
+
if (includeDocs) {
|
|
1394
|
+
const { docs } = await inquirer_1.default.prompt([
|
|
1395
|
+
{
|
|
1396
|
+
type: 'checkbox',
|
|
1397
|
+
name: 'docs',
|
|
1398
|
+
message: t('prompts.plan.selectDocs'),
|
|
1399
|
+
choices: DOC_CHOICES,
|
|
1400
|
+
default: DOC_CHOICES.map(choice => choice.value)
|
|
1401
|
+
}
|
|
1402
|
+
]);
|
|
1403
|
+
selectedDocs = docs.length > 0 ? docs : null;
|
|
1404
|
+
}
|
|
1405
|
+
const generator = new planGenerator_1.PlanGenerator();
|
|
1406
|
+
ui.startSpinner(t('spinner.plan.creating'));
|
|
1407
|
+
try {
|
|
1408
|
+
const result = await generator.generatePlan({
|
|
1409
|
+
planName,
|
|
1410
|
+
outputDir: path.resolve(outputDir.trim() || defaultOutput),
|
|
1411
|
+
summary: summary || undefined,
|
|
1412
|
+
selectedAgentTypes: selectedAgents,
|
|
1413
|
+
selectedDocKeys: selectedDocs,
|
|
1414
|
+
verbose: false
|
|
1415
|
+
});
|
|
1416
|
+
ui.updateSpinner(t('spinner.plan.created'), 'success');
|
|
1417
|
+
ui.displaySuccess(t('success.plan.createdAt', { path: chalk_1.default.cyan(result.relativePath) }));
|
|
1418
|
+
}
|
|
1419
|
+
catch (error) {
|
|
1420
|
+
ui.updateSpinner(t('spinner.plan.creationFailed'), 'fail');
|
|
1421
|
+
ui.displayError(t('errors.plan.creationFailed'), error);
|
|
1422
|
+
}
|
|
1423
|
+
finally {
|
|
750
1424
|
ui.stopSpinner();
|
|
751
|
-
// Display the token estimate
|
|
752
|
-
console.log(chalk_1.default.bold('\nš® Token & Cost Estimate for Preview Changes:'));
|
|
753
|
-
console.log(chalk_1.default.gray('ā'.repeat(60)));
|
|
754
|
-
console.log(tokenEstimator.formatTokenEstimate(tokenEstimate));
|
|
755
1425
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1426
|
+
}
|
|
1427
|
+
function getAgentFilesByTypes(types) {
|
|
1428
|
+
if (!types || types.length === 0) {
|
|
1429
|
+
return undefined;
|
|
760
1430
|
}
|
|
761
|
-
|
|
762
|
-
|
|
1431
|
+
const allowed = new Set(agentTypes_1.AGENT_TYPES);
|
|
1432
|
+
const files = types
|
|
1433
|
+
.filter(type => allowed.has(type))
|
|
1434
|
+
.map(type => `${type}.md`);
|
|
1435
|
+
return files.length ? new Set(files) : undefined;
|
|
1436
|
+
}
|
|
1437
|
+
function filterOutLocaleArgs(args) {
|
|
1438
|
+
const filtered = [];
|
|
1439
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1440
|
+
const current = args[index];
|
|
1441
|
+
if (current === '--lang' || current === '--language' || current === '-l') {
|
|
1442
|
+
index += 1;
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (current.startsWith('--lang=') || current.startsWith('--language=')) {
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
filtered.push(current);
|
|
763
1449
|
}
|
|
1450
|
+
return filtered;
|
|
764
1451
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
1452
|
+
async function main() {
|
|
1453
|
+
const userArgs = process.argv.slice(2);
|
|
1454
|
+
const meaningfulArgs = filterOutLocaleArgs(userArgs);
|
|
1455
|
+
if (meaningfulArgs.length === 0) {
|
|
1456
|
+
await runInteractive();
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
await program.parseAsync(process.argv);
|
|
1460
|
+
}
|
|
1461
|
+
if (require.main === module) {
|
|
1462
|
+
main().catch(error => {
|
|
1463
|
+
ui.displayError(t('errors.cli.executionFailed'), error);
|
|
770
1464
|
process.exit(1);
|
|
771
1465
|
});
|
|
772
1466
|
}
|
|
773
|
-
else {
|
|
774
|
-
program.parse();
|
|
775
|
-
}
|
|
776
1467
|
//# sourceMappingURL=index.js.map
|