@esotech/contextuate 2.1.0 → 2.1.2
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/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +131 -0
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.js +164 -228
- package/dist/commands/install.js +8 -24
- package/dist/index.js +7 -0
- package/dist/templates/agents/aegis.md +1 -1
- package/dist/templates/agents/archon.md +116 -8
- package/dist/templates/agents/atlas.md +17 -17
- package/dist/templates/agents/canvas.md +1 -1
- package/dist/templates/agents/chronicle.md +1 -1
- package/dist/templates/agents/chronos.md +1 -1
- package/dist/templates/agents/cipher.md +1 -1
- package/dist/templates/agents/crucible.md +1 -1
- package/dist/templates/agents/echo.md +1 -1
- package/dist/templates/agents/forge.md +1 -1
- package/dist/templates/agents/ledger.md +34 -3
- package/dist/templates/agents/meridian.md +1 -1
- package/dist/templates/agents/nexus.md +1 -1
- package/dist/templates/agents/pythia.md +1 -1
- package/dist/templates/agents/scribe.md +1 -1
- package/dist/templates/agents/sentinel.md +1 -1
- package/dist/templates/agents/thoth.md +1 -1
- package/dist/templates/agents/unity.md +1 -1
- package/dist/templates/agents/vox.md +1 -1
- package/dist/templates/agents/weaver.md +1 -1
- package/dist/templates/{skills → commands}/consult.md +1 -1
- package/dist/templates/commands/orchestrate.md +49 -0
- package/dist/templates/framework-agents/documentation-expert.md +3 -3
- package/dist/templates/framework-agents/tools-expert.md +3 -3
- package/dist/templates/templates/contextuate.md +1 -1
- package/dist/templates/tools/agent-creator.md +4 -4
- package/dist/templates/tools/standards-detector.md +1 -1
- package/dist/utils/tools.d.ts +60 -0
- package/dist/utils/tools.js +260 -0
- package/package.json +2 -2
- package/dist/templates/skills/orchestrate.md +0 -173
- package/dist/templates/skills/pythia.md +0 -37
- package/dist/templates/templates/standards/go.standards.md +0 -167
- package/dist/templates/templates/standards/java.standards.md +0 -167
- package/dist/templates/templates/standards/javascript.standards.md +0 -292
- package/dist/templates/templates/standards/php.standards.md +0 -181
- package/dist/templates/templates/standards/python.standards.md +0 -175
- package/dist/templates/tools/agent-creator.tool.md +0 -252
- package/dist/templates/tools/quickref.tool.md +0 -216
- package/dist/templates/tools/spawn.tool.md +0 -31
- package/dist/templates/tools/standards-detector.tool.md +0 -301
- package/dist/templates/version.json +0 -8
package/dist/commands/init.js
CHANGED
|
@@ -9,6 +9,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const fs_1 = require("fs");
|
|
12
|
+
const tools_1 = require("../utils/tools");
|
|
12
13
|
// Get package version dynamically
|
|
13
14
|
function getPackageVersion() {
|
|
14
15
|
try {
|
|
@@ -19,7 +20,7 @@ function getPackageVersion() {
|
|
|
19
20
|
return '0.0.0';
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
// Get package info for
|
|
23
|
+
// Get package info for contextuate.json
|
|
23
24
|
function getPackageInfo() {
|
|
24
25
|
try {
|
|
25
26
|
const packageJson = JSON.parse((0, fs_1.readFileSync)(path_1.default.join(__dirname, '../../package.json'), 'utf-8'));
|
|
@@ -70,10 +71,45 @@ function fuzzyMatchPlatform(input) {
|
|
|
70
71
|
}
|
|
71
72
|
return null;
|
|
72
73
|
}
|
|
74
|
+
// Get template source directory
|
|
75
|
+
function getTemplateSource() {
|
|
76
|
+
let templateSource = path_1.default.join(__dirname, '../templates');
|
|
77
|
+
// Handle ts-node vs compiled paths
|
|
78
|
+
if (path_1.default.basename(path_1.default.join(__dirname, '..')) === 'src') {
|
|
79
|
+
templateSource = path_1.default.join(__dirname, '../../src/templates');
|
|
80
|
+
}
|
|
81
|
+
else if (path_1.default.basename(__dirname) === 'commands') {
|
|
82
|
+
templateSource = path_1.default.join(__dirname, '../templates');
|
|
83
|
+
}
|
|
84
|
+
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
85
|
+
templateSource = path_1.default.join(__dirname, '../../templates');
|
|
86
|
+
}
|
|
87
|
+
return templateSource;
|
|
88
|
+
}
|
|
89
|
+
// Discover available agents from template source
|
|
90
|
+
async function discoverAgents(templateSource) {
|
|
91
|
+
const agentDir = path_1.default.join(templateSource, 'agents');
|
|
92
|
+
if (!fs_extra_1.default.existsSync(agentDir)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const files = await fs_extra_1.default.readdir(agentDir);
|
|
96
|
+
return files
|
|
97
|
+
.filter(f => f.endsWith('.md'))
|
|
98
|
+
.map(f => f.replace('.md', ''));
|
|
99
|
+
}
|
|
100
|
+
// Discover available skills from template source
|
|
101
|
+
async function discoverSkills(templateSource) {
|
|
102
|
+
const commandsDir = path_1.default.join(templateSource, 'commands');
|
|
103
|
+
if (!fs_extra_1.default.existsSync(commandsDir)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const files = await fs_extra_1.default.readdir(commandsDir);
|
|
107
|
+
return files
|
|
108
|
+
.filter(f => f.endsWith('.md'))
|
|
109
|
+
.map(f => f.replace('.md', ''));
|
|
110
|
+
}
|
|
73
111
|
async function initCommand(platformArgs, options) {
|
|
74
112
|
// Handle both old signature (no args) and new signature (with variadic args)
|
|
75
|
-
// When called with no args: first param is options object
|
|
76
|
-
// When called with args: first param is array, second param is options
|
|
77
113
|
let platforms = [];
|
|
78
114
|
let opts = {};
|
|
79
115
|
if (Array.isArray(platformArgs)) {
|
|
@@ -81,14 +117,12 @@ async function initCommand(platformArgs, options) {
|
|
|
81
117
|
opts = options || {};
|
|
82
118
|
}
|
|
83
119
|
else {
|
|
84
|
-
// Old signature - first param is actually options
|
|
85
120
|
opts = platformArgs || {};
|
|
86
121
|
platforms = [];
|
|
87
122
|
}
|
|
88
123
|
console.log(chalk_1.default.blue('╔════════════════════════════════════════╗'));
|
|
89
124
|
console.log(chalk_1.default.blue('║ Contextuate Installer ║'));
|
|
90
125
|
console.log(chalk_1.default.blue('║ AI Context Framework ║'));
|
|
91
|
-
console.log(chalk_1.default.blue('║ ║'));
|
|
92
126
|
console.log(chalk_1.default.blue('╚════════════════════════════════════════╝'));
|
|
93
127
|
console.log('');
|
|
94
128
|
try {
|
|
@@ -113,18 +147,23 @@ async function initCommand(platformArgs, options) {
|
|
|
113
147
|
}
|
|
114
148
|
}
|
|
115
149
|
else if (!hasMarker && nonInteractive) {
|
|
116
|
-
console.log(chalk_1.default.yellow('[WARN] No project markers found
|
|
150
|
+
console.log(chalk_1.default.yellow('[WARN] No project markers found - continuing anyway'));
|
|
117
151
|
}
|
|
152
|
+
// Get template source
|
|
153
|
+
const templateSource = getTemplateSource();
|
|
154
|
+
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
155
|
+
console.error(chalk_1.default.red(`[ERROR] Could not find template source at ${templateSource}`));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Determine selected platforms
|
|
118
159
|
let selectedPlatforms = [];
|
|
119
|
-
// Non-interactive mode - process CLI arguments
|
|
120
160
|
if (nonInteractive) {
|
|
121
|
-
//
|
|
161
|
+
// Non-interactive: parse CLI arguments
|
|
122
162
|
if (platforms.some(arg => arg.toLowerCase() === 'all')) {
|
|
123
163
|
selectedPlatforms = PLATFORMS;
|
|
124
164
|
console.log(chalk_1.default.blue('[INFO] Installing all platforms'));
|
|
125
165
|
}
|
|
126
166
|
else {
|
|
127
|
-
// Fuzzy match each platform argument
|
|
128
167
|
for (const arg of platforms) {
|
|
129
168
|
const matchedId = fuzzyMatchPlatform(arg);
|
|
130
169
|
if (matchedId) {
|
|
@@ -146,126 +185,46 @@ async function initCommand(platformArgs, options) {
|
|
|
146
185
|
}
|
|
147
186
|
}
|
|
148
187
|
else {
|
|
149
|
-
// Interactive
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
message: 'Select the platforms to install:',
|
|
155
|
-
choices: [
|
|
156
|
-
{ name: 'Select All', value: 'all' },
|
|
157
|
-
new inquirer_1.default.Separator(),
|
|
158
|
-
...PLATFORMS.map(p => ({
|
|
159
|
-
name: p.name,
|
|
160
|
-
value: p.id,
|
|
161
|
-
checked: false,
|
|
162
|
-
}))
|
|
163
|
-
],
|
|
164
|
-
validate: (answer) => {
|
|
165
|
-
if (answer.length < 1) {
|
|
166
|
-
return 'You must select at least one platform.';
|
|
167
|
-
}
|
|
168
|
-
return true;
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
]);
|
|
172
|
-
if (platforms.includes('all')) {
|
|
173
|
-
selectedPlatforms = PLATFORMS;
|
|
188
|
+
// Interactive: default to Claude Code (opinionated)
|
|
189
|
+
const claudePlatform = PLATFORMS.find(p => p.id === 'claude');
|
|
190
|
+
if (claudePlatform) {
|
|
191
|
+
selectedPlatforms = [claudePlatform];
|
|
192
|
+
console.log(chalk_1.default.blue(`[INFO] Installing for ${claudePlatform.name} (default)`));
|
|
174
193
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// Handle agent installation
|
|
180
|
-
let selectedAgents = [];
|
|
181
|
-
// Dynamically discover available agents from template source
|
|
182
|
-
let agentTemplateDir = path_1.default.join(__dirname, '../templates/agents');
|
|
183
|
-
// Handle ts-node vs compiled paths
|
|
184
|
-
if (path_1.default.basename(path_1.default.join(__dirname, '..')) === 'src') {
|
|
185
|
-
agentTemplateDir = path_1.default.join(__dirname, '../../src/templates/agents');
|
|
186
|
-
}
|
|
187
|
-
else if (path_1.default.basename(__dirname) === 'commands') {
|
|
188
|
-
agentTemplateDir = path_1.default.join(__dirname, '../templates/agents');
|
|
189
|
-
}
|
|
190
|
-
if (!fs_extra_1.default.existsSync(agentTemplateDir)) {
|
|
191
|
-
agentTemplateDir = path_1.default.join(__dirname, '../../templates/agents');
|
|
192
|
-
}
|
|
193
|
-
let availableAgents = [];
|
|
194
|
-
if (fs_extra_1.default.existsSync(agentTemplateDir)) {
|
|
195
|
-
const agentFiles = await fs_extra_1.default.readdir(agentTemplateDir);
|
|
196
|
-
availableAgents = agentFiles
|
|
197
|
-
.filter(f => f.endsWith('.md'))
|
|
198
|
-
.map(f => f.replace('.md', ''));
|
|
199
|
-
}
|
|
200
|
-
if (opts.agents && opts.agents.length > 0) {
|
|
201
|
-
// Non-interactive agent selection via --agents flag
|
|
202
|
-
if (opts.agents.includes('none')) {
|
|
203
|
-
// Explicitly skip agent installation
|
|
204
|
-
console.log(chalk_1.default.blue('[INFO] Skipping agent installation (--agents none)'));
|
|
205
|
-
}
|
|
206
|
-
else if (opts.agents.includes('all')) {
|
|
207
|
-
selectedAgents = availableAgents;
|
|
208
|
-
console.log(chalk_1.default.blue('[INFO] Installing all agents'));
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
// Match specific agents
|
|
212
|
-
for (const agentArg of opts.agents) {
|
|
213
|
-
const normalized = agentArg.toLowerCase().trim();
|
|
214
|
-
const matched = availableAgents.find(a => a.toLowerCase() === normalized);
|
|
215
|
-
if (matched) {
|
|
216
|
-
selectedAgents.push(matched);
|
|
217
|
-
console.log(chalk_1.default.green(`[OK] Matched agent "${agentArg}" to ${matched}`));
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
console.log(chalk_1.default.yellow(`[WARN] Could not match agent "${agentArg}" - skipping`));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (selectedAgents.length === 0 && opts.agents.length > 0) {
|
|
224
|
-
console.log(chalk_1.default.yellow('[WARN] No valid agents matched. Available agents:'));
|
|
225
|
-
availableAgents.forEach(a => console.log(` - ${a}`));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
else if (!nonInteractive) {
|
|
230
|
-
// Interactive mode - ask about agent installation
|
|
231
|
-
const { installAgents } = await inquirer_1.default.prompt([
|
|
194
|
+
// Ask if they want additional platforms
|
|
195
|
+
const { addMorePlatforms } = await inquirer_1.default.prompt([
|
|
232
196
|
{
|
|
233
197
|
type: 'confirm',
|
|
234
|
-
name: '
|
|
235
|
-
message: 'Would you like to
|
|
236
|
-
default:
|
|
198
|
+
name: 'addMorePlatforms',
|
|
199
|
+
message: 'Would you like to add other platforms?',
|
|
200
|
+
default: false,
|
|
237
201
|
},
|
|
238
202
|
]);
|
|
239
|
-
if (
|
|
240
|
-
const
|
|
203
|
+
if (addMorePlatforms) {
|
|
204
|
+
const otherPlatforms = PLATFORMS.filter(p => p.id !== 'claude');
|
|
205
|
+
const { additionalPlatforms } = await inquirer_1.default.prompt([
|
|
241
206
|
{
|
|
242
207
|
type: 'checkbox',
|
|
243
|
-
name: '
|
|
244
|
-
message: 'Select
|
|
245
|
-
choices:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
value: agent,
|
|
251
|
-
checked: false,
|
|
252
|
-
}))
|
|
253
|
-
],
|
|
208
|
+
name: 'additionalPlatforms',
|
|
209
|
+
message: 'Select additional platforms:',
|
|
210
|
+
choices: otherPlatforms.map(p => ({
|
|
211
|
+
name: p.name,
|
|
212
|
+
value: p.id,
|
|
213
|
+
checked: false,
|
|
214
|
+
})),
|
|
254
215
|
},
|
|
255
216
|
]);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
217
|
+
for (const platformId of additionalPlatforms) {
|
|
218
|
+
const platform = PLATFORMS.find(p => p.id === platformId);
|
|
219
|
+
if (platform) {
|
|
220
|
+
selectedPlatforms.push(platform);
|
|
221
|
+
}
|
|
261
222
|
}
|
|
262
223
|
}
|
|
263
224
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
console.log(chalk_1.default.blue('[INFO] Installing all agents (default in non-interactive mode)'));
|
|
268
|
-
}
|
|
225
|
+
// Discover all agents and skills
|
|
226
|
+
const availableAgents = await discoverAgents(templateSource);
|
|
227
|
+
const availableSkills = await discoverSkills(templateSource);
|
|
269
228
|
console.log('');
|
|
270
229
|
console.log(chalk_1.default.blue('[INFO] Installing Contextuate framework...'));
|
|
271
230
|
console.log('');
|
|
@@ -287,170 +246,142 @@ async function initCommand(platformArgs, options) {
|
|
|
287
246
|
await fs_extra_1.default.ensureDir(dir);
|
|
288
247
|
console.log(chalk_1.default.green(`[OK] Created directory: ${dir}`));
|
|
289
248
|
}
|
|
290
|
-
// Cleanup legacy
|
|
291
|
-
const legacyDirs = [
|
|
292
|
-
'docs/ai/.contextuate/templates',
|
|
293
|
-
];
|
|
249
|
+
// Cleanup legacy directories
|
|
250
|
+
const legacyDirs = ['docs/ai/.contextuate/templates'];
|
|
294
251
|
for (const dir of legacyDirs) {
|
|
295
252
|
if (fs_extra_1.default.existsSync(dir)) {
|
|
296
253
|
await fs_extra_1.default.remove(dir);
|
|
297
254
|
console.log(chalk_1.default.yellow(`[CLEANUP] Removed legacy directory: ${dir}`));
|
|
298
255
|
}
|
|
299
256
|
}
|
|
257
|
+
// Cleanup legacy version.json (replaced by contextuate.json)
|
|
258
|
+
const legacyVersionJson = 'docs/ai/.contextuate/version.json';
|
|
259
|
+
if (fs_extra_1.default.existsSync(legacyVersionJson)) {
|
|
260
|
+
await fs_extra_1.default.remove(legacyVersionJson);
|
|
261
|
+
console.log(chalk_1.default.yellow(`[CLEANUP] Removed legacy version.json (migrated to contextuate.json)`));
|
|
262
|
+
}
|
|
300
263
|
console.log('');
|
|
301
|
-
// 2.
|
|
264
|
+
// 2. Detect tools
|
|
265
|
+
console.log(chalk_1.default.blue('[INFO] Checking for optional tools...'));
|
|
266
|
+
let tools = (0, tools_1.detectTools)();
|
|
267
|
+
(0, tools_1.printToolStatus)(tools);
|
|
268
|
+
// Offer to install missing tools
|
|
269
|
+
tools = await (0, tools_1.promptInstallTools)(tools, nonInteractive);
|
|
270
|
+
console.log('');
|
|
271
|
+
// 3. Copy framework files
|
|
302
272
|
console.log(chalk_1.default.blue('[INFO] Installing framework files...'));
|
|
303
|
-
// In development (ts-node), this is ../../src/templates relative to src/commands/init.ts
|
|
304
|
-
// In production (dist), this is ../templates relative to dist/commands/init.js
|
|
305
|
-
let templateSource = path_1.default.join(__dirname, '../templates');
|
|
306
|
-
// If running from src/commands (ts-node), we need to go up one more level if structure is src/commands/init.ts
|
|
307
|
-
if (path_1.default.basename(path_1.default.join(__dirname, '..')) === 'src') {
|
|
308
|
-
templateSource = path_1.default.join(__dirname, '../../src/templates');
|
|
309
|
-
}
|
|
310
|
-
else if (path_1.default.basename(__dirname) === 'commands') {
|
|
311
|
-
// dist/commands/init.js -> dist/templates
|
|
312
|
-
templateSource = path_1.default.join(__dirname, '../templates');
|
|
313
|
-
}
|
|
314
|
-
// Fallback/Verify
|
|
315
|
-
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
316
|
-
// Try one level up just in case
|
|
317
|
-
templateSource = path_1.default.join(__dirname, '../../templates');
|
|
318
|
-
}
|
|
319
|
-
if (!fs_extra_1.default.existsSync(templateSource)) {
|
|
320
|
-
console.error(chalk_1.default.red(`[ERROR] Could not find template source at ${templateSource}`));
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
273
|
const installDir = 'docs/ai/.contextuate';
|
|
324
|
-
// Helper to copy files
|
|
325
|
-
const copyFile = async (src, dest) => {
|
|
326
|
-
// Resolve absolute paths to check for equality
|
|
274
|
+
// Helper to copy and process files
|
|
275
|
+
const copyFile = async (src, dest, processTemplates = false) => {
|
|
327
276
|
const absSrc = path_1.default.resolve(src);
|
|
328
277
|
const absDest = path_1.default.resolve(dest);
|
|
329
|
-
if (absSrc === absDest)
|
|
278
|
+
if (absSrc === absDest)
|
|
330
279
|
return;
|
|
331
|
-
}
|
|
332
280
|
if (!fs_extra_1.default.existsSync(src)) {
|
|
333
281
|
console.log(chalk_1.default.red(`[ERROR] Source file missing: ${src}`));
|
|
334
282
|
return;
|
|
335
283
|
}
|
|
336
|
-
|
|
337
|
-
|
|
284
|
+
// Only context.md is protected from overwrites
|
|
285
|
+
const isContextMd = path_1.default.basename(dest) === 'context.md';
|
|
286
|
+
if (isContextMd && fs_extra_1.default.existsSync(dest)) {
|
|
287
|
+
console.log(chalk_1.default.yellow(`[PRESERVE] Kept existing: ${dest}`));
|
|
338
288
|
return;
|
|
339
289
|
}
|
|
340
|
-
await fs_extra_1.default.
|
|
290
|
+
let content = await fs_extra_1.default.readFile(src, 'utf-8');
|
|
291
|
+
// Process template variables if needed
|
|
292
|
+
if (processTemplates && dest.endsWith('.md')) {
|
|
293
|
+
content = (0, tools_1.processTemplateVariables)(content, tools);
|
|
294
|
+
}
|
|
295
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(dest));
|
|
296
|
+
await fs_extra_1.default.writeFile(dest, content);
|
|
341
297
|
console.log(chalk_1.default.green(`[OK] Created: ${dest}`));
|
|
342
298
|
};
|
|
343
|
-
// Copy
|
|
344
|
-
|
|
345
|
-
const pkgInfo = getPackageInfo();
|
|
346
|
-
const versionData = {
|
|
347
|
-
name: pkgInfo.name,
|
|
348
|
-
version: pkgInfo.version,
|
|
349
|
-
description: pkgInfo.description,
|
|
350
|
-
repository: pkgInfo.repository,
|
|
351
|
-
installed: new Date().toISOString(),
|
|
352
|
-
updated: new Date().toISOString()
|
|
353
|
-
};
|
|
354
|
-
await fs_extra_1.default.writeFile(path_1.default.join(installDir, 'version.json'), JSON.stringify(versionData, null, 2));
|
|
355
|
-
console.log(chalk_1.default.green(`[OK] Created: ${path_1.default.join(installDir, 'version.json')}`));
|
|
356
|
-
await copyFile(path_1.default.join(templateSource, 'README.md'), path_1.default.join(installDir, 'README.md'));
|
|
357
|
-
// Copy directories - only core framework files
|
|
358
|
-
const copyDirContents = async (srcSubDir, destDir) => {
|
|
299
|
+
// Copy directories with template processing
|
|
300
|
+
const copyDirContents = async (srcSubDir, destDir, processTemplates = false) => {
|
|
359
301
|
const srcDir = path_1.default.join(templateSource, srcSubDir);
|
|
360
302
|
if (fs_extra_1.default.existsSync(srcDir)) {
|
|
361
303
|
await fs_extra_1.default.ensureDir(destDir);
|
|
362
304
|
const files = await fs_extra_1.default.readdir(srcDir);
|
|
363
305
|
for (const file of files) {
|
|
364
|
-
await copyFile(path_1.default.join(srcDir, file), path_1.default.join(destDir, file));
|
|
306
|
+
await copyFile(path_1.default.join(srcDir, file), path_1.default.join(destDir, file), processTemplates);
|
|
365
307
|
}
|
|
366
308
|
}
|
|
367
309
|
};
|
|
368
|
-
// Copy
|
|
310
|
+
// Copy README
|
|
311
|
+
await copyFile(path_1.default.join(templateSource, 'README.md'), path_1.default.join(installDir, 'README.md'));
|
|
312
|
+
// Copy core standards, tools, and framework agents
|
|
369
313
|
await copyDirContents('standards', path_1.default.join(installDir, 'standards'));
|
|
370
314
|
await copyDirContents('tools', path_1.default.join(installDir, 'tools'));
|
|
371
315
|
await copyDirContents('framework-agents', path_1.default.join(installDir, 'agents'));
|
|
372
316
|
console.log(chalk_1.default.green('[OK] Copied framework files'));
|
|
373
317
|
console.log('');
|
|
374
|
-
//
|
|
375
|
-
if (
|
|
376
|
-
console.log(chalk_1.default.blue(
|
|
377
|
-
for (const agent of
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
await copyFile(srcPath, destPath);
|
|
318
|
+
// 4. Install ALL agents (opinionated - no prompt)
|
|
319
|
+
if (availableAgents.length > 0) {
|
|
320
|
+
console.log(chalk_1.default.blue(`[INFO] Installing agents (${availableAgents.length} agents)...`));
|
|
321
|
+
for (const agent of availableAgents) {
|
|
322
|
+
const srcPath = path_1.default.join(templateSource, 'agents', `${agent}.md`);
|
|
323
|
+
const destPath = path_1.default.join('docs/ai/agents', `${agent}.md`);
|
|
324
|
+
await copyFile(srcPath, destPath, true); // Process templates
|
|
382
325
|
}
|
|
383
|
-
console.log(chalk_1.default.green(`[OK] Installed ${
|
|
326
|
+
console.log(chalk_1.default.green(`[OK] Installed ${availableAgents.length} agent(s)`));
|
|
384
327
|
console.log('');
|
|
385
328
|
}
|
|
386
|
-
//
|
|
329
|
+
// 5. Install ALL skills (opinionated - no prompt)
|
|
330
|
+
if (availableSkills.length > 0) {
|
|
331
|
+
console.log(chalk_1.default.blue(`[INFO] Installing skills (${availableSkills.length} skills)...`));
|
|
332
|
+
for (const skill of availableSkills) {
|
|
333
|
+
const srcPath = path_1.default.join(templateSource, 'commands', `${skill}.md`);
|
|
334
|
+
const destPath = path_1.default.join('docs/ai/commands', `${skill}.md`);
|
|
335
|
+
await copyFile(srcPath, destPath, true); // Process templates
|
|
336
|
+
}
|
|
337
|
+
console.log(chalk_1.default.green(`[OK] Installed ${availableSkills.length} skill(s)`));
|
|
338
|
+
console.log('');
|
|
339
|
+
}
|
|
340
|
+
// 6. Setup project context files
|
|
387
341
|
console.log(chalk_1.default.blue('[INFO] Setting up project context...'));
|
|
388
|
-
// Copy contextuate.md (main entry point) directly from templates to docs/ai/.contextuate/ (protected)
|
|
389
342
|
await copyFile(path_1.default.join(templateSource, 'templates/contextuate.md'), 'docs/ai/.contextuate/contextuate.md');
|
|
390
|
-
|
|
391
|
-
// IMPORTANT: Never overwrite context.md even with --force, as it contains project-specific context
|
|
392
|
-
const contextMdPath = 'docs/ai/context.md';
|
|
393
|
-
if (fs_extra_1.default.existsSync(contextMdPath)) {
|
|
394
|
-
console.log(chalk_1.default.yellow(`[WARN] Preserved (project context): ${contextMdPath}`));
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
await copyFile(path_1.default.join(templateSource, 'templates/context.md'), contextMdPath);
|
|
398
|
-
}
|
|
343
|
+
await copyFile(path_1.default.join(templateSource, 'templates/context.md'), 'docs/ai/context.md');
|
|
399
344
|
console.log('');
|
|
400
|
-
//
|
|
401
|
-
console.log(chalk_1.default.blue('[INFO] Generating platform
|
|
345
|
+
// 7. Generate platform files
|
|
346
|
+
console.log(chalk_1.default.blue('[INFO] Generating platform files...'));
|
|
402
347
|
for (const platform of selectedPlatforms) {
|
|
403
348
|
if (platform.ensureDir) {
|
|
404
349
|
await fs_extra_1.default.ensureDir(platform.ensureDir);
|
|
405
350
|
}
|
|
406
|
-
// Copy directly from template source, not from .contextuate
|
|
407
351
|
await copyFile(path_1.default.join(templateSource, platform.src), platform.dest);
|
|
408
352
|
}
|
|
409
353
|
console.log('');
|
|
410
|
-
//
|
|
354
|
+
// 8. Create symlinks for platforms that need them
|
|
411
355
|
const platformsWithSymlinks = selectedPlatforms.filter(p => p.symlinks);
|
|
412
356
|
if (platformsWithSymlinks.length > 0) {
|
|
413
357
|
console.log(chalk_1.default.blue('[INFO] Creating platform symlinks...'));
|
|
414
|
-
// Helper to create symlinks
|
|
415
358
|
const createSymlink = async (target, linkPath) => {
|
|
416
359
|
const linkDir = path_1.default.dirname(linkPath);
|
|
417
360
|
await fs_extra_1.default.ensureDir(linkDir);
|
|
418
|
-
// Calculate relative path from link location to target
|
|
419
361
|
const relativeTarget = path_1.default.relative(linkDir, target);
|
|
420
|
-
// Check if symlink already exists
|
|
421
362
|
try {
|
|
422
363
|
const existingLink = await fs_extra_1.default.readlink(linkPath);
|
|
423
364
|
if (existingLink === relativeTarget) {
|
|
424
|
-
console.log(chalk_1.default.yellow(`[
|
|
365
|
+
console.log(chalk_1.default.yellow(`[SKIP] Symlink exists: ${linkPath}`));
|
|
425
366
|
return;
|
|
426
367
|
}
|
|
427
|
-
// Remove existing symlink if it points elsewhere
|
|
428
368
|
await fs_extra_1.default.remove(linkPath);
|
|
429
369
|
}
|
|
430
370
|
catch {
|
|
431
|
-
// Not a symlink or doesn't exist
|
|
432
371
|
if (fs_extra_1.default.existsSync(linkPath)) {
|
|
433
|
-
|
|
434
|
-
await fs_extra_1.default.remove(linkPath);
|
|
435
|
-
}
|
|
436
|
-
else {
|
|
437
|
-
console.log(chalk_1.default.yellow(`[WARN] Skipped (path exists): ${linkPath}`));
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
372
|
+
await fs_extra_1.default.remove(linkPath);
|
|
440
373
|
}
|
|
441
374
|
}
|
|
442
375
|
await fs_extra_1.default.ensureSymlink(relativeTarget, linkPath);
|
|
443
376
|
console.log(chalk_1.default.green(`[OK] Symlink: ${linkPath} -> ${relativeTarget}`));
|
|
444
377
|
};
|
|
445
|
-
//
|
|
378
|
+
// Claude Code symlinks
|
|
446
379
|
if (selectedPlatforms.some(p => p.id === 'claude')) {
|
|
447
380
|
const symlinks = [
|
|
448
381
|
{ target: 'docs/ai/commands', link: '.claude/commands' },
|
|
449
382
|
{ target: 'docs/ai/agents', link: '.claude/agents' },
|
|
450
383
|
{ target: 'docs/ai/hooks', link: '.claude/hooks' },
|
|
451
384
|
{ target: 'docs/ai/skills', link: '.claude/skills' },
|
|
452
|
-
// Link .contextuate so relative paths from agents work correctly
|
|
453
|
-
// (e.g., ../.contextuate/agents/base.md resolves properly)
|
|
454
385
|
{ target: 'docs/ai/.contextuate', link: '.claude/.contextuate' },
|
|
455
386
|
];
|
|
456
387
|
for (const symlink of symlinks) {
|
|
@@ -459,6 +390,20 @@ async function initCommand(platformArgs, options) {
|
|
|
459
390
|
}
|
|
460
391
|
console.log('');
|
|
461
392
|
}
|
|
393
|
+
// 9. Create contextuate.json
|
|
394
|
+
console.log(chalk_1.default.blue('[INFO] Creating contextuate.json...'));
|
|
395
|
+
const pkgInfo = getPackageInfo();
|
|
396
|
+
const now = new Date().toISOString();
|
|
397
|
+
const config = {
|
|
398
|
+
...pkgInfo,
|
|
399
|
+
initialized: now,
|
|
400
|
+
updated: now,
|
|
401
|
+
tools
|
|
402
|
+
};
|
|
403
|
+
await (0, tools_1.writeContextuateConfig)(installDir, config);
|
|
404
|
+
console.log(chalk_1.default.green('[OK] Created contextuate.json'));
|
|
405
|
+
console.log('');
|
|
406
|
+
// Summary
|
|
462
407
|
console.log(chalk_1.default.green('╔════════════════════════════════════════╗'));
|
|
463
408
|
console.log(chalk_1.default.green('║ Installation Complete! ║'));
|
|
464
409
|
console.log(chalk_1.default.green('╚════════════════════════════════════════╝'));
|
|
@@ -468,44 +413,35 @@ async function initCommand(platformArgs, options) {
|
|
|
468
413
|
console.log(` - ${chalk_1.default.cyan(platform.name)} (${platform.dest})`);
|
|
469
414
|
}
|
|
470
415
|
console.log('');
|
|
471
|
-
|
|
472
|
-
console.log('Installed agents:');
|
|
473
|
-
for (const agent of selectedAgents) {
|
|
474
|
-
console.log(` - ${chalk_1.default.cyan(agent)} (docs/ai/agents/${agent}.md)`);
|
|
475
|
-
}
|
|
476
|
-
console.log('');
|
|
477
|
-
}
|
|
478
|
-
console.log('Next steps:');
|
|
416
|
+
console.log(`Installed: ${chalk_1.default.cyan(availableAgents.length)} agents, ${chalk_1.default.cyan(availableSkills.length)} skills`);
|
|
479
417
|
console.log('');
|
|
480
|
-
|
|
481
|
-
if (
|
|
482
|
-
console.log(`
|
|
483
|
-
console.log(` 3. Add custom agents or quickrefs as needed`);
|
|
418
|
+
// Tool status summary
|
|
419
|
+
if (tools.ripgrep.installed) {
|
|
420
|
+
console.log(`Search tool: ${chalk_1.default.green('ripgrep')} (fast)`);
|
|
484
421
|
}
|
|
485
422
|
else {
|
|
486
|
-
console.log(`
|
|
487
|
-
console.log(` 3. Add quickrefs in ${chalk_1.default.blue('docs/ai/quickrefs/')}`);
|
|
423
|
+
console.log(`Search tool: ${chalk_1.default.yellow('grep')} (fallback - consider installing ripgrep)`);
|
|
488
424
|
}
|
|
489
425
|
console.log('');
|
|
426
|
+
console.log('Next steps:');
|
|
490
427
|
console.log('');
|
|
491
|
-
console.log(
|
|
428
|
+
console.log(` 1. Edit ${chalk_1.default.blue('docs/ai/context.md')} with your project details`);
|
|
429
|
+
console.log(` 2. Review agents in ${chalk_1.default.blue('docs/ai/agents/')}`);
|
|
430
|
+
console.log(` 3. Run ${chalk_1.default.blue('contextuate doctor')} to check configuration`);
|
|
492
431
|
console.log('');
|
|
493
|
-
console.log(
|
|
494
|
-
console.log(chalk_1.default.gray('Created by Alexander Conroy (@geilt)'));
|
|
432
|
+
console.log('Documentation: https://contextuate.md');
|
|
495
433
|
console.log('');
|
|
496
434
|
}
|
|
497
435
|
catch (error) {
|
|
498
436
|
if (error.isTtyError) {
|
|
499
|
-
// Prompt couldn't be rendered in the current environment
|
|
500
437
|
console.error(chalk_1.default.red('[ERROR] Prompt could not be rendered in the current environment'));
|
|
501
438
|
}
|
|
502
439
|
else if (error.name === 'ExitPromptError' || error.message.includes('User force closed the prompt')) {
|
|
503
440
|
console.log('');
|
|
504
|
-
console.log(chalk_1.default.yellow('
|
|
441
|
+
console.log(chalk_1.default.yellow('Installation cancelled by user.'));
|
|
505
442
|
process.exit(0);
|
|
506
443
|
}
|
|
507
444
|
else {
|
|
508
|
-
// Something else went wrong
|
|
509
445
|
console.error(chalk_1.default.red('[ERROR] An unexpected error occurred:'), error);
|
|
510
446
|
process.exit(1);
|
|
511
447
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -60,10 +60,10 @@ async function discoverTemplates() {
|
|
|
60
60
|
.filter(f => f.endsWith('.md'))
|
|
61
61
|
.map(f => f.replace('.md', ''));
|
|
62
62
|
}
|
|
63
|
-
// Discover skills
|
|
64
|
-
const
|
|
65
|
-
if (fs_extra_1.default.existsSync(
|
|
66
|
-
const files = await fs_extra_1.default.readdir(
|
|
63
|
+
// Discover skills (stored in commands/ directory)
|
|
64
|
+
const commandsDir = path_1.default.join(templateSource, 'commands');
|
|
65
|
+
if (fs_extra_1.default.existsSync(commandsDir)) {
|
|
66
|
+
const files = await fs_extra_1.default.readdir(commandsDir);
|
|
67
67
|
result.skills = files
|
|
68
68
|
.filter(f => f.endsWith('.md'))
|
|
69
69
|
.map(f => f.replace('.md', ''));
|
|
@@ -188,7 +188,7 @@ async function installTools(names, force) {
|
|
|
188
188
|
return installed;
|
|
189
189
|
}
|
|
190
190
|
// Install skills (slash commands)
|
|
191
|
-
// Skills are installed
|
|
191
|
+
// Skills are installed directly to docs/ai/commands/ for Claude Code
|
|
192
192
|
async function installSkills(names, force) {
|
|
193
193
|
const templateSource = getTemplateSource();
|
|
194
194
|
const templates = await discoverTemplates();
|
|
@@ -197,26 +197,10 @@ async function installSkills(names, force) {
|
|
|
197
197
|
const normalized = name.toLowerCase().trim().replace(/^\//, ''); // Remove leading slash if present
|
|
198
198
|
const matched = templates.skills.find(s => s.toLowerCase() === normalized);
|
|
199
199
|
if (matched) {
|
|
200
|
-
const src = path_1.default.join(templateSource, '
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
// Install skill file to skills/
|
|
204
|
-
if (await copyFile(src, skillDest, force)) {
|
|
200
|
+
const src = path_1.default.join(templateSource, 'commands', `${matched}.md`);
|
|
201
|
+
const dest = path_1.default.join('docs/ai/commands', `${matched}.md`);
|
|
202
|
+
if (await copyFile(src, dest, force)) {
|
|
205
203
|
installed++;
|
|
206
|
-
// Create symlink in commands/ for Claude Code to pick up
|
|
207
|
-
await fs_extra_1.default.ensureDir(path_1.default.dirname(commandDest));
|
|
208
|
-
const symlinkTarget = path_1.default.relative(path_1.default.dirname(commandDest), skillDest);
|
|
209
|
-
if (fs_extra_1.default.existsSync(commandDest)) {
|
|
210
|
-
if (force) {
|
|
211
|
-
await fs_extra_1.default.remove(commandDest);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
console.log(chalk_1.default.yellow(`[SKIP] Symlink already exists: ${commandDest}`));
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
await fs_extra_1.default.symlink(symlinkTarget, commandDest);
|
|
219
|
-
console.log(chalk_1.default.green(`[OK] Symlinked: ${commandDest} -> ${symlinkTarget}`));
|
|
220
204
|
}
|
|
221
205
|
}
|
|
222
206
|
else {
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const create_1 = require("./commands/create");
|
|
|
9
9
|
const index_1 = require("./commands/index");
|
|
10
10
|
const context_1 = require("./commands/context");
|
|
11
11
|
const install_1 = require("./commands/install");
|
|
12
|
+
const doctor_1 = require("./commands/doctor");
|
|
12
13
|
const monitor_1 = require("./commands/monitor");
|
|
13
14
|
const claude_1 = require("./commands/claude");
|
|
14
15
|
const fs_1 = require("fs");
|
|
@@ -54,6 +55,12 @@ program
|
|
|
54
55
|
.command('add-context')
|
|
55
56
|
.description('Interactively add files to docs/ai/context.md')
|
|
56
57
|
.action(context_1.addContextCommand);
|
|
58
|
+
program
|
|
59
|
+
.command('doctor')
|
|
60
|
+
.description('Check and fix Contextuate configuration')
|
|
61
|
+
.option('--fix', 'Attempt to fix issues and update template variables')
|
|
62
|
+
.option('--install', 'Offer to install missing tools')
|
|
63
|
+
.action(doctor_1.doctorCommand);
|
|
57
64
|
program
|
|
58
65
|
.command('run')
|
|
59
66
|
.description('Run an agent definition')
|