@arka-labs/nemesis 1.2.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/LICENSE +201 -0
- package/README.md +668 -0
- package/lib/core/agent-launcher.js +193 -0
- package/lib/core/audit.js +210 -0
- package/lib/core/connexions.js +80 -0
- package/lib/core/flowmap/api.js +111 -0
- package/lib/core/flowmap/cli-helpers.js +80 -0
- package/lib/core/flowmap/machine.js +281 -0
- package/lib/core/flowmap/persistence.js +83 -0
- package/lib/core/generators.js +183 -0
- package/lib/core/inbox.js +275 -0
- package/lib/core/logger.js +20 -0
- package/lib/core/mission.js +109 -0
- package/lib/core/notewriter/config.js +36 -0
- package/lib/core/notewriter/cr.js +237 -0
- package/lib/core/notewriter/log.js +112 -0
- package/lib/core/notewriter/notes.js +168 -0
- package/lib/core/notewriter/paths.js +45 -0
- package/lib/core/notewriter/reader.js +121 -0
- package/lib/core/notewriter/registry.js +80 -0
- package/lib/core/odm.js +191 -0
- package/lib/core/profile-picker.js +323 -0
- package/lib/core/project.js +287 -0
- package/lib/core/registry.js +129 -0
- package/lib/core/secrets.js +137 -0
- package/lib/core/services.js +45 -0
- package/lib/core/team.js +287 -0
- package/lib/core/templates.js +80 -0
- package/lib/kairos/agent-runner.js +261 -0
- package/lib/kairos/claude-invoker.js +90 -0
- package/lib/kairos/context-injector.js +331 -0
- package/lib/kairos/context-loader.js +108 -0
- package/lib/kairos/context-writer.js +45 -0
- package/lib/kairos/dispatcher-router.js +173 -0
- package/lib/kairos/dispatcher.js +139 -0
- package/lib/kairos/event-bus.js +287 -0
- package/lib/kairos/event-router.js +131 -0
- package/lib/kairos/flowmap-bridge.js +120 -0
- package/lib/kairos/hook-handlers.js +351 -0
- package/lib/kairos/hook-installer.js +207 -0
- package/lib/kairos/hook-prompts.js +54 -0
- package/lib/kairos/leader-rules.js +94 -0
- package/lib/kairos/pid-checker.js +108 -0
- package/lib/kairos/situation-detector.js +123 -0
- package/lib/sync/fallback-engine.js +97 -0
- package/lib/sync/hcm-client.js +170 -0
- package/lib/sync/health.js +47 -0
- package/lib/sync/llm-client.js +387 -0
- package/lib/sync/nemesis-client.js +379 -0
- package/lib/sync/service-session.js +74 -0
- package/lib/sync/sync-engine.js +178 -0
- package/lib/ui/box.js +104 -0
- package/lib/ui/brand.js +42 -0
- package/lib/ui/colors.js +57 -0
- package/lib/ui/dashboard.js +580 -0
- package/lib/ui/error-hints.js +49 -0
- package/lib/ui/format.js +61 -0
- package/lib/ui/menu.js +306 -0
- package/lib/ui/note-card.js +198 -0
- package/lib/ui/note-colors.js +26 -0
- package/lib/ui/note-detail.js +297 -0
- package/lib/ui/note-filters.js +252 -0
- package/lib/ui/note-views.js +283 -0
- package/lib/ui/prompt.js +81 -0
- package/lib/ui/spinner.js +139 -0
- package/lib/ui/streambox.js +46 -0
- package/lib/ui/table.js +42 -0
- package/lib/ui/tree.js +33 -0
- package/package.json +53 -0
- package/src/cli.js +457 -0
- package/src/commands/_helpers.js +119 -0
- package/src/commands/audit.js +187 -0
- package/src/commands/auth.js +316 -0
- package/src/commands/doctor.js +243 -0
- package/src/commands/hcm.js +147 -0
- package/src/commands/inbox.js +333 -0
- package/src/commands/init.js +160 -0
- package/src/commands/kairos.js +216 -0
- package/src/commands/kars.js +134 -0
- package/src/commands/mission.js +275 -0
- package/src/commands/notes.js +316 -0
- package/src/commands/notewriter.js +296 -0
- package/src/commands/odm.js +329 -0
- package/src/commands/orch.js +68 -0
- package/src/commands/project.js +123 -0
- package/src/commands/run.js +123 -0
- package/src/commands/services.js +705 -0
- package/src/commands/status.js +231 -0
- package/src/commands/team.js +572 -0
- package/src/config.js +84 -0
- package/src/index.js +5 -0
- package/templates/project-context.json +10 -0
- package/templates/template_CONTRIB-NAME.json +22 -0
- package/templates/template_CR-ODM-NAME-000.exemple.json +32 -0
- package/templates/template_DEC-NAME-000.json +18 -0
- package/templates/template_INTV-NAME-000.json +15 -0
- package/templates/template_MISSION_CONTRACT.json +46 -0
- package/templates/template_ODM-NAME-000.json +89 -0
- package/templates/template_REGISTRY-PROJECT.json +26 -0
- package/templates/template_TXN-NAME-000.json +24 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { getProjectStatus } from '../../lib/core/project.js';
|
|
2
|
+
import { readRegistry, getLanes } from '../../lib/core/registry.js';
|
|
3
|
+
import { readNwRegistry } from '../../lib/core/notewriter/registry.js';
|
|
4
|
+
import { existsSync, statSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { style } from '../../lib/ui/colors.js';
|
|
7
|
+
import { interactiveMenu } from '../../lib/ui/menu.js';
|
|
8
|
+
import { ensureProject } from './_helpers.js';
|
|
9
|
+
import { getFlowmapSummary } from '../../lib/core/flowmap/cli-helpers.js';
|
|
10
|
+
|
|
11
|
+
const HELP = `
|
|
12
|
+
nemesis status — Raccourci project status + alertes
|
|
13
|
+
|
|
14
|
+
Usage: nemesis status
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
-h, --help Aide
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
export async function handler({ args, flags, config }) {
|
|
21
|
+
if (flags.help || args.includes('--help') || args.includes('-h')) {
|
|
22
|
+
console.log(HELP);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const project = await ensureProject(config, flags);
|
|
27
|
+
if (!project) return;
|
|
28
|
+
|
|
29
|
+
const status = getProjectStatus(project.hcm_dir);
|
|
30
|
+
const registry = readRegistry(project.hcm_dir);
|
|
31
|
+
const lanes = registry ? getLanes(registry) : [];
|
|
32
|
+
|
|
33
|
+
const odmCount = status.odms.length;
|
|
34
|
+
const missionCount = status.contracts.length;
|
|
35
|
+
const pendingOdms = status.odms.filter(o =>
|
|
36
|
+
o.status === 'EN_COURS' || o.status === 'REDIGE' || o.status === 'VALIDE'
|
|
37
|
+
).length;
|
|
38
|
+
|
|
39
|
+
console.log(`\n ${style.bold(project.id)} \u2014 ${lanes.length} agent(s), ${odmCount} OdM, ${pendingOdms} en cours\n`);
|
|
40
|
+
|
|
41
|
+
// Fresh project — quickstart guide
|
|
42
|
+
if (odmCount === 0 && lanes.length === 0 && missionCount === 0) {
|
|
43
|
+
console.log(` ${style.bold('Quickstart')} :`);
|
|
44
|
+
console.log(` 1. ${style.dim('nemesis team add')} \u2192 Onboard un agent`);
|
|
45
|
+
console.log(` 2. ${style.dim('nemesis mission init')} \u2192 Creer un Mission Contract`);
|
|
46
|
+
console.log(` 3. ${style.dim('nemesis odm init')} \u2192 Creer un OdM`);
|
|
47
|
+
console.log(` 4. ${style.dim('nemesis doctor')} \u2192 Verifier la configuration`);
|
|
48
|
+
console.log('');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// FLOWMAP section
|
|
53
|
+
const flowmapEntries = getFlowmapSummary(config.cwd);
|
|
54
|
+
if (flowmapEntries.length > 0) {
|
|
55
|
+
console.log(` ${style.bold('FLOWMAP')} :`);
|
|
56
|
+
for (const entry of flowmapEntries) {
|
|
57
|
+
console.log(` \u25B6 ${entry.state} [${entry.phase}] ${entry.label} \u2014 ${entry.missionId}`);
|
|
58
|
+
}
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// System alerts
|
|
63
|
+
const alerts = generateAlerts(status, lanes, project, config.cwd);
|
|
64
|
+
|
|
65
|
+
// L4+ note alerts (decisions, pivots)
|
|
66
|
+
const noteAlerts = generateNoteAlerts(lanes, project);
|
|
67
|
+
|
|
68
|
+
if (alerts.length > 0 || noteAlerts.length > 0) {
|
|
69
|
+
for (const alert of alerts) {
|
|
70
|
+
console.log(` ${style.yellow('\u26A0')} ${alert}`);
|
|
71
|
+
}
|
|
72
|
+
for (const na of noteAlerts) {
|
|
73
|
+
const icon = na.level === 'L5' ? style.red('\u2605') : style.yellow('\u2605');
|
|
74
|
+
const agentShort = (na.agentId || '').replace(/^Agent_/i, '');
|
|
75
|
+
const time = formatAlertTime(na.timestamp);
|
|
76
|
+
console.log(` ${icon} ${style.bold(`[${na.level}]`)} ${agentShort} \u2014 ${na.summary || na.id} ${style.dim(time)}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Offer ack if note alerts exist
|
|
80
|
+
if (noteAlerts.length > 0) {
|
|
81
|
+
console.log('');
|
|
82
|
+
const action = await interactiveMenu([
|
|
83
|
+
{ label: 'Continuer', value: 'continue', description: '' },
|
|
84
|
+
{ label: `Acquitter les ${noteAlerts.length} alerte(s) note`, value: 'ack', description: 'Supprime de la vue status' },
|
|
85
|
+
], { title: '' });
|
|
86
|
+
|
|
87
|
+
if (action === 'ack') {
|
|
88
|
+
ackNoteAlerts(noteAlerts, project);
|
|
89
|
+
console.log(` ${style.green('\u2713')} ${noteAlerts.length} alerte(s) acquittee(s).`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
console.log(` ${style.green('Aucune alerte.')}`);
|
|
94
|
+
}
|
|
95
|
+
console.log('');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function generateAlerts(status, lanes, project, root) {
|
|
99
|
+
const alerts = [];
|
|
100
|
+
|
|
101
|
+
// Empty team
|
|
102
|
+
if (lanes.length === 0) {
|
|
103
|
+
alerts.push(`Equipe vide \u2014 ajoutez un agent : ${style.dim('nemesis team add')}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// No missions
|
|
107
|
+
if (status.contracts.length === 0) {
|
|
108
|
+
alerts.push(`Aucun Mission Contract \u2014 creez-en un : ${style.dim('nemesis mission init')}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// OdMs without CRs
|
|
112
|
+
for (const odm of status.odms) {
|
|
113
|
+
if (odm.status === 'EN_COURS') {
|
|
114
|
+
const hasCR = status.crs.some(cr => cr.id.includes(odm.id.replace('ODM-', '')));
|
|
115
|
+
if (!hasCR) {
|
|
116
|
+
alerts.push(`${odm.id} en cours mais aucun CR depose`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Agents without recent activity
|
|
122
|
+
for (const lane of lanes) {
|
|
123
|
+
const memoryPath = lane.memory_path;
|
|
124
|
+
if (memoryPath) {
|
|
125
|
+
const fullPath = join(project.root, memoryPath);
|
|
126
|
+
if (existsSync(fullPath)) {
|
|
127
|
+
const stat = statSync(fullPath);
|
|
128
|
+
const daysSince = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24);
|
|
129
|
+
if (daysSince > 3) {
|
|
130
|
+
alerts.push(`${lane.name || lane.id} n'a pas mis a jour sa memoire depuis ${Math.floor(daysSince)} jours`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// OdMs with DRAFT status not assigned
|
|
137
|
+
for (const odm of status.odms) {
|
|
138
|
+
if (odm.status === 'DRAFT' && !odm.assignee) {
|
|
139
|
+
alerts.push(`${odm.id} en DRAFT mais non assigne`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// FLOWMAP alerts
|
|
144
|
+
if (root) {
|
|
145
|
+
const flowmapEntries = getFlowmapSummary(root);
|
|
146
|
+
for (const entry of flowmapEntries) {
|
|
147
|
+
if (entry.state.startsWith('FX_')) {
|
|
148
|
+
alerts.push(`Mission ${entry.missionId} en exception (${entry.state.replace('_', '-')})`);
|
|
149
|
+
}
|
|
150
|
+
if (entry.state === 'FIN') {
|
|
151
|
+
alerts.push(style.green(`Mission ${entry.missionId} terminee`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return alerts;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- L4+ Note alerts ---
|
|
160
|
+
|
|
161
|
+
function getAckPath(project) {
|
|
162
|
+
return join(project.hcm_dir, 'project', 'alerts-ack.json');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function readAckedIds(project) {
|
|
166
|
+
const path = getAckPath(project);
|
|
167
|
+
if (!existsSync(path)) return new Set();
|
|
168
|
+
try {
|
|
169
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
170
|
+
return new Set(Array.isArray(data) ? data : []);
|
|
171
|
+
} catch {
|
|
172
|
+
return new Set();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function writeAckedIds(ids, project) {
|
|
177
|
+
const path = getAckPath(project);
|
|
178
|
+
writeFileSync(path, JSON.stringify([...ids], null, 2) + '\n', 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function generateNoteAlerts(lanes, project) {
|
|
182
|
+
const acked = readAckedIds(project);
|
|
183
|
+
const alerts = [];
|
|
184
|
+
|
|
185
|
+
for (const lane of lanes) {
|
|
186
|
+
const agentName = lane.name || lane.id;
|
|
187
|
+
try {
|
|
188
|
+
const reg = readNwRegistry(project.root, agentName);
|
|
189
|
+
for (const noteEntry of reg.notes) {
|
|
190
|
+
if ((noteEntry.level === 'L4' || noteEntry.level === 'L5') && !acked.has(noteEntry.id)) {
|
|
191
|
+
alerts.push({
|
|
192
|
+
id: noteEntry.id,
|
|
193
|
+
level: noteEntry.level,
|
|
194
|
+
agentId: agentName,
|
|
195
|
+
timestamp: noteEntry.timestamp,
|
|
196
|
+
tags: noteEntry.tags || [],
|
|
197
|
+
summary: noteEntry.tags?.length > 0
|
|
198
|
+
? noteEntry.tags.join(', ')
|
|
199
|
+
: null,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch { /* agent without notes */ }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Sort by timestamp desc (most recent first)
|
|
207
|
+
alerts.sort((a, b) => (b.timestamp || '').localeCompare(a.timestamp || ''));
|
|
208
|
+
|
|
209
|
+
return alerts;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function ackNoteAlerts(noteAlerts, project) {
|
|
213
|
+
const acked = readAckedIds(project);
|
|
214
|
+
for (const na of noteAlerts) {
|
|
215
|
+
acked.add(na.id);
|
|
216
|
+
}
|
|
217
|
+
writeAckedIds(acked, project);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function formatAlertTime(isoString) {
|
|
221
|
+
if (!isoString) return '';
|
|
222
|
+
const d = new Date(isoString);
|
|
223
|
+
const now = new Date();
|
|
224
|
+
const diffMs = now - d;
|
|
225
|
+
const diffMin = Math.floor(diffMs / 60000);
|
|
226
|
+
if (diffMin < 60) return `il y a ${diffMin}min`;
|
|
227
|
+
const diffH = Math.floor(diffMin / 60);
|
|
228
|
+
if (diffH < 24) return `il y a ${diffH}h`;
|
|
229
|
+
const diffD = Math.floor(diffH / 24);
|
|
230
|
+
return `il y a ${diffD}j`;
|
|
231
|
+
}
|