midas-mcp 2.0.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +60 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/docs/CURSORRULES_TEMPLATE.md +61 -46
- package/dist/docs/USER_RULES.md +62 -81
- package/dist/metrics.d.ts +36 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +144 -0
- package/dist/metrics.js.map +1 -0
- package/dist/tests/analyze.test.d.ts +2 -0
- package/dist/tests/analyze.test.d.ts.map +1 -0
- package/dist/tests/analyze.test.js +120 -0
- package/dist/tests/analyze.test.js.map +1 -0
- package/dist/tests/edge-cases.test.d.ts +2 -0
- package/dist/tests/edge-cases.test.d.ts.map +1 -0
- package/dist/tests/edge-cases.test.js +232 -0
- package/dist/tests/edge-cases.test.js.map +1 -0
- package/dist/tests/journal.test.d.ts +2 -0
- package/dist/tests/journal.test.d.ts.map +1 -0
- package/dist/tests/journal.test.js +181 -0
- package/dist/tests/journal.test.js.map +1 -0
- package/dist/tests/metrics.test.d.ts +2 -0
- package/dist/tests/metrics.test.d.ts.map +1 -0
- package/dist/tests/metrics.test.js +178 -0
- package/dist/tests/metrics.test.js.map +1 -0
- package/dist/tests/prompts.test.d.ts +2 -0
- package/dist/tests/prompts.test.d.ts.map +1 -0
- package/dist/tests/prompts.test.js +157 -0
- package/dist/tests/prompts.test.js.map +1 -0
- package/dist/tests/server.test.d.ts +2 -0
- package/dist/tests/server.test.d.ts.map +1 -0
- package/dist/tests/server.test.js +93 -0
- package/dist/tests/server.test.js.map +1 -0
- package/dist/tests/tracker.test.d.ts +2 -0
- package/dist/tests/tracker.test.d.ts.map +1 -0
- package/dist/tests/tracker.test.js +197 -0
- package/dist/tests/tracker.test.js.map +1 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +66 -57
- package/dist/tui.js.map +1 -1
- package/docs/CURSORRULES_TEMPLATE.md +61 -46
- package/docs/USER_RULES.md +62 -81
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { createServer } from '../server.js';
|
|
4
|
+
describe('MCP Server', () => {
|
|
5
|
+
describe('createServer', () => {
|
|
6
|
+
it('creates a server instance', () => {
|
|
7
|
+
const server = createServer();
|
|
8
|
+
assert.notStrictEqual(server, null);
|
|
9
|
+
assert.notStrictEqual(server, undefined);
|
|
10
|
+
});
|
|
11
|
+
it('server has name and version', () => {
|
|
12
|
+
const server = createServer();
|
|
13
|
+
// The server object is created - basic smoke test
|
|
14
|
+
assert.strictEqual(typeof server, 'object');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('Tool Registration', () => {
|
|
18
|
+
it('registers all expected tools', () => {
|
|
19
|
+
// This is a smoke test - server creation registers tools
|
|
20
|
+
// If any tool registration fails, createServer will throw
|
|
21
|
+
assert.doesNotThrow(() => {
|
|
22
|
+
createServer();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('Prompt Registration', () => {
|
|
27
|
+
it('registers all prompts without error', () => {
|
|
28
|
+
assert.doesNotThrow(() => {
|
|
29
|
+
createServer();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('Resource Registration', () => {
|
|
34
|
+
it('registers all resources without error', () => {
|
|
35
|
+
assert.doesNotThrow(() => {
|
|
36
|
+
createServer();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('Tool Schemas', () => {
|
|
42
|
+
// Import all schemas to verify they're valid Zod schemas
|
|
43
|
+
it('all tool schemas are valid', async () => {
|
|
44
|
+
const { startProjectSchema, getPhaseSchema, setPhaseSchema, auditSchema, checkDocsSchema, oneshotSchema, tornadoSchema, horizonSchema, analyzeSchema, suggestPromptSchema, advancePhaseSchema, saveJournalSchema, getJournalSchema, searchJournalSchema, } = await import('../tools/index.js');
|
|
45
|
+
// All schemas should have a parse method (Zod schemas)
|
|
46
|
+
assert.strictEqual(typeof startProjectSchema.parse, 'function');
|
|
47
|
+
assert.strictEqual(typeof getPhaseSchema.parse, 'function');
|
|
48
|
+
assert.strictEqual(typeof setPhaseSchema.parse, 'function');
|
|
49
|
+
assert.strictEqual(typeof auditSchema.parse, 'function');
|
|
50
|
+
assert.strictEqual(typeof checkDocsSchema.parse, 'function');
|
|
51
|
+
assert.strictEqual(typeof oneshotSchema.parse, 'function');
|
|
52
|
+
assert.strictEqual(typeof tornadoSchema.parse, 'function');
|
|
53
|
+
assert.strictEqual(typeof horizonSchema.parse, 'function');
|
|
54
|
+
assert.strictEqual(typeof analyzeSchema.parse, 'function');
|
|
55
|
+
assert.strictEqual(typeof suggestPromptSchema.parse, 'function');
|
|
56
|
+
assert.strictEqual(typeof advancePhaseSchema.parse, 'function');
|
|
57
|
+
assert.strictEqual(typeof saveJournalSchema.parse, 'function');
|
|
58
|
+
assert.strictEqual(typeof getJournalSchema.parse, 'function');
|
|
59
|
+
assert.strictEqual(typeof searchJournalSchema.parse, 'function');
|
|
60
|
+
});
|
|
61
|
+
it('schemas validate correct input', async () => {
|
|
62
|
+
const { startProjectSchema, oneshotSchema, tornadoSchema } = await import('../tools/index.js');
|
|
63
|
+
// startProjectSchema
|
|
64
|
+
const startInput = { projectName: 'test-app' };
|
|
65
|
+
assert.doesNotThrow(() => startProjectSchema.parse(startInput));
|
|
66
|
+
// oneshotSchema
|
|
67
|
+
const oneshotInput = { originalPrompt: 'test', error: 'error' };
|
|
68
|
+
assert.doesNotThrow(() => oneshotSchema.parse(oneshotInput));
|
|
69
|
+
// tornadoSchema
|
|
70
|
+
const tornadoInput = { problem: 'test problem' };
|
|
71
|
+
assert.doesNotThrow(() => tornadoSchema.parse(tornadoInput));
|
|
72
|
+
});
|
|
73
|
+
it('schemas reject invalid input', async () => {
|
|
74
|
+
const { oneshotSchema, tornadoSchema, saveJournalSchema } = await import('../tools/index.js');
|
|
75
|
+
// oneshotSchema requires originalPrompt and error
|
|
76
|
+
assert.throws(() => oneshotSchema.parse({}));
|
|
77
|
+
// tornadoSchema requires problem
|
|
78
|
+
assert.throws(() => tornadoSchema.parse({}));
|
|
79
|
+
// saveJournalSchema requires title and conversation
|
|
80
|
+
assert.throws(() => saveJournalSchema.parse({}));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('Logger', () => {
|
|
84
|
+
it('logger exports expected methods', async () => {
|
|
85
|
+
const { logger } = await import('../logger.js');
|
|
86
|
+
assert.strictEqual(typeof logger.info, 'function');
|
|
87
|
+
assert.strictEqual(typeof logger.debug, 'function');
|
|
88
|
+
assert.strictEqual(typeof logger.warn, 'function');
|
|
89
|
+
assert.strictEqual(typeof logger.error, 'function');
|
|
90
|
+
assert.strictEqual(typeof logger.tool, 'function');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../src/tests/server.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.test.d.ts","sourceRoot":"","sources":["../../src/tests/tracker.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { loadTracker, saveTracker, trackToolCall, scanRecentFiles, getGitActivity, checkCompletionSignals, updateTracker, } from '../tracker.js';
|
|
8
|
+
describe('Tracker Module', () => {
|
|
9
|
+
const testDir = join(tmpdir(), 'midas-tracker-test-' + Date.now());
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mkdirSync(testDir, { recursive: true });
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
describe('loadTracker', () => {
|
|
17
|
+
it('returns default tracker for new project', () => {
|
|
18
|
+
const tracker = loadTracker(testDir);
|
|
19
|
+
assert.strictEqual(tracker.recentFiles.length, 0);
|
|
20
|
+
assert.strictEqual(tracker.recentToolCalls.length, 0);
|
|
21
|
+
assert.strictEqual(tracker.gitActivity, null);
|
|
22
|
+
assert.deepStrictEqual(tracker.inferredPhase, { phase: 'IDLE' });
|
|
23
|
+
});
|
|
24
|
+
it('loads saved tracker state', () => {
|
|
25
|
+
const state = {
|
|
26
|
+
lastUpdated: new Date().toISOString(),
|
|
27
|
+
recentFiles: [{ path: 'test.ts', lastModified: Date.now() }],
|
|
28
|
+
recentToolCalls: [],
|
|
29
|
+
gitActivity: null,
|
|
30
|
+
completionSignals: { testsExist: true, docsComplete: false },
|
|
31
|
+
inferredPhase: { phase: 'BUILD', step: 'IMPLEMENT' },
|
|
32
|
+
confidence: 70,
|
|
33
|
+
};
|
|
34
|
+
mkdirSync(join(testDir, '.midas'), { recursive: true });
|
|
35
|
+
writeFileSync(join(testDir, '.midas', 'tracker.json'), JSON.stringify(state));
|
|
36
|
+
const tracker = loadTracker(testDir);
|
|
37
|
+
assert.strictEqual(tracker.recentFiles.length, 1);
|
|
38
|
+
assert.strictEqual(tracker.inferredPhase.phase, 'BUILD');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('saveTracker', () => {
|
|
42
|
+
it('creates .midas directory', () => {
|
|
43
|
+
const tracker = loadTracker(testDir);
|
|
44
|
+
saveTracker(testDir, tracker);
|
|
45
|
+
assert.strictEqual(existsSync(join(testDir, '.midas')), true);
|
|
46
|
+
});
|
|
47
|
+
it('persists tracker state', () => {
|
|
48
|
+
const tracker = loadTracker(testDir);
|
|
49
|
+
tracker.recentToolCalls.push({ tool: 'test_tool', timestamp: Date.now() });
|
|
50
|
+
saveTracker(testDir, tracker);
|
|
51
|
+
const loaded = loadTracker(testDir);
|
|
52
|
+
assert.strictEqual(loaded.recentToolCalls.length, 1);
|
|
53
|
+
assert.strictEqual(loaded.recentToolCalls[0].tool, 'test_tool');
|
|
54
|
+
});
|
|
55
|
+
it('updates lastUpdated timestamp', () => {
|
|
56
|
+
const tracker = loadTracker(testDir);
|
|
57
|
+
const before = tracker.lastUpdated;
|
|
58
|
+
// Wait a tiny bit
|
|
59
|
+
saveTracker(testDir, tracker);
|
|
60
|
+
const loaded = loadTracker(testDir);
|
|
61
|
+
assert.strictEqual(loaded.lastUpdated >= before, true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('trackToolCall', () => {
|
|
65
|
+
it('adds tool call to tracker', () => {
|
|
66
|
+
trackToolCall(testDir, 'midas_audit', { projectPath: testDir });
|
|
67
|
+
const tracker = loadTracker(testDir);
|
|
68
|
+
assert.strictEqual(tracker.recentToolCalls.length, 1);
|
|
69
|
+
assert.strictEqual(tracker.recentToolCalls[0].tool, 'midas_audit');
|
|
70
|
+
});
|
|
71
|
+
it('stores tool arguments', () => {
|
|
72
|
+
const args = { projectPath: testDir, updatePhase: true };
|
|
73
|
+
trackToolCall(testDir, 'midas_analyze', args);
|
|
74
|
+
const tracker = loadTracker(testDir);
|
|
75
|
+
assert.deepStrictEqual(tracker.recentToolCalls[0].args, args);
|
|
76
|
+
});
|
|
77
|
+
it('keeps most recent calls first', () => {
|
|
78
|
+
trackToolCall(testDir, 'first_tool', {});
|
|
79
|
+
trackToolCall(testDir, 'second_tool', {});
|
|
80
|
+
trackToolCall(testDir, 'third_tool', {});
|
|
81
|
+
const tracker = loadTracker(testDir);
|
|
82
|
+
assert.strictEqual(tracker.recentToolCalls[0].tool, 'third_tool');
|
|
83
|
+
assert.strictEqual(tracker.recentToolCalls[2].tool, 'first_tool');
|
|
84
|
+
});
|
|
85
|
+
it('limits stored tool calls to 50', () => {
|
|
86
|
+
for (let i = 0; i < 60; i++) {
|
|
87
|
+
trackToolCall(testDir, `tool_${i}`, {});
|
|
88
|
+
}
|
|
89
|
+
const tracker = loadTracker(testDir);
|
|
90
|
+
assert.strictEqual(tracker.recentToolCalls.length, 50);
|
|
91
|
+
});
|
|
92
|
+
it('infers phase from specific tool calls', () => {
|
|
93
|
+
trackToolCall(testDir, 'midas_tornado', { problem: 'test' });
|
|
94
|
+
const tracker = loadTracker(testDir);
|
|
95
|
+
assert.strictEqual(tracker.inferredPhase.phase, 'BUILD');
|
|
96
|
+
assert.strictEqual(tracker.inferredPhase.step, 'DEBUG');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('scanRecentFiles', () => {
|
|
100
|
+
it('returns empty for empty directory', () => {
|
|
101
|
+
const files = scanRecentFiles(testDir);
|
|
102
|
+
assert.strictEqual(files.length, 0);
|
|
103
|
+
});
|
|
104
|
+
it('finds recently modified files', () => {
|
|
105
|
+
writeFileSync(join(testDir, 'test.ts'), 'content');
|
|
106
|
+
const files = scanRecentFiles(testDir);
|
|
107
|
+
assert.strictEqual(files.length, 1);
|
|
108
|
+
assert.strictEqual(files[0].path, 'test.ts');
|
|
109
|
+
});
|
|
110
|
+
it('ignores node_modules', () => {
|
|
111
|
+
mkdirSync(join(testDir, 'node_modules'), { recursive: true });
|
|
112
|
+
writeFileSync(join(testDir, 'node_modules', 'package.json'), '{}');
|
|
113
|
+
writeFileSync(join(testDir, 'src.ts'), 'code');
|
|
114
|
+
const files = scanRecentFiles(testDir);
|
|
115
|
+
assert.strictEqual(files.length, 1);
|
|
116
|
+
assert.strictEqual(files[0].path, 'src.ts');
|
|
117
|
+
});
|
|
118
|
+
it('ignores .git directory', () => {
|
|
119
|
+
mkdirSync(join(testDir, '.git'), { recursive: true });
|
|
120
|
+
writeFileSync(join(testDir, '.git', 'config'), 'gitconfig');
|
|
121
|
+
writeFileSync(join(testDir, 'app.ts'), 'code');
|
|
122
|
+
const files = scanRecentFiles(testDir);
|
|
123
|
+
assert.strictEqual(files.every(f => !f.path.includes('.git')), true);
|
|
124
|
+
});
|
|
125
|
+
it('sorts by most recent first', () => {
|
|
126
|
+
writeFileSync(join(testDir, 'old.ts'), 'old');
|
|
127
|
+
// Slight delay to ensure different timestamps
|
|
128
|
+
writeFileSync(join(testDir, 'new.ts'), 'new');
|
|
129
|
+
const files = scanRecentFiles(testDir);
|
|
130
|
+
if (files.length >= 2) {
|
|
131
|
+
assert.strictEqual(files[0].lastModified >= files[1].lastModified, true);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('getGitActivity', () => {
|
|
136
|
+
it('returns null for non-git directory', () => {
|
|
137
|
+
const activity = getGitActivity(testDir);
|
|
138
|
+
assert.strictEqual(activity, null);
|
|
139
|
+
});
|
|
140
|
+
it('detects git repository', () => {
|
|
141
|
+
// Initialize a git repo
|
|
142
|
+
execSync('git init', { cwd: testDir, stdio: 'ignore' });
|
|
143
|
+
execSync('git config user.email "test@test.com"', { cwd: testDir, stdio: 'ignore' });
|
|
144
|
+
execSync('git config user.name "Test"', { cwd: testDir, stdio: 'ignore' });
|
|
145
|
+
const activity = getGitActivity(testDir);
|
|
146
|
+
assert.notStrictEqual(activity, null);
|
|
147
|
+
assert.strictEqual(typeof activity?.branch, 'string');
|
|
148
|
+
});
|
|
149
|
+
it('counts uncommitted changes', () => {
|
|
150
|
+
execSync('git init', { cwd: testDir, stdio: 'ignore' });
|
|
151
|
+
execSync('git config user.email "test@test.com"', { cwd: testDir, stdio: 'ignore' });
|
|
152
|
+
execSync('git config user.name "Test"', { cwd: testDir, stdio: 'ignore' });
|
|
153
|
+
writeFileSync(join(testDir, 'file.txt'), 'content');
|
|
154
|
+
const activity = getGitActivity(testDir);
|
|
155
|
+
assert.strictEqual(activity?.uncommittedChanges, 1);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('checkCompletionSignals', () => {
|
|
159
|
+
it('detects test files', () => {
|
|
160
|
+
writeFileSync(join(testDir, 'app.test.ts'), 'test');
|
|
161
|
+
const signals = checkCompletionSignals(testDir);
|
|
162
|
+
assert.strictEqual(signals.testsExist, true);
|
|
163
|
+
});
|
|
164
|
+
it('detects spec files', () => {
|
|
165
|
+
writeFileSync(join(testDir, 'app.spec.ts'), 'spec');
|
|
166
|
+
const signals = checkCompletionSignals(testDir);
|
|
167
|
+
assert.strictEqual(signals.testsExist, true);
|
|
168
|
+
});
|
|
169
|
+
it('detects docs completeness', () => {
|
|
170
|
+
mkdirSync(join(testDir, 'docs'), { recursive: true });
|
|
171
|
+
writeFileSync(join(testDir, 'docs', 'brainlift.md'), '# Brainlift');
|
|
172
|
+
writeFileSync(join(testDir, 'docs', 'prd.md'), '# PRD');
|
|
173
|
+
writeFileSync(join(testDir, 'docs', 'gameplan.md'), '# Gameplan');
|
|
174
|
+
const signals = checkCompletionSignals(testDir);
|
|
175
|
+
assert.strictEqual(signals.docsComplete, true);
|
|
176
|
+
});
|
|
177
|
+
it('returns false when docs missing', () => {
|
|
178
|
+
const signals = checkCompletionSignals(testDir);
|
|
179
|
+
assert.strictEqual(signals.docsComplete, false);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe('updateTracker', () => {
|
|
183
|
+
it('updates all tracker fields', () => {
|
|
184
|
+
writeFileSync(join(testDir, 'test.ts'), 'code');
|
|
185
|
+
const tracker = updateTracker(testDir);
|
|
186
|
+
assert.strictEqual(tracker.recentFiles.length >= 1, true);
|
|
187
|
+
assert.strictEqual(typeof tracker.lastUpdated, 'string');
|
|
188
|
+
});
|
|
189
|
+
it('persists updated tracker', () => {
|
|
190
|
+
writeFileSync(join(testDir, 'src.ts'), 'code');
|
|
191
|
+
updateTracker(testDir);
|
|
192
|
+
const loaded = loadTracker(testDir);
|
|
193
|
+
assert.strictEqual(loaded.recentFiles.some(f => f.path === 'src.ts'), true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
//# sourceMappingURL=tracker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.test.js","sourceRoot":"","sources":["../../src/tests/tracker.test.ts"],"names":[],"mappings":"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"}
|
package/dist/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAuTA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAoLpD"}
|
package/dist/tui.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { exec } from 'child_process';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, appendFileSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
2
|
import { loadState, saveState, PHASE_INFO } from './state/phase.js';
|
|
5
3
|
import { hasApiKey, ensureApiKey } from './config.js';
|
|
6
4
|
import { logEvent, watchEvents } from './events.js';
|
|
7
5
|
import { analyzeProject } from './analyzer.js';
|
|
8
6
|
import { getActivitySummary, loadTracker } from './tracker.js';
|
|
9
7
|
import { getJournalEntries } from './tools/journal.js';
|
|
8
|
+
import { startSession, endSession, recordPromptCopied, loadMetrics } from './metrics.js';
|
|
10
9
|
// ANSI codes
|
|
11
10
|
const ESC = '\x1b';
|
|
12
11
|
const reset = `${ESC}[0m`;
|
|
@@ -26,19 +25,36 @@ const PHASE_COLORS = {
|
|
|
26
25
|
SHIP: green,
|
|
27
26
|
GROW: magenta,
|
|
28
27
|
};
|
|
29
|
-
// The Midas
|
|
30
|
-
const
|
|
31
|
-
# Golden Code Methodology (via Midas MCP)
|
|
28
|
+
// The Midas User Rules content (for Cursor Settings → Rules for AI)
|
|
29
|
+
const MIDAS_USER_RULES = `# Golden Code Methodology (via Midas MCP)
|
|
32
30
|
|
|
33
|
-
##
|
|
34
|
-
1. **Before any code changes**: Call \`midas_analyze\` to understand current phase
|
|
35
|
-
2. **After important discussions**: Call \`midas_journal_save\` to preserve context
|
|
36
|
-
3. **When stuck**: Call \`midas_tornado\` for Research + Logs + Tests cycle
|
|
37
|
-
4. **When output doesn't fit**: Call \`midas_horizon\` to expand context
|
|
38
|
-
5. **When retrying failed prompts**: Call \`midas_oneshot\` to construct better retry
|
|
31
|
+
## AUTOMATIC BEHAVIOR - Do these without being asked:
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
### On EVERY new chat session:
|
|
34
|
+
1. Call \`midas_journal_list\` to load context from previous sessions
|
|
35
|
+
2. Call \`midas_analyze\` to understand current project phase
|
|
36
|
+
3. Follow the suggested approach from the analysis
|
|
37
|
+
|
|
38
|
+
### After EVERY significant code change:
|
|
39
|
+
1. Git commit with descriptive message before AND after major changes
|
|
40
|
+
2. Call \`midas_journal_save\` with title describing what was done
|
|
41
|
+
3. Include the full conversation in the journal entry
|
|
42
|
+
|
|
43
|
+
### Before EVERY implementation:
|
|
44
|
+
1. Call \`midas_suggest_prompt\` to get phase-appropriate guidance
|
|
45
|
+
2. Follow the Golden Code methodology for current phase
|
|
46
|
+
|
|
47
|
+
## When Things Go Wrong:
|
|
48
|
+
- Stuck/confused: Call \`midas_tornado\` for Research + Logs + Tests cycle
|
|
49
|
+
- Output doesn't fit: Call \`midas_horizon\` to expand context
|
|
50
|
+
- Retry after error: Call \`midas_oneshot\` to construct better retry
|
|
51
|
+
|
|
52
|
+
## Git Discipline:
|
|
53
|
+
- Commit BEFORE starting any significant change (checkpoint)
|
|
54
|
+
- Commit AFTER completing each logical unit of work
|
|
55
|
+
|
|
56
|
+
## The 7-Step BUILD Cycle:
|
|
57
|
+
RULES → INDEX → READ → RESEARCH → IMPLEMENT → TEST → DEBUG
|
|
42
58
|
`;
|
|
43
59
|
// Session starter prompt
|
|
44
60
|
function getSessionStarterPrompt(projectPath) {
|
|
@@ -49,23 +65,9 @@ function getSessionStarterPrompt(projectPath) {
|
|
|
49
65
|
}
|
|
50
66
|
return `Before we begin, please call midas_analyze to understand where we are in the project and what to do next.`;
|
|
51
67
|
}
|
|
52
|
-
//
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
if (!existsSync(rulesPath))
|
|
56
|
-
return false;
|
|
57
|
-
const content = readFileSync(rulesPath, 'utf-8');
|
|
58
|
-
return content.includes('midas_analyze') || content.includes('Golden Code');
|
|
59
|
-
}
|
|
60
|
-
// Add Midas rules to .cursorrules
|
|
61
|
-
function addMidasRules(projectPath) {
|
|
62
|
-
const rulesPath = join(projectPath, '.cursorrules');
|
|
63
|
-
if (existsSync(rulesPath)) {
|
|
64
|
-
appendFileSync(rulesPath, '\n' + MIDAS_CURSORRULES);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
writeFileSync(rulesPath, MIDAS_CURSORRULES.trim());
|
|
68
|
-
}
|
|
68
|
+
// Copy User Rules to clipboard for pasting into Cursor Settings
|
|
69
|
+
function copyUserRules() {
|
|
70
|
+
return copyToClipboard(MIDAS_USER_RULES);
|
|
69
71
|
}
|
|
70
72
|
function copyToClipboard(text) {
|
|
71
73
|
return new Promise((resolve, reject) => {
|
|
@@ -134,13 +136,14 @@ function drawUI(state, _projectPath) {
|
|
|
134
136
|
const lines = [];
|
|
135
137
|
// Header
|
|
136
138
|
lines.push(`${cyan}╔${hLine}╗${reset}`);
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
+
const streakStr = state.sessionStreak > 0 ? `${yellow}${state.sessionStreak}d streak${reset}` : '';
|
|
140
|
+
const statusIcons = `${streakStr} ${state.hasApiKey ? `${green}[ok]${reset}AI` : `${dim}[--]${reset}`}`;
|
|
141
|
+
lines.push(row(`${bold}${white}MIDAS${reset} ${dim}- Golden Code Coach${reset} ${statusIcons}`, I));
|
|
139
142
|
lines.push(`${cyan}╠${hLine}╣${reset}`);
|
|
140
143
|
// Show session starter prompt first
|
|
141
144
|
if (state.showingSessionStart) {
|
|
142
145
|
lines.push(emptyRow());
|
|
143
|
-
lines.push(row(`${bold}${yellow}
|
|
146
|
+
lines.push(row(`${bold}${yellow}NEW SESSION${reset}`, 12));
|
|
144
147
|
lines.push(emptyRow());
|
|
145
148
|
lines.push(row(`${dim}Paste this in your new Cursor chat:${reset}`, 36));
|
|
146
149
|
lines.push(`${cyan}║${reset} ${dim}┌${hLineLight}┐${reset}${cyan}║${reset}`);
|
|
@@ -150,19 +153,17 @@ function drawUI(state, _projectPath) {
|
|
|
150
153
|
}
|
|
151
154
|
lines.push(`${cyan}║${reset} ${dim}└${hLineLight}┘${reset}${cyan}║${reset}`);
|
|
152
155
|
lines.push(emptyRow());
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
lines.push(emptyRow());
|
|
157
|
-
}
|
|
156
|
+
lines.push(row(`${dim}TIP: Add User Rules in Cursor Settings for auto-behavior${reset}`, 57));
|
|
157
|
+
lines.push(row(`${dim}Press ${bold}[u]${reset}${dim} to copy User Rules to clipboard${reset}`, 42));
|
|
158
|
+
lines.push(emptyRow());
|
|
158
159
|
lines.push(`${cyan}╠${hLine}╣${reset}`);
|
|
159
|
-
lines.push(row(`${dim}[c]${reset} Copy starter ${dim}[
|
|
160
|
+
lines.push(row(`${dim}[c]${reset} Copy starter ${dim}[u]${reset} Copy User Rules ${dim}[s]${reset} Skip ${dim}[q]${reset} Quit`, 54));
|
|
160
161
|
lines.push(`${cyan}╚${hLine}╝${reset}`);
|
|
161
162
|
return lines.join('\n');
|
|
162
163
|
}
|
|
163
164
|
if (state.isAnalyzing) {
|
|
164
165
|
lines.push(emptyRow());
|
|
165
|
-
lines.push(row(`${magenta}
|
|
166
|
+
lines.push(row(`${magenta}...${reset} ${bold}Analyzing project${reset}`, 22));
|
|
166
167
|
lines.push(row(`${dim}Reading codebase, chat history, docs...${reset}`, 40));
|
|
167
168
|
lines.push(emptyRow());
|
|
168
169
|
lines.push(`${cyan}╚${hLine}╝${reset}`);
|
|
@@ -199,13 +200,13 @@ function drawUI(state, _projectPath) {
|
|
|
199
200
|
const color = PHASE_COLORS[p];
|
|
200
201
|
const sep = i < phases.length - 1 ? ' ' : '';
|
|
201
202
|
if (i < currentPhaseIdx) {
|
|
202
|
-
phaseBarText += `${green}
|
|
203
|
+
phaseBarText += `${green}[x]${reset} ${dim}${info.name}${reset}${sep}`;
|
|
203
204
|
}
|
|
204
205
|
else if (i === currentPhaseIdx) {
|
|
205
|
-
phaseBarText += `${color}
|
|
206
|
+
phaseBarText += `${color}[>]${reset} ${bold}${info.name}${reset}${sep}`;
|
|
206
207
|
}
|
|
207
208
|
else {
|
|
208
|
-
phaseBarText += `${dim}
|
|
209
|
+
phaseBarText += `${dim}[ ] ${info.name}${reset}${sep}`;
|
|
209
210
|
}
|
|
210
211
|
phaseBarLen += 2 + info.name.length + sep.length;
|
|
211
212
|
}
|
|
@@ -221,7 +222,7 @@ function drawUI(state, _projectPath) {
|
|
|
221
222
|
lines.push(row(`${dim}Completed:${reset}`, 10));
|
|
222
223
|
for (const done of a.whatsDone.slice(0, 4)) {
|
|
223
224
|
const t = done.length > I - 4 ? done.slice(0, I - 7) + '...' : done;
|
|
224
|
-
lines.push(row(`${green}
|
|
225
|
+
lines.push(row(`${green}[x]${reset} ${dim}${t}${reset}`, 4 + t.length));
|
|
225
226
|
}
|
|
226
227
|
lines.push(emptyRow());
|
|
227
228
|
}
|
|
@@ -250,7 +251,7 @@ function drawUI(state, _projectPath) {
|
|
|
250
251
|
lines.push(emptyRow());
|
|
251
252
|
lines.push(row(`${dim}Recent MCP Activity:${reset}`, 20));
|
|
252
253
|
for (const evt of state.recentEvents.slice(-3)) {
|
|
253
|
-
const icon = evt.type === 'tool_called' ? `${green}
|
|
254
|
+
const icon = evt.type === 'tool_called' ? `${green}>${reset}` : `${dim}-${reset}`;
|
|
254
255
|
const label = evt.tool || evt.type;
|
|
255
256
|
const time = new Date(evt.timestamp).toLocaleTimeString().slice(0, 5);
|
|
256
257
|
lines.push(row(`${icon} ${label} ${dim}(${time})${reset}`, 3 + label.length + 2 + time.length + 2));
|
|
@@ -272,6 +273,10 @@ export async function runInteractive() {
|
|
|
272
273
|
}
|
|
273
274
|
process.stdin.resume();
|
|
274
275
|
process.stdin.setEncoding('utf8');
|
|
276
|
+
// Start a new session for metrics tracking
|
|
277
|
+
const currentPhase = loadState(projectPath).current;
|
|
278
|
+
const sessionId = startSession(projectPath, currentPhase);
|
|
279
|
+
const metrics = loadMetrics(projectPath);
|
|
275
280
|
const tuiState = {
|
|
276
281
|
analysis: null,
|
|
277
282
|
isAnalyzing: false,
|
|
@@ -280,9 +285,10 @@ export async function runInteractive() {
|
|
|
280
285
|
recentEvents: [],
|
|
281
286
|
message: '',
|
|
282
287
|
hasApiKey: hasApiKey(),
|
|
283
|
-
hasMidasRules: hasMidasRules(projectPath),
|
|
284
288
|
showingSessionStart: true, // Start with session starter prompt
|
|
285
289
|
sessionStarterPrompt: getSessionStarterPrompt(projectPath),
|
|
290
|
+
sessionId,
|
|
291
|
+
sessionStreak: metrics.currentStreak,
|
|
286
292
|
};
|
|
287
293
|
const render = () => {
|
|
288
294
|
console.log(clearScreen);
|
|
@@ -340,7 +346,7 @@ export async function runInteractive() {
|
|
|
340
346
|
const toolEvents = newEvents.filter(e => e.type === 'tool_called');
|
|
341
347
|
if (toolEvents.length > 0) {
|
|
342
348
|
const lastTool = toolEvents[toolEvents.length - 1];
|
|
343
|
-
tuiState.message = `${green}
|
|
349
|
+
tuiState.message = `${green}>${reset} Cursor called ${bold}${lastTool.tool}${reset}`;
|
|
344
350
|
// Auto-refresh analysis after certain tools
|
|
345
351
|
const refreshTools = ['midas_journal_save', 'midas_set_phase', 'midas_advance_phase'];
|
|
346
352
|
if (refreshTools.includes(lastTool.tool || '')) {
|
|
@@ -353,8 +359,11 @@ export async function runInteractive() {
|
|
|
353
359
|
});
|
|
354
360
|
process.stdin.on('data', async (key) => {
|
|
355
361
|
if (key === 'q' || key === '\u0003') {
|
|
362
|
+
// End session and save metrics
|
|
363
|
+
const endPhase = tuiState.analysis?.currentPhase || { phase: 'IDLE' };
|
|
364
|
+
endSession(projectPath, tuiState.sessionId, endPhase);
|
|
356
365
|
console.log(clearScreen);
|
|
357
|
-
console.log(`\n ${cyan}Midas${reset} signing off. Happy vibecoding!\n`);
|
|
366
|
+
console.log(`\n ${cyan}Midas${reset} signing off. Session saved. Happy vibecoding!\n`);
|
|
358
367
|
clearInterval(activityInterval);
|
|
359
368
|
stopWatchingEvents();
|
|
360
369
|
process.exit(0);
|
|
@@ -364,7 +373,7 @@ export async function runInteractive() {
|
|
|
364
373
|
if (key === 'c') {
|
|
365
374
|
try {
|
|
366
375
|
await copyToClipboard(tuiState.sessionStarterPrompt);
|
|
367
|
-
tuiState.message = `${green}
|
|
376
|
+
tuiState.message = `${green}OK${reset} Session starter copied! Paste it in your new Cursor chat.`;
|
|
368
377
|
}
|
|
369
378
|
catch {
|
|
370
379
|
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
@@ -380,14 +389,13 @@ export async function runInteractive() {
|
|
|
380
389
|
}
|
|
381
390
|
return;
|
|
382
391
|
}
|
|
383
|
-
if (key === '
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
tuiState.
|
|
387
|
-
tuiState.message = `${green}✓${reset} Added Golden Code rules to .cursorrules`;
|
|
392
|
+
if (key === 'u') {
|
|
393
|
+
try {
|
|
394
|
+
await copyUserRules();
|
|
395
|
+
tuiState.message = `${green}OK${reset} User Rules copied! Paste in Cursor Settings -> Rules for AI`;
|
|
388
396
|
}
|
|
389
|
-
|
|
390
|
-
tuiState.message = `${
|
|
397
|
+
catch {
|
|
398
|
+
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
391
399
|
}
|
|
392
400
|
render();
|
|
393
401
|
return;
|
|
@@ -398,8 +406,9 @@ export async function runInteractive() {
|
|
|
398
406
|
if (tuiState.analysis?.suggestedPrompt) {
|
|
399
407
|
try {
|
|
400
408
|
await copyToClipboard(tuiState.analysis.suggestedPrompt);
|
|
401
|
-
tuiState.message = `${green}
|
|
409
|
+
tuiState.message = `${green}OK${reset} Prompt copied to clipboard!`;
|
|
402
410
|
logEvent(projectPath, { type: 'prompt_copied', message: tuiState.analysis.suggestedPrompt.slice(0, 100) });
|
|
411
|
+
recordPromptCopied(projectPath, tuiState.sessionId);
|
|
403
412
|
}
|
|
404
413
|
catch {
|
|
405
414
|
tuiState.message = `${yellow}!${reset} Could not copy.`;
|