@xelth/eck-snapshot 4.2.4 → 5.4.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.

Potentially problematic release.


This version of @xelth/eck-snapshot might be problematic. Click here for more details.

Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/index.js +14 -0
  4. package/package.json +64 -9
  5. package/scripts/mcp-eck-core.js +101 -0
  6. package/scripts/mcp-glm-zai-worker.mjs +243 -0
  7. package/scripts/verify_changes.js +68 -0
  8. package/setup.json +845 -0
  9. package/src/cli/cli.js +369 -0
  10. package/src/cli/commands/claudeSettings.js +93 -0
  11. package/src/cli/commands/consilium.js +86 -0
  12. package/src/cli/commands/createSnapshot.js +906 -0
  13. package/src/cli/commands/detectProfiles.js +98 -0
  14. package/src/cli/commands/detectProject.js +112 -0
  15. package/src/cli/commands/doctor.js +60 -0
  16. package/src/cli/commands/envSync.js +319 -0
  17. package/src/cli/commands/generateProfileGuide.js +144 -0
  18. package/src/cli/commands/pruneSnapshot.js +106 -0
  19. package/src/cli/commands/restoreSnapshot.js +173 -0
  20. package/src/cli/commands/setupGemini.js +149 -0
  21. package/src/cli/commands/setupGemini.test.js +115 -0
  22. package/src/cli/commands/setupMcp.js +269 -0
  23. package/src/cli/commands/showFile.js +39 -0
  24. package/src/cli/commands/trainTokens.js +38 -0
  25. package/src/cli/commands/updateSnapshot.js +219 -0
  26. package/src/config.js +125 -0
  27. package/src/core/skeletonizer.js +201 -0
  28. package/src/mcp-server/index.js +211 -0
  29. package/src/services/claudeCliService.js +626 -0
  30. package/src/services/claudeCliService.test.js +267 -0
  31. package/src/templates/agent-prompt.template.md +43 -0
  32. package/src/templates/architect-prompt.template.md +164 -0
  33. package/src/templates/claude-code/README.md +105 -0
  34. package/src/templates/claude-code/mcp-config-template.json +11 -0
  35. package/src/templates/claude-code/mcp-server-template.js +206 -0
  36. package/src/templates/claude-code/settings-claude.json +1 -0
  37. package/src/templates/envScanRequest.md +4 -0
  38. package/src/templates/gitWorkflow.md +32 -0
  39. package/src/templates/multiAgent.md +118 -0
  40. package/src/templates/opencode/coder.template.md +22 -0
  41. package/src/templates/opencode/junior-architect.template.md +85 -0
  42. package/src/templates/skeleton-instruction.md +16 -0
  43. package/src/templates/update-prompt.template.md +19 -0
  44. package/src/utils/aiHeader.js +678 -0
  45. package/src/utils/claudeMdGenerator.js +148 -0
  46. package/src/utils/eckProtocolParser.js +221 -0
  47. package/src/utils/fileUtils.js +1017 -0
  48. package/src/utils/gitUtils.js +44 -0
  49. package/src/utils/opencodeAgentsGenerator.js +271 -0
  50. package/src/utils/projectDetector.js +704 -0
  51. package/src/utils/tokenEstimator.js +201 -0
@@ -0,0 +1,678 @@
1
+ import { loadSetupConfig, getAllProfiles } from '../config.js';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ // Helper to extract specific markdown sections by header keywords
10
+ function extractSections(content, keywords) {
11
+ if (!content) return '';
12
+ const lines = content.split('\n');
13
+ let extracted = [];
14
+ let capturing = false;
15
+ let currentHeaderLevel = 0;
16
+
17
+ for (const line of lines) {
18
+ const headerMatch = line.match(/^(#{2,})\s+(.+)/);
19
+
20
+ if (headerMatch) {
21
+ const level = headerMatch[1].length;
22
+ const title = headerMatch[2].toLowerCase();
23
+
24
+ // Check if we hit a start keyword
25
+ const isStart = keywords.some(k => title.includes(k.toLowerCase()));
26
+
27
+ if (isStart) {
28
+ capturing = true;
29
+ currentHeaderLevel = level;
30
+ extracted.push(line); // Include the header
31
+ continue;
32
+ }
33
+
34
+ // Stop capturing if we hit a header of same or higher level
35
+ if (capturing && level <= currentHeaderLevel) {
36
+ capturing = false;
37
+ }
38
+ }
39
+
40
+ if (capturing) {
41
+ extracted.push(line);
42
+ }
43
+ }
44
+
45
+ return extracted.join('\n');
46
+ }
47
+
48
+ // Simple template renderer for basic variable substitution
49
+ function render(template, data) {
50
+ let output = template;
51
+ for (const key in data) {
52
+ const value = data[key];
53
+ if (typeof value === 'object' && value !== null) {
54
+ for (const nestedKey in value) {
55
+ output = output.replace(new RegExp(`{{${key}.${nestedKey}}}`, 'g'), value[nestedKey]);
56
+ }
57
+ } else {
58
+ output = output.replace(new RegExp(`{{${key}}}`, 'g'), value);
59
+ }
60
+ }
61
+ return output;
62
+ }
63
+
64
+ /**
65
+ * Filters execution agents based on the current mode.
66
+ * Senior Architect should only see relevant agents, not internal workers.
67
+ */
68
+ function getVisibleAgents(executionAgents, options) {
69
+ const visible = {};
70
+
71
+ // 1. Define Standard Coders (Always available as fallback)
72
+ // These keys must match IDs in setup.json
73
+ const standardCoders = ['local_dev', 'production_server', 'android_wsl_dev'];
74
+
75
+ // 2. Determine Priority Agent (The Junior Architect)
76
+ let priorityAgentKey = null;
77
+ if (options.jas) priorityAgentKey = 'jas';
78
+ if (options.jao) priorityAgentKey = 'jao';
79
+ if (options.jag) priorityAgentKey = 'jag';
80
+
81
+ // 3. Build the list
82
+ // If a JA is selected, add them FIRST with a note
83
+ if (priorityAgentKey && executionAgents[priorityAgentKey]) {
84
+ const ja = executionAgents[priorityAgentKey];
85
+ visible[priorityAgentKey] = {
86
+ ...ja,
87
+ description: `⭐ **PRIMARY AGENT** ⭐ ${ja.description} (Delegates to GLM Z.AI)`
88
+ };
89
+ }
90
+
91
+ // Add standard coders
92
+ for (const key of standardCoders) {
93
+ if (executionAgents[key] && executionAgents[key].active) {
94
+ visible[key] = executionAgents[key];
95
+ }
96
+ }
97
+
98
+ // NOTE: We deliberately EXCLUDE 'glm_zai_worker' here.
99
+ // The Senior Architect does not call GLM Z.AI directly; the JA does.
100
+
101
+ return visible;
102
+ }
103
+
104
+ function buildAgentDefinitions(filteredAgents) {
105
+ let definitions = '';
106
+ for (const key in filteredAgents) {
107
+ const agent = filteredAgents[key];
108
+ definitions += `
109
+ ### ${agent.name} (ID: "${key}")
110
+ - **Description:** ${agent.description}
111
+ - **GUI Support:** ${agent.guiSupport ? 'Yes' : 'No (Headless)'}
112
+ - **Capabilities:** ${agent.capabilities.join(', ')}
113
+ - **Restrictions:** ${agent.restrictions.join(', ')}
114
+ `;
115
+ }
116
+ return definitions;
117
+ }
118
+
119
+ /**
120
+ * Parse journal entries from JOURNAL.md content
121
+ * @param {string} journalContent - Raw content of JOURNAL.md
122
+ * @returns {Array} Array of parsed journal entries
123
+ */
124
+ function parseJournalEntries(journalContent) {
125
+ if (!journalContent || typeof journalContent !== 'string') {
126
+ return [];
127
+ }
128
+
129
+ // Split by --- separators, filter empty blocks
130
+ const blocks = journalContent.split(/^---$/m).filter(b => b.trim());
131
+ const entries = [];
132
+
133
+ for (let i = 0; i < blocks.length; i += 2) {
134
+ const frontmatter = blocks[i];
135
+ const body = blocks[i + 1] || '';
136
+
137
+ // Parse frontmatter
138
+ const typeMatch = frontmatter.match(/^type:\s*(.+)$/m);
139
+ const scopeMatch = frontmatter.match(/^scope:\s*(.+)$/m);
140
+ const summaryMatch = frontmatter.match(/^summary:\s*(.+)$/m);
141
+ const dateMatch = frontmatter.match(/^(?:date|timestamp):\s*(.+)$/m);
142
+ const taskIdMatch = frontmatter.match(/^task_id:\s*(.+)$/m);
143
+
144
+ // Extract title from body (first # heading)
145
+ const titleMatch = body.match(/^#\s+(.+)$/m);
146
+
147
+ entries.push({
148
+ type: typeMatch ? typeMatch[1].trim() : 'unknown',
149
+ scope: scopeMatch ? scopeMatch[1].trim() : '',
150
+ summary: summaryMatch ? summaryMatch[1].trim() : (titleMatch ? titleMatch[1].trim() : ''),
151
+ date: dateMatch ? dateMatch[1].trim() : '',
152
+ taskId: taskIdMatch ? taskIdMatch[1].trim() : '',
153
+ body: body.trim()
154
+ });
155
+ }
156
+
157
+ return entries;
158
+ }
159
+
160
+ /**
161
+ * Build a compact journal summary for the architect
162
+ * Shows: last entry (full) + 5 previous (headers only) + total count
163
+ */
164
+ function buildJournalSummary(journalContent) {
165
+ const entries = parseJournalEntries(journalContent);
166
+
167
+ if (entries.length === 0) {
168
+ return 'No journal entries found.';
169
+ }
170
+
171
+ let summary = '';
172
+
173
+ // Last entry - show full details
174
+ const lastEntry = entries[0];
175
+ summary += `**Latest Entry** (${lastEntry.date || 'no date'}):\n`;
176
+ summary += `- Type: \`${lastEntry.type}\` | Scope: \`${lastEntry.scope}\`\n`;
177
+ summary += `- ${lastEntry.summary}\n`;
178
+ if (lastEntry.body) {
179
+ // Include body but limit to first 3 lines
180
+ const bodyLines = lastEntry.body.split('\n').filter(l => l.trim()).slice(0, 4);
181
+ summary += bodyLines.map(l => ` ${l}`).join('\n') + '\n';
182
+ }
183
+
184
+ // Previous 5 entries - headers only
185
+ if (entries.length > 1) {
186
+ summary += '\n**Previous entries:**\n';
187
+ const previousEntries = entries.slice(1, 6);
188
+ for (const entry of previousEntries) {
189
+ summary += `- \`${entry.type}(${entry.scope})\`: ${entry.summary}\n`;
190
+ }
191
+ }
192
+
193
+ // Total count
194
+ if (entries.length > 6) {
195
+ summary += `\n*...and ${entries.length - 6} more entries in .eck/JOURNAL.md*\n`;
196
+ }
197
+
198
+ return summary;
199
+ }
200
+
201
+ function buildEckManifestSection(eckManifest) {
202
+ if (!eckManifest) {
203
+ return '';
204
+ }
205
+
206
+ let section = '\n## Project Context (.eck Directory)\n\n';
207
+ section += 'This project has a `.eck/` directory with project-specific context files.\n';
208
+ section += 'The coder agent can read these files when needed. Available files:\n\n';
209
+ section += '- `CONTEXT.md` - Project overview and architecture\n';
210
+ section += '- `OPERATIONS.md` - Common commands and workflows\n';
211
+ section += '- `JOURNAL.md` - Development history\n';
212
+ section += '- `ROADMAP.md` - Planned features\n';
213
+ section += '- `TECH_DEBT.md` - Known issues and refactoring needs\n';
214
+ section += '- `ENVIRONMENT.md` - Environment-specific settings\n\n';
215
+
216
+ // Add journal summary (compact view for architect)
217
+ if (eckManifest.journal) {
218
+ section += '### Recent Development Activity\n\n';
219
+ section += buildJournalSummary(eckManifest.journal) + '\n';
220
+ }
221
+
222
+ section += '---\n\n';
223
+
224
+ return section;
225
+ }
226
+
227
+ function extractMeaningfulLine(block) {
228
+ if (!block || typeof block !== 'string') {
229
+ return null;
230
+ }
231
+
232
+ const lines = block.split(/\r?\n/);
233
+ for (const line of lines) {
234
+ const trimmed = line.trim();
235
+ if (!trimmed || trimmed.startsWith('#')) {
236
+ continue;
237
+ }
238
+ const withoutBullet = trimmed.replace(/^[-*]\s*/, '').trim();
239
+ if (withoutBullet) {
240
+ return withoutBullet.replace(/\s+/g, ' ');
241
+ }
242
+ }
243
+ return null;
244
+ }
245
+
246
+ function extractDescriptionFromManifest(eckManifest) {
247
+ if (!eckManifest) {
248
+ return null;
249
+ }
250
+
251
+ if (typeof eckManifest.description === 'string' && eckManifest.description.trim()) {
252
+ return eckManifest.description.trim();
253
+ }
254
+
255
+ if (eckManifest.project && typeof eckManifest.project.description === 'string' && eckManifest.project.description.trim()) {
256
+ return eckManifest.project.description.trim();
257
+ }
258
+
259
+ if (typeof eckManifest.context === 'string' && eckManifest.context.trim()) {
260
+ const sectionMatch = eckManifest.context.match(/##\s*Description\s*([\s\S]*?)(?=^##\s|^#\s|\Z)/im);
261
+ if (sectionMatch && sectionMatch[1]) {
262
+ const meaningful = extractMeaningfulLine(sectionMatch[1]);
263
+ if (meaningful) {
264
+ return meaningful;
265
+ }
266
+ }
267
+
268
+ const fallback = extractMeaningfulLine(eckManifest.context);
269
+ if (fallback) {
270
+ return fallback;
271
+ }
272
+ }
273
+
274
+ return null;
275
+ }
276
+
277
+ async function resolveProjectDescription(context) {
278
+ const defaultDescription = 'Project description not provided.';
279
+
280
+ const manifestDescription = extractDescriptionFromManifest(context.eckManifest);
281
+ if (manifestDescription) {
282
+ const normalized = manifestDescription.trim();
283
+ const genericPatterns = [
284
+ /^brief description of what this project does/i,
285
+ /^no project context provided/i
286
+ ];
287
+ const isGeneric = genericPatterns.some(pattern => pattern.test(normalized));
288
+ if (!isGeneric) {
289
+ return normalized;
290
+ }
291
+ }
292
+
293
+ if (context.repoPath) {
294
+ try {
295
+ const packageJsonPath = path.join(context.repoPath, 'package.json');
296
+ const pkgRaw = await fs.readFile(packageJsonPath, 'utf-8');
297
+ const pkg = JSON.parse(pkgRaw);
298
+ if (typeof pkg.description === 'string' && pkg.description.trim()) {
299
+ return pkg.description.trim();
300
+ }
301
+ } catch (error) {
302
+ // Ignore errors - package.json may not exist or be readable
303
+ }
304
+ }
305
+
306
+ return defaultDescription;
307
+ }
308
+
309
+ export async function generateEnhancedAIHeader(context, isGitRepo = false) {
310
+ try {
311
+ const setupConfig = await loadSetupConfig();
312
+ const { aiInstructions } = setupConfig;
313
+ const { architectPersona, executionAgents, promptTemplates } = aiInstructions;
314
+
315
+ // Helper function to read a template file or return the string if it's not a path
316
+ const loadTemplate = async (templatePathOrString) => {
317
+ if (templatePathOrString && (templatePathOrString.endsWith('.md') || templatePathOrString.endsWith('.txt'))) {
318
+ try {
319
+ // Resolve path relative to the project root. __dirname is src/utils.
320
+ const resolvedPath = path.join(__dirname, '..', '..', templatePathOrString);
321
+ return await fs.readFile(resolvedPath, 'utf-8');
322
+ } catch (e) {
323
+ return `ERROR: FAILED TO LOAD TEMPLATE ${templatePathOrString}: ${e.message}`;
324
+ }
325
+ }
326
+ return templatePathOrString; // Fallback for old-style inline strings or errors
327
+ };
328
+
329
+ // P1 Bug Fix: Normalize manifest structure as per Consilium report
330
+ function normalizeManifest(raw) {
331
+ if (!raw) return null;
332
+ const out = {};
333
+ // Handle `setup.json` structure (e.g., `projectContext.name`)
334
+ if (raw.projectContext) {
335
+ out.context = raw.projectContext.description || JSON.stringify(raw.projectContext, null, 2);
336
+ out.operations = raw.operations || raw.projectContext.operations || ''; // Assuming .eck/OPERATIONS.md is separate
337
+ out.journal = raw.journal || raw.projectContext.journal || ''; // Assuming .eck/JOURNAL.md is separate
338
+ out.environment = raw.environment || raw.projectContext.environment || {}; // Assuming .eck/ENVIRONMENT.md is separate
339
+ } else {
340
+ // Handle direct .eck file structure (e.g., raw.context from CONTEXT.md)
341
+ out.context = raw.context || '';
342
+ out.operations = raw.operations || '';
343
+ out.journal = raw.journal || '';
344
+ out.environment = raw.environment || {};
345
+ }
346
+ // Add fallback text if still empty
347
+ if (!out.context) out.context = 'No project context provided.';
348
+ if (!out.operations) out.operations = 'No operations guide provided.';
349
+ if (!out.journal) out.journal = 'No journal entries found.';
350
+
351
+ return out;
352
+ }
353
+
354
+ // --- Build common context sections ---
355
+
356
+ // 1. Project Context (Architecture & Overview)
357
+ let projectContextBody = '';
358
+ if (context.eckManifest?.context) {
359
+ // Clean up [STUB] markers if present, but prefer full content
360
+ projectContextBody = context.eckManifest.context
361
+ .replace(/# \[STUB:.*?\]/g, '')
362
+ .replace(/## 🚨 ATTENTION[\s\S]*?(?=##)/, '') // Remove alert blocks
363
+ .trim();
364
+ } else {
365
+ projectContextBody = await resolveProjectDescription(context);
366
+ }
367
+
368
+ // 2. Strategic Context (Roadmap & Tech Debt)
369
+ let strategicSection = '';
370
+
371
+ // Extract active roadmap items (Phase 2, Current Status)
372
+ if (context.eckManifest?.roadmap) {
373
+ const activeRoadmap = extractSections(context.eckManifest.roadmap, ['phase 2', 'current status']);
374
+ if (activeRoadmap) {
375
+ strategicSection += `\n### 🚩 ACTIVE ROADMAP\n${activeRoadmap}\n`;
376
+ }
377
+ }
378
+
379
+ // Extract critical tech debt
380
+ if (context.eckManifest?.techDebt) {
381
+ const activeDebt = extractSections(context.eckManifest.techDebt, ['high priority', 'current', 'critical']);
382
+ if (activeDebt) {
383
+ strategicSection += `\n### 🔧 TECHNICAL DEBT FOCUS\n${activeDebt}\n`;
384
+ }
385
+ }
386
+
387
+ // 3. Operational Protocols (Royal Court / Advanced)
388
+ let operationsSection = '';
389
+ if (context.eckManifest?.operations) {
390
+ // Look for autonomous protocols
391
+ const protocols = extractSections(context.eckManifest.operations, ['Protocol', 'Autonomous', 'Rules']);
392
+ if (protocols) {
393
+ operationsSection += `\n### 🛡️ OPERATIONAL PROTOCOLS\n${protocols}\n`;
394
+ }
395
+ }
396
+
397
+ // Combine into the master PROJECT OVERVIEW variable
398
+ // This injects it right at the top of the prompt
399
+ const projectOverview = `### PROJECT OVERVIEW
400
+
401
+ * **Project:** ${context.repoName || 'Unknown'}
402
+
403
+ ${projectContextBody}
404
+ ${strategicSection}
405
+ ${operationsSection}
406
+ `;
407
+
408
+ const normalizedEck = normalizeManifest(context.eckManifest);
409
+ let eckManifestSection = '';
410
+ if (normalizedEck) {
411
+ eckManifestSection = buildEckManifestSection(normalizedEck);
412
+ } else {
413
+ eckManifestSection = '### PROJECT-SPECIFIC MANIFEST (.eck Directory)\n\nWARNING: .eck manifest was not found or was empty.\n';
414
+ }
415
+ // --- End context building ---
416
+
417
+
418
+ // --- LOGIC CHANGE: Snapshot is ALWAYS for Senior Architect ---
419
+ // The `agent` prompt template is used ONLY in CLAUDE.md (via claudeMdGenerator.js)
420
+ // NOT in the snapshot itself.
421
+
422
+ const isJag = context.options && context.options.jag;
423
+ const isJas = context.options && context.options.jas;
424
+ const isJao = context.options && context.options.jao;
425
+ const isJaMode = isJag || isJas || isJao;
426
+
427
+ // --- Determine Workflow Content based on JA Flag ---
428
+ let hierarchicalWorkflow = '';
429
+ let commandFormats = '';
430
+
431
+ if (isJaMode) {
432
+ // Instructions strictly for the Senior Architect on how to use the JA
433
+ hierarchicalWorkflow = `### 👑 ROYAL COURT ARCHITECTURE (Active)
434
+
435
+ You are the **Senior Architect**. You have a **Junior Architect** available to handle implementation.
436
+
437
+ **PROTOCOL:**
438
+ 1. **Prefer Delegation:** Unless the task is trivial (1-2 file edits), assign it to the **Junior Architect** (ID: \`jas\`, \`jao\`, or \`jag\` - see agents list above).
439
+ 2. **Direct Execution:** Only use \`local_dev\` or \`production_server\` directly if the Junior Architect fails or for simple "hotfixes".
440
+ 3. **No Micro-Management:** Do not tell the Junior Architect *how* to use GLM Z.AI or internal tools. Just give them the strategic objective.
441
+ `;
442
+
443
+ commandFormats = `### COMMAND FORMATS (Eck-Protocol v2)
444
+
445
+ You MUST use the **Eck-Protocol v2** format for all code execution tasks. This format combines Markdown for analysis, XML tags for file operations, and JSON for routing metadata.
446
+
447
+ **CRITICAL DISPLAY RULE (THE 4-BACKTICK WRAPPER):**
448
+ To ensure your command is copy-pasteable without breaking UI rendering, you **MUST** wrap the ENTIRE protocol output in a \`text\` block using **QUADRUPLE BACKTICKS** (\` \`\`\`\` \`).
449
+
450
+ **Why?** Your command contains internal code blocks with 3 backticks. To escape them, the outer container needs 4.
451
+
452
+ **Required Output Format:**
453
+
454
+ \`\`\`\`text
455
+ # Analysis
456
+ [Your reasoning...]
457
+
458
+ ## Changes
459
+ <file path="example.js" action="replace">
460
+ \\\`\\\`\\\`javascript
461
+ // Internal code block uses 3 backticks
462
+ const x = 1;
463
+ \\\`\\\`\\\`
464
+ </file>
465
+
466
+ ## Metadata
467
+ \\\`\\\`\\\`json
468
+ { "target_agent": "jas", "task_id": "unique-id" }
469
+ \\\`\\\`\\\`
470
+ \`\`\`\`
471
+
472
+ **File Actions:**
473
+ - \`create\`: Create a new file (requires full content)
474
+ - \`replace\`: Overwrite existing file (requires full content)
475
+ - \`modify\`: Replace specific sections (provide context)
476
+ - \`delete\`: Delete the file
477
+ `;
478
+ } else if (context.options && context.options.withJa) {
479
+ hierarchicalWorkflow = `### HIERARCHICAL AGENT WORKFLOW
480
+
481
+ Your primary role is **Senior Architect**. You formulate high-level strategy. For complex code implementation, you will delegate to a **Junior Architect** agent (\`gemini_wsl\`), who has a detailed (\`_ja.md\`) snapshot and the ability to command a **Coder** agent (\`claude\`).
482
+
483
+ - **Senior Architect (You):** Sets strategy, defines high-level tasks.
484
+ - **Junior Architect (\`gemini_wsl\`):** Receives strategic tasks, analyzes the \`_ja.md\` snapshot, breaks the task down, and commands the Coder.
485
+ - **Coder (\`claude\`):** Receives small, precise coding tasks from the Junior Architect. **Claude is responsible for keeping the .eck/ manifest files accurate and synchronized with the code.**`;
486
+
487
+ commandFormats = `### COMMAND FORMATS
488
+
489
+ You MUST use one of two JSON command formats based on your target:
490
+
491
+ **1. For Coders (\`local_dev\`, \`production_server\`, \`android_wsl_dev\`, \`gemini_windows\`) - LOW-LEVEL EXECUTION:**
492
+ Use \`apply_code_changes\` for simple, direct tasks where you provide all details.
493
+
494
+ \`\`\`json
495
+ {
496
+ "target_agent": "local_dev",
497
+ "agent_environment": "Development environment with full GUI support and development tools",
498
+ "command_for_agent": "apply_code_changes",
499
+ "task_id": "unique-task-id",
500
+ "payload": {
501
+ "objective": "Brief, clear task description",
502
+ "context": "Why this change is needed - include relevant .eck manifest context",
503
+ "files_to_modify": [
504
+ {
505
+ "path": "exact/file/path.js",
506
+ "action": "specific action (add, modify, replace, delete)",
507
+ "location": "line numbers, function name, or search pattern",
508
+ "details": "precise description of the change"
509
+ }
510
+ ],
511
+ "new_files": [
512
+ {
513
+ "path": "path/to/new/file.js",
514
+ "content_type": "javascript/json/markdown/config",
515
+ "purpose": "why this file is needed"
516
+ }
517
+ ],
518
+ "dependencies": {
519
+ "install": ["package-name@version"],
520
+ "remove": ["old-package-name"]
521
+ },
522
+ "validation_steps": [
523
+ "npm run test",
524
+ "node index.js --help",
525
+ "specific command to verify functionality"
526
+ ],
527
+ "expected_outcome": "what should work after changes",
528
+ "post_execution_steps": {
529
+ "journal_entry": {
530
+ "type": "feat",
531
+ "scope": "authentication",
532
+ "summary": "Brief description of what was accomplished",
533
+ "details": "Detailed explanation of changes, impacts, and technical notes"
534
+ },
535
+ "mcp_feedback": {
536
+ "success": true,
537
+ "errors": [],
538
+ "mcp_version": "1.0"
539
+ }
540
+ }
541
+ }
542
+ }
543
+ \`\`\`
544
+
545
+ **2. For Junior Architects (\`gemini_wsl\`) - HIGH-LEVEL DELEGATION:**
546
+ Use \`execute_strategic_task\` for complex features. The JA will use its own snapshot and Coder agent to complete the task.
547
+
548
+ \`\`\`json
549
+ {
550
+ "target_agent": "gemini_wsl",
551
+ "command_for_agent": "execute_strategic_task",
552
+ "payload": {
553
+ "objective": "Implement the user authentication feature",
554
+ "context": "This is a high-level task. Use your _ja.md snapshot to analyze the codebase. Use your 'claude (delegate)' capability to implement the necessary code across all required files (routes, controllers, services).",
555
+ "constraints": [
556
+ "Must use JWT for tokens",
557
+ "Add new routes to \`routes/api.js\`",
558
+ "Ensure all new code is covered by tests"
559
+ ],
560
+ "validation_steps": [
561
+ "npm run test"
562
+ ]
563
+ }
564
+ }
565
+ \`\`\``;
566
+ } else {
567
+ hierarchicalWorkflow = `### AGENT WORKFLOW
568
+
569
+ Your role is **Architect**. You formulate technical plans and delegate code implementation tasks directly to the **Coder** agents.
570
+
571
+ **Your secondary duty is DOCUMENTATION INTEGRITY.** You must ensure the Coder updates .eck/ files whenever the project structure, roadmap, or debt changes.
572
+
573
+ - **Architect (You):** Sets strategy, defines tasks, enforces manifest maintenance.
574
+ - **Coder (e.g., \`local_dev\`):** Receives precise coding tasks and executes them, including manifest updates.`;
575
+
576
+ commandFormats = `### COMMAND FORMATS (Eck-Protocol v2)
577
+
578
+ You MUST use the **Eck-Protocol v2** format for all code execution tasks. This format combines Markdown for analysis, XML tags for file operations, and JSON for routing metadata.
579
+
580
+ **CRITICAL DISPLAY RULE (THE 4-BACKTICK WRAPPER):**
581
+ To ensure your command is copy-pasteable without breaking UI rendering, you **MUST** wrap the ENTIRE protocol output in a \`text\` block using **QUADRUPLE BACKTICKS** (\` \`\`\`\` \`).
582
+
583
+ **Why?** Your command contains internal code blocks with 3 backticks. To escape them, the outer container needs 4.
584
+
585
+ **Required Output Format:**
586
+
587
+ \`\`\`\`text
588
+ # Analysis
589
+ [Your reasoning...]
590
+
591
+ ## Changes
592
+ <file path="example.js" action="replace">
593
+ \\\`\\\`\\\`javascript
594
+ // Internal code block uses 3 backticks
595
+ const x = 1;
596
+ \\\`\\\`\\\`
597
+ </file>
598
+
599
+ ## Metadata
600
+ \\\`\\\`\\\`json
601
+ { ... }
602
+ \\\`\\\`\\\`
603
+ \`\`\`\`
604
+
605
+ **File Actions:**
606
+ - \`create\`: Create a new file (requires full content)
607
+ - \`replace\`: Overwrite existing file (requires full content)
608
+ - \`modify\`: Replace specific sections (provide context)
609
+ - \`delete\`: Delete the file
610
+ `;
611
+ }
612
+
613
+ // --- This is the main/Senior Architect prompt logic ---
614
+ let template;
615
+ template = await loadTemplate(promptTemplates.multiAgent);
616
+ // --- INJECT DYNAMIC CONTEXT ---
617
+ template = template.replace('{{projectOverview}}', projectOverview);
618
+ template = template.replace('{{eckManifestSection}}', eckManifestSection);
619
+ // --- END INJECT ---
620
+
621
+ // Use the new filtering function to get visible agents
622
+ const filteredExecutionAgents = getVisibleAgents(executionAgents, context.options || {});
623
+
624
+ const agentDefinitions = buildAgentDefinitions(filteredExecutionAgents);
625
+
626
+ const data = {
627
+ ...context,
628
+ timestamp: new Date().toLocaleString(),
629
+ architectPersona,
630
+ agentDefinitions,
631
+ hierarchicalWorkflow,
632
+ commandFormats
633
+ };
634
+
635
+ let renderedTemplate = render(template, data);
636
+
637
+ // Inject skeleton mode instructions if enabled
638
+ if (context.options && context.options.skeleton) {
639
+ try {
640
+ const skeletonInstructionPath = path.join(__dirname, '..', 'templates', 'skeleton-instruction.md');
641
+ const skeletonInstructions = await fs.readFile(skeletonInstructionPath, 'utf-8');
642
+ renderedTemplate += '\n\n' + skeletonInstructions + '\n\n';
643
+ } catch (e) {
644
+ console.warn('Warning: Could not load skeleton-instruction.md', e.message);
645
+ }
646
+ }
647
+
648
+ // Inject dynamic profile context if a profile is active
649
+ if (context.options && context.options.profile && context.repoPath) {
650
+ let metadataHeader = '\n\n## Partial Snapshot Context\n';
651
+ metadataHeader += `- **Profile(s) Active:** ${context.options.profile}\n`;
652
+ try {
653
+ const allProfiles = await getAllProfiles(context.repoPath);
654
+ const activeProfileNames = context.options.profile.split(',').map(p => p.trim().replace(/^-/, ''));
655
+ const allProfileNames = Object.keys(allProfiles).filter(p => !activeProfileNames.includes(p));
656
+ if (allProfileNames.length > 0) {
657
+ metadataHeader += `- **Other Available Profiles:** ${allProfileNames.join(', ')}\n`;
658
+ }
659
+ } catch (e) { /* fail silently on metadata generation */ }
660
+
661
+ const insertMarker = "### "; // Generic marker since we change the H1s
662
+ // Insert before first H3 (WORKFLOW usually)
663
+ renderedTemplate = renderedTemplate.replace(/### /, metadataHeader + '\n### ');
664
+ }
665
+
666
+ return renderedTemplate;
667
+
668
+ } catch (error) {
669
+ console.warn('Warning: Could not load setup.json, using minimal header', error.message);
670
+ return `# Snapshot for ${context.repoName || 'Project'}
671
+
672
+ Generated: ${new Date().toISOString()}
673
+
674
+ ---
675
+
676
+ `;
677
+ }
678
+ }