@ekkos/cli 0.2.18 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/agent/daemon.d.ts +27 -0
- package/dist/agent/daemon.js +254 -29
- package/dist/agent/health-check.d.ts +35 -0
- package/dist/agent/health-check.js +243 -0
- package/dist/agent/pty-runner.d.ts +1 -0
- package/dist/agent/pty-runner.js +6 -1
- package/dist/capture/eviction-client.d.ts +139 -0
- package/dist/capture/eviction-client.js +454 -0
- package/dist/capture/index.d.ts +2 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/jsonl-rewriter.d.ts +96 -0
- package/dist/capture/jsonl-rewriter.js +1369 -0
- package/dist/capture/transcript-repair.d.ts +51 -0
- package/dist/capture/transcript-repair.js +319 -0
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +244 -0
- package/dist/commands/dashboard.d.ts +25 -0
- package/dist/commands/dashboard.js +1175 -0
- package/dist/commands/doctor.js +23 -1
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +1605 -516
- package/dist/commands/setup-remote.js +146 -37
- package/dist/commands/swarm-dashboard.d.ts +20 -0
- package/dist/commands/swarm-dashboard.js +735 -0
- package/dist/commands/swarm-setup.d.ts +10 -0
- package/dist/commands/swarm-setup.js +956 -0
- package/dist/commands/swarm.d.ts +46 -0
- package/dist/commands/swarm.js +441 -0
- package/dist/commands/test-claude.d.ts +16 -0
- package/dist/commands/test-claude.js +156 -0
- package/dist/commands/usage/blocks.d.ts +8 -0
- package/dist/commands/usage/blocks.js +60 -0
- package/dist/commands/usage/daily.d.ts +9 -0
- package/dist/commands/usage/daily.js +96 -0
- package/dist/commands/usage/dashboard.d.ts +8 -0
- package/dist/commands/usage/dashboard.js +104 -0
- package/dist/commands/usage/formatters.d.ts +41 -0
- package/dist/commands/usage/formatters.js +147 -0
- package/dist/commands/usage/index.d.ts +13 -0
- package/dist/commands/usage/index.js +87 -0
- package/dist/commands/usage/monthly.d.ts +8 -0
- package/dist/commands/usage/monthly.js +66 -0
- package/dist/commands/usage/session.d.ts +11 -0
- package/dist/commands/usage/session.js +193 -0
- package/dist/commands/usage/weekly.d.ts +9 -0
- package/dist/commands/usage/weekly.js +61 -0
- package/dist/commands/usage.d.ts +7 -0
- package/dist/commands/usage.js +214 -0
- package/dist/cron/index.d.ts +7 -0
- package/dist/cron/index.js +13 -0
- package/dist/cron/promoter.d.ts +70 -0
- package/dist/cron/promoter.js +403 -0
- package/dist/deploy/instructions.d.ts +5 -2
- package/dist/deploy/instructions.js +11 -8
- package/dist/index.js +262 -5
- package/dist/lib/tmux-scrollbar.d.ts +14 -0
- package/dist/lib/tmux-scrollbar.js +296 -0
- package/dist/lib/usage-monitor.d.ts +47 -0
- package/dist/lib/usage-monitor.js +124 -0
- package/dist/lib/usage-parser.d.ts +162 -0
- package/dist/lib/usage-parser.js +583 -0
- package/dist/restore/RestoreOrchestrator.d.ts +4 -0
- package/dist/restore/RestoreOrchestrator.js +118 -30
- package/dist/utils/log-rotate.d.ts +18 -0
- package/dist/utils/log-rotate.js +74 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/utils/platform.js +3 -1
- package/dist/utils/session-binding.d.ts +5 -0
- package/dist/utils/session-binding.js +46 -0
- package/dist/utils/state.js +4 -0
- package/dist/utils/verify-remote-terminal.d.ts +10 -0
- package/dist/utils/verify-remote-terminal.js +415 -0
- package/package.json +9 -2
- package/templates/CLAUDE.md +135 -23
- package/templates/ekkos-manifest.json +5 -5
- package/templates/hooks/lib/contract.sh +43 -31
- package/templates/hooks/lib/count-tokens.cjs +86 -0
- package/templates/hooks/lib/ekkos-reminders.sh +98 -0
- package/templates/hooks/lib/state.sh +53 -1
- package/templates/hooks/stop.sh +150 -388
- package/templates/hooks/user-prompt-submit.sh +353 -443
- package/templates/windsurf-hooks/README.md +212 -0
- package/templates/windsurf-hooks/hooks.json +9 -2
- package/templates/windsurf-hooks/install.sh +148 -0
- package/templates/windsurf-hooks/lib/contract.sh +2 -0
- package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
- package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
- package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
- package/templates/agents/README.md +0 -182
- package/templates/agents/code-reviewer.md +0 -166
- package/templates/agents/debug-detective.md +0 -169
- package/templates/agents/ekkOS_Vercel.md +0 -99
- package/templates/agents/extension-manager.md +0 -229
- package/templates/agents/git-companion.md +0 -185
- package/templates/agents/github-test-agent.md +0 -321
- package/templates/agents/railway-manager.md +0 -215
- package/templates/windsurf-hooks/before-submit-prompt.sh +0 -238
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ekkOS PROMETHEUS Pattern Promoter
|
|
5
|
+
* ==================================
|
|
6
|
+
*
|
|
7
|
+
* Daily cron job that evaluates patterns for promotion to constitutional memory.
|
|
8
|
+
*
|
|
9
|
+
* Constitutional patterns are injected into the system prompt via @ekkos/patch,
|
|
10
|
+
* making them "instincts" that don't require retrieval.
|
|
11
|
+
*
|
|
12
|
+
* Promotion Criteria (default):
|
|
13
|
+
* - Success rate ≥ 85%
|
|
14
|
+
* - Applications ≥ 5
|
|
15
|
+
* - Skip rate ≤ 10%
|
|
16
|
+
* - Confidence ≥ 70%
|
|
17
|
+
* - Used within last 30 days
|
|
18
|
+
* - Used in ≥ 2 unique sessions
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* npx ekkos-promote # Run promotion evaluation
|
|
22
|
+
* npx ekkos-promote --dry-run # Preview without applying changes
|
|
23
|
+
* npx ekkos-promote --user <uuid> # Promote for specific user
|
|
24
|
+
*
|
|
25
|
+
* Schedule via launchd (macOS) or cron:
|
|
26
|
+
* 0 6 * * * /path/to/node /path/to/ekkos-promote
|
|
27
|
+
*/
|
|
28
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
31
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
32
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
33
|
+
}
|
|
34
|
+
Object.defineProperty(o, k2, desc);
|
|
35
|
+
}) : (function(o, m, k, k2) {
|
|
36
|
+
if (k2 === undefined) k2 = k;
|
|
37
|
+
o[k2] = m[k];
|
|
38
|
+
}));
|
|
39
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
40
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
41
|
+
}) : function(o, v) {
|
|
42
|
+
o["default"] = v;
|
|
43
|
+
});
|
|
44
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
45
|
+
var ownKeys = function(o) {
|
|
46
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
47
|
+
var ar = [];
|
|
48
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
49
|
+
return ar;
|
|
50
|
+
};
|
|
51
|
+
return ownKeys(o);
|
|
52
|
+
};
|
|
53
|
+
return function (mod) {
|
|
54
|
+
if (mod && mod.__esModule) return mod;
|
|
55
|
+
var result = {};
|
|
56
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
57
|
+
__setModuleDefault(result, mod);
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
})();
|
|
61
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
+
exports.evaluatePromotions = evaluatePromotions;
|
|
63
|
+
exports.queryPatternStats = queryPatternStats;
|
|
64
|
+
exports.writePatchConfig = writePatchConfig;
|
|
65
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
66
|
+
const prometheus_1 = require("@ekkos/prometheus");
|
|
67
|
+
const fs = __importStar(require("fs"));
|
|
68
|
+
const path = __importStar(require("path"));
|
|
69
|
+
const os = __importStar(require("os"));
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
|
+
// Pattern Stats Query
|
|
72
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
73
|
+
async function queryPatternStats(supabase, userId) {
|
|
74
|
+
// Query patterns with their application stats
|
|
75
|
+
let query = supabase
|
|
76
|
+
.from('patterns')
|
|
77
|
+
.select(`
|
|
78
|
+
pattern_id,
|
|
79
|
+
title,
|
|
80
|
+
content,
|
|
81
|
+
success_rate,
|
|
82
|
+
applied_count,
|
|
83
|
+
confidence_score,
|
|
84
|
+
created_at,
|
|
85
|
+
updated_at,
|
|
86
|
+
tags,
|
|
87
|
+
user_id
|
|
88
|
+
`)
|
|
89
|
+
.eq('quarantined', false)
|
|
90
|
+
.gte('applied_count', 1); // Only consider applied patterns
|
|
91
|
+
if (userId) {
|
|
92
|
+
query = query.eq('user_id', userId);
|
|
93
|
+
}
|
|
94
|
+
const { data: patterns, error } = await query;
|
|
95
|
+
if (error) {
|
|
96
|
+
throw new Error(`Failed to query patterns: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
if (!patterns || patterns.length === 0) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
// Get application details for skip rate and session count
|
|
102
|
+
const patternIds = patterns.map(p => p.pattern_id);
|
|
103
|
+
const { data: applications } = await supabase
|
|
104
|
+
.from('pattern_applications')
|
|
105
|
+
.select('pattern_id, outcome_success, created_at, user_id')
|
|
106
|
+
.in('pattern_id', patternIds);
|
|
107
|
+
const { data: skips } = await supabase
|
|
108
|
+
.from('pattern_skips')
|
|
109
|
+
.select('pattern_id')
|
|
110
|
+
.in('pattern_id', patternIds);
|
|
111
|
+
// Aggregate stats per pattern
|
|
112
|
+
const appsByPattern = new Map();
|
|
113
|
+
const skipsByPattern = new Map();
|
|
114
|
+
for (const app of applications || []) {
|
|
115
|
+
if (!appsByPattern.has(app.pattern_id)) {
|
|
116
|
+
appsByPattern.set(app.pattern_id, []);
|
|
117
|
+
}
|
|
118
|
+
appsByPattern.get(app.pattern_id).push(app);
|
|
119
|
+
}
|
|
120
|
+
for (const skip of skips || []) {
|
|
121
|
+
const count = skipsByPattern.get(skip.pattern_id) || 0;
|
|
122
|
+
skipsByPattern.set(skip.pattern_id, count + 1);
|
|
123
|
+
}
|
|
124
|
+
// Build PatternStats for each pattern
|
|
125
|
+
return patterns.map(p => {
|
|
126
|
+
const apps = appsByPattern.get(p.pattern_id) || [];
|
|
127
|
+
const skipCount = skipsByPattern.get(p.pattern_id) || 0;
|
|
128
|
+
const successCount = apps.filter(a => a.outcome_success === true).length;
|
|
129
|
+
const failureCount = apps.filter(a => a.outcome_success === false).length;
|
|
130
|
+
const totalApplications = successCount + failureCount;
|
|
131
|
+
// Count unique sessions (using created_at date as proxy)
|
|
132
|
+
const uniqueDates = new Set(apps.map(a => new Date(a.created_at).toISOString().split('T')[0]));
|
|
133
|
+
const stats = {
|
|
134
|
+
patternId: p.pattern_id,
|
|
135
|
+
title: p.title || 'Untitled',
|
|
136
|
+
successCount,
|
|
137
|
+
failureCount,
|
|
138
|
+
skipCount,
|
|
139
|
+
totalApplications: totalApplications || p.applied_count || 0,
|
|
140
|
+
uniqueSessions: uniqueDates.size,
|
|
141
|
+
firstApplied: new Date(p.created_at),
|
|
142
|
+
lastApplied: new Date(p.updated_at || p.created_at),
|
|
143
|
+
currentConfidence: p.confidence_score || p.success_rate || 0.5,
|
|
144
|
+
tags: p.tags,
|
|
145
|
+
};
|
|
146
|
+
return stats;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
150
|
+
// Promotion Logic
|
|
151
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
152
|
+
async function evaluatePromotions(supabase, config) {
|
|
153
|
+
const result = {
|
|
154
|
+
evaluated: 0,
|
|
155
|
+
promoted: 0,
|
|
156
|
+
demoted: 0,
|
|
157
|
+
patterns: [],
|
|
158
|
+
};
|
|
159
|
+
// Get pattern stats
|
|
160
|
+
const patternStats = await queryPatternStats(supabase, config.userId);
|
|
161
|
+
result.evaluated = patternStats.length;
|
|
162
|
+
if (config.verbose) {
|
|
163
|
+
console.log(`[PROMOTER] Evaluating ${patternStats.length} patterns...`);
|
|
164
|
+
}
|
|
165
|
+
// Get currently promoted patterns (from promoted_patterns table or metadata)
|
|
166
|
+
const { data: currentlyPromoted } = await supabase
|
|
167
|
+
.from('patterns')
|
|
168
|
+
.select('pattern_id')
|
|
169
|
+
.eq('evolution_stage', 'constitutional');
|
|
170
|
+
const promotedSet = new Set((currentlyPromoted || []).map(p => p.pattern_id));
|
|
171
|
+
const promotedPatterns = [];
|
|
172
|
+
// Evaluate each pattern
|
|
173
|
+
for (const stats of patternStats) {
|
|
174
|
+
const evaluation = prometheus_1.promotionEvaluator.evaluate(stats);
|
|
175
|
+
const wasPromoted = promotedSet.has(stats.patternId);
|
|
176
|
+
if (evaluation.eligible && !wasPromoted) {
|
|
177
|
+
// PROMOTE: Pattern meets criteria and wasn't promoted before
|
|
178
|
+
result.promoted++;
|
|
179
|
+
result.patterns.push({
|
|
180
|
+
patternId: stats.patternId,
|
|
181
|
+
title: stats.title,
|
|
182
|
+
action: 'promoted',
|
|
183
|
+
score: evaluation.score,
|
|
184
|
+
});
|
|
185
|
+
if (!config.dryRun) {
|
|
186
|
+
// Update pattern to constitutional
|
|
187
|
+
await supabase
|
|
188
|
+
.from('patterns')
|
|
189
|
+
.update({
|
|
190
|
+
evolution_stage: 'constitutional',
|
|
191
|
+
promoted_at: new Date().toISOString(),
|
|
192
|
+
})
|
|
193
|
+
.eq('pattern_id', stats.patternId);
|
|
194
|
+
// Emit PATTERN_PROMOTED event
|
|
195
|
+
prometheus_1.learningEvents.emitPatternPromoted({
|
|
196
|
+
id: stats.patternId,
|
|
197
|
+
title: stats.title,
|
|
198
|
+
confidenceBefore: stats.currentConfidence,
|
|
199
|
+
confidenceAfter: 1.0,
|
|
200
|
+
tier: 'constitutional',
|
|
201
|
+
successRate: stats.successCount / Math.max(stats.totalApplications, 1),
|
|
202
|
+
appliedCount: stats.totalApplications,
|
|
203
|
+
}, {
|
|
204
|
+
daysInEpisodic: Math.floor((Date.now() - stats.firstApplied.getTime()) / (1000 * 60 * 60 * 24)),
|
|
205
|
+
totalApplications: stats.totalApplications,
|
|
206
|
+
finalSuccessRate: stats.successCount / Math.max(stats.totalApplications, 1),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// Fetch full pattern for patch config
|
|
210
|
+
const { data: fullPattern } = await supabase
|
|
211
|
+
.from('patterns')
|
|
212
|
+
.select('content, tags')
|
|
213
|
+
.eq('pattern_id', stats.patternId)
|
|
214
|
+
.single();
|
|
215
|
+
if (fullPattern) {
|
|
216
|
+
// Parse problem/solution from content
|
|
217
|
+
const content = fullPattern.content || '';
|
|
218
|
+
const problemMatch = content.match(/## Problem\n([\s\S]*?)(?=\n## Solution|$)/);
|
|
219
|
+
const solutionMatch = content.match(/## Solution\n([\s\S]*?)$/);
|
|
220
|
+
promotedPatterns.push({
|
|
221
|
+
id: stats.patternId,
|
|
222
|
+
title: stats.title,
|
|
223
|
+
problem: problemMatch?.[1]?.trim() || 'N/A',
|
|
224
|
+
solution: solutionMatch?.[1]?.trim() || content,
|
|
225
|
+
promotedAt: new Date().toISOString(),
|
|
226
|
+
successRate: stats.successCount / Math.max(stats.totalApplications, 1),
|
|
227
|
+
appliedCount: stats.totalApplications,
|
|
228
|
+
tags: fullPattern.tags,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (config.verbose) {
|
|
232
|
+
console.log(` ✓ PROMOTE: "${stats.title}" (score: ${evaluation.score.toFixed(2)})`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else if (!evaluation.eligible && wasPromoted) {
|
|
236
|
+
// DEMOTE: Pattern no longer meets criteria
|
|
237
|
+
result.demoted++;
|
|
238
|
+
// Find the main blocker for demotion reason
|
|
239
|
+
const reason = evaluation.blockers[0]?.includes('success')
|
|
240
|
+
? 'low_success_rate'
|
|
241
|
+
: evaluation.blockers[0]?.includes('skip')
|
|
242
|
+
? 'high_skip_rate'
|
|
243
|
+
: 'outdated';
|
|
244
|
+
result.patterns.push({
|
|
245
|
+
patternId: stats.patternId,
|
|
246
|
+
title: stats.title,
|
|
247
|
+
action: 'demoted',
|
|
248
|
+
score: evaluation.score,
|
|
249
|
+
reason,
|
|
250
|
+
});
|
|
251
|
+
if (!config.dryRun) {
|
|
252
|
+
// Update pattern back to episodic
|
|
253
|
+
await supabase
|
|
254
|
+
.from('patterns')
|
|
255
|
+
.update({
|
|
256
|
+
evolution_stage: 'episodic',
|
|
257
|
+
demoted_at: new Date().toISOString(),
|
|
258
|
+
})
|
|
259
|
+
.eq('pattern_id', stats.patternId);
|
|
260
|
+
// Emit PATTERN_DEMOTED event
|
|
261
|
+
prometheus_1.learningEvents.emitPatternDemoted({
|
|
262
|
+
id: stats.patternId,
|
|
263
|
+
title: stats.title,
|
|
264
|
+
confidenceBefore: 1.0,
|
|
265
|
+
confidenceAfter: stats.currentConfidence,
|
|
266
|
+
tier: 'episodic',
|
|
267
|
+
successRate: stats.successCount / Math.max(stats.totalApplications, 1),
|
|
268
|
+
appliedCount: stats.totalApplications,
|
|
269
|
+
}, reason);
|
|
270
|
+
}
|
|
271
|
+
if (config.verbose) {
|
|
272
|
+
console.log(` ✗ DEMOTE: "${stats.title}" (${reason})`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
result.patterns.push({
|
|
277
|
+
patternId: stats.patternId,
|
|
278
|
+
title: stats.title,
|
|
279
|
+
action: 'unchanged',
|
|
280
|
+
score: evaluation.score,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Also include patterns that are already promoted and still eligible
|
|
285
|
+
for (const stats of patternStats) {
|
|
286
|
+
const evaluation = prometheus_1.promotionEvaluator.evaluate(stats);
|
|
287
|
+
if (evaluation.eligible && promotedSet.has(stats.patternId)) {
|
|
288
|
+
// Fetch and include in patch config
|
|
289
|
+
const { data: fullPattern } = await supabase
|
|
290
|
+
.from('patterns')
|
|
291
|
+
.select('content, tags')
|
|
292
|
+
.eq('pattern_id', stats.patternId)
|
|
293
|
+
.single();
|
|
294
|
+
if (fullPattern) {
|
|
295
|
+
const content = fullPattern.content || '';
|
|
296
|
+
const problemMatch = content.match(/## Problem\n([\s\S]*?)(?=\n## Solution|$)/);
|
|
297
|
+
const solutionMatch = content.match(/## Solution\n([\s\S]*?)$/);
|
|
298
|
+
promotedPatterns.push({
|
|
299
|
+
id: stats.patternId,
|
|
300
|
+
title: stats.title,
|
|
301
|
+
problem: problemMatch?.[1]?.trim() || 'N/A',
|
|
302
|
+
solution: solutionMatch?.[1]?.trim() || content,
|
|
303
|
+
promotedAt: new Date().toISOString(),
|
|
304
|
+
successRate: stats.successCount / Math.max(stats.totalApplications, 1),
|
|
305
|
+
appliedCount: stats.totalApplications,
|
|
306
|
+
tags: fullPattern.tags,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Generate patch config
|
|
312
|
+
result.patchConfig = {
|
|
313
|
+
version: '1.0.0',
|
|
314
|
+
generatedAt: new Date().toISOString(),
|
|
315
|
+
promotedPatterns,
|
|
316
|
+
totalPatterns: promotedPatterns.length,
|
|
317
|
+
};
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
321
|
+
// Patch Config Generation
|
|
322
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
323
|
+
function writePatchConfig(config, outputPath) {
|
|
324
|
+
const dir = path.dirname(outputPath);
|
|
325
|
+
if (!fs.existsSync(dir)) {
|
|
326
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
327
|
+
}
|
|
328
|
+
fs.writeFileSync(outputPath, JSON.stringify(config, null, 2));
|
|
329
|
+
console.log(`[PROMOTER] Wrote patch config to: ${outputPath}`);
|
|
330
|
+
}
|
|
331
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
332
|
+
// CLI Entry Point
|
|
333
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
334
|
+
async function main() {
|
|
335
|
+
const args = process.argv.slice(2);
|
|
336
|
+
const config = {
|
|
337
|
+
supabaseUrl: process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || '',
|
|
338
|
+
supabaseKey: process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY || '',
|
|
339
|
+
dryRun: args.includes('--dry-run'),
|
|
340
|
+
userId: args.includes('--user') ? args[args.indexOf('--user') + 1] : undefined,
|
|
341
|
+
patchConfigPath: path.join(os.homedir(), '.ekkos', 'promoted-patterns.json'),
|
|
342
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
343
|
+
};
|
|
344
|
+
// Allow custom output path
|
|
345
|
+
if (args.includes('--output')) {
|
|
346
|
+
config.patchConfigPath = args[args.indexOf('--output') + 1];
|
|
347
|
+
}
|
|
348
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
349
|
+
console.log(' ekkOS PROMETHEUS Pattern Promoter');
|
|
350
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
351
|
+
console.log(` Mode: ${config.dryRun ? 'DRY RUN (no changes)' : 'LIVE'}`);
|
|
352
|
+
console.log(` User: ${config.userId || 'All users'}`);
|
|
353
|
+
console.log(` Output: ${config.patchConfigPath}`);
|
|
354
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
355
|
+
console.log('');
|
|
356
|
+
if (!config.supabaseUrl || !config.supabaseKey) {
|
|
357
|
+
console.error('[ERROR] Missing SUPABASE_URL or SUPABASE_SECRET_KEY environment variables');
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
const supabase = (0, supabase_js_1.createClient)(config.supabaseUrl, config.supabaseKey);
|
|
361
|
+
try {
|
|
362
|
+
const result = await evaluatePromotions(supabase, config);
|
|
363
|
+
console.log('');
|
|
364
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
365
|
+
console.log(' Results');
|
|
366
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
367
|
+
console.log(` Patterns evaluated: ${result.evaluated}`);
|
|
368
|
+
console.log(` Promoted: ${result.promoted}`);
|
|
369
|
+
console.log(` Demoted: ${result.demoted}`);
|
|
370
|
+
console.log(` Constitutional total: ${result.patchConfig?.totalPatterns || 0}`);
|
|
371
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
372
|
+
// Write patch config
|
|
373
|
+
if (!config.dryRun && result.patchConfig) {
|
|
374
|
+
writePatchConfig(result.patchConfig, config.patchConfigPath);
|
|
375
|
+
}
|
|
376
|
+
else if (config.dryRun && result.patchConfig) {
|
|
377
|
+
console.log('\n[DRY RUN] Would write patch config with:');
|
|
378
|
+
console.log(` - ${result.patchConfig.totalPatterns} promoted patterns`);
|
|
379
|
+
}
|
|
380
|
+
// Summary of changes
|
|
381
|
+
if (result.patterns.filter(p => p.action !== 'unchanged').length > 0) {
|
|
382
|
+
console.log('\nChanges:');
|
|
383
|
+
for (const p of result.patterns) {
|
|
384
|
+
if (p.action === 'promoted') {
|
|
385
|
+
console.log(` 🎓 PROMOTED: "${p.title}" (score: ${p.score.toFixed(2)})`);
|
|
386
|
+
}
|
|
387
|
+
else if (p.action === 'demoted') {
|
|
388
|
+
console.log(` ⬇️ DEMOTED: "${p.title}" (${p.reason})`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
console.log('\nNo promotion changes needed.');
|
|
394
|
+
}
|
|
395
|
+
console.log('\n✓ Promotion evaluation complete');
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
console.error('[ERROR]', error);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Run if executed directly
|
|
403
|
+
main().catch(console.error);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Deploy
|
|
2
|
+
* Deploy ekkOS instructions to ~/.claude/rules/ekkos.md
|
|
3
|
+
*
|
|
4
|
+
* Uses Claude Code's user-level rules directory — auto-loaded for all projects
|
|
5
|
+
* without touching the user's existing CLAUDE.md.
|
|
3
6
|
*/
|
|
4
7
|
export declare function deployInstructions(): void;
|
|
5
8
|
/**
|
|
6
|
-
* Check if
|
|
9
|
+
* Check if ekkOS instructions are deployed
|
|
7
10
|
*/
|
|
8
11
|
export declare function isInstructionsDeployed(): boolean;
|
|
9
12
|
/**
|
|
@@ -7,21 +7,24 @@ const fs_1 = require("fs");
|
|
|
7
7
|
const platform_1 = require("../utils/platform");
|
|
8
8
|
const templates_1 = require("../utils/templates");
|
|
9
9
|
/**
|
|
10
|
-
* Deploy
|
|
10
|
+
* Deploy ekkOS instructions to ~/.claude/rules/ekkos.md
|
|
11
|
+
*
|
|
12
|
+
* Uses Claude Code's user-level rules directory — auto-loaded for all projects
|
|
13
|
+
* without touching the user's existing CLAUDE.md.
|
|
11
14
|
*/
|
|
12
15
|
function deployInstructions() {
|
|
13
|
-
// Ensure
|
|
14
|
-
if (!(0, fs_1.existsSync)(platform_1.
|
|
15
|
-
(0, fs_1.mkdirSync)(platform_1.
|
|
16
|
+
// Ensure ~/.claude/rules/ exists
|
|
17
|
+
if (!(0, fs_1.existsSync)(platform_1.CLAUDE_RULES_DIR)) {
|
|
18
|
+
(0, fs_1.mkdirSync)(platform_1.CLAUDE_RULES_DIR, { recursive: true });
|
|
16
19
|
}
|
|
17
|
-
//
|
|
18
|
-
(0, templates_1.copyTemplateFile)('CLAUDE.md', platform_1.
|
|
20
|
+
// Deploy to rules/ekkos.md (safe to overwrite — this is our file)
|
|
21
|
+
(0, templates_1.copyTemplateFile)('CLAUDE.md', platform_1.CLAUDE_EKKOS_RULES);
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
|
-
* Check if
|
|
24
|
+
* Check if ekkOS instructions are deployed
|
|
22
25
|
*/
|
|
23
26
|
function isInstructionsDeployed() {
|
|
24
|
-
return (0, fs_1.existsSync)(platform_1.
|
|
27
|
+
return (0, fs_1.existsSync)(platform_1.CLAUDE_EKKOS_RULES);
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
30
|
* Get the CLAUDE.md content (for preview)
|