@qlucent/fishi-core 0.4.0 → 0.6.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/index.d.ts +48 -1
- package/dist/index.js +629 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -345,6 +345,16 @@ declare function getLearningsManagerScript(): string;
|
|
|
345
345
|
*/
|
|
346
346
|
declare function getDocCheckerScript(): string;
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Monitor Emitter Script
|
|
350
|
+
*
|
|
351
|
+
* Shared helper that other hooks can import to emit agent observability events.
|
|
352
|
+
* Also callable directly via CLI for one-off event emission.
|
|
353
|
+
*
|
|
354
|
+
* Zero dependencies — uses only Node.js built-ins.
|
|
355
|
+
*/
|
|
356
|
+
declare function getMonitorEmitterScript(): string;
|
|
357
|
+
|
|
348
358
|
declare function getInitCommand(): string;
|
|
349
359
|
|
|
350
360
|
declare function getStatusCommand(): string;
|
|
@@ -523,4 +533,41 @@ interface McpJson {
|
|
|
523
533
|
declare function mergeMcpJson(existing: McpJson, fishi: McpJson): McpJson;
|
|
524
534
|
declare function mergeGitignore(existing: string, fishiAdditions: string): string;
|
|
525
535
|
|
|
526
|
-
|
|
536
|
+
interface MonitorEvent {
|
|
537
|
+
type: string;
|
|
538
|
+
agent: string;
|
|
539
|
+
data: Record<string, unknown>;
|
|
540
|
+
timestamp?: string;
|
|
541
|
+
}
|
|
542
|
+
interface MonitorSummary {
|
|
543
|
+
totalAgentCompletions: number;
|
|
544
|
+
totalFilesChanged: number;
|
|
545
|
+
totalTokens: number;
|
|
546
|
+
tokensByModel: Record<string, number>;
|
|
547
|
+
tokensByAgent: Record<string, number>;
|
|
548
|
+
toolsUsed: Record<string, number>;
|
|
549
|
+
dynamicAgentsCreated: number;
|
|
550
|
+
}
|
|
551
|
+
interface DynamicAgent {
|
|
552
|
+
name: string;
|
|
553
|
+
coordinator: string;
|
|
554
|
+
}
|
|
555
|
+
interface MonitorState {
|
|
556
|
+
events: (MonitorEvent & {
|
|
557
|
+
timestamp: string;
|
|
558
|
+
})[];
|
|
559
|
+
summary: MonitorSummary;
|
|
560
|
+
dynamicAgents: DynamicAgent[];
|
|
561
|
+
lastUpdated: string;
|
|
562
|
+
}
|
|
563
|
+
declare function readMonitorState(projectDir: string): MonitorState;
|
|
564
|
+
declare function emitEvent(projectDir: string, event: MonitorEvent): void;
|
|
565
|
+
declare function getAgentSummary(projectDir: string): Record<string, {
|
|
566
|
+
completions: number;
|
|
567
|
+
failures: number;
|
|
568
|
+
filesChanged: number;
|
|
569
|
+
}>;
|
|
570
|
+
|
|
571
|
+
declare function getDashboardHtml(): string;
|
|
572
|
+
|
|
573
|
+
export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DetectionCheck, type DetectionResult, type DynamicAgent, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type MonitorEvent, type MonitorState, type MonitorSummary, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, createBackup, detectConflicts, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, emitEvent, frontendAgentTemplate, fullstackAgentTemplate, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getAgentSummary, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDashboardHtml, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, researchAgentTemplate, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
|
package/dist/index.js
CHANGED
|
@@ -8217,6 +8217,183 @@ try {
|
|
|
8217
8217
|
`;
|
|
8218
8218
|
}
|
|
8219
8219
|
|
|
8220
|
+
// src/templates/hooks/monitor-emitter.ts
|
|
8221
|
+
function getMonitorEmitterScript() {
|
|
8222
|
+
return `#!/usr/bin/env node
|
|
8223
|
+
// monitor-emitter.mjs \u2014 FISHI agent observability event emitter
|
|
8224
|
+
//
|
|
8225
|
+
// Usage as a library (ESM import in another .mjs hook):
|
|
8226
|
+
// const { emitMonitorEvent } = await import('./.fishi/scripts/monitor-emitter.mjs');
|
|
8227
|
+
// await emitMonitorEvent(process.cwd(), { type: 'agent.completed', agent: 'backend-agent', data: { filesChanged: 3 } });
|
|
8228
|
+
//
|
|
8229
|
+
// Usage via CLI:
|
|
8230
|
+
// node .fishi/scripts/monitor-emitter.mjs '{"type":"agent.completed","agent":"backend-agent","data":{"filesChanged":3}}'
|
|
8231
|
+
|
|
8232
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
8233
|
+
import { join, dirname } from 'path';
|
|
8234
|
+
|
|
8235
|
+
// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8236
|
+
|
|
8237
|
+
const MAX_EVENTS = 500;
|
|
8238
|
+
|
|
8239
|
+
// \u2500\u2500 State helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8240
|
+
|
|
8241
|
+
function monitorPath(root) {
|
|
8242
|
+
return join(root, '.fishi', 'state', 'monitor.json');
|
|
8243
|
+
}
|
|
8244
|
+
|
|
8245
|
+
function emptySummary() {
|
|
8246
|
+
return {
|
|
8247
|
+
totalAgentCompletions: 0,
|
|
8248
|
+
totalFilesChanged: 0,
|
|
8249
|
+
totalTokens: 0,
|
|
8250
|
+
tokensByModel: {},
|
|
8251
|
+
tokensByAgent: {},
|
|
8252
|
+
toolsUsed: {},
|
|
8253
|
+
dynamicAgentsCreated: 0,
|
|
8254
|
+
};
|
|
8255
|
+
}
|
|
8256
|
+
|
|
8257
|
+
function emptyState() {
|
|
8258
|
+
return {
|
|
8259
|
+
events: [],
|
|
8260
|
+
summary: emptySummary(),
|
|
8261
|
+
dynamicAgents: [],
|
|
8262
|
+
lastUpdated: new Date().toISOString(),
|
|
8263
|
+
};
|
|
8264
|
+
}
|
|
8265
|
+
|
|
8266
|
+
function readState(root) {
|
|
8267
|
+
const p = monitorPath(root);
|
|
8268
|
+
if (!existsSync(p)) return emptyState();
|
|
8269
|
+
try {
|
|
8270
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
8271
|
+
} catch {
|
|
8272
|
+
return emptyState();
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
|
|
8276
|
+
function writeState(root, state) {
|
|
8277
|
+
const p = monitorPath(root);
|
|
8278
|
+
const dir = dirname(p);
|
|
8279
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
8280
|
+
writeFileSync(p, JSON.stringify(state, null, 2) + '\\n', 'utf-8');
|
|
8281
|
+
}
|
|
8282
|
+
|
|
8283
|
+
// \u2500\u2500 Summary updater \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8284
|
+
|
|
8285
|
+
function updateSummary(state, event) {
|
|
8286
|
+
switch (event.type) {
|
|
8287
|
+
case 'agent.completed': {
|
|
8288
|
+
state.summary.totalAgentCompletions++;
|
|
8289
|
+
const files = (event.data && typeof event.data.filesChanged === 'number')
|
|
8290
|
+
? event.data.filesChanged
|
|
8291
|
+
: 0;
|
|
8292
|
+
state.summary.totalFilesChanged += files;
|
|
8293
|
+
break;
|
|
8294
|
+
}
|
|
8295
|
+
case 'tokens.used': {
|
|
8296
|
+
const input = (event.data && typeof event.data.inputTokens === 'number')
|
|
8297
|
+
? event.data.inputTokens
|
|
8298
|
+
: 0;
|
|
8299
|
+
const output = (event.data && typeof event.data.outputTokens === 'number')
|
|
8300
|
+
? event.data.outputTokens
|
|
8301
|
+
: 0;
|
|
8302
|
+
const total = input + output;
|
|
8303
|
+
const model = (event.data && event.data.model) || 'unknown';
|
|
8304
|
+
state.summary.totalTokens += total;
|
|
8305
|
+
state.summary.tokensByModel[model] = (state.summary.tokensByModel[model] || 0) + total;
|
|
8306
|
+
state.summary.tokensByAgent[event.agent] =
|
|
8307
|
+
(state.summary.tokensByAgent[event.agent] || 0) + total;
|
|
8308
|
+
break;
|
|
8309
|
+
}
|
|
8310
|
+
case 'tool.used': {
|
|
8311
|
+
const tool = event.data && event.data.tool;
|
|
8312
|
+
if (tool) {
|
|
8313
|
+
state.summary.toolsUsed[tool] = (state.summary.toolsUsed[tool] || 0) + 1;
|
|
8314
|
+
}
|
|
8315
|
+
break;
|
|
8316
|
+
}
|
|
8317
|
+
case 'agent.created': {
|
|
8318
|
+
if (event.data && event.data.dynamic) {
|
|
8319
|
+
state.summary.dynamicAgentsCreated++;
|
|
8320
|
+
state.dynamicAgents.push({
|
|
8321
|
+
name: event.agent,
|
|
8322
|
+
coordinator: (event.data.coordinator) || 'unknown',
|
|
8323
|
+
});
|
|
8324
|
+
}
|
|
8325
|
+
break;
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
|
|
8330
|
+
// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8331
|
+
|
|
8332
|
+
/**
|
|
8333
|
+
* Emit a monitor event.
|
|
8334
|
+
*
|
|
8335
|
+
* @param {string} root - Project root directory (where .fishi/ lives)
|
|
8336
|
+
* @param {{ type: string, agent: string, data: object, timestamp?: string }} event
|
|
8337
|
+
*/
|
|
8338
|
+
export function emitMonitorEvent(root, event) {
|
|
8339
|
+
const state = readState(root);
|
|
8340
|
+
|
|
8341
|
+
const timestamped = {
|
|
8342
|
+
...event,
|
|
8343
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
8344
|
+
};
|
|
8345
|
+
|
|
8346
|
+
state.events.push(timestamped);
|
|
8347
|
+
if (state.events.length > MAX_EVENTS) {
|
|
8348
|
+
state.events = state.events.slice(-MAX_EVENTS);
|
|
8349
|
+
}
|
|
8350
|
+
|
|
8351
|
+
updateSummary(state, event);
|
|
8352
|
+
|
|
8353
|
+
state.lastUpdated = new Date().toISOString();
|
|
8354
|
+
writeState(root, state);
|
|
8355
|
+
}
|
|
8356
|
+
|
|
8357
|
+
// \u2500\u2500 CLI entry point \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8358
|
+
|
|
8359
|
+
const isMain = process.argv[1] &&
|
|
8360
|
+
(process.argv[1].endsWith('monitor-emitter.mjs') || process.argv[1].endsWith('monitor-emitter'));
|
|
8361
|
+
|
|
8362
|
+
if (isMain) {
|
|
8363
|
+
const raw = process.argv[2];
|
|
8364
|
+
if (!raw) {
|
|
8365
|
+
console.error('[FISHI] Usage: node .fishi/scripts/monitor-emitter.mjs '{"type":"...","agent":"...","data":{...}}'');
|
|
8366
|
+
process.exit(1);
|
|
8367
|
+
}
|
|
8368
|
+
|
|
8369
|
+
let event;
|
|
8370
|
+
try {
|
|
8371
|
+
event = JSON.parse(raw);
|
|
8372
|
+
} catch (err) {
|
|
8373
|
+
console.error('[FISHI] monitor-emitter: invalid JSON:', err.message);
|
|
8374
|
+
process.exit(1);
|
|
8375
|
+
}
|
|
8376
|
+
|
|
8377
|
+
if (!event.type || !event.agent) {
|
|
8378
|
+
console.error('[FISHI] monitor-emitter: event must have "type" and "agent" fields');
|
|
8379
|
+
process.exit(1);
|
|
8380
|
+
}
|
|
8381
|
+
|
|
8382
|
+
if (!event.data || typeof event.data !== 'object') {
|
|
8383
|
+
event.data = {};
|
|
8384
|
+
}
|
|
8385
|
+
|
|
8386
|
+
try {
|
|
8387
|
+
emitMonitorEvent(process.cwd(), event);
|
|
8388
|
+
console.log('[FISHI] monitor event emitted:', event.type, '/', event.agent);
|
|
8389
|
+
} catch (err) {
|
|
8390
|
+
console.error('[FISHI] monitor-emitter error:', err.message);
|
|
8391
|
+
process.exit(1);
|
|
8392
|
+
}
|
|
8393
|
+
}
|
|
8394
|
+
`;
|
|
8395
|
+
}
|
|
8396
|
+
|
|
8220
8397
|
// src/templates/commands/init-command.ts
|
|
8221
8398
|
function getInitCommand() {
|
|
8222
8399
|
return `# /fishi-init \u2014 Launch the FISHI Orchestration Pipeline
|
|
@@ -10585,7 +10762,8 @@ async function generateScaffold(targetDir, options) {
|
|
|
10585
10762
|
await write(".fishi/scripts/memory-manager.mjs", getMemoryManagerScript());
|
|
10586
10763
|
await write(".fishi/scripts/learnings-manager.mjs", getLearningsManagerScript());
|
|
10587
10764
|
await write(".fishi/scripts/doc-checker.mjs", getDocCheckerScript());
|
|
10588
|
-
|
|
10765
|
+
await write(".fishi/scripts/monitor-emitter.mjs", getMonitorEmitterScript());
|
|
10766
|
+
const hookCount = 16;
|
|
10589
10767
|
const todoTemplate = (name) => `# TODO \u2014 ${name}
|
|
10590
10768
|
|
|
10591
10769
|
## Active
|
|
@@ -10670,6 +10848,12 @@ async function generateScaffold(targetDir, options) {
|
|
|
10670
10848
|
await write(".fishi/state/agent-registry.yaml", getAgentRegistryTemplate());
|
|
10671
10849
|
await write(".fishi/state/task-graph.yaml", "tasks: []\ndependencies: []\n");
|
|
10672
10850
|
await write(".fishi/state/gates.yaml", "gates: []\n");
|
|
10851
|
+
await write(".fishi/state/monitor.json", JSON.stringify({
|
|
10852
|
+
events: [],
|
|
10853
|
+
summary: { totalAgentCompletions: 0, totalFilesChanged: 0, totalTokens: 0, tokensByModel: {}, tokensByAgent: {}, toolsUsed: {}, dynamicAgentsCreated: 0 },
|
|
10854
|
+
dynamicAgents: [],
|
|
10855
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
10856
|
+
}, null, 2) + "\n");
|
|
10673
10857
|
await write(".fishi/mcp-registry.yaml", getMcpRegistryTemplate());
|
|
10674
10858
|
await write(".fishi/model-routing.md", getModelRoutingReference());
|
|
10675
10859
|
await write(".fishi/memory/project-context.md", getProjectContextTemplate(ctx));
|
|
@@ -10927,7 +11111,7 @@ async function createBackup(targetDir, conflictingFiles) {
|
|
|
10927
11111
|
manifestFiles.push({ path: relPath, size: stat.size });
|
|
10928
11112
|
}
|
|
10929
11113
|
}
|
|
10930
|
-
const fishiVersion = "0.
|
|
11114
|
+
const fishiVersion = "0.6.0";
|
|
10931
11115
|
const manifest = {
|
|
10932
11116
|
timestamp: now.toISOString(),
|
|
10933
11117
|
fishi_version: fishiVersion,
|
|
@@ -10939,6 +11123,444 @@ async function createBackup(targetDir, conflictingFiles) {
|
|
|
10939
11123
|
await rename(tmpPath, manifestPath);
|
|
10940
11124
|
return backupDir;
|
|
10941
11125
|
}
|
|
11126
|
+
|
|
11127
|
+
// src/generators/monitor.ts
|
|
11128
|
+
import { existsSync as existsSync4, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
11129
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
11130
|
+
var MAX_EVENTS = 500;
|
|
11131
|
+
function monitorPath(projectDir) {
|
|
11132
|
+
return join4(projectDir, ".fishi", "state", "monitor.json");
|
|
11133
|
+
}
|
|
11134
|
+
function emptySummary() {
|
|
11135
|
+
return {
|
|
11136
|
+
totalAgentCompletions: 0,
|
|
11137
|
+
totalFilesChanged: 0,
|
|
11138
|
+
totalTokens: 0,
|
|
11139
|
+
tokensByModel: {},
|
|
11140
|
+
tokensByAgent: {},
|
|
11141
|
+
toolsUsed: {},
|
|
11142
|
+
dynamicAgentsCreated: 0
|
|
11143
|
+
};
|
|
11144
|
+
}
|
|
11145
|
+
function emptyState() {
|
|
11146
|
+
return {
|
|
11147
|
+
events: [],
|
|
11148
|
+
summary: emptySummary(),
|
|
11149
|
+
dynamicAgents: [],
|
|
11150
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
11151
|
+
};
|
|
11152
|
+
}
|
|
11153
|
+
function readMonitorState(projectDir) {
|
|
11154
|
+
const p = monitorPath(projectDir);
|
|
11155
|
+
if (!existsSync4(p)) return emptyState();
|
|
11156
|
+
try {
|
|
11157
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
11158
|
+
} catch {
|
|
11159
|
+
return emptyState();
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
11162
|
+
function emitEvent(projectDir, event) {
|
|
11163
|
+
const state = readMonitorState(projectDir);
|
|
11164
|
+
const timestamped = { ...event, timestamp: event.timestamp || (/* @__PURE__ */ new Date()).toISOString() };
|
|
11165
|
+
state.events.push(timestamped);
|
|
11166
|
+
if (state.events.length > MAX_EVENTS) {
|
|
11167
|
+
state.events = state.events.slice(-MAX_EVENTS);
|
|
11168
|
+
}
|
|
11169
|
+
switch (event.type) {
|
|
11170
|
+
case "agent.completed": {
|
|
11171
|
+
state.summary.totalAgentCompletions++;
|
|
11172
|
+
const files = event.data.filesChanged || 0;
|
|
11173
|
+
state.summary.totalFilesChanged += files;
|
|
11174
|
+
break;
|
|
11175
|
+
}
|
|
11176
|
+
case "tokens.used": {
|
|
11177
|
+
const input = event.data.inputTokens || 0;
|
|
11178
|
+
const output = event.data.outputTokens || 0;
|
|
11179
|
+
const total = input + output;
|
|
11180
|
+
const model = event.data.model || "unknown";
|
|
11181
|
+
state.summary.totalTokens += total;
|
|
11182
|
+
state.summary.tokensByModel[model] = (state.summary.tokensByModel[model] || 0) + total;
|
|
11183
|
+
state.summary.tokensByAgent[event.agent] = (state.summary.tokensByAgent[event.agent] || 0) + total;
|
|
11184
|
+
break;
|
|
11185
|
+
}
|
|
11186
|
+
case "tool.used": {
|
|
11187
|
+
const tool = event.data.tool;
|
|
11188
|
+
state.summary.toolsUsed[tool] = (state.summary.toolsUsed[tool] || 0) + 1;
|
|
11189
|
+
break;
|
|
11190
|
+
}
|
|
11191
|
+
case "agent.created": {
|
|
11192
|
+
if (event.data.dynamic) {
|
|
11193
|
+
state.summary.dynamicAgentsCreated++;
|
|
11194
|
+
state.dynamicAgents.push({
|
|
11195
|
+
name: event.agent,
|
|
11196
|
+
coordinator: event.data.coordinator || "unknown"
|
|
11197
|
+
});
|
|
11198
|
+
}
|
|
11199
|
+
break;
|
|
11200
|
+
}
|
|
11201
|
+
}
|
|
11202
|
+
state.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
11203
|
+
const dir = dirname3(monitorPath(projectDir));
|
|
11204
|
+
if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
|
|
11205
|
+
writeFileSync(monitorPath(projectDir), JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
11206
|
+
}
|
|
11207
|
+
function getAgentSummary(projectDir) {
|
|
11208
|
+
const state = readMonitorState(projectDir);
|
|
11209
|
+
const agents = {};
|
|
11210
|
+
for (const event of state.events) {
|
|
11211
|
+
if (event.type === "agent.completed") {
|
|
11212
|
+
if (!agents[event.agent]) agents[event.agent] = { completions: 0, failures: 0, filesChanged: 0 };
|
|
11213
|
+
agents[event.agent].completions++;
|
|
11214
|
+
if (event.data.status === "failed") agents[event.agent].failures++;
|
|
11215
|
+
agents[event.agent].filesChanged += event.data.filesChanged || 0;
|
|
11216
|
+
}
|
|
11217
|
+
}
|
|
11218
|
+
return agents;
|
|
11219
|
+
}
|
|
11220
|
+
|
|
11221
|
+
// src/templates/dashboard/index-html.ts
|
|
11222
|
+
function getDashboardHtml() {
|
|
11223
|
+
return `<!DOCTYPE html>
|
|
11224
|
+
<html lang="en">
|
|
11225
|
+
<head>
|
|
11226
|
+
<meta charset="UTF-8" />
|
|
11227
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
11228
|
+
<title>FISHI Agent Dashboard</title>
|
|
11229
|
+
<style>
|
|
11230
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
11231
|
+
|
|
11232
|
+
body {
|
|
11233
|
+
background: #0a0a0f;
|
|
11234
|
+
color: #e2e8f0;
|
|
11235
|
+
font-family: 'Courier New', Courier, monospace;
|
|
11236
|
+
font-size: 14px;
|
|
11237
|
+
min-height: 100vh;
|
|
11238
|
+
}
|
|
11239
|
+
|
|
11240
|
+
/* \u2500\u2500 Header \u2500\u2500 */
|
|
11241
|
+
header {
|
|
11242
|
+
display: flex;
|
|
11243
|
+
align-items: center;
|
|
11244
|
+
justify-content: space-between;
|
|
11245
|
+
padding: 14px 24px;
|
|
11246
|
+
background: #12121a;
|
|
11247
|
+
border-bottom: 1px solid #1e1e2e;
|
|
11248
|
+
}
|
|
11249
|
+
header h1 { font-size: 18px; color: #06b6d4; letter-spacing: 0.05em; }
|
|
11250
|
+
.status-dot {
|
|
11251
|
+
width: 10px; height: 10px;
|
|
11252
|
+
border-radius: 50%;
|
|
11253
|
+
background: #22c55e;
|
|
11254
|
+
display: inline-block;
|
|
11255
|
+
margin-right: 8px;
|
|
11256
|
+
animation: pulse 2s infinite;
|
|
11257
|
+
}
|
|
11258
|
+
.status-dot.offline { background: #ef4444; animation: none; }
|
|
11259
|
+
@keyframes pulse {
|
|
11260
|
+
0%, 100% { opacity: 1; }
|
|
11261
|
+
50% { opacity: 0.4; }
|
|
11262
|
+
}
|
|
11263
|
+
.last-updated { color: #6b7280; font-size: 12px; }
|
|
11264
|
+
|
|
11265
|
+
/* \u2500\u2500 Layout \u2500\u2500 */
|
|
11266
|
+
main { padding: 20px 24px; }
|
|
11267
|
+
|
|
11268
|
+
/* \u2500\u2500 Stat cards \u2500\u2500 */
|
|
11269
|
+
.stat-grid {
|
|
11270
|
+
display: grid;
|
|
11271
|
+
grid-template-columns: repeat(4, 1fr);
|
|
11272
|
+
gap: 12px;
|
|
11273
|
+
margin-bottom: 20px;
|
|
11274
|
+
}
|
|
11275
|
+
@media (max-width: 900px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }
|
|
11276
|
+
.card {
|
|
11277
|
+
background: #12121a;
|
|
11278
|
+
border: 1px solid #1e1e2e;
|
|
11279
|
+
border-radius: 8px;
|
|
11280
|
+
padding: 16px;
|
|
11281
|
+
}
|
|
11282
|
+
.card-label { color: #6b7280; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px; }
|
|
11283
|
+
.card-value { font-size: 28px; color: #06b6d4; font-weight: bold; }
|
|
11284
|
+
|
|
11285
|
+
/* \u2500\u2500 Phase bar \u2500\u2500 */
|
|
11286
|
+
.phase-section { margin-bottom: 20px; }
|
|
11287
|
+
.phase-bar {
|
|
11288
|
+
display: flex;
|
|
11289
|
+
gap: 4px;
|
|
11290
|
+
flex-wrap: wrap;
|
|
11291
|
+
background: #12121a;
|
|
11292
|
+
border: 1px solid #1e1e2e;
|
|
11293
|
+
border-radius: 8px;
|
|
11294
|
+
padding: 12px 16px;
|
|
11295
|
+
}
|
|
11296
|
+
.phase-pill {
|
|
11297
|
+
padding: 4px 12px;
|
|
11298
|
+
border-radius: 4px;
|
|
11299
|
+
font-size: 12px;
|
|
11300
|
+
color: #6b7280;
|
|
11301
|
+
border: 1px solid #2d2d3d;
|
|
11302
|
+
background: #0a0a0f;
|
|
11303
|
+
}
|
|
11304
|
+
.phase-pill.done { color: #22c55e; border-color: #22c55e; background: rgba(34,197,94,0.08); }
|
|
11305
|
+
.phase-pill.active { color: #06b6d4; border-color: #06b6d4; background: rgba(6,182,212,0.12); font-weight: bold; }
|
|
11306
|
+
.phase-arrow { color: #2d2d3d; align-self: center; }
|
|
11307
|
+
|
|
11308
|
+
/* \u2500\u2500 Main grid \u2500\u2500 */
|
|
11309
|
+
.main-grid {
|
|
11310
|
+
display: grid;
|
|
11311
|
+
grid-template-columns: 1fr 1fr;
|
|
11312
|
+
gap: 16px;
|
|
11313
|
+
margin-bottom: 16px;
|
|
11314
|
+
}
|
|
11315
|
+
@media (max-width: 800px) { .main-grid { grid-template-columns: 1fr; } }
|
|
11316
|
+
|
|
11317
|
+
.panel {
|
|
11318
|
+
background: #12121a;
|
|
11319
|
+
border: 1px solid #1e1e2e;
|
|
11320
|
+
border-radius: 8px;
|
|
11321
|
+
padding: 16px;
|
|
11322
|
+
}
|
|
11323
|
+
.panel-title {
|
|
11324
|
+
color: #94a3b8;
|
|
11325
|
+
font-size: 12px;
|
|
11326
|
+
text-transform: uppercase;
|
|
11327
|
+
letter-spacing: 0.1em;
|
|
11328
|
+
margin-bottom: 12px;
|
|
11329
|
+
border-bottom: 1px solid #1e1e2e;
|
|
11330
|
+
padding-bottom: 8px;
|
|
11331
|
+
}
|
|
11332
|
+
|
|
11333
|
+
/* \u2500\u2500 Events \u2500\u2500 */
|
|
11334
|
+
.event-row {
|
|
11335
|
+
display: flex;
|
|
11336
|
+
gap: 10px;
|
|
11337
|
+
padding: 5px 0;
|
|
11338
|
+
border-bottom: 1px solid #1a1a28;
|
|
11339
|
+
font-size: 12px;
|
|
11340
|
+
}
|
|
11341
|
+
.event-row:last-child { border-bottom: none; }
|
|
11342
|
+
.event-ts { color: #4b5563; min-width: 80px; }
|
|
11343
|
+
.event-type { color: #06b6d4; min-width: 130px; }
|
|
11344
|
+
.event-agent { color: #a78bfa; }
|
|
11345
|
+
.empty-msg { color: #4b5563; font-style: italic; }
|
|
11346
|
+
|
|
11347
|
+
/* \u2500\u2500 Agent activity \u2500\u2500 */
|
|
11348
|
+
.agent-row {
|
|
11349
|
+
display: flex;
|
|
11350
|
+
justify-content: space-between;
|
|
11351
|
+
padding: 6px 0;
|
|
11352
|
+
border-bottom: 1px solid #1a1a28;
|
|
11353
|
+
font-size: 12px;
|
|
11354
|
+
}
|
|
11355
|
+
.agent-row:last-child { border-bottom: none; }
|
|
11356
|
+
.agent-name { color: #a78bfa; }
|
|
11357
|
+
.agent-stats { color: #6b7280; display: flex; gap: 12px; }
|
|
11358
|
+
.stat-ok { color: #22c55e; }
|
|
11359
|
+
.stat-fail { color: #ef4444; }
|
|
11360
|
+
.stat-files { color: #06b6d4; }
|
|
11361
|
+
|
|
11362
|
+
/* \u2500\u2500 Bottom row \u2500\u2500 */
|
|
11363
|
+
.bottom-grid {
|
|
11364
|
+
display: grid;
|
|
11365
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
11366
|
+
gap: 16px;
|
|
11367
|
+
}
|
|
11368
|
+
@media (max-width: 900px) { .bottom-grid { grid-template-columns: 1fr; } }
|
|
11369
|
+
|
|
11370
|
+
/* \u2500\u2500 KV table \u2500\u2500 */
|
|
11371
|
+
.kv-row {
|
|
11372
|
+
display: flex;
|
|
11373
|
+
justify-content: space-between;
|
|
11374
|
+
padding: 4px 0;
|
|
11375
|
+
border-bottom: 1px solid #1a1a28;
|
|
11376
|
+
font-size: 12px;
|
|
11377
|
+
}
|
|
11378
|
+
.kv-row:last-child { border-bottom: none; }
|
|
11379
|
+
.kv-key { color: #fbbf24; }
|
|
11380
|
+
.kv-val { color: #06b6d4; }
|
|
11381
|
+
|
|
11382
|
+
/* \u2500\u2500 Gates \u2500\u2500 */
|
|
11383
|
+
.gate-row {
|
|
11384
|
+
display: flex;
|
|
11385
|
+
align-items: center;
|
|
11386
|
+
gap: 8px;
|
|
11387
|
+
padding: 4px 0;
|
|
11388
|
+
font-size: 12px;
|
|
11389
|
+
border-bottom: 1px solid #1a1a28;
|
|
11390
|
+
}
|
|
11391
|
+
.gate-row:last-child { border-bottom: none; }
|
|
11392
|
+
.gate-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
11393
|
+
.gate-dot.passed { background: #22c55e; }
|
|
11394
|
+
.gate-dot.failed { background: #ef4444; }
|
|
11395
|
+
.gate-dot.pending { background: #f59e0b; }
|
|
11396
|
+
.gate-name { color: #e2e8f0; flex: 1; }
|
|
11397
|
+
.gate-status { font-size: 11px; }
|
|
11398
|
+
.gate-status.passed { color: #22c55e; }
|
|
11399
|
+
.gate-status.failed { color: #ef4444; }
|
|
11400
|
+
.gate-status.pending { color: #f59e0b; }
|
|
11401
|
+
</style>
|
|
11402
|
+
</head>
|
|
11403
|
+
<body>
|
|
11404
|
+
<header>
|
|
11405
|
+
<h1><span class="status-dot" id="statusDot"></span>FISHI Agent Dashboard</h1>
|
|
11406
|
+
<span class="last-updated" id="lastUpdated">Loading...</span>
|
|
11407
|
+
</header>
|
|
11408
|
+
<main>
|
|
11409
|
+
<!-- Stat cards -->
|
|
11410
|
+
<div class="stat-grid">
|
|
11411
|
+
<div class="card"><div class="card-label">Agent Completions</div><div class="card-value" id="statCompletions">\u2014</div></div>
|
|
11412
|
+
<div class="card"><div class="card-label">Files Changed</div><div class="card-value" id="statFiles">\u2014</div></div>
|
|
11413
|
+
<div class="card"><div class="card-label">Total Tokens</div><div class="card-value" id="statTokens">\u2014</div></div>
|
|
11414
|
+
<div class="card"><div class="card-label">Dynamic Agents</div><div class="card-value" id="statDynamic">\u2014</div></div>
|
|
11415
|
+
</div>
|
|
11416
|
+
|
|
11417
|
+
<!-- Phase bar -->
|
|
11418
|
+
<div class="phase-section">
|
|
11419
|
+
<div class="phase-bar" id="phaseBar">Loading phases...</div>
|
|
11420
|
+
</div>
|
|
11421
|
+
|
|
11422
|
+
<!-- Main grid -->
|
|
11423
|
+
<div class="main-grid">
|
|
11424
|
+
<div class="panel">
|
|
11425
|
+
<div class="panel-title">Recent Events</div>
|
|
11426
|
+
<div id="eventsPanel"><span class="empty-msg">No events yet.</span></div>
|
|
11427
|
+
</div>
|
|
11428
|
+
<div class="panel">
|
|
11429
|
+
<div class="panel-title">Agent Activity</div>
|
|
11430
|
+
<div id="agentsPanel"><span class="empty-msg">No agent activity yet.</span></div>
|
|
11431
|
+
</div>
|
|
11432
|
+
</div>
|
|
11433
|
+
|
|
11434
|
+
<!-- Bottom row -->
|
|
11435
|
+
<div class="bottom-grid">
|
|
11436
|
+
<div class="panel">
|
|
11437
|
+
<div class="panel-title">Tokens by Model</div>
|
|
11438
|
+
<div id="tokensPanel"><span class="empty-msg">No data.</span></div>
|
|
11439
|
+
</div>
|
|
11440
|
+
<div class="panel">
|
|
11441
|
+
<div class="panel-title">Tools Used</div>
|
|
11442
|
+
<div id="toolsPanel"><span class="empty-msg">No data.</span></div>
|
|
11443
|
+
</div>
|
|
11444
|
+
<div class="panel">
|
|
11445
|
+
<div class="panel-title">Gates</div>
|
|
11446
|
+
<div id="gatesPanel"><span class="empty-msg">No gates.</span></div>
|
|
11447
|
+
</div>
|
|
11448
|
+
</div>
|
|
11449
|
+
</main>
|
|
11450
|
+
|
|
11451
|
+
<script>
|
|
11452
|
+
const PHASES = ['init', 'discovery', 'planning', 'development', 'testing', 'review', 'deployed'];
|
|
11453
|
+
|
|
11454
|
+
function fmt(n) {
|
|
11455
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
|
|
11456
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
|
|
11457
|
+
return String(n);
|
|
11458
|
+
}
|
|
11459
|
+
|
|
11460
|
+
function fmtTime(iso) {
|
|
11461
|
+
try { return new Date(iso).toLocaleTimeString(); } catch { return iso; }
|
|
11462
|
+
}
|
|
11463
|
+
|
|
11464
|
+
function renderPhase(currentPhase) {
|
|
11465
|
+
const idx = PHASES.indexOf(currentPhase);
|
|
11466
|
+
const bar = document.getElementById('phaseBar');
|
|
11467
|
+
let html = '';
|
|
11468
|
+
PHASES.forEach((p, i) => {
|
|
11469
|
+
if (i > 0) html += '<span class="phase-arrow">→</span>';
|
|
11470
|
+
const cls = i < idx ? 'done' : i === idx ? 'active' : '';
|
|
11471
|
+
html += \`<span class="phase-pill \${cls}">\${p}</span>\`;
|
|
11472
|
+
});
|
|
11473
|
+
bar.innerHTML = html;
|
|
11474
|
+
}
|
|
11475
|
+
|
|
11476
|
+
function renderEvents(events) {
|
|
11477
|
+
const panel = document.getElementById('eventsPanel');
|
|
11478
|
+
const recent = events.slice(-10).reverse();
|
|
11479
|
+
if (!recent.length) { panel.innerHTML = '<span class="empty-msg">No events yet.</span>'; return; }
|
|
11480
|
+
panel.innerHTML = recent.map(ev =>
|
|
11481
|
+
\`<div class="event-row">
|
|
11482
|
+
<span class="event-ts">\${fmtTime(ev.timestamp)}</span>
|
|
11483
|
+
<span class="event-type">\${ev.type}</span>
|
|
11484
|
+
<span class="event-agent">\${ev.agent}</span>
|
|
11485
|
+
</div>\`
|
|
11486
|
+
).join('');
|
|
11487
|
+
}
|
|
11488
|
+
|
|
11489
|
+
function renderAgents(agentSummary) {
|
|
11490
|
+
const panel = document.getElementById('agentsPanel');
|
|
11491
|
+
const entries = Object.entries(agentSummary);
|
|
11492
|
+
if (!entries.length) { panel.innerHTML = '<span class="empty-msg">No agent activity yet.</span>'; return; }
|
|
11493
|
+
entries.sort((a, b) => b[1].completions - a[1].completions);
|
|
11494
|
+
panel.innerHTML = entries.map(([name, s]) =>
|
|
11495
|
+
\`<div class="agent-row">
|
|
11496
|
+
<span class="agent-name">\${name}</span>
|
|
11497
|
+
<span class="agent-stats">
|
|
11498
|
+
<span class="stat-ok">✓ \${s.completions}</span>
|
|
11499
|
+
<span class="stat-fail">\${s.failures > 0 ? '✗ ' + s.failures : ''}</span>
|
|
11500
|
+
<span class="stat-files">📄 \${s.filesChanged}</span>
|
|
11501
|
+
</span>
|
|
11502
|
+
</div>\`
|
|
11503
|
+
).join('');
|
|
11504
|
+
}
|
|
11505
|
+
|
|
11506
|
+
function renderKV(data, panelId, keyClass) {
|
|
11507
|
+
const panel = document.getElementById(panelId);
|
|
11508
|
+
const entries = Object.entries(data).sort((a, b) => b[1] - a[1]);
|
|
11509
|
+
if (!entries.length) { panel.innerHTML = '<span class="empty-msg">No data.</span>'; return; }
|
|
11510
|
+
panel.innerHTML = entries.map(([k, v]) =>
|
|
11511
|
+
\`<div class="kv-row"><span class="kv-key">\${k}</span><span class="kv-val">\${fmt(v)}</span></div>\`
|
|
11512
|
+
).join('');
|
|
11513
|
+
}
|
|
11514
|
+
|
|
11515
|
+
function renderGates(gates) {
|
|
11516
|
+
const panel = document.getElementById('gatesPanel');
|
|
11517
|
+
if (!gates || !gates.length) { panel.innerHTML = '<span class="empty-msg">No gates.</span>'; return; }
|
|
11518
|
+
panel.innerHTML = gates.map(g => {
|
|
11519
|
+
const cls = g.status === 'passed' ? 'passed' : g.status === 'failed' ? 'failed' : 'pending';
|
|
11520
|
+
return \`<div class="gate-row">
|
|
11521
|
+
<span class="gate-dot \${cls}"></span>
|
|
11522
|
+
<span class="gate-name">\${g.name}</span>
|
|
11523
|
+
<span class="gate-status \${cls}">\${g.status}</span>
|
|
11524
|
+
</div>\`;
|
|
11525
|
+
}).join('');
|
|
11526
|
+
}
|
|
11527
|
+
|
|
11528
|
+
async function fetchAndRender() {
|
|
11529
|
+
const dot = document.getElementById('statusDot');
|
|
11530
|
+
const lu = document.getElementById('lastUpdated');
|
|
11531
|
+
try {
|
|
11532
|
+
const res = await fetch('/api/state');
|
|
11533
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
11534
|
+
const data = await res.json();
|
|
11535
|
+
|
|
11536
|
+
dot.classList.remove('offline');
|
|
11537
|
+
|
|
11538
|
+
const s = data.summary || {};
|
|
11539
|
+
document.getElementById('statCompletions').textContent = fmt(s.totalAgentCompletions || 0);
|
|
11540
|
+
document.getElementById('statFiles').textContent = fmt(s.totalFilesChanged || 0);
|
|
11541
|
+
document.getElementById('statTokens').textContent = fmt(s.totalTokens || 0);
|
|
11542
|
+
document.getElementById('statDynamic').textContent = fmt(s.dynamicAgentsCreated || 0);
|
|
11543
|
+
|
|
11544
|
+
renderPhase(data.phase || 'init');
|
|
11545
|
+
renderEvents(data.events || []);
|
|
11546
|
+
renderAgents(data.agentSummary || {});
|
|
11547
|
+
renderKV(s.tokensByModel || {}, 'tokensPanel');
|
|
11548
|
+
renderKV(s.toolsUsed || {}, 'toolsPanel');
|
|
11549
|
+
renderGates(data.gates || []);
|
|
11550
|
+
|
|
11551
|
+
lu.textContent = 'Updated: ' + new Date().toLocaleTimeString();
|
|
11552
|
+
} catch (err) {
|
|
11553
|
+
dot.classList.add('offline');
|
|
11554
|
+
lu.textContent = 'Error: ' + err.message;
|
|
11555
|
+
}
|
|
11556
|
+
}
|
|
11557
|
+
|
|
11558
|
+
fetchAndRender();
|
|
11559
|
+
setInterval(fetchAndRender, 2000);
|
|
11560
|
+
</script>
|
|
11561
|
+
</body>
|
|
11562
|
+
</html>`;
|
|
11563
|
+
}
|
|
10942
11564
|
export {
|
|
10943
11565
|
architectAgentTemplate,
|
|
10944
11566
|
backendAgentTemplate,
|
|
@@ -10947,6 +11569,7 @@ export {
|
|
|
10947
11569
|
devLeadTemplate,
|
|
10948
11570
|
devopsAgentTemplate,
|
|
10949
11571
|
docsAgentTemplate,
|
|
11572
|
+
emitEvent,
|
|
10950
11573
|
frontendAgentTemplate,
|
|
10951
11574
|
fullstackAgentTemplate,
|
|
10952
11575
|
generateScaffold,
|
|
@@ -10954,6 +11577,7 @@ export {
|
|
|
10954
11577
|
getAgentCompleteHook,
|
|
10955
11578
|
getAgentFactoryTemplate,
|
|
10956
11579
|
getAgentRegistryTemplate,
|
|
11580
|
+
getAgentSummary,
|
|
10957
11581
|
getApiDesignSkill,
|
|
10958
11582
|
getAutoCheckpointHook,
|
|
10959
11583
|
getBoardCommand,
|
|
@@ -10963,6 +11587,7 @@ export {
|
|
|
10963
11587
|
getClaudeMdTemplate,
|
|
10964
11588
|
getCodeGenSkill,
|
|
10965
11589
|
getCoordinatorFactoryTemplate,
|
|
11590
|
+
getDashboardHtml,
|
|
10966
11591
|
getDebuggingSkill,
|
|
10967
11592
|
getDeploymentSkill,
|
|
10968
11593
|
getDocCheckerScript,
|
|
@@ -10977,6 +11602,7 @@ export {
|
|
|
10977
11602
|
getMcpJsonTemplate,
|
|
10978
11603
|
getMemoryManagerScript,
|
|
10979
11604
|
getModelRoutingReference,
|
|
11605
|
+
getMonitorEmitterScript,
|
|
10980
11606
|
getPhaseRunnerScript,
|
|
10981
11607
|
getPostEditHook,
|
|
10982
11608
|
getPrdCommand,
|
|
@@ -11006,6 +11632,7 @@ export {
|
|
|
11006
11632
|
planningAgentTemplate,
|
|
11007
11633
|
planningLeadTemplate,
|
|
11008
11634
|
qualityLeadTemplate,
|
|
11635
|
+
readMonitorState,
|
|
11009
11636
|
researchAgentTemplate,
|
|
11010
11637
|
securityAgentTemplate,
|
|
11011
11638
|
testingAgentTemplate,
|