@ai-coders/context 0.1.0 ā 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/agentConfig.d.ts +4 -0
- package/dist/generators/agents/agentConfig.d.ts.map +1 -0
- package/dist/generators/agents/agentConfig.js +138 -0
- package/dist/generators/agents/agentConfig.js.map +1 -0
- package/dist/generators/agents/agentGenerator.d.ts +10 -0
- package/dist/generators/agents/agentGenerator.d.ts.map +1 -0
- package/dist/generators/agents/agentGenerator.js +137 -0
- package/dist/generators/agents/agentGenerator.js.map +1 -0
- package/dist/generators/agents/agentTypes.d.ts +4 -0
- package/dist/generators/agents/agentTypes.d.ts.map +1 -0
- package/dist/generators/agents/agentTypes.js +22 -0
- package/dist/generators/agents/agentTypes.js.map +1 -0
- package/dist/generators/agents/contextUtils.d.ts +8 -0
- package/dist/generators/agents/contextUtils.d.ts.map +1 -0
- package/dist/generators/agents/contextUtils.js +15 -0
- package/dist/generators/agents/contextUtils.js.map +1 -0
- package/dist/generators/agents/index.d.ts +4 -0
- package/dist/generators/agents/index.d.ts.map +1 -0
- package/dist/generators/agents/index.js +12 -0
- package/dist/generators/agents/index.js.map +1 -0
- package/dist/generators/agents/promptFormatter.d.ts +9 -0
- package/dist/generators/agents/promptFormatter.d.ts.map +1 -0
- package/dist/generators/agents/promptFormatter.js +84 -0
- package/dist/generators/agents/promptFormatter.js.map +1 -0
- 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/analyzers/codebaseAnalyzer.d.ts +45 -0
- package/dist/generators/analyzers/codebaseAnalyzer.d.ts.map +1 -0
- package/dist/generators/analyzers/codebaseAnalyzer.js +293 -0
- package/dist/generators/analyzers/codebaseAnalyzer.js.map +1 -0
- package/dist/generators/analyzers/index.d.ts +3 -0
- package/dist/generators/analyzers/index.d.ts.map +1 -0
- package/dist/generators/analyzers/index.js +6 -0
- package/dist/generators/analyzers/index.js.map +1 -0
- package/dist/generators/documentation/documentationGenerator.d.ts +13 -0
- package/dist/generators/documentation/documentationGenerator.d.ts.map +1 -0
- package/dist/generators/documentation/documentationGenerator.js +115 -0
- package/dist/generators/documentation/documentationGenerator.js.map +1 -0
- package/dist/generators/documentation/documentationTemplates.d.ts +21 -0
- package/dist/generators/documentation/documentationTemplates.d.ts.map +1 -0
- package/dist/generators/documentation/documentationTemplates.js +359 -0
- package/dist/generators/documentation/documentationTemplates.js.map +1 -0
- package/dist/generators/documentation/documentationTypes.d.ts +11 -0
- package/dist/generators/documentation/documentationTypes.d.ts.map +1 -0
- package/dist/generators/documentation/documentationTypes.js +22 -0
- package/dist/generators/documentation/documentationTypes.js.map +1 -0
- package/dist/generators/documentation/documentationUtils.d.ts +7 -0
- package/dist/generators/documentation/documentationUtils.d.ts.map +1 -0
- package/dist/generators/documentation/documentationUtils.js +28 -0
- package/dist/generators/documentation/documentationUtils.js.map +1 -0
- 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/{incrementalDocumentationGenerator.d.ts ā documentation/incrementalDocumentationGenerator.d.ts} +4 -4
- package/dist/generators/documentation/incrementalDocumentationGenerator.d.ts.map +1 -0
- package/dist/generators/documentation/incrementalDocumentationGenerator.js.map +1 -0
- package/dist/generators/documentation/index.d.ts +2 -0
- package/dist/generators/documentation/index.d.ts.map +1 -0
- package/dist/generators/documentation/index.js +6 -0
- package/dist/generators/documentation/index.js.map +1 -0
- 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/guidelines/agentIntegration.d.ts +43 -0
- package/dist/generators/guidelines/agentIntegration.d.ts.map +1 -0
- package/dist/generators/guidelines/agentIntegration.js +157 -0
- package/dist/generators/guidelines/agentIntegration.js.map +1 -0
- package/dist/generators/guidelines/guidelineTypes.d.ts +40 -0
- package/dist/generators/guidelines/guidelineTypes.d.ts.map +1 -0
- package/dist/generators/guidelines/guidelineTypes.js +144 -0
- package/dist/generators/guidelines/guidelineTypes.js.map +1 -0
- package/dist/generators/guidelines/guidelinesAnalyzer.d.ts +30 -0
- package/dist/generators/guidelines/guidelinesAnalyzer.d.ts.map +1 -0
- package/dist/generators/guidelines/guidelinesAnalyzer.js +263 -0
- package/dist/generators/guidelines/guidelinesAnalyzer.js.map +1 -0
- package/dist/generators/guidelines/guidelinesGenerator.d.ts +30 -0
- package/dist/generators/guidelines/guidelinesGenerator.d.ts.map +1 -0
- package/dist/generators/guidelines/guidelinesGenerator.js +249 -0
- package/dist/generators/guidelines/guidelinesGenerator.js.map +1 -0
- package/dist/generators/guidelines/guidelinesTemplates.d.ts +23 -0
- package/dist/generators/guidelines/guidelinesTemplates.d.ts.map +1 -0
- package/dist/generators/guidelines/guidelinesTemplates.js +304 -0
- package/dist/generators/guidelines/guidelinesTemplates.js.map +1 -0
- package/dist/generators/guidelines/index.d.ts +6 -0
- package/dist/generators/guidelines/index.d.ts.map +1 -0
- package/dist/generators/guidelines/index.js +16 -0
- package/dist/generators/guidelines/index.js.map +1 -0
- package/dist/generators/moduleGrouper.d.ts +14 -0
- package/dist/generators/moduleGrouper.d.ts.map +1 -0
- package/dist/generators/moduleGrouper.js +82 -0
- package/dist/generators/moduleGrouper.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/projectAnalyzer.d.ts +14 -0
- package/dist/generators/projectAnalyzer.d.ts.map +1 -0
- package/dist/generators/projectAnalyzer.js +217 -0
- package/dist/generators/projectAnalyzer.js.map +1 -0
- package/dist/generators/shared/contextGenerator.d.ts +7 -0
- package/dist/generators/shared/contextGenerator.d.ts.map +1 -0
- package/dist/generators/shared/contextGenerator.js +13 -0
- package/dist/generators/shared/contextGenerator.js.map +1 -0
- 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/generatorUtils.d.ts +16 -0
- package/dist/generators/shared/generatorUtils.d.ts.map +1 -0
- package/dist/generators/shared/generatorUtils.js +119 -0
- package/dist/generators/shared/generatorUtils.js.map +1 -0
- package/dist/generators/shared/index.d.ts +4 -0
- package/dist/generators/shared/index.d.ts.map +1 -0
- package/dist/generators/shared/index.js +10 -0
- package/dist/generators/shared/index.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1327 -419
- 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/dist/utils/interactiveMode.d.ts +6 -1
- package/dist/utils/interactiveMode.d.ts.map +1 -1
- package/dist/utils/interactiveMode.js +375 -70
- package/dist/utils/interactiveMode.js.map +1 -1
- package/dist/utils/tokenEstimator.d.ts +10 -9
- package/dist/utils/tokenEstimator.d.ts.map +1 -1
- package/dist/utils/tokenEstimator.js +45 -109
- package/dist/utils/tokenEstimator.js.map +1 -1
- 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/documentationGenerator.d.ts +0 -40
- package/dist/generators/documentationGenerator.d.ts.map +0 -1
- package/dist/generators/documentationGenerator.js +0 -786
- package/dist/generators/documentationGenerator.js.map +0 -1
- package/dist/generators/incrementalDocumentationGenerator.d.ts.map +0 -1
- package/dist/generators/incrementalDocumentationGenerator.js.map +0 -1
- /package/dist/generators/{incrementalDocumentationGenerator.js ā documentation/incrementalDocumentationGenerator.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -41,519 +41,1427 @@ exports.runGenerate = runGenerate;
|
|
|
41
41
|
exports.runAnalyze = runAnalyze;
|
|
42
42
|
exports.runUpdate = runUpdate;
|
|
43
43
|
exports.runPreview = runPreview;
|
|
44
|
+
exports.runGuidelines = runGuidelines;
|
|
45
|
+
exports.runInit = runInit;
|
|
44
46
|
const commander_1 = require("commander");
|
|
45
47
|
const path = __importStar(require("path"));
|
|
46
|
-
const
|
|
48
|
+
const fs = __importStar(require("fs-extra"));
|
|
49
|
+
const glob_1 = require("glob");
|
|
47
50
|
const dotenv = __importStar(require("dotenv"));
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
52
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
50
53
|
const fileMapper_1 = require("./utils/fileMapper");
|
|
51
|
-
const documentationGenerator_1 = require("./generators/documentationGenerator");
|
|
52
|
-
const agentGenerator_1 = require("./generators/agentGenerator");
|
|
53
|
-
const
|
|
54
|
+
const documentationGenerator_1 = require("./generators/documentation/documentationGenerator");
|
|
55
|
+
const agentGenerator_1 = require("./generators/agents/agentGenerator");
|
|
56
|
+
const planGenerator_1 = require("./generators/plans/planGenerator");
|
|
57
|
+
const shared_1 = require("./generators/shared");
|
|
54
58
|
const cliUI_1 = require("./utils/cliUI");
|
|
59
|
+
const i18n_1 = require("./utils/i18n");
|
|
55
60
|
const llmClientFactory_1 = require("./services/llmClientFactory");
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
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
|
+
};
|
|
60
72
|
const program = new commander_1.Command();
|
|
61
|
-
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
|
+
}));
|
|
62
84
|
program
|
|
63
85
|
.name('ai-context')
|
|
64
|
-
.description('
|
|
65
|
-
.version(
|
|
86
|
+
.description(t('cli.description'))
|
|
87
|
+
.version(VERSION);
|
|
88
|
+
program.option('-l, --lang <locale>', t('global.options.lang'), initialLocale);
|
|
66
89
|
program
|
|
67
90
|
.command('init')
|
|
68
|
-
.description('
|
|
69
|
-
.argument('<repo-path>', '
|
|
70
|
-
.argument('[type]', '
|
|
71
|
-
.option('-o, --output <dir>', '
|
|
72
|
-
.option('
|
|
73
|
-
.option('
|
|
74
|
-
.option('
|
|
75
|
-
.option('--
|
|
76
|
-
.option('--
|
|
77
|
-
.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'))
|
|
78
100
|
.action(async (repoPath, type, options) => {
|
|
79
101
|
try {
|
|
80
|
-
|
|
81
|
-
if (!['docs', 'agents', 'both'].includes(type)) {
|
|
82
|
-
ui.displayError(`Invalid type "${type}". Must be "docs", "agents", or "both".`);
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
// Set options based on type argument
|
|
86
|
-
if (type === 'docs') {
|
|
87
|
-
options.docsOnly = true;
|
|
88
|
-
}
|
|
89
|
-
else if (type === 'agents') {
|
|
90
|
-
options.agentsOnly = true;
|
|
91
|
-
}
|
|
92
|
-
// For 'both', neither flag is set
|
|
93
|
-
await runGenerate(repoPath, options);
|
|
102
|
+
await runInit(repoPath, type, options);
|
|
94
103
|
}
|
|
95
104
|
catch (error) {
|
|
96
|
-
ui.displayError('
|
|
105
|
+
ui.displayError(t('errors.init.scaffoldFailed'), error);
|
|
97
106
|
process.exit(1);
|
|
98
107
|
}
|
|
99
108
|
});
|
|
100
109
|
program
|
|
101
|
-
.command('
|
|
102
|
-
.description('
|
|
103
|
-
.argument('<repo-path>', '
|
|
104
|
-
.
|
|
105
|
-
.option('-
|
|
106
|
-
.option('
|
|
107
|
-
.option('
|
|
108
|
-
.option('--
|
|
109
|
-
.option('--
|
|
110
|
-
.option('--
|
|
111
|
-
.
|
|
112
|
-
.option('--include <patterns...>', 'Patterns to include in analysis')
|
|
113
|
-
.option('-v, --verbose', 'Verbose output')
|
|
114
|
-
.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) => {
|
|
115
121
|
try {
|
|
116
|
-
await
|
|
122
|
+
await runInit(repoPath, type, options);
|
|
117
123
|
}
|
|
118
124
|
catch (error) {
|
|
119
|
-
ui.displayError('
|
|
125
|
+
ui.displayError(t('errors.init.scaffoldFailed'), error);
|
|
120
126
|
process.exit(1);
|
|
121
127
|
}
|
|
122
128
|
});
|
|
123
129
|
program
|
|
124
|
-
.command('
|
|
125
|
-
.description('
|
|
126
|
-
.argument('<repo-path>', '
|
|
127
|
-
.option('--
|
|
128
|
-
.option('--
|
|
129
|
-
.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'))
|
|
130
147
|
.action(async (repoPath, options) => {
|
|
131
148
|
try {
|
|
132
|
-
await
|
|
149
|
+
await runLlmFill(repoPath, options);
|
|
133
150
|
}
|
|
134
151
|
catch (error) {
|
|
135
|
-
ui.displayError('
|
|
152
|
+
ui.displayError(t('errors.fill.failed'), error);
|
|
136
153
|
process.exit(1);
|
|
137
154
|
}
|
|
138
155
|
});
|
|
139
156
|
program
|
|
140
|
-
.command('
|
|
141
|
-
.description('
|
|
142
|
-
.argument('<
|
|
143
|
-
.option('--
|
|
144
|
-
.option('--
|
|
145
|
-
.option('--
|
|
146
|
-
.option('--
|
|
147
|
-
.option('
|
|
148
|
-
.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
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(', ') }));
|
|
181
|
+
}
|
|
182
|
+
const docSelection = parseDocSelection(rawOptions.docs);
|
|
183
|
+
if (docSelection.invalid.length > 0) {
|
|
184
|
+
ui.displayWarning(t('warnings.docs.unknown', { values: docSelection.invalid.join(', ') }));
|
|
185
|
+
}
|
|
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'));
|
|
155
207
|
try {
|
|
156
|
-
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) }));
|
|
157
220
|
}
|
|
158
221
|
catch (error) {
|
|
159
|
-
ui.
|
|
222
|
+
ui.updateSpinner(t('spinner.plan.creationFailed'), 'fail');
|
|
223
|
+
ui.displayError(t('errors.plan.creationFailed'), error);
|
|
160
224
|
process.exit(1);
|
|
161
225
|
}
|
|
226
|
+
finally {
|
|
227
|
+
ui.stopSpinner();
|
|
228
|
+
}
|
|
162
229
|
});
|
|
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(', ') }));
|
|
236
|
+
}
|
|
237
|
+
if (agentSelection.invalid.length > 0) {
|
|
238
|
+
ui.displayWarning(t('warnings.agents.unknown', { values: agentSelection.invalid.join(', ') }));
|
|
239
|
+
}
|
|
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
|
|
250
|
+
};
|
|
251
|
+
if (!options.scaffoldDocs && !options.scaffoldAgents) {
|
|
252
|
+
ui.displayWarning(t('warnings.scaffold.noneSelected'));
|
|
253
|
+
return;
|
|
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
|
+
let docsGenerated = 0;
|
|
267
|
+
let agentsGenerated = 0;
|
|
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
|
+
}
|
|
163
306
|
async function runGenerate(repoPath, options) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
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
|
+
}
|
|
350
|
+
}
|
|
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
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!provider) {
|
|
365
|
+
if (model) {
|
|
366
|
+
provider = llmClientFactory_1.LLMClientFactory.detectProviderFromModel(model);
|
|
367
|
+
}
|
|
368
|
+
else if (apiKey) {
|
|
369
|
+
provider = llmClientFactory_1.LLMClientFactory.getProviderFromApiKey(apiKey);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (!model) {
|
|
373
|
+
if (provider === 'openrouter' && process.env.OPENROUTER_MODEL) {
|
|
374
|
+
model = process.env.OPENROUTER_MODEL;
|
|
375
|
+
}
|
|
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);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!provider) {
|
|
385
|
+
provider = llmClientFactory_1.LLMClientFactory.detectProviderFromModel(model || defaults.fallbackModel);
|
|
386
|
+
}
|
|
167
387
|
if (!apiKey) {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
388
|
+
for (const envVar of providerEnvMap[provider]) {
|
|
389
|
+
const value = process.env[envVar];
|
|
390
|
+
if (value) {
|
|
391
|
+
apiKey = value;
|
|
172
392
|
break;
|
|
393
|
+
}
|
|
173
394
|
}
|
|
174
395
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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 {
|
|
180
404
|
provider,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
405
|
+
model: model || defaults.fallbackModel,
|
|
406
|
+
apiKey,
|
|
407
|
+
promptPath,
|
|
408
|
+
baseUrl: rawOptions.baseUrl
|
|
184
409
|
};
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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(', ') }));
|
|
189
422
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
423
|
+
if (agentSelection.invalid.length > 0) {
|
|
424
|
+
ui.displayWarning(t('warnings.agents.unknown', { values: agentSelection.invalid.join(', ') }));
|
|
425
|
+
}
|
|
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
|
|
203
454
|
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
ui.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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;
|
|
222
476
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ui.startSpinner('
|
|
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 }));
|
|
229
483
|
try {
|
|
230
|
-
await
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
|
|
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' });
|
|
234
503
|
}
|
|
235
504
|
catch (error) {
|
|
236
|
-
ui.updateSpinner('
|
|
237
|
-
|
|
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
|
+
});
|
|
238
511
|
}
|
|
239
512
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const ext = file.extension || 'no-extension';
|
|
276
|
-
extensions.set(ext, (extensions.get(ext) || 0) + 1);
|
|
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)
|
|
277
548
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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'));
|
|
289
563
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// Get API key from options or environment variables
|
|
301
|
-
let apiKey = options.apiKey;
|
|
302
|
-
if (!apiKey) {
|
|
303
|
-
const envVars = llmClientFactory_1.LLMClientFactory.getEnvironmentVariables()[provider];
|
|
304
|
-
for (const envVar of envVars) {
|
|
305
|
-
apiKey = process.env[envVar];
|
|
306
|
-
if (apiKey)
|
|
307
|
-
break;
|
|
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;
|
|
308
574
|
}
|
|
309
575
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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({
|
|
314
609
|
apiKey,
|
|
610
|
+
model,
|
|
315
611
|
provider,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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 }));
|
|
618
|
+
try {
|
|
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' });
|
|
644
|
+
}
|
|
327
645
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
646
|
+
catch (error) {
|
|
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
|
+
});
|
|
333
653
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
ui.displayError('No documentation context found. You should run analyze and init before updating.');
|
|
337
|
-
console.log(chalk_1.default.bold('\nš” Getting Started:'));
|
|
338
|
-
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
339
|
-
console.log(`${chalk_1.default.blue('1. Analyze:')} ai-context analyze ${cliOptions.repoPath}`);
|
|
340
|
-
console.log(`${chalk_1.default.blue('2. Initialize:')} ai-context init ${cliOptions.repoPath}`);
|
|
341
|
-
console.log(`${chalk_1.default.blue('3. Update:')} ai-context update ${cliOptions.repoPath}`);
|
|
342
|
-
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
343
|
-
console.log(chalk_1.default.gray('The analyze command shows token estimates and costs.'));
|
|
344
|
-
console.log(chalk_1.default.gray('The init command creates the initial documentation.'));
|
|
345
|
-
console.log(chalk_1.default.gray('The update command incrementally updates existing documentation.'));
|
|
346
|
-
process.exit(1);
|
|
654
|
+
finally {
|
|
655
|
+
ui.stopSpinner();
|
|
347
656
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
ui.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
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);
|
|
707
|
+
}
|
|
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.');
|
|
380
726
|
}
|
|
381
727
|
else {
|
|
382
|
-
|
|
383
|
-
changes = gitService.getChangedFiles();
|
|
728
|
+
guidance.push('- Maintain accurate cross-links between docs and referenced resources.');
|
|
384
729
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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>');
|
|
390
767
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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);
|
|
404
794
|
}
|
|
405
795
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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;
|
|
409
805
|
}
|
|
806
|
+
const normalized = path.normalize(cleanName).replace(/^\.\//, '');
|
|
807
|
+
if (normalized.includes('..')) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const fullPath = path.join(baseDir, normalized);
|
|
811
|
+
if (!(await fs.pathExists(fullPath))) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
815
|
+
results.push({ path: normalized, content });
|
|
816
|
+
seen.add(cleanName);
|
|
817
|
+
}
|
|
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'));
|
|
828
|
+
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
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) {
|
|
833
|
+
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
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}`);
|
|
410
839
|
}
|
|
411
|
-
|
|
412
|
-
if (result.updatedFiles.length > 0 || result.removedFiles.length > 0) {
|
|
413
|
-
console.log(chalk_1.default.bold('\nš Documentation Files Changed:'));
|
|
840
|
+
if (failed.length > 0) {
|
|
414
841
|
console.log(chalk_1.default.gray('ā'.repeat(50)));
|
|
415
|
-
|
|
416
|
-
console.log(chalk_1.default.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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')}`);
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function parseDocSelection(input) {
|
|
848
|
+
if (input === undefined) {
|
|
849
|
+
return { selected: undefined, invalid: [], provided: false, explicitNone: false };
|
|
850
|
+
}
|
|
851
|
+
if (Array.isArray(input) && input.length === 0) {
|
|
852
|
+
return { selected: [], invalid: [], provided: true, explicitNone: true };
|
|
853
|
+
}
|
|
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 };
|
|
862
|
+
}
|
|
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
|
+
}))
|
|
420
940
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
+
]
|
|
426
956
|
}
|
|
427
|
-
|
|
957
|
+
]);
|
|
958
|
+
if (action === 'scaffold') {
|
|
959
|
+
await runInteractiveScaffold();
|
|
428
960
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
ui.displayGenerationSummary(result.updated, 0, usageStats, true);
|
|
432
|
-
ui.displaySuccess(`Documentation updated! Processed ${result.updated} files.`);
|
|
433
|
-
}
|
|
434
|
-
async function runPreview(repoPath, options) {
|
|
435
|
-
const resolvedPath = path.resolve(repoPath);
|
|
436
|
-
// Display welcome
|
|
437
|
-
ui.displayWelcome('0.1.0');
|
|
438
|
-
ui.startSpinner('Initializing analysis...');
|
|
439
|
-
// Initialize services
|
|
440
|
-
const fileMapper = new fileMapper_1.FileMapper(options.exclude || []);
|
|
441
|
-
const gitService = new gitService_1.GitService(resolvedPath);
|
|
442
|
-
const changeAnalyzer = new changeAnalyzer_1.ChangeAnalyzer(gitService, fileMapper);
|
|
443
|
-
// Check if it's a git repository
|
|
444
|
-
if (!gitService.isGitRepository()) {
|
|
445
|
-
ui.updateSpinner('Not a git repository', 'fail');
|
|
446
|
-
ui.displayError('The specified path is not a git repository. Preview requires git tracking.');
|
|
447
|
-
process.exit(1);
|
|
961
|
+
else if (action === 'fill') {
|
|
962
|
+
await runInteractiveLlmFill();
|
|
448
963
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
964
|
+
else {
|
|
965
|
+
await runInteractivePlan();
|
|
966
|
+
}
|
|
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;
|
|
464
1007
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const repoStructure = await fileMapper.mapRepository(resolvedPath, options.include);
|
|
468
|
-
ui.updateSpinner('Analyzing changes...');
|
|
469
|
-
// Detect changes
|
|
470
|
-
let changes;
|
|
471
|
-
if (options.staged) {
|
|
472
|
-
// For pre-commit hooks - analyze only staged files
|
|
473
|
-
changes = gitService.getStagedChanges();
|
|
1008
|
+
else {
|
|
1009
|
+
selectedDocs = [];
|
|
474
1010
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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;
|
|
478
1031
|
}
|
|
479
1032
|
else {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
1033
|
+
selectedAgents = [];
|
|
1034
|
+
}
|
|
1035
|
+
if ((selectedDocs?.length ?? 0) === 0 && (selectedAgents?.length ?? 0) === 0) {
|
|
1036
|
+
ui.displayWarning(t('warnings.interactive.nothingSelected'));
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
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
|
|
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;
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
selectedDocs = [];
|
|
1137
|
+
}
|
|
1138
|
+
const { includeAgents } = await inquirer_1.default.prompt([
|
|
1139
|
+
{
|
|
1140
|
+
type: 'confirm',
|
|
1141
|
+
name: 'includeAgents',
|
|
1142
|
+
message: t('prompts.fill.includeAgents'),
|
|
1143
|
+
default: true
|
|
1144
|
+
}
|
|
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;
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
selectedAgents = [];
|
|
1161
|
+
}
|
|
1162
|
+
if ((selectedDocs?.length ?? 0) === 0 && (selectedAgents?.length ?? 0) === 0) {
|
|
1163
|
+
ui.displayWarning(t('warnings.interactive.nothingSelected'));
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
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();
|
|
1195
|
+
}
|
|
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();
|
|
1215
|
+
}
|
|
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)
|
|
500
1294
|
}
|
|
501
|
-
|
|
1295
|
+
]);
|
|
1296
|
+
selectedAgents = agents;
|
|
502
1297
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
1298
|
+
const { includeDocs } = await inquirer_1.default.prompt([
|
|
1299
|
+
{
|
|
1300
|
+
type: 'confirm',
|
|
1301
|
+
name: 'includeDocs',
|
|
1302
|
+
message: t('prompts.plan.includeDocs'),
|
|
1303
|
+
default: true
|
|
508
1304
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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()
|
|
516
1327
|
}
|
|
517
|
-
|
|
518
|
-
|
|
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
|
|
519
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
|
+
});
|
|
520
1349
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
|
530
1390
|
}
|
|
531
|
-
|
|
532
|
-
|
|
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 {
|
|
533
1424
|
ui.stopSpinner();
|
|
534
|
-
// Display the token estimate
|
|
535
|
-
console.log(chalk_1.default.bold('\nš® Token & Cost Estimate for Preview Changes:'));
|
|
536
|
-
console.log(chalk_1.default.gray('ā'.repeat(60)));
|
|
537
|
-
console.log(tokenEstimator.formatTokenEstimate(tokenEstimate));
|
|
538
1425
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1426
|
+
}
|
|
1427
|
+
function getAgentFilesByTypes(types) {
|
|
1428
|
+
if (!types || types.length === 0) {
|
|
1429
|
+
return undefined;
|
|
1430
|
+
}
|
|
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);
|
|
543
1449
|
}
|
|
544
|
-
|
|
545
|
-
|
|
1450
|
+
return filtered;
|
|
1451
|
+
}
|
|
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;
|
|
546
1458
|
}
|
|
1459
|
+
await program.parseAsync(process.argv);
|
|
547
1460
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
interactive.start().catch((error) => {
|
|
552
|
-
ui.displayError('Interactive mode failed', error);
|
|
1461
|
+
if (require.main === module) {
|
|
1462
|
+
main().catch(error => {
|
|
1463
|
+
ui.displayError(t('errors.cli.executionFailed'), error);
|
|
553
1464
|
process.exit(1);
|
|
554
1465
|
});
|
|
555
1466
|
}
|
|
556
|
-
else {
|
|
557
|
-
program.parse();
|
|
558
|
-
}
|
|
559
1467
|
//# sourceMappingURL=index.js.map
|