@esotech/contextuate 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +287 -0
- package/dist/commands/context.js +80 -0
- package/dist/commands/create.js +93 -0
- package/dist/commands/index.js +46 -0
- package/dist/commands/init.js +452 -0
- package/dist/commands/install.js +359 -0
- package/dist/commands/remove.js +77 -0
- package/dist/commands/run.js +205 -0
- package/dist/index.js +96 -0
- package/dist/runtime/driver.js +64 -0
- package/dist/runtime/tools.js +48 -0
- package/dist/templates/README.md +152 -0
- package/dist/templates/agents/aegis.md +366 -0
- package/dist/templates/agents/archon.md +247 -0
- package/dist/templates/agents/atlas.md +326 -0
- package/dist/templates/agents/canvas.md +19 -0
- package/dist/templates/agents/chronicle.md +424 -0
- package/dist/templates/agents/chronos.md +20 -0
- package/dist/templates/agents/cipher.md +360 -0
- package/dist/templates/agents/crucible.md +375 -0
- package/dist/templates/agents/echo.md +297 -0
- package/dist/templates/agents/forge.md +613 -0
- package/dist/templates/agents/ledger.md +317 -0
- package/dist/templates/agents/meridian.md +281 -0
- package/dist/templates/agents/nexus.md +600 -0
- package/dist/templates/agents/oracle.md +281 -0
- package/dist/templates/agents/scribe.md +612 -0
- package/dist/templates/agents/sentinel.md +312 -0
- package/dist/templates/agents/unity.md +17 -0
- package/dist/templates/agents/vox.md +19 -0
- package/dist/templates/agents/weaver.md +334 -0
- package/dist/templates/framework-agents/base.md +166 -0
- package/dist/templates/framework-agents/documentation-expert.md +292 -0
- package/dist/templates/framework-agents/tools-expert.md +245 -0
- package/dist/templates/standards/agent-roles.md +34 -0
- package/dist/templates/standards/agent-workflow.md +170 -0
- package/dist/templates/standards/behavioral-guidelines.md +145 -0
- package/dist/templates/standards/coding-standards.md +171 -0
- package/dist/templates/standards/task-workflow.md +246 -0
- package/dist/templates/templates/context.md +33 -0
- package/dist/templates/templates/contextuate.md +109 -0
- package/dist/templates/templates/platforms/AGENTS.md +5 -0
- package/dist/templates/templates/platforms/CLAUDE.md +5 -0
- package/dist/templates/templates/platforms/GEMINI.md +5 -0
- package/dist/templates/templates/platforms/clinerules.md +5 -0
- package/dist/templates/templates/platforms/copilot.md +5 -0
- package/dist/templates/templates/platforms/cursor.mdc +9 -0
- package/dist/templates/templates/platforms/windsurf.md +5 -0
- package/dist/templates/templates/standards/go.standards.md +167 -0
- package/dist/templates/templates/standards/java.standards.md +167 -0
- package/dist/templates/templates/standards/javascript.standards.md +292 -0
- package/dist/templates/templates/standards/php.standards.md +181 -0
- package/dist/templates/templates/standards/python.standards.md +175 -0
- package/dist/templates/tools/agent-creator.tool.md +252 -0
- package/dist/templates/tools/quickref.tool.md +216 -0
- package/dist/templates/tools/spawn.tool.md +31 -0
- package/dist/templates/tools/standards-detector.tool.md +301 -0
- package/dist/templates/version.json +8 -0
- package/dist/utils/git.js +62 -0
- package/dist/utils/tokens.js +74 -0
- package/package.json +59 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initCommand = initCommand;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
// Platform definitions with metadata
|
|
12
|
+
const PLATFORMS = [
|
|
13
|
+
{ id: 'agents', name: 'Agents.ai', src: 'templates/platforms/AGENTS.md', dest: 'AGENTS.md' },
|
|
14
|
+
{ id: 'antigravity', name: 'Antigravity', src: 'templates/platforms/GEMINI.md', dest: '.gemini/rules.md', ensureDir: '.gemini' },
|
|
15
|
+
{ id: 'claude', name: 'Claude Code', src: 'templates/platforms/CLAUDE.md', dest: 'CLAUDE.md', symlinks: true },
|
|
16
|
+
{ id: 'cline', name: 'Cline', src: 'templates/platforms/clinerules.md', dest: '.clinerules/cline-memory-bank.md', ensureDir: '.clinerules' },
|
|
17
|
+
{ id: 'cursor', name: 'Cursor IDE', src: 'templates/platforms/cursor.mdc', dest: '.cursor/rules/project.mdc', ensureDir: '.cursor/rules' },
|
|
18
|
+
{ id: 'gemini', name: 'Google Gemini', src: 'templates/platforms/GEMINI.md', dest: 'GEMINI.md' },
|
|
19
|
+
{ id: 'copilot', name: 'GitHub Copilot', src: 'templates/platforms/copilot.md', dest: '.github/copilot-instructions.md', ensureDir: '.github' },
|
|
20
|
+
{ id: 'windsurf', name: 'Windsurf IDE', src: 'templates/platforms/windsurf.md', dest: '.windsurf/rules/project.md', ensureDir: '.windsurf/rules' },
|
|
21
|
+
];
|
|
22
|
+
// Fuzzy match platform names
|
|
23
|
+
function fuzzyMatchPlatform(input) {
|
|
24
|
+
const normalized = input.toLowerCase().trim();
|
|
25
|
+
// Direct ID match
|
|
26
|
+
const directMatch = PLATFORMS.find(p => p.id === normalized);
|
|
27
|
+
if (directMatch) {
|
|
28
|
+
return directMatch.id;
|
|
29
|
+
}
|
|
30
|
+
// Partial match - starts with or includes
|
|
31
|
+
const partialMatch = PLATFORMS.find(p => p.id.startsWith(normalized) ||
|
|
32
|
+
p.name.toLowerCase().includes(normalized));
|
|
33
|
+
if (partialMatch) {
|
|
34
|
+
return partialMatch.id;
|
|
35
|
+
}
|
|
36
|
+
// Special case for common variations
|
|
37
|
+
if (normalized === 'github' || normalized === 'copilot') {
|
|
38
|
+
return 'copilot';
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
async function initCommand(platformArgs, options) {
|
|
43
|
+
// Handle both old signature (no args) and new signature (with variadic args)
|
|
44
|
+
// When called with no args: first param is options object
|
|
45
|
+
// When called with args: first param is array, second param is options
|
|
46
|
+
let platforms = [];
|
|
47
|
+
let opts = {};
|
|
48
|
+
if (Array.isArray(platformArgs)) {
|
|
49
|
+
platforms = platformArgs;
|
|
50
|
+
opts = options || {};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Old signature - first param is actually options
|
|
54
|
+
opts = platformArgs || {};
|
|
55
|
+
platforms = [];
|
|
56
|
+
}
|
|
57
|
+
console.log(chalk_1.default.blue('╔════════════════════════════════════════╗'));
|
|
58
|
+
console.log(chalk_1.default.blue('║ Contextuate Installer ║'));
|
|
59
|
+
console.log(chalk_1.default.blue('║ AI Context Framework ║'));
|
|
60
|
+
console.log(chalk_1.default.blue('║ Powered by Esotech ║'));
|
|
61
|
+
console.log(chalk_1.default.blue('╚════════════════════════════════════════╝'));
|
|
62
|
+
console.log('');
|
|
63
|
+
try {
|
|
64
|
+
// Determine if running in non-interactive mode
|
|
65
|
+
const nonInteractive = platforms.length > 0;
|
|
66
|
+
// Check for project markers
|
|
67
|
+
const projectMarkers = ['.git', 'package.json', 'composer.json', 'Cargo.toml', 'go.mod'];
|
|
68
|
+
const hasMarker = projectMarkers.some(marker => fs_extra_1.default.existsSync(marker));
|
|
69
|
+
if (!hasMarker && !nonInteractive) {
|
|
70
|
+
console.log(chalk_1.default.yellow('[WARN] No project markers found (.git, package.json, etc.)'));
|
|
71
|
+
const { continueAnyway } = await inquirer_1.default.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: 'confirm',
|
|
74
|
+
name: 'continueAnyway',
|
|
75
|
+
message: 'Continue anyway?',
|
|
76
|
+
default: true,
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
if (!continueAnyway) {
|
|
80
|
+
console.log(chalk_1.default.blue('[INFO] Installation cancelled.'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (!hasMarker && nonInteractive) {
|
|
85
|
+
console.log(chalk_1.default.yellow('[WARN] No project markers found (.git, package.json, etc.) - continuing anyway in non-interactive mode'));
|
|
86
|
+
}
|
|
87
|
+
let selectedPlatforms = [];
|
|
88
|
+
// Non-interactive mode - process CLI arguments
|
|
89
|
+
if (nonInteractive) {
|
|
90
|
+
// Check for "all" argument
|
|
91
|
+
if (platforms.some(arg => arg.toLowerCase() === 'all')) {
|
|
92
|
+
selectedPlatforms = PLATFORMS;
|
|
93
|
+
console.log(chalk_1.default.blue('[INFO] Installing all platforms'));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Fuzzy match each platform argument
|
|
97
|
+
for (const arg of platforms) {
|
|
98
|
+
const matchedId = fuzzyMatchPlatform(arg);
|
|
99
|
+
if (matchedId) {
|
|
100
|
+
const platform = PLATFORMS.find(p => p.id === matchedId);
|
|
101
|
+
if (platform && !selectedPlatforms.includes(platform)) {
|
|
102
|
+
selectedPlatforms.push(platform);
|
|
103
|
+
console.log(chalk_1.default.green(`[OK] Matched "${arg}" to ${platform.name}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log(chalk_1.default.yellow(`[WARN] Could not match platform "${arg}" - skipping`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (selectedPlatforms.length === 0) {
|
|
111
|
+
console.log(chalk_1.default.red('[ERROR] No valid platforms matched. Available platforms:'));
|
|
112
|
+
PLATFORMS.forEach(p => console.log(` - ${p.id} (${p.name})`));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Interactive mode - ask about platform selection
|
|
119
|
+
const { platforms } = await inquirer_1.default.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'checkbox',
|
|
122
|
+
name: 'platforms',
|
|
123
|
+
message: 'Select the platforms to install:',
|
|
124
|
+
choices: [
|
|
125
|
+
{ name: 'Select All', value: 'all' },
|
|
126
|
+
new inquirer_1.default.Separator(),
|
|
127
|
+
...PLATFORMS.map(p => ({
|
|
128
|
+
name: p.name,
|
|
129
|
+
value: p.id,
|
|
130
|
+
checked: false,
|
|
131
|
+
}))
|
|
132
|
+
],
|
|
133
|
+
validate: (answer) => {
|
|
134
|
+
if (answer.length < 1) {
|
|
135
|
+
return 'You must select at least one platform.';
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
if (platforms.includes('all')) {
|
|
142
|
+
selectedPlatforms = PLATFORMS;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
selectedPlatforms = PLATFORMS.filter(p => platforms.includes(p.id));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Handle agent installation
|
|
149
|
+
let selectedAgents = [];
|
|
150
|
+
// Dynamically discover available agents from template source
|
|
151
|
+
let agentTemplateDir = path_1.default.join(__dirname, '../templates/agents');
|
|
152
|
+
// Handle ts-node vs compiled paths
|
|
153
|
+
if (path_1.default.basename(path_1.default.join(__dirname, '..')) === 'src') {
|
|
154
|
+
agentTemplateDir = path_1.default.join(__dirname, '../../src/templates/agents');
|
|
155
|
+
}
|
|
156
|
+
else if (path_1.default.basename(__dirname) === 'commands') {
|
|
157
|
+
agentTemplateDir = path_1.default.join(__dirname, '../templates/agents');
|
|
158
|
+
}
|
|
159
|
+
if (!fs_extra_1.default.existsSync(agentTemplateDir)) {
|
|
160
|
+
agentTemplateDir = path_1.default.join(__dirname, '../../templates/agents');
|
|
161
|
+
}
|
|
162
|
+
let availableAgents = [];
|
|
163
|
+
if (fs_extra_1.default.existsSync(agentTemplateDir)) {
|
|
164
|
+
const agentFiles = await fs_extra_1.default.readdir(agentTemplateDir);
|
|
165
|
+
availableAgents = agentFiles
|
|
166
|
+
.filter(f => f.endsWith('.md'))
|
|
167
|
+
.map(f => f.replace('.md', ''));
|
|
168
|
+
}
|
|
169
|
+
if (opts.agents && opts.agents.length > 0) {
|
|
170
|
+
// Non-interactive agent selection via --agents flag
|
|
171
|
+
if (opts.agents.includes('all')) {
|
|
172
|
+
selectedAgents = availableAgents;
|
|
173
|
+
console.log(chalk_1.default.blue('[INFO] Installing all agents'));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Match specific agents
|
|
177
|
+
for (const agentArg of opts.agents) {
|
|
178
|
+
const normalized = agentArg.toLowerCase().trim();
|
|
179
|
+
const matched = availableAgents.find(a => a.toLowerCase() === normalized);
|
|
180
|
+
if (matched) {
|
|
181
|
+
selectedAgents.push(matched);
|
|
182
|
+
console.log(chalk_1.default.green(`[OK] Matched agent "${agentArg}" to ${matched}`));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(chalk_1.default.yellow(`[WARN] Could not match agent "${agentArg}" - skipping`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (selectedAgents.length === 0 && opts.agents.length > 0 && !opts.agents.includes('all')) {
|
|
189
|
+
console.log(chalk_1.default.yellow('[WARN] No valid agents matched. Available agents:'));
|
|
190
|
+
availableAgents.forEach(a => console.log(` - ${a}`));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (!nonInteractive) {
|
|
195
|
+
// Interactive mode - ask about agent installation
|
|
196
|
+
const { installAgents } = await inquirer_1.default.prompt([
|
|
197
|
+
{
|
|
198
|
+
type: 'confirm',
|
|
199
|
+
name: 'installAgents',
|
|
200
|
+
message: 'Would you like to install pre-built AI agents?',
|
|
201
|
+
default: true,
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
204
|
+
if (installAgents && availableAgents.length > 0) {
|
|
205
|
+
const { agents } = await inquirer_1.default.prompt([
|
|
206
|
+
{
|
|
207
|
+
type: 'checkbox',
|
|
208
|
+
name: 'agents',
|
|
209
|
+
message: 'Select agents to install:',
|
|
210
|
+
choices: [
|
|
211
|
+
{ name: 'Select All', value: 'all' },
|
|
212
|
+
new inquirer_1.default.Separator(),
|
|
213
|
+
...availableAgents.map(agent => ({
|
|
214
|
+
name: agent.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
|
|
215
|
+
value: agent,
|
|
216
|
+
checked: false,
|
|
217
|
+
}))
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
]);
|
|
221
|
+
if (agents.includes('all')) {
|
|
222
|
+
selectedAgents = availableAgents;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
selectedAgents = agents;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log('');
|
|
230
|
+
console.log(chalk_1.default.blue('[INFO] Installing Contextuate framework...'));
|
|
231
|
+
console.log('');
|
|
232
|
+
// 1. Create directory structure
|
|
233
|
+
console.log(chalk_1.default.blue('[INFO] Creating directory structure...'));
|
|
234
|
+
const dirs = [
|
|
235
|
+
'docs/ai/.contextuate/standards',
|
|
236
|
+
'docs/ai/.contextuate/tools',
|
|
237
|
+
'docs/ai/.contextuate/agents',
|
|
238
|
+
'docs/ai/agents',
|
|
239
|
+
'docs/ai/standards',
|
|
240
|
+
'docs/ai/quickrefs',
|
|
241
|
+
'docs/ai/tasks',
|
|
242
|
+
'docs/ai/commands',
|
|
243
|
+
'docs/ai/hooks',
|
|
244
|
+
'docs/ai/skills',
|
|
245
|
+
];
|
|
246
|
+
for (const dir of dirs) {
|
|
247
|
+
await fs_extra_1.default.ensureDir(dir);
|
|
248
|
+
console.log(chalk_1.default.green(`[OK] Created directory: ${dir}`));
|
|
249
|
+
}
|
|
250
|
+
// Cleanup legacy template directories if they exist
|
|
251
|
+
const legacyDirs = [
|
|
252
|
+
'docs/ai/.contextuate/templates',
|
|
253
|
+
];
|
|
254
|
+
for (const dir of legacyDirs) {
|
|
255
|
+
if (fs_extra_1.default.existsSync(dir)) {
|
|
256
|
+
await fs_extra_1.default.remove(dir);
|
|
257
|
+
console.log(chalk_1.default.yellow(`[CLEANUP] Removed legacy directory: ${dir}`));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
console.log('');
|
|
261
|
+
// 2. Copy templates
|
|
262
|
+
console.log(chalk_1.default.blue('[INFO] Installing framework files...'));
|
|
263
|
+
// In development (ts-node), this is ../../src/templates relative to src/commands/init.ts
|
|
264
|
+
// In production (dist), this is ../templates relative to dist/commands/init.js
|
|
265
|
+
let templateSource = path_1.default.join(__dirname, '../templates');
|
|
266
|
+
// If running from src/commands (ts-node), we need to go up one more level if structure is src/commands/init.ts
|
|
267
|
+
if (path_1.default.basename(path_1.default.join(__dirname, '..')) === 'src') {
|
|
268
|
+
templateSource = path_1.default.join(__dirname, '../../src/templates');
|
|
269
|
+
}
|
|
270
|
+
else if (path_1.default.basename(__dirname) === 'commands') {
|
|
271
|
+
// dist/commands/init.js -> dist/templates
|
|
272
|
+
templateSource = path_1.default.join(__dirname, '../templates');
|
|
273
|
+
}
|
|
274
|
+
// Fallback/Verify
|
|
275
|
+
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
276
|
+
// Try one level up just in case
|
|
277
|
+
templateSource = path_1.default.join(__dirname, '../../templates');
|
|
278
|
+
}
|
|
279
|
+
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
280
|
+
console.error(chalk_1.default.red(`[ERROR] Could not find template source at ${templateSource}`));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const installDir = 'docs/ai/.contextuate';
|
|
284
|
+
// Helper to copy files
|
|
285
|
+
const copyFile = async (src, dest) => {
|
|
286
|
+
// Resolve absolute paths to check for equality
|
|
287
|
+
const absSrc = path_1.default.resolve(src);
|
|
288
|
+
const absDest = path_1.default.resolve(dest);
|
|
289
|
+
if (absSrc === absDest) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (!fs_extra_1.default.existsSync(src)) {
|
|
293
|
+
console.log(chalk_1.default.red(`[ERROR] Source file missing: ${src}`));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (fs_extra_1.default.existsSync(dest) && !opts.force) {
|
|
297
|
+
console.log(chalk_1.default.yellow(`[WARN] Skipped (exists): ${dest}`));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
await fs_extra_1.default.copy(src, dest);
|
|
301
|
+
console.log(chalk_1.default.green(`[OK] Created: ${dest}`));
|
|
302
|
+
};
|
|
303
|
+
// Copy core engine files only to .contextuate
|
|
304
|
+
await copyFile(path_1.default.join(templateSource, 'version.json'), path_1.default.join(installDir, 'version.json'));
|
|
305
|
+
await copyFile(path_1.default.join(templateSource, 'README.md'), path_1.default.join(installDir, 'README.md'));
|
|
306
|
+
// Copy directories - only core framework files
|
|
307
|
+
const copyDirContents = async (srcSubDir, destDir) => {
|
|
308
|
+
const srcDir = path_1.default.join(templateSource, srcSubDir);
|
|
309
|
+
if (fs_extra_1.default.existsSync(srcDir)) {
|
|
310
|
+
await fs_extra_1.default.ensureDir(destDir);
|
|
311
|
+
const files = await fs_extra_1.default.readdir(srcDir);
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
await copyFile(path_1.default.join(srcDir, file), path_1.default.join(destDir, file));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
// Copy core standards, tools, and framework agents to .contextuate (engine files)
|
|
318
|
+
await copyDirContents('standards', path_1.default.join(installDir, 'standards'));
|
|
319
|
+
await copyDirContents('tools', path_1.default.join(installDir, 'tools'));
|
|
320
|
+
await copyDirContents('framework-agents', path_1.default.join(installDir, 'agents'));
|
|
321
|
+
console.log(chalk_1.default.green('[OK] Copied framework files'));
|
|
322
|
+
console.log('');
|
|
323
|
+
// 3. Install selected agents to docs/ai/agents/
|
|
324
|
+
if (selectedAgents.length > 0) {
|
|
325
|
+
console.log(chalk_1.default.blue('[INFO] Installing selected agents...'));
|
|
326
|
+
for (const agent of selectedAgents) {
|
|
327
|
+
const agentFile = `${agent}.md`;
|
|
328
|
+
const srcPath = path_1.default.join(templateSource, 'agents', agentFile);
|
|
329
|
+
const destPath = path_1.default.join('docs/ai/agents', agentFile);
|
|
330
|
+
await copyFile(srcPath, destPath);
|
|
331
|
+
}
|
|
332
|
+
console.log(chalk_1.default.green(`[OK] Installed ${selectedAgents.length} agent(s) to docs/ai/agents/`));
|
|
333
|
+
console.log('');
|
|
334
|
+
}
|
|
335
|
+
// 4. Setup project context files
|
|
336
|
+
console.log(chalk_1.default.blue('[INFO] Setting up project context...'));
|
|
337
|
+
// Copy contextuate.md (main entry point) directly from templates to docs/ai/.contextuate/ (protected)
|
|
338
|
+
await copyFile(path_1.default.join(templateSource, 'templates/contextuate.md'), 'docs/ai/.contextuate/contextuate.md');
|
|
339
|
+
// Copy context.md (user customizable) directly from templates to docs/ai/
|
|
340
|
+
await copyFile(path_1.default.join(templateSource, 'templates/context.md'), 'docs/ai/context.md');
|
|
341
|
+
console.log('');
|
|
342
|
+
// 5. Generate jump files for selected platforms
|
|
343
|
+
console.log(chalk_1.default.blue('[INFO] Generating platform jump files...'));
|
|
344
|
+
for (const platform of selectedPlatforms) {
|
|
345
|
+
if (platform.ensureDir) {
|
|
346
|
+
await fs_extra_1.default.ensureDir(platform.ensureDir);
|
|
347
|
+
}
|
|
348
|
+
// Copy directly from template source, not from .contextuate
|
|
349
|
+
await copyFile(path_1.default.join(templateSource, platform.src), platform.dest);
|
|
350
|
+
}
|
|
351
|
+
console.log('');
|
|
352
|
+
// 6. Create platform-specific symlinks (only for platforms that need them)
|
|
353
|
+
const platformsWithSymlinks = selectedPlatforms.filter(p => p.symlinks);
|
|
354
|
+
if (platformsWithSymlinks.length > 0) {
|
|
355
|
+
console.log(chalk_1.default.blue('[INFO] Creating platform symlinks...'));
|
|
356
|
+
// Helper to create symlinks
|
|
357
|
+
const createSymlink = async (target, linkPath) => {
|
|
358
|
+
const linkDir = path_1.default.dirname(linkPath);
|
|
359
|
+
await fs_extra_1.default.ensureDir(linkDir);
|
|
360
|
+
// Calculate relative path from link location to target
|
|
361
|
+
const relativeTarget = path_1.default.relative(linkDir, target);
|
|
362
|
+
// Check if symlink already exists
|
|
363
|
+
try {
|
|
364
|
+
const existingLink = await fs_extra_1.default.readlink(linkPath);
|
|
365
|
+
if (existingLink === relativeTarget) {
|
|
366
|
+
console.log(chalk_1.default.yellow(`[WARN] Symlink exists: ${linkPath} -> ${relativeTarget}`));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// Remove existing symlink if it points elsewhere
|
|
370
|
+
await fs_extra_1.default.remove(linkPath);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// Not a symlink or doesn't exist
|
|
374
|
+
if (fs_extra_1.default.existsSync(linkPath)) {
|
|
375
|
+
if (opts.force) {
|
|
376
|
+
await fs_extra_1.default.remove(linkPath);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
console.log(chalk_1.default.yellow(`[WARN] Skipped (path exists): ${linkPath}`));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
await fs_extra_1.default.ensureSymlink(relativeTarget, linkPath);
|
|
385
|
+
console.log(chalk_1.default.green(`[OK] Symlink: ${linkPath} -> ${relativeTarget}`));
|
|
386
|
+
};
|
|
387
|
+
// Create symlinks for Claude Code
|
|
388
|
+
if (selectedPlatforms.some(p => p.id === 'claude')) {
|
|
389
|
+
const symlinks = [
|
|
390
|
+
{ target: 'docs/ai/commands', link: '.claude/commands' },
|
|
391
|
+
{ target: 'docs/ai/agents', link: '.claude/agents' },
|
|
392
|
+
{ target: 'docs/ai/hooks', link: '.claude/hooks' },
|
|
393
|
+
{ target: 'docs/ai/skills', link: '.claude/skills' },
|
|
394
|
+
];
|
|
395
|
+
for (const symlink of symlinks) {
|
|
396
|
+
await createSymlink(symlink.target, symlink.link);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
console.log('');
|
|
400
|
+
}
|
|
401
|
+
console.log(chalk_1.default.green('╔════════════════════════════════════════╗'));
|
|
402
|
+
console.log(chalk_1.default.green('║ Installation Complete! ║'));
|
|
403
|
+
console.log(chalk_1.default.green('╚════════════════════════════════════════╝'));
|
|
404
|
+
console.log('');
|
|
405
|
+
console.log('Installed platforms:');
|
|
406
|
+
for (const platform of selectedPlatforms) {
|
|
407
|
+
console.log(` - ${chalk_1.default.cyan(platform.name)} (${platform.dest})`);
|
|
408
|
+
}
|
|
409
|
+
console.log('');
|
|
410
|
+
if (selectedAgents.length > 0) {
|
|
411
|
+
console.log('Installed agents:');
|
|
412
|
+
for (const agent of selectedAgents) {
|
|
413
|
+
console.log(` - ${chalk_1.default.cyan(agent)} (docs/ai/agents/${agent}.md)`);
|
|
414
|
+
}
|
|
415
|
+
console.log('');
|
|
416
|
+
}
|
|
417
|
+
console.log('Next steps:');
|
|
418
|
+
console.log('');
|
|
419
|
+
console.log(` 1. Edit ${chalk_1.default.blue('docs/ai/context.md')} with your project details`);
|
|
420
|
+
if (selectedAgents.length > 0) {
|
|
421
|
+
console.log(` 2. Review installed agents in ${chalk_1.default.blue('docs/ai/agents/')}`);
|
|
422
|
+
console.log(` 3. Add custom agents or quickrefs as needed`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
console.log(` 2. Create custom agents in ${chalk_1.default.blue('docs/ai/agents/')}`);
|
|
426
|
+
console.log(` 3. Add quickrefs in ${chalk_1.default.blue('docs/ai/quickrefs/')}`);
|
|
427
|
+
}
|
|
428
|
+
console.log('');
|
|
429
|
+
console.log('');
|
|
430
|
+
console.log('Documentation: https://contextuate.md');
|
|
431
|
+
console.log('');
|
|
432
|
+
console.log(chalk_1.default.gray('Powered by Esotech.'));
|
|
433
|
+
console.log(chalk_1.default.gray('Created by Alexander Conroy (@geilt)'));
|
|
434
|
+
console.log('');
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
if (error.isTtyError) {
|
|
438
|
+
// Prompt couldn't be rendered in the current environment
|
|
439
|
+
console.error(chalk_1.default.red('[ERROR] Prompt could not be rendered in the current environment'));
|
|
440
|
+
}
|
|
441
|
+
else if (error.name === 'ExitPromptError' || error.message.includes('User force closed the prompt')) {
|
|
442
|
+
console.log('');
|
|
443
|
+
console.log(chalk_1.default.yellow('👋 Installation cancelled by user.'));
|
|
444
|
+
process.exit(0);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
// Something else went wrong
|
|
448
|
+
console.error(chalk_1.default.red('[ERROR] An unexpected error occurred:'), error);
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|