@xelth/eck-snapshot 5.0.2 → 5.0.3
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.
- package/README.md +24 -3
- package/package.json +3 -3
- package/setup.json +74 -88
- package/src/cli/cli.js +11 -2
- package/src/cli/commands/createSnapshot.js +94 -43
- package/src/cli/commands/updateSnapshot.js +133 -31
- package/src/templates/agent-prompt.template.md +28 -112
- package/src/templates/architect-prompt.template.md +25 -0
- package/src/utils/aiHeader.js +99 -37
- package/src/utils/claudeMdGenerator.js +148 -0
- package/src/utils/fileUtils.js +14 -13
package/README.md
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
|
|
2
|
-
# eckSnapshot (v5.0.
|
|
2
|
+
# eckSnapshot (v5.0.3)
|
|
3
3
|
|
|
4
4
|
A lightweight, platform-independent CLI for creating focused, AI-ready project snapshots.
|
|
5
5
|
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## ⚠️ NPM Publishing Discontinued | Публикация в NPM прекращена
|
|
9
|
+
|
|
10
|
+
**EN:** We've stopped publishing to npm due to their excessive security requirements (10-second tokens, 10-factor authentication, and constant token revocations). If you're interested in this project, **all updates will be released exclusively on GitHub**. Install directly from the repository.
|
|
11
|
+
|
|
12
|
+
**RU:** npm может идти лесом. Кому интересно - смотрите на гите.
|
|
13
|
+
|
|
14
|
+
**GitHub Repository:** https://github.com/xelth-com/eckSnapshot
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
6
18
|
`eckSnapshot` is a powerful command-line tool designed to solve a critical problem in AI-assisted development: providing clear, complete, and focused context to Large Language Models (LLMs). It allows you to package an entire project codebase—or just specific parts of it—into a single, clean text file.
|
|
7
19
|
|
|
8
20
|
This tool is built for a modern workflow where you act as the architect, guiding the overall strategy, while AI agents handle the detailed implementation.
|
|
@@ -24,10 +36,19 @@ This tool is built for a modern workflow where you act as the architect, guiding
|
|
|
24
36
|
|
|
25
37
|
## Installation
|
|
26
38
|
|
|
27
|
-
Install `eckSnapshot` globally
|
|
39
|
+
Install `eckSnapshot` globally directly from GitHub:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g github:xelth-com/eckSnapshot
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or clone and link locally:
|
|
28
46
|
|
|
29
47
|
```bash
|
|
30
|
-
|
|
48
|
+
git clone https://github.com/xelth-com/eckSnapshot.git
|
|
49
|
+
cd eckSnapshot
|
|
50
|
+
npm install
|
|
51
|
+
npm link
|
|
31
52
|
```
|
|
32
53
|
|
|
33
54
|
Once installed, you can run the tool using the `eck-snapshot` command from any directory.
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xelth/eck-snapshot",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "A powerful CLI tool to create and restore single-file text snapshots of Git repositories and directories. Optimized for AI context and LLM workflows.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"eck-snapshot": "
|
|
8
|
+
"eck-snapshot": "index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/xelth-com/eckSnapshot.git"
|
|
28
|
+
"url": "git+https://github.com/xelth-com/eckSnapshot.git"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@anthropic-ai/sdk": "^0.33.1",
|
package/setup.json
CHANGED
|
@@ -552,53 +552,85 @@
|
|
|
552
552
|
},
|
|
553
553
|
"aiInstructions": {
|
|
554
554
|
"architectPersona": {
|
|
555
|
-
"role": "
|
|
556
|
-
"goal": "
|
|
555
|
+
"role": "Senior Architect (Gemini 3 Pro) & Orchestrator",
|
|
556
|
+
"goal": "Lead the project globally. You see the WHOLE project. Delegate implementation management to Junior Architects (Sonnet 4.5 / Opus 4.5).",
|
|
557
557
|
"contextRequirement": "ALWAYS check environment context before generating commands",
|
|
558
558
|
"workflow": [
|
|
559
|
-
"Request ENV scan
|
|
560
|
-
"Analyze
|
|
561
|
-
"
|
|
562
|
-
"
|
|
563
|
-
"
|
|
564
|
-
"ONLY after approval: Generate environment-specific JSON command block in a SEPARATE message",
|
|
565
|
-
"Communicate with user in their language, commands in ENGLISH",
|
|
566
|
-
"CRITICAL: Never output JSON commands until the user agrees to the plan"
|
|
559
|
+
"Request ENV scan",
|
|
560
|
+
"Analyze Request using Global Context (Gemini 3 Pro)",
|
|
561
|
+
"Select the best Junior Architect (JAS for speed, JAO for complexity)",
|
|
562
|
+
"Delegate the task using 'execute_strategic_task'",
|
|
563
|
+
"Review the result"
|
|
567
564
|
]
|
|
568
565
|
},
|
|
569
566
|
"executionAgents": {
|
|
570
567
|
"local_dev": {
|
|
571
568
|
"active": true,
|
|
572
|
-
"name": "Local
|
|
573
|
-
"description": "
|
|
569
|
+
"name": "Local Terminal (Sonnet 4.5)",
|
|
570
|
+
"description": "Direct execution interface. Use for quick commands.",
|
|
574
571
|
"guiSupport": true,
|
|
575
572
|
"identification": {
|
|
576
|
-
"markers": [
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
573
|
+
"markers": ["local_dev"]
|
|
574
|
+
},
|
|
575
|
+
"capabilities": ["npm", "git", "basic editing"],
|
|
576
|
+
"restrictions": ["Do not use for complex architectural tasks"]
|
|
577
|
+
},
|
|
578
|
+
"jas": {
|
|
579
|
+
"active": true,
|
|
580
|
+
"name": "Junior Architect (Sonnet 4.5)",
|
|
581
|
+
"description": "Smart & Fast Manager. Best for standard features and refactoring. Manages MiniMax workers.",
|
|
582
|
+
"guiSupport": true,
|
|
583
|
+
"identification": {
|
|
584
|
+
"markers": ["sonnet", "fast_architect"]
|
|
583
585
|
},
|
|
584
586
|
"capabilities": [
|
|
585
|
-
"
|
|
586
|
-
"
|
|
587
|
-
"
|
|
587
|
+
"minimax_frontend",
|
|
588
|
+
"minimax_backend",
|
|
589
|
+
"minimax_qa",
|
|
590
|
+
"minimax_refactor",
|
|
588
591
|
"git operations",
|
|
589
|
-
"
|
|
590
|
-
"electron debug",
|
|
591
|
-
"file editing",
|
|
592
|
-
"testing commands",
|
|
593
|
-
"browser automation (chrome_mcp)",
|
|
594
|
-
"visual regression testing",
|
|
595
|
-
"network logging"
|
|
592
|
+
"npm run dev"
|
|
596
593
|
],
|
|
597
594
|
"restrictions": [
|
|
598
|
-
"
|
|
599
|
-
"
|
|
600
|
-
|
|
601
|
-
|
|
595
|
+
"Prefer delegating heavy coding to MiniMax workers",
|
|
596
|
+
"Focus on glue code and logic verification"
|
|
597
|
+
]
|
|
598
|
+
},
|
|
599
|
+
"jao": {
|
|
600
|
+
"active": true,
|
|
601
|
+
"name": "Junior Architect (Opus 4.5)",
|
|
602
|
+
"description": "Deep Thinker & Planner. Expensive. Use for critical architecture, security, and complex logic.",
|
|
603
|
+
"guiSupport": true,
|
|
604
|
+
"identification": {
|
|
605
|
+
"markers": ["opus", "deep_architect"]
|
|
606
|
+
},
|
|
607
|
+
"capabilities": [
|
|
608
|
+
"minimax_frontend",
|
|
609
|
+
"minimax_backend",
|
|
610
|
+
"minimax_qa",
|
|
611
|
+
"minimax_refactor",
|
|
612
|
+
"analysis"
|
|
613
|
+
],
|
|
614
|
+
"restrictions": [
|
|
615
|
+
"STRICTLY DELEGATE boilerplate to MiniMax",
|
|
616
|
+
"Verify every line of code generated by workers",
|
|
617
|
+
"Plan before execution"
|
|
618
|
+
]
|
|
619
|
+
},
|
|
620
|
+
"jag": {
|
|
621
|
+
"active": true,
|
|
622
|
+
"name": "Junior Architect (Gemini 3 Pro)",
|
|
623
|
+
"description": "Massive Context Handler. Use when changes span >50 files or require full repo understanding.",
|
|
624
|
+
"guiSupport": false,
|
|
625
|
+
"identification": {
|
|
626
|
+
"markers": ["gemini_wsl"]
|
|
627
|
+
},
|
|
628
|
+
"capabilities": [
|
|
629
|
+
"read entire repo",
|
|
630
|
+
"delegate to claude"
|
|
631
|
+
],
|
|
632
|
+
"restrictions": [
|
|
633
|
+
"Experimental environment"
|
|
602
634
|
]
|
|
603
635
|
},
|
|
604
636
|
"production_server": {
|
|
@@ -671,69 +703,23 @@
|
|
|
671
703
|
"no hardware debugging interfaces"
|
|
672
704
|
]
|
|
673
705
|
},
|
|
674
|
-
"gemini_wsl": {
|
|
675
|
-
"active": true,
|
|
676
|
-
"name": "Gemini WSL Agent (Junior Architect)",
|
|
677
|
-
"description": "Gemini, running in WSL. Can access all WSL tools and delegate to other agents like claude.",
|
|
678
|
-
"guiSupport": false,
|
|
679
|
-
"identification": {
|
|
680
|
-
"markers": [
|
|
681
|
-
"WSL",
|
|
682
|
-
"gemini",
|
|
683
|
-
"claude"
|
|
684
|
-
]
|
|
685
|
-
},
|
|
686
|
-
"capabilities": [
|
|
687
|
-
"git operations",
|
|
688
|
-
"npm install",
|
|
689
|
-
"file editing",
|
|
690
|
-
"gemini (self)",
|
|
691
|
-
"claude (delegate)"
|
|
692
|
-
],
|
|
693
|
-
"restrictions": [
|
|
694
|
-
"Runs only inside the WSL environment"
|
|
695
|
-
]
|
|
696
|
-
},
|
|
697
|
-
"gemini_windows": {
|
|
698
|
-
"active": true,
|
|
699
|
-
"name": "Gemini Windows Agent (Standalone)",
|
|
700
|
-
"description": "Gemini, running in native Windows (PowerShell). Can only access Windows tools.",
|
|
701
|
-
"guiSupport": true,
|
|
702
|
-
"identification": {
|
|
703
|
-
"markers": [
|
|
704
|
-
"Windows",
|
|
705
|
-
"gemini",
|
|
706
|
-
"!WSL"
|
|
707
|
-
]
|
|
708
|
-
},
|
|
709
|
-
"capabilities": [
|
|
710
|
-
"git operations",
|
|
711
|
-
"npm install",
|
|
712
|
-
"file editing",
|
|
713
|
-
"gemini (self)"
|
|
714
|
-
],
|
|
715
|
-
"restrictions": [
|
|
716
|
-
"Runs only in native Windows",
|
|
717
|
-
"Cannot access WSL-only tools like claude"
|
|
718
|
-
]
|
|
719
|
-
},
|
|
720
706
|
"minimax_worker": {
|
|
721
707
|
"active": true,
|
|
722
|
-
"name": "MiniMax
|
|
723
|
-
"description": "
|
|
708
|
+
"name": "MiniMax Swarm (MCP)",
|
|
709
|
+
"description": "The worker fleet. Accessed via MCP tools (minimax_backend, etc).",
|
|
724
710
|
"guiSupport": false,
|
|
725
711
|
"identification": {
|
|
726
|
-
"markers": ["minimax"
|
|
712
|
+
"markers": ["minimax"]
|
|
727
713
|
},
|
|
728
714
|
"capabilities": [
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
"
|
|
732
|
-
"
|
|
715
|
+
"backend engineering",
|
|
716
|
+
"frontend engineering",
|
|
717
|
+
"qa testing",
|
|
718
|
+
"refactoring"
|
|
733
719
|
],
|
|
734
720
|
"restrictions": [
|
|
735
|
-
"
|
|
736
|
-
"
|
|
721
|
+
"Requires MINIMAX_API_KEY",
|
|
722
|
+
"No direct executive power"
|
|
737
723
|
]
|
|
738
724
|
}
|
|
739
725
|
},
|
package/src/cli/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
|
|
9
9
|
import { createRepoSnapshot } from './commands/createSnapshot.js';
|
|
10
|
-
import { updateSnapshot } from './commands/updateSnapshot.js';
|
|
10
|
+
import { updateSnapshot, updateSnapshotJson } from './commands/updateSnapshot.js';
|
|
11
11
|
import { restoreSnapshot } from './commands/restoreSnapshot.js';
|
|
12
12
|
import { pruneSnapshot } from './commands/pruneSnapshot.js';
|
|
13
13
|
import { generateConsilium } from './commands/consilium.js';
|
|
@@ -140,7 +140,9 @@ Option C: Using Profiles
|
|
|
140
140
|
.option('--enhanced', 'Use enhanced multi-agent headers (default: true)', true)
|
|
141
141
|
.option('--profile <name>', 'Filter files using profiles and/or ad-hoc glob patterns.')
|
|
142
142
|
.option('--agent', 'Generate a snapshot optimized for a command-line agent')
|
|
143
|
-
.option('--
|
|
143
|
+
.option('--jag', 'Generate snapshot for Junior Architect Gemini (Gemini 3 Pro)')
|
|
144
|
+
.option('--jas', 'Configure project for Junior Architect Sonnet (Sonnet 4.5)')
|
|
145
|
+
.option('--jao', 'Configure project for Junior Architect Opus (Opus 4.5)')
|
|
144
146
|
.option('--skeleton', 'Enable skeleton mode: strip function bodies to save context window tokens')
|
|
145
147
|
.option('--max-lines-per-file <number>', 'Truncate files to max N lines (e.g., 200 for compact snapshots)', (val) => parseInt(val))
|
|
146
148
|
.action(createRepoSnapshot)
|
|
@@ -198,6 +200,13 @@ Creating Custom Profiles:
|
|
|
198
200
|
.option('--config <path>', 'Configuration file path')
|
|
199
201
|
.action(updateSnapshot);
|
|
200
202
|
|
|
203
|
+
// Auto/Silent Update command for Agents
|
|
204
|
+
program
|
|
205
|
+
.command('update-auto')
|
|
206
|
+
.description('Silent update for AI agents (JSON output)')
|
|
207
|
+
.argument('[repoPath]', 'Path to the repository', process.cwd())
|
|
208
|
+
.action(updateSnapshotJson);
|
|
209
|
+
|
|
201
210
|
// Restore command
|
|
202
211
|
program
|
|
203
212
|
.command('restore')
|
|
@@ -23,6 +23,7 @@ import { loadSetupConfig, getProfile } from '../../config.js';
|
|
|
23
23
|
import { applyProfileFilter } from '../../utils/fileUtils.js';
|
|
24
24
|
import { saveGitAnchor } from '../../utils/gitUtils.js';
|
|
25
25
|
import { skeletonize } from '../../core/skeletonizer.js';
|
|
26
|
+
import { updateClaudeMd } from '../../utils/claudeMdGenerator.js';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Creates dynamic project context based on detection results
|
|
@@ -141,8 +142,9 @@ const gzip = promisify(zlib.gzip);
|
|
|
141
142
|
*/
|
|
142
143
|
function isHiddenPath(filePath) {
|
|
143
144
|
// Check if path or any parent directory starts with '.'
|
|
145
|
+
// Allow .eck directory to be visible, hide others (like .git, .vscode)
|
|
144
146
|
const parts = filePath.split('/');
|
|
145
|
-
return parts.some(part => part.startsWith('.'));
|
|
147
|
+
return parts.some(part => part.startsWith('.') && part !== '.eck');
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
/**
|
|
@@ -526,8 +528,13 @@ export async function createRepoSnapshot(repoPath, options) {
|
|
|
526
528
|
...options // Command-line options have the final say
|
|
527
529
|
};
|
|
528
530
|
|
|
531
|
+
// Detect architect modes
|
|
532
|
+
const isJag = options.jag;
|
|
533
|
+
const isJas = options.jas;
|
|
534
|
+
const isJao = options.jao;
|
|
535
|
+
|
|
529
536
|
// If NOT in Junior Architect mode, hide JA-specific documentation to prevent context pollution
|
|
530
|
-
if (!options.withJa) {
|
|
537
|
+
if (!options.withJa && !isJag && !isJas && !isJao) {
|
|
531
538
|
if (!config.filesToIgnore) config.filesToIgnore = [];
|
|
532
539
|
config.filesToIgnore.push(
|
|
533
540
|
'COMMANDS_REFERENCE.md',
|
|
@@ -555,15 +562,18 @@ export async function createRepoSnapshot(repoPath, options) {
|
|
|
555
562
|
|
|
556
563
|
spinner.succeed('Creating snapshots...');
|
|
557
564
|
|
|
558
|
-
//
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
565
|
+
// --- LOGIC UPDATE: Always include content ---
|
|
566
|
+
// The Architect needs full visibility of the code to make decisions.
|
|
567
|
+
// We strictly use processProjectFiles for all modes.
|
|
568
|
+
|
|
569
|
+
let stats, contentArray, successfulFileObjects, allFiles, processedRepoPath;
|
|
570
|
+
|
|
571
|
+
const result = await processProjectFiles(repoPath, options, config, projectDetection.type);
|
|
572
|
+
stats = result.stats;
|
|
573
|
+
contentArray = result.contentArray;
|
|
574
|
+
successfulFileObjects = result.successfulFileObjects;
|
|
575
|
+
allFiles = result.allFiles;
|
|
576
|
+
processedRepoPath = result.repoPath;
|
|
567
577
|
|
|
568
578
|
const originalCwd = process.cwd(); // Get CWD *before* chdir
|
|
569
579
|
process.chdir(processedRepoPath); // Go back to repo path for git hash and tree
|
|
@@ -598,58 +608,99 @@ export async function createRepoSnapshot(repoPath, options) {
|
|
|
598
608
|
// Calculate Top 10 Largest Files
|
|
599
609
|
const largestFiles = [...successfulFileObjects].sort((a, b) => b.size - a.size).slice(0, 10);
|
|
600
610
|
|
|
601
|
-
const fileBody = (directoryTree ? `\n## Directory Structure\n\n\`\`\`\n${directoryTree}\`\`\`\n\n` : '') + contentArray.join('');
|
|
602
|
-
|
|
603
|
-
// --- File 1: Architect Snapshot ---
|
|
604
|
-
const architectOptions = { ...options, agent: false };
|
|
605
611
|
// Load manifest for headers
|
|
606
612
|
const eckManifest = await loadProjectEckManifest(processedRepoPath);
|
|
607
613
|
const isGitRepo = await checkGitRepository(processedRepoPath);
|
|
608
614
|
|
|
609
|
-
|
|
610
|
-
let
|
|
615
|
+
// --- BRANCH 1: Generate Snapshot File (ALWAYS) ---
|
|
616
|
+
let architectFilePath = null;
|
|
617
|
+
let jaFilePath = null;
|
|
611
618
|
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
architectBaseFilename += '_sk';
|
|
615
|
-
}
|
|
619
|
+
// File body always includes full content
|
|
620
|
+
let fileBody = (directoryTree ? `\n## Directory Structure\n\n\`\`\`\n${directoryTree}\`\`\`\n\n` : '') + contentArray.join('');
|
|
616
621
|
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
|
|
622
|
+
// Helper to write snapshot file
|
|
623
|
+
const writeSnapshot = async (suffix, isAgentMode) => {
|
|
624
|
+
// CHANGE: Force agent to FALSE for the main snapshot header.
|
|
625
|
+
// The snapshot is read by the Human/Senior Arch, not the Agent itself.
|
|
626
|
+
// The Agent reads CLAUDE.md.
|
|
627
|
+
const opts = { ...options, agent: false, jag: isJag, jas: isJas, jao: isJao };
|
|
628
|
+
const header = await generateEnhancedAIHeader({ stats, repoName, mode: 'file', eckManifest, options: opts, repoPath: processedRepoPath }, isGitRepo);
|
|
620
629
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
+
// Compact filename format: eck{timestamp}_{hash}_{suffix}.md
|
|
631
|
+
const shortHash = gitHash ? gitHash.substring(0, 7) : '';
|
|
632
|
+
let fname = `eck${timestamp}`;
|
|
633
|
+
if (shortHash) fname += `_${shortHash}`;
|
|
634
|
+
|
|
635
|
+
// Add mode suffix
|
|
636
|
+
if (options.skeleton) {
|
|
637
|
+
fname += '_sk';
|
|
638
|
+
} else if (suffix) {
|
|
639
|
+
fname += suffix;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
fname += `.${fileExtension}`;
|
|
643
|
+
const fpath = path.join(outputPath, fname);
|
|
644
|
+
await fs.writeFile(fpath, header + fileBody);
|
|
645
|
+
console.log(`📄 Generated Snapshot: ${fname}`);
|
|
646
|
+
return fpath;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
// Generate snapshot file for ALL modes
|
|
650
|
+
if (isJag) {
|
|
651
|
+
architectFilePath = await writeSnapshot('_jag', true);
|
|
652
|
+
} else if (isJas) {
|
|
653
|
+
architectFilePath = await writeSnapshot('_jas', true);
|
|
654
|
+
} else if (isJao) {
|
|
655
|
+
architectFilePath = await writeSnapshot('_jao', true);
|
|
656
|
+
} else {
|
|
657
|
+
// Standard snapshot behavior
|
|
658
|
+
architectFilePath = await writeSnapshot('', false);
|
|
659
|
+
|
|
660
|
+
// --- File 2: Junior Architect Snapshot (legacy --with-ja support) ---
|
|
661
|
+
if (options.withJa && fileExtension === 'md') {
|
|
662
|
+
console.log('🖋️ Generating Junior Architect (_ja) snapshot...');
|
|
663
|
+
jaFilePath = await writeSnapshot('_ja', true);
|
|
664
|
+
}
|
|
630
665
|
}
|
|
631
666
|
|
|
632
667
|
// Save git anchor for future delta updates
|
|
633
668
|
await saveGitAnchor(processedRepoPath);
|
|
634
669
|
|
|
635
|
-
//
|
|
636
|
-
|
|
670
|
+
// Reset update counter for sequential tracking
|
|
671
|
+
try {
|
|
672
|
+
const counterPath = path.join(processedRepoPath, '.eck', 'update_seq');
|
|
673
|
+
await fs.mkdir(path.dirname(counterPath), { recursive: true });
|
|
674
|
+
// Format: HASH:COUNT
|
|
675
|
+
const shortHash = gitHash ? gitHash.substring(0, 7) : 'nohash';
|
|
676
|
+
await fs.writeFile(counterPath, `${shortHash}:0`);
|
|
677
|
+
} catch (e) {
|
|
678
|
+
// Non-critical, continue
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// --- BRANCH 2: Update CLAUDE.md (JAS / JAO / Default) ---
|
|
682
|
+
console.log('🔐 Scanning for confidential files...');
|
|
637
683
|
const confidentialFiles = await scanEckForConfidentialFiles(processedRepoPath, config);
|
|
638
684
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
685
|
+
let claudeMode = 'coder';
|
|
686
|
+
if (isJas) claudeMode = 'jas';
|
|
687
|
+
if (isJao) claudeMode = 'jao';
|
|
688
|
+
if (isJag) claudeMode = 'jag';
|
|
689
|
+
|
|
690
|
+
await updateClaudeMd(processedRepoPath, claudeMode, directoryTree, confidentialFiles);
|
|
645
691
|
|
|
646
|
-
// --- Combined Report ---
|
|
692
|
+
// --- Combined Report ---
|
|
647
693
|
console.log('\n✅ Snapshot generation complete!');
|
|
648
694
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
649
|
-
|
|
695
|
+
|
|
696
|
+
if (architectFilePath) {
|
|
697
|
+
console.log(`📄 Snapshot File: ${architectFilePath}`);
|
|
698
|
+
}
|
|
650
699
|
if (jaFilePath) {
|
|
651
700
|
console.log(`📄 Junior Arch File: ${jaFilePath}`);
|
|
652
701
|
}
|
|
702
|
+
|
|
703
|
+
console.log(`📊 Files scanned: ${stats.totalFiles}`);
|
|
653
704
|
console.log(`📊 Files processed: ${stats.includedFiles}/${stats.totalFiles}`);
|
|
654
705
|
console.log(`📏 Total size: ${formatSize(stats.totalSize)}`);
|
|
655
706
|
console.log(`📦 Processed size: ${formatSize(stats.processedSize)}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
+
import chalk from 'chalk';
|
|
4
5
|
import { getGitAnchor, getChangedFiles, getGitDiffOutput } from '../../utils/gitUtils.js';
|
|
5
6
|
import { loadSetupConfig } from '../../config.js';
|
|
6
7
|
import { readFileWithSizeCheck, parseSize, formatSize, matchesPattern, loadGitignore, generateTimestamp } from '../../utils/fileUtils.js';
|
|
@@ -9,6 +10,61 @@ import { fileURLToPath } from 'url';
|
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
11
12
|
|
|
13
|
+
// Shared logic to generate the snapshot content string
|
|
14
|
+
async function generateSnapshotContent(repoPath, changedFiles, anchor, config, gitignore) {
|
|
15
|
+
let contentOutput = '';
|
|
16
|
+
let includedCount = 0;
|
|
17
|
+
const fileList = [];
|
|
18
|
+
|
|
19
|
+
// Check for Agent Report
|
|
20
|
+
const reportPath = path.join(repoPath, '.eck', 'AnswerToSA.md');
|
|
21
|
+
let agentReport = null;
|
|
22
|
+
try {
|
|
23
|
+
agentReport = await fs.readFile(reportPath, 'utf-8');
|
|
24
|
+
if (!changedFiles.includes('.eck/AnswerToSA.md')) {
|
|
25
|
+
changedFiles.push('.eck/AnswerToSA.md');
|
|
26
|
+
}
|
|
27
|
+
} catch (e) { /* No report */ }
|
|
28
|
+
|
|
29
|
+
for (const filePath of changedFiles) {
|
|
30
|
+
if (config.dirsToIgnore.some(d => filePath.startsWith(d))) continue;
|
|
31
|
+
if (gitignore.ignores(filePath) && filePath !== '.eck/AnswerToSA.md') continue;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const fullPath = path.join(repoPath, filePath);
|
|
35
|
+
const content = await readFileWithSizeCheck(fullPath, parseSize(config.maxFileSize));
|
|
36
|
+
contentOutput += `--- File: /${filePath} ---\n\n${content}\n\n`;
|
|
37
|
+
fileList.push(`- ${filePath}`);
|
|
38
|
+
includedCount++;
|
|
39
|
+
} catch (e) { /* Skip */ }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Load Template
|
|
43
|
+
const templatePath = path.join(__dirname, '../../templates/update-prompt.template.md');
|
|
44
|
+
let header = await fs.readFile(templatePath, 'utf-8');
|
|
45
|
+
|
|
46
|
+
// Inject Agent Report
|
|
47
|
+
let reportSection = '';
|
|
48
|
+
if (agentReport) {
|
|
49
|
+
reportSection = `\n#######################################################\n# 📨 MESSAGE FROM EXECUTION AGENT (Claude)\n#######################################################\n${agentReport}\n#######################################################\n\n`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
header = header.replace('{{anchor}}', anchor.substring(0, 7))
|
|
53
|
+
.replace('{{timestamp}}', new Date().toLocaleString())
|
|
54
|
+
.replace('{{fileList}}', fileList.join('\n'));
|
|
55
|
+
|
|
56
|
+
header = reportSection + header;
|
|
57
|
+
|
|
58
|
+
const diffOutput = await getGitDiffOutput(repoPath, anchor);
|
|
59
|
+
const diffSection = `\n--- GIT DIFF (For Context) ---\n\n\`\`\`diff\n${diffOutput}\n\`\`\``;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
fullContent: header + contentOutput + diffSection,
|
|
63
|
+
includedCount,
|
|
64
|
+
anchor
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
12
68
|
export async function updateSnapshot(repoPath, options) {
|
|
13
69
|
const spinner = ora('Generating update snapshot...').start();
|
|
14
70
|
try {
|
|
@@ -23,53 +79,99 @@ export async function updateSnapshot(repoPath, options) {
|
|
|
23
79
|
return;
|
|
24
80
|
}
|
|
25
81
|
|
|
26
|
-
// Load configs for filtering logic
|
|
27
82
|
const setupConfig = await loadSetupConfig();
|
|
28
83
|
const config = { ...setupConfig.fileFiltering, ...setupConfig.performance, ...options };
|
|
29
84
|
const gitignore = await loadGitignore(repoPath);
|
|
30
85
|
|
|
31
|
-
|
|
32
|
-
let includedCount = 0;
|
|
33
|
-
const fileList = [];
|
|
86
|
+
const { fullContent, includedCount } = await generateSnapshotContent(repoPath, changedFiles, anchor, config, gitignore);
|
|
34
87
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
contentOutput += `--- File: /${filePath} ---\n\n${content}\n\n`;
|
|
45
|
-
fileList.push(`- ${filePath}`);
|
|
46
|
-
includedCount++;
|
|
47
|
-
} catch (e) {
|
|
48
|
-
// Skip deleted files or read errors
|
|
88
|
+
// Determine sequence number
|
|
89
|
+
let seqNum = 1;
|
|
90
|
+
const counterPath = path.join(repoPath, '.eck', 'update_seq');
|
|
91
|
+
try {
|
|
92
|
+
const seqData = await fs.readFile(counterPath, 'utf-8');
|
|
93
|
+
const [savedHash, savedCount] = seqData.split(':');
|
|
94
|
+
if (savedHash && savedHash.trim() === anchor.substring(0, 7).trim()) {
|
|
95
|
+
seqNum = parseInt(savedCount || '0') + 1;
|
|
49
96
|
}
|
|
50
|
-
}
|
|
97
|
+
} catch (e) {}
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
header = header.replace('{{anchor}}', anchor.substring(0, 7))
|
|
56
|
-
.replace('{{timestamp}}', new Date().toLocaleString())
|
|
57
|
-
.replace('{{fileList}}', fileList.join('\n'));
|
|
99
|
+
try {
|
|
100
|
+
await fs.writeFile(counterPath, `${anchor.substring(0, 7)}:${seqNum}`);
|
|
101
|
+
} catch (e) {}
|
|
58
102
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const diffSection = `\n--- GIT DIFF (For Context) ---\n\n\`\`\`diff\n${diffOutput}\n\`\`\``;
|
|
62
|
-
|
|
63
|
-
const outputFilename = `update_${generateTimestamp()}.md`;
|
|
103
|
+
const timestamp = generateTimestamp();
|
|
104
|
+
const outputFilename = `eck${timestamp}_${anchor.substring(0, 7)}_up${seqNum}.md`;
|
|
64
105
|
const outputPath = path.join(repoPath, '.eck', 'snapshots', outputFilename);
|
|
65
106
|
|
|
66
107
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
67
|
-
await fs.writeFile(outputPath,
|
|
108
|
+
await fs.writeFile(outputPath, fullContent);
|
|
68
109
|
|
|
69
110
|
spinner.succeed(`Update snapshot created: .eck/snapshots/${outputFilename}`);
|
|
111
|
+
|
|
112
|
+
// Check if agent report was included
|
|
113
|
+
const reportPath = path.join(repoPath, '.eck', 'AnswerToSA.md');
|
|
114
|
+
try {
|
|
115
|
+
await fs.access(reportPath);
|
|
116
|
+
console.log(chalk.green('📨 Included Agent Report (.eck/AnswerToSA.md)'));
|
|
117
|
+
} catch (e) {}
|
|
118
|
+
|
|
70
119
|
console.log(`📦 Included ${includedCount} changed files.`);
|
|
71
120
|
|
|
72
121
|
} catch (error) {
|
|
73
122
|
spinner.fail(`Update failed: ${error.message}`);
|
|
74
123
|
}
|
|
75
124
|
}
|
|
125
|
+
|
|
126
|
+
// New Silent/JSON command for Agents
|
|
127
|
+
export async function updateSnapshotJson(repoPath) {
|
|
128
|
+
try {
|
|
129
|
+
const anchor = await getGitAnchor(repoPath);
|
|
130
|
+
if (!anchor) {
|
|
131
|
+
console.log(JSON.stringify({ status: "error", message: "No snapshot anchor found" }));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const changedFiles = await getChangedFiles(repoPath, anchor);
|
|
136
|
+
if (changedFiles.length === 0) {
|
|
137
|
+
console.log(JSON.stringify({ status: "no_changes", message: "No changes detected" }));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const setupConfig = await loadSetupConfig();
|
|
142
|
+
const config = { ...setupConfig.fileFiltering, ...setupConfig.performance };
|
|
143
|
+
const gitignore = await loadGitignore(repoPath);
|
|
144
|
+
|
|
145
|
+
const { fullContent, includedCount } = await generateSnapshotContent(repoPath, changedFiles, anchor, config, gitignore);
|
|
146
|
+
|
|
147
|
+
let seqNum = 1;
|
|
148
|
+
const counterPath = path.join(repoPath, '.eck', 'update_seq');
|
|
149
|
+
try {
|
|
150
|
+
const seqData = await fs.readFile(counterPath, 'utf-8');
|
|
151
|
+
const [savedHash, savedCount] = seqData.split(':');
|
|
152
|
+
if (savedHash && savedHash.trim() === anchor.substring(0, 7).trim()) {
|
|
153
|
+
seqNum = parseInt(savedCount || '0') + 1;
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await fs.writeFile(counterPath, `${anchor.substring(0, 7)}:${seqNum}`);
|
|
159
|
+
} catch (e) {}
|
|
160
|
+
|
|
161
|
+
const timestamp = generateTimestamp();
|
|
162
|
+
const outputFilename = `eck${timestamp}_${anchor.substring(0, 7)}_up${seqNum}.md`;
|
|
163
|
+
const outputPath = path.join(repoPath, '.eck', 'snapshots', outputFilename);
|
|
164
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
165
|
+
await fs.writeFile(outputPath, fullContent);
|
|
166
|
+
|
|
167
|
+
console.log(JSON.stringify({
|
|
168
|
+
status: "success",
|
|
169
|
+
snapshot_file: `.eck/snapshots/${outputFilename}`,
|
|
170
|
+
files_count: includedCount,
|
|
171
|
+
timestamp: timestamp
|
|
172
|
+
}));
|
|
173
|
+
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.log(JSON.stringify({ status: "error", message: error.message }));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -1,126 +1,42 @@
|
|
|
1
1
|
# AI Junior Architect Instructions
|
|
2
2
|
|
|
3
|
-
You are
|
|
3
|
+
You are a **Junior Architect** ({{agentName}}). Your primary goal is to execute strategic tasks delegated to you by the Senior Architect (Gemini 3 Pro).
|
|
4
4
|
|
|
5
5
|
## Your Context
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
- You have
|
|
9
|
-
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
- **Role:** {{agentDescription}}
|
|
7
|
+
- **Model:** {{modelName}}
|
|
8
|
+
- **Snapshot:** You have access to a specific task snapshot.
|
|
9
|
+
|
|
10
|
+
## Your Workflow: The Manager
|
|
11
|
+
You are NOT a solitary coder. You are a **Manager**.
|
|
12
|
+
1. **Analyze** the task from the Senior Architect.
|
|
13
|
+
2. **Plan** the changes.
|
|
14
|
+
3. **Delegate** implementation details to your **MiniMax Worker Swarm** using tools like:
|
|
15
|
+
- `minimax_backend` (for logic/db)
|
|
16
|
+
- `minimax_frontend` (for UI)
|
|
17
|
+
- `minimax_qa` (for tests)
|
|
18
|
+
- `minimax_refactor` (for cleanup)
|
|
19
|
+
4. **Review** their work.
|
|
20
|
+
5. **Assemble** the final result using `apply_code_changes` or file editing tools.
|
|
21
|
+
|
|
22
|
+
## CRITICAL: Use the Swarm
|
|
23
|
+
Do not waste your expensive context window on reading massive files or writing boilerplate.
|
|
24
|
+
- **BAD:** Reading a 2000-line file to change one function.
|
|
25
|
+
- **GOOD:** Calling `minimax_refactor` with the file path and instruction: "Change function X to do Y".
|
|
26
|
+
|
|
27
|
+
## Response Format (Eck-Protocol v2)
|
|
28
|
+
|
|
29
|
+
Use the standard Eck-Protocol v2 for your outputs:
|
|
23
30
|
|
|
24
31
|
````text
|
|
25
32
|
# Analysis
|
|
26
|
-
|
|
27
|
-
[Your thinking and analysis of the task goes here.
|
|
28
|
-
Explain what you're going to do and why.]
|
|
33
|
+
[Your managerial plan]
|
|
29
34
|
|
|
30
35
|
## Changes
|
|
31
|
-
|
|
32
|
-
<file path="src/path/to/file.js" action="replace">
|
|
33
|
-
```javascript
|
|
34
|
-
// Your code here - no escaping needed!
|
|
35
|
-
async function example() {
|
|
36
|
-
console.log("Clean code with quotes!");
|
|
37
|
-
return { success: true };
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
</file>
|
|
41
|
-
|
|
42
|
-
<file path="src/another/file.js" action="create">
|
|
43
|
-
```javascript
|
|
44
|
-
export const helper = () => true;
|
|
45
|
-
```
|
|
46
|
-
</file>
|
|
36
|
+
[Your direct changes or delegation results]
|
|
47
37
|
|
|
48
38
|
## Metadata
|
|
49
|
-
|
|
50
39
|
```json
|
|
51
|
-
{
|
|
52
|
-
"journal": {
|
|
53
|
-
"type": "feat",
|
|
54
|
-
"scope": "api",
|
|
55
|
-
"summary": "Add example function"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
40
|
+
{ "journal": { ... } }
|
|
58
41
|
```
|
|
59
42
|
````
|
|
60
|
-
|
|
61
|
-
### File Actions
|
|
62
|
-
|
|
63
|
-
| Action | Description |
|
|
64
|
-
|--------|-------------|
|
|
65
|
-
| `create` | Create a new file |
|
|
66
|
-
| `replace` | Replace entire file content |
|
|
67
|
-
| `modify` | Partial modification (include context) |
|
|
68
|
-
| `delete` | Delete the file (no content needed) |
|
|
69
|
-
|
|
70
|
-
### Example Command
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
/claude
|
|
74
|
-
````text
|
|
75
|
-
# Analysis
|
|
76
|
-
|
|
77
|
-
I need to fix the null check in auth.js and add a helper function.
|
|
78
|
-
|
|
79
|
-
## Changes
|
|
80
|
-
|
|
81
|
-
<file path="src/auth.js" action="replace">
|
|
82
|
-
```javascript
|
|
83
|
-
async function login(user) {
|
|
84
|
-
if (!user) throw new Error("No user provided");
|
|
85
|
-
return await db.authenticate(user);
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
</file>
|
|
89
|
-
|
|
90
|
-
<file path="src/utils/validate.js" action="create">
|
|
91
|
-
```javascript
|
|
92
|
-
export const validateUser = (user) => {
|
|
93
|
-
return user && typeof user.id === 'string';
|
|
94
|
-
};
|
|
95
|
-
```
|
|
96
|
-
</file>
|
|
97
|
-
|
|
98
|
-
## Metadata
|
|
99
|
-
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"journal": {
|
|
103
|
-
"type": "fix",
|
|
104
|
-
"scope": "auth",
|
|
105
|
-
"summary": "Add null check and validation helper"
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
````
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Why This Format?
|
|
113
|
-
|
|
114
|
-
1. **No escaping hell** - Code is written in standard markdown fences, no `\"` or `\n`
|
|
115
|
-
2. **Readable** - Both humans and AI can easily read and write this format
|
|
116
|
-
3. **Parseable** - XML tags provide clear boundaries for automated processing
|
|
117
|
-
4. **Flexible** - Markdown sections allow for thinking and context
|
|
118
|
-
|
|
119
|
-
### Important Rules
|
|
120
|
-
|
|
121
|
-
- Always wrap code in markdown fences (` ``` `) inside `<file>` tags
|
|
122
|
-
- Always include the `path` and `action` attributes on `<file>` tags
|
|
123
|
-
- Use the `## Metadata` section for journal entries and other structured data
|
|
124
|
-
- The `# Analysis` section is optional but recommended for complex tasks
|
|
125
|
-
|
|
126
|
-
Your other tools (like `bash`) can be used for analysis and validation.
|
|
@@ -134,6 +134,31 @@ To understand the project state, you can command the `eck-snapshot` tool directl
|
|
|
134
134
|
- `eck-snapshot detect`: Analyze the project structure
|
|
135
135
|
- `eck-snapshot restore <snapshot_file> --include ...`: View specific files
|
|
136
136
|
|
|
137
|
+
## CAPABILITIES & DELEGATION PROTOCOL
|
|
138
|
+
|
|
139
|
+
You are managing an advanced instance of **Claude Code** equipped with specific plugins and tools. You must structure your commands to leverage these capabilities:
|
|
140
|
+
|
|
141
|
+
1. **Token Economy (MiniMax Swarm):**
|
|
142
|
+
* **Goal:** Save money and Claude's context window.
|
|
143
|
+
* **Rule:** NEVER ask Claude to write >50 lines of code from scratch or refactor huge files personally.
|
|
144
|
+
* **Command:** Instruct Claude to use the `minimax_worker` MCP tool.
|
|
145
|
+
* **Phrasing:** "Delegate the implementation of [feature] to MiniMax Backend Worker. Review their output."
|
|
146
|
+
|
|
147
|
+
2. **Self-Correction (The Ralph Loop):**
|
|
148
|
+
* **Goal:** Autonomous task completion.
|
|
149
|
+
* **Rule:** Tasks like "Fix bugs" or "Make tests pass" imply iteration.
|
|
150
|
+
* **Command:** Explicitly tell Claude: "Do not report back on the first error. Read the error, fix the code, and retry. Only report back when tests pass or after 3 failed attempts."
|
|
151
|
+
|
|
152
|
+
3. **Project Memory (.eck context):**
|
|
153
|
+
* **Goal:** Instant onboarding.
|
|
154
|
+
* **Command:** "Read `.eck/CONTEXT.md` and `.eck/TECH_DEBT.md` before starting to understand the architectural constraints."
|
|
155
|
+
|
|
156
|
+
### AVAILABLE EXECUTION AGENTS
|
|
157
|
+
|
|
158
|
+
You can command multiple specialized agents. **YOU must choose the most appropriate agent** based on the task requirements and target environment:
|
|
159
|
+
|
|
160
|
+
{{agentDefinitions}}
|
|
161
|
+
|
|
137
162
|
## Final Mandate
|
|
138
163
|
|
|
139
164
|
Your existence is defined by this loop. Think, act by issuing a command using Eck-Protocol v2, and then wait for the observation. This is the only way you can make progress.
|
package/src/utils/aiHeader.js
CHANGED
|
@@ -22,19 +22,57 @@ function render(template, data) {
|
|
|
22
22
|
return output;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Filters execution agents based on the current mode.
|
|
27
|
+
* Senior Architect should only see relevant agents, not internal workers.
|
|
28
|
+
*/
|
|
29
|
+
function getVisibleAgents(executionAgents, options) {
|
|
30
|
+
const visible = {};
|
|
31
|
+
|
|
32
|
+
// 1. Define Standard Coders (Always available as fallback)
|
|
33
|
+
// These keys must match IDs in setup.json
|
|
34
|
+
const standardCoders = ['local_dev', 'production_server', 'android_wsl_dev'];
|
|
35
|
+
|
|
36
|
+
// 2. Determine Priority Agent (The Junior Architect)
|
|
37
|
+
let priorityAgentKey = null;
|
|
38
|
+
if (options.jas) priorityAgentKey = 'jas';
|
|
39
|
+
if (options.jao) priorityAgentKey = 'jao';
|
|
40
|
+
if (options.jag) priorityAgentKey = 'jag';
|
|
41
|
+
|
|
42
|
+
// 3. Build the list
|
|
43
|
+
// If a JA is selected, add them FIRST with a note
|
|
44
|
+
if (priorityAgentKey && executionAgents[priorityAgentKey]) {
|
|
45
|
+
const ja = executionAgents[priorityAgentKey];
|
|
46
|
+
visible[priorityAgentKey] = {
|
|
47
|
+
...ja,
|
|
48
|
+
description: `⭐ **PRIMARY AGENT** ⭐ ${ja.description} (Delegates to MiniMax)`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Add standard coders
|
|
53
|
+
for (const key of standardCoders) {
|
|
54
|
+
if (executionAgents[key] && executionAgents[key].active) {
|
|
55
|
+
visible[key] = executionAgents[key];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// NOTE: We deliberately EXCLUDE 'minimax_worker' here.
|
|
60
|
+
// The Senior Architect does not call MiniMax directly; the JA does.
|
|
61
|
+
|
|
62
|
+
return visible;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildAgentDefinitions(filteredAgents) {
|
|
26
66
|
let definitions = '';
|
|
27
|
-
for (const key in
|
|
28
|
-
const agent =
|
|
29
|
-
|
|
30
|
-
definitions += `
|
|
67
|
+
for (const key in filteredAgents) {
|
|
68
|
+
const agent = filteredAgents[key];
|
|
69
|
+
definitions += `
|
|
31
70
|
### ${agent.name} (ID: "${key}")
|
|
32
71
|
- **Description:** ${agent.description}
|
|
33
72
|
- **GUI Support:** ${agent.guiSupport ? 'Yes' : 'No (Headless)'}
|
|
34
73
|
- **Capabilities:** ${agent.capabilities.join(', ')}
|
|
35
74
|
- **Restrictions:** ${agent.restrictions.join(', ')}
|
|
36
75
|
`;
|
|
37
|
-
}
|
|
38
76
|
}
|
|
39
77
|
return definitions;
|
|
40
78
|
}
|
|
@@ -290,35 +328,67 @@ export async function generateEnhancedAIHeader(context, isGitRepo = false) {
|
|
|
290
328
|
// --- End context building ---
|
|
291
329
|
|
|
292
330
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
331
|
+
// --- LOGIC CHANGE: Snapshot is ALWAYS for Senior Architect ---
|
|
332
|
+
// The `agent` prompt template is used ONLY in CLAUDE.md (via claudeMdGenerator.js)
|
|
333
|
+
// NOT in the snapshot itself.
|
|
296
334
|
|
|
297
|
-
|
|
335
|
+
const isJag = context.options && context.options.jag;
|
|
336
|
+
const isJas = context.options && context.options.jas;
|
|
337
|
+
const isJao = context.options && context.options.jao;
|
|
338
|
+
const isJaMode = isJag || isJas || isJao;
|
|
298
339
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
## Project Snapshot Information
|
|
340
|
+
// --- Determine Workflow Content based on JA Flag ---
|
|
341
|
+
let hierarchicalWorkflow = '';
|
|
342
|
+
let commandFormats = '';
|
|
304
343
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
- **Total Files in Repo**: ${context.stats ? context.stats.totalFiles : 'Unknown'}
|
|
344
|
+
if (isJaMode) {
|
|
345
|
+
// Instructions strictly for the Senior Architect on how to use the JA
|
|
346
|
+
hierarchicalWorkflow = `### 👑 ROYAL COURT ARCHITECTURE (Active)
|
|
309
347
|
|
|
310
|
-
|
|
348
|
+
You are the **Senior Architect**. You have a **Junior Architect** available to handle implementation.
|
|
311
349
|
|
|
350
|
+
**PROTOCOL:**
|
|
351
|
+
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).
|
|
352
|
+
2. **Direct Execution:** Only use \`local_dev\` or \`production_server\` directly if the Junior Architect fails or for simple "hotfixes".
|
|
353
|
+
3. **No Micro-Management:** Do not tell the Junior Architect *how* to use MiniMax or internal tools. Just give them the strategic objective.
|
|
312
354
|
`;
|
|
313
|
-
return agentHeader;
|
|
314
|
-
}
|
|
315
355
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
356
|
+
commandFormats = `### COMMAND FORMATS (Eck-Protocol v2)
|
|
357
|
+
|
|
358
|
+
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.
|
|
359
|
+
|
|
360
|
+
**CRITICAL DISPLAY RULE (THE 4-BACKTICK WRAPPER):**
|
|
361
|
+
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** (\` \`\`\`\` \`).
|
|
362
|
+
|
|
363
|
+
**Why?** Your command contains internal code blocks with 3 backticks. To escape them, the outer container needs 4.
|
|
364
|
+
|
|
365
|
+
**Required Output Format:**
|
|
366
|
+
|
|
367
|
+
\`\`\`\`text
|
|
368
|
+
# Analysis
|
|
369
|
+
[Your reasoning...]
|
|
370
|
+
|
|
371
|
+
## Changes
|
|
372
|
+
<file path="example.js" action="replace">
|
|
373
|
+
\\\`\\\`\\\`javascript
|
|
374
|
+
// Internal code block uses 3 backticks
|
|
375
|
+
const x = 1;
|
|
376
|
+
\\\`\\\`\\\`
|
|
377
|
+
</file>
|
|
320
378
|
|
|
321
|
-
|
|
379
|
+
## Metadata
|
|
380
|
+
\\\`\\\`\\\`json
|
|
381
|
+
{ "target_agent": "jas", "task_id": "unique-id" }
|
|
382
|
+
\\\`\\\`\\\`
|
|
383
|
+
\`\`\`\`
|
|
384
|
+
|
|
385
|
+
**File Actions:**
|
|
386
|
+
- \`create\`: Create a new file (requires full content)
|
|
387
|
+
- \`replace\`: Overwrite existing file (requires full content)
|
|
388
|
+
- \`modify\`: Replace specific sections (provide context)
|
|
389
|
+
- \`delete\`: Delete the file
|
|
390
|
+
`;
|
|
391
|
+
} else if (context.options && context.options.withJa) {
|
|
322
392
|
hierarchicalWorkflow = `### HIERARCHICAL AGENT WORKFLOW
|
|
323
393
|
|
|
324
394
|
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\`).
|
|
@@ -461,16 +531,8 @@ const x = 1;
|
|
|
461
531
|
template = template.replace('{{eckManifestSection}}', eckManifestSection);
|
|
462
532
|
// --- END INJECT ---
|
|
463
533
|
|
|
464
|
-
//
|
|
465
|
-
const filteredExecutionAgents = {};
|
|
466
|
-
for (const [key, agent] of Object.entries(executionAgents)) {
|
|
467
|
-
const isGeminiAgent = key.includes('gemini') || (agent.name && agent.name.toLowerCase().includes('gemini'));
|
|
468
|
-
if (isGeminiAgent) {
|
|
469
|
-
if (withJa && agent.active) filteredExecutionAgents[key] = agent;
|
|
470
|
-
} else {
|
|
471
|
-
if (agent.active) filteredExecutionAgents[key] = agent;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
534
|
+
// Use the new filtering function to get visible agents
|
|
535
|
+
const filteredExecutionAgents = getVisibleAgents(executionAgents, context.options || {});
|
|
474
536
|
|
|
475
537
|
const agentDefinitions = buildAgentDefinitions(filteredExecutionAgents);
|
|
476
538
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates the Smart Delegation Protocol based on the specific Architect persona.
|
|
6
|
+
*/
|
|
7
|
+
function getArchitectInstructions(modelName, tree) {
|
|
8
|
+
const isOpus = modelName.includes('Opus');
|
|
9
|
+
|
|
10
|
+
return `# 🧠 ROLE: Junior Architect (${modelName})
|
|
11
|
+
|
|
12
|
+
## 1. PROJECT CONTEXT & MEMORY
|
|
13
|
+
You are working inside the repository.
|
|
14
|
+
- **Source of Truth:** The file system is your source of truth.
|
|
15
|
+
- **Documentation:** The \`.eck/\` directory contains project context. READ filenames to understand what is available.
|
|
16
|
+
- **Directory Structure:**
|
|
17
|
+
\`\`\`
|
|
18
|
+
${tree}
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
## 2. SMART DELEGATION PROTOCOL (TOKEN ECONOMY)
|
|
22
|
+
|
|
23
|
+
### A. Token Efficiency: When NOT to Delegate
|
|
24
|
+
**DO NOT delegate tasks where explanation costs more tokens than execution.**
|
|
25
|
+
* *Examples:*
|
|
26
|
+
- Reading a config file → Just use \`Read\` tool (1 tool call vs explaining to MiniMax)
|
|
27
|
+
- Checking if file exists → Use \`Bash test -f\` or \`Read\`
|
|
28
|
+
- Fixing a typo → Use \`Edit\` tool directly
|
|
29
|
+
- Writing < 50 lines of glue code
|
|
30
|
+
* **Rule:** If you can solve it in 1-2 tool calls, DO IT YOURSELF.
|
|
31
|
+
|
|
32
|
+
### B. Heavy Lifting (DELEGATE TO MINIMAX)
|
|
33
|
+
For bulk work where delegation saves YOUR expensive context:
|
|
34
|
+
* *Examples:*
|
|
35
|
+
- Implementing complex business logic (> 100 lines)
|
|
36
|
+
- Refactoring entire modules
|
|
37
|
+
- Writing comprehensive test suites
|
|
38
|
+
- Generating boilerplate code
|
|
39
|
+
* **Action:** Use \`minimax_backend\`, \`minimax_frontend\`, \`minimax_qa\`, or \`minimax_refactor\`.
|
|
40
|
+
|
|
41
|
+
## 3. DEFINITION OF DONE (CRITICAL)
|
|
42
|
+
When you have completed your coding task and verified it works:
|
|
43
|
+
1. **DO NOT** run \`git commit\` manually.
|
|
44
|
+
2. **DO NOT** just say "I'm done".
|
|
45
|
+
3. **YOU MUST use the tool \`eck_finish_task\`**.
|
|
46
|
+
- This tool handles the commit AND automatically updates your context snapshot.
|
|
47
|
+
- Using this tool is the ONLY way to successfully close a task.
|
|
48
|
+
|
|
49
|
+
## 4. THE RALPH WIGGUM PROTOCOL (AUTONOMOUS LOOPS)
|
|
50
|
+
**Core Directive:** You are "deterministically persistent". Failures are expected, giving up is not.
|
|
51
|
+
|
|
52
|
+
1. **Iterative Correction:**
|
|
53
|
+
* If a build fails or tests turn red: **DO NOT STOP**.
|
|
54
|
+
* **Read** the error message.
|
|
55
|
+
* **Think** about the cause.
|
|
56
|
+
* **Fix** the code.
|
|
57
|
+
* **Retry** the verification command.
|
|
58
|
+
* *Repeat this loop up to 3-4 times.*
|
|
59
|
+
|
|
60
|
+
2. **Intelligent Retry (MiniMax Supervision):**
|
|
61
|
+
* If a MiniMax worker produces bad code:
|
|
62
|
+
* **DON'T** repeat the same prompt.
|
|
63
|
+
* **Analyze WHY** it failed (missing context? wrong import?).
|
|
64
|
+
* **Guide** the worker: "Previous attempt failed because X. Try again using pattern Y."
|
|
65
|
+
* **Takeover:** If MiniMax fails twice, **DO IT YOURSELF**.
|
|
66
|
+
|
|
67
|
+
3. **Definition of Done:**
|
|
68
|
+
* A task is ONLY done when the verification command (e.g., \`npm test\`) exits with code 0.
|
|
69
|
+
* If you cannot achieve green tests after max retries, produce a detailed report of *why* it is blocked.
|
|
70
|
+
|
|
71
|
+
## 5. REPORTING PROTOCOL
|
|
72
|
+
At the end of your task, you **MUST** create or overwrite the file \`.eck/AnswerToSA.md\` BEFORE calling \`eck_finish_task\`.
|
|
73
|
+
This file communicates your results back to the Senior Architect (Gemini).
|
|
74
|
+
|
|
75
|
+
**Format for .eck/AnswerToSA.md:**
|
|
76
|
+
\`\`\`markdown
|
|
77
|
+
# Report: [Task Name]
|
|
78
|
+
**Status:** [SUCCESS / BLOCKED / FAILED]
|
|
79
|
+
**Changes:**
|
|
80
|
+
- Modified X
|
|
81
|
+
- Created Y
|
|
82
|
+
**Verification:**
|
|
83
|
+
- Ran test Z -> Passed
|
|
84
|
+
**Next Steps / Questions:**
|
|
85
|
+
- [What should the Architect do next?]
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
## 6. OPERATIONAL RULES
|
|
89
|
+
- **Commits:** Use the \`eck_finish_task\` tool for committing and updating context.
|
|
90
|
+
- **Manifests:** If you see [STUB] in .eck/ files, update them.
|
|
91
|
+
- **Reporting:** NEVER finish a session without writing \`.eck/AnswerToSA.md\` and calling \`eck_finish_task\`.
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const CODER_INSTRUCTIONS = `# 🛠️ ROLE: Expert Developer (The Fixer)
|
|
96
|
+
|
|
97
|
+
## CORE DIRECTIVE
|
|
98
|
+
You are an Expert Developer. The architecture is already decided. Your job is to **execute**, **fix**, and **polish**.
|
|
99
|
+
|
|
100
|
+
## DEFINITION OF DONE (CRITICAL)
|
|
101
|
+
When the task is complete:
|
|
102
|
+
1. **UPDATE** the \`.eck/AnswerToSA.md\` file with your status.
|
|
103
|
+
2. **CALL** the tool \`eck_finish_task\` to commit and sync context.
|
|
104
|
+
3. **DO NOT** use raw git commands for the final commit.
|
|
105
|
+
|
|
106
|
+
## CONTEXT
|
|
107
|
+
- The MiniMax swarm might have struggled or produced code that needs refinement.
|
|
108
|
+
- You are here to solve the hard problems manually.
|
|
109
|
+
- You have full permission to edit files directly.
|
|
110
|
+
|
|
111
|
+
## WORKFLOW
|
|
112
|
+
1. Read the code.
|
|
113
|
+
2. Fix the bugs / Implement the feature.
|
|
114
|
+
3. Verify functionality (Run tests!).
|
|
115
|
+
4. **Loop:** If verification fails, fix it immediately. Do not ask for permission.
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generates and writes the CLAUDE.md file based on the selected mode.
|
|
120
|
+
*/
|
|
121
|
+
export async function updateClaudeMd(repoPath, mode, tree, confidentialFiles = []) {
|
|
122
|
+
let content = '';
|
|
123
|
+
|
|
124
|
+
if (mode === 'jas') {
|
|
125
|
+
content = getArchitectInstructions('Sonnet 4.5', tree);
|
|
126
|
+
} else if (mode === 'jao') {
|
|
127
|
+
content = getArchitectInstructions('Opus 4.5', tree);
|
|
128
|
+
} else if (mode === 'jag') {
|
|
129
|
+
content = getArchitectInstructions('Gemini 3 Pro', tree);
|
|
130
|
+
} else {
|
|
131
|
+
// Default coder mode (or if flags are missing)
|
|
132
|
+
content = CODER_INSTRUCTIONS;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Append Confidential Files Reference
|
|
136
|
+
if (confidentialFiles.length > 0) {
|
|
137
|
+
content += '\n\n## 🔐 Access & Credentials\n';
|
|
138
|
+
content += 'The following confidential files are available locally but excluded from snapshots/tree:\n';
|
|
139
|
+
for (const file of confidentialFiles) {
|
|
140
|
+
content += `- \`${file}\`\n`;
|
|
141
|
+
}
|
|
142
|
+
content += '> **Note:** Read these files only when strictly necessary.\n';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const claudeMdPath = path.join(repoPath, 'CLAUDE.md');
|
|
146
|
+
await fs.writeFile(claudeMdPath, content, 'utf-8');
|
|
147
|
+
console.log(`📝 Updated CLAUDE.md for role: **${mode.toUpperCase()}** (Ralph Loop + MiniMax Protocol Active)`);
|
|
148
|
+
}
|
package/src/utils/fileUtils.js
CHANGED
|
@@ -262,8 +262,8 @@ export async function generateDirectoryTree(dir, prefix = '', allFiles, depth =
|
|
|
262
262
|
|
|
263
263
|
for (const entry of sortedEntries) {
|
|
264
264
|
// Skip hidden directories and files (starting with '.')
|
|
265
|
-
// EXCEPT:
|
|
266
|
-
if (entry.name.startsWith('.')) {
|
|
265
|
+
// EXCEPT: Allow .eck to be visible
|
|
266
|
+
if (entry.name.startsWith('.') && entry.name !== '.eck') {
|
|
267
267
|
continue;
|
|
268
268
|
}
|
|
269
269
|
if (config.dirsToIgnore.some(d => entry.name.includes(d.replace('/', '')))) continue;
|
|
@@ -283,19 +283,20 @@ export async function generateDirectoryTree(dir, prefix = '', allFiles, depth =
|
|
|
283
283
|
|
|
284
284
|
if (entry.isDirectory()) {
|
|
285
285
|
tree += `${prefix}${connector}${entry.name}/\n`;
|
|
286
|
-
|
|
286
|
+
|
|
287
|
+
// RECURSION CONTROL:
|
|
288
|
+
// If we are currently inside .eck, do NOT recurse deeper into subdirectories (like snapshots, logs).
|
|
289
|
+
// We want to see that 'snapshots/' exists, but not list its contents.
|
|
290
|
+
const isInsideEckRoot = path.basename(dir) === '.eck';
|
|
291
|
+
|
|
292
|
+
if (!isInsideEckRoot) {
|
|
293
|
+
tree += await generateDirectoryTree(fullPath, nextPrefix, allFiles, depth + 1, maxDepth, config);
|
|
294
|
+
}
|
|
287
295
|
} else {
|
|
288
296
|
tree += `${prefix}${connector}${entry.name}\n`;
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
|
|
292
|
-
// Add .eck placeholder at root level
|
|
293
|
-
if (depth === 0) {
|
|
294
|
-
const isLast = validEntries.length === 0;
|
|
295
|
-
const connector = isLast ? '└── ' : '├── ';
|
|
296
|
-
tree += `${prefix}${connector}.eck/\n`;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
300
|
return tree;
|
|
300
301
|
} catch (error) {
|
|
301
302
|
console.warn(`⚠️ Warning: Could not read directory: ${dir}`);
|
|
@@ -406,13 +407,13 @@ export async function loadConfig(configPath) {
|
|
|
406
407
|
|
|
407
408
|
export function generateTimestamp() {
|
|
408
409
|
const now = new Date();
|
|
409
|
-
const
|
|
410
|
+
const YY = String(now.getFullYear()).slice(-2);
|
|
410
411
|
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
|
411
412
|
const DD = String(now.getDate()).padStart(2, '0');
|
|
412
413
|
const hh = String(now.getHours()).padStart(2, '0');
|
|
413
414
|
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
414
|
-
|
|
415
|
-
return `${
|
|
415
|
+
// Compact format: YY-MM-DD_HH-mm (no seconds)
|
|
416
|
+
return `${YY}-${MM}-${DD}_${hh}-${mm}`;
|
|
416
417
|
}
|
|
417
418
|
|
|
418
419
|
/**
|