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