@hivehub/rulebook 5.5.2 → 5.7.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/.claude/commands/rulebook-learn-capture.md +41 -48
- package/.claude/commands/rulebook-learn-list.md +13 -13
- package/README.md +332 -394
- package/dist/cli/commands/context-intelligence.d.ts +0 -1
- package/dist/cli/commands/context-intelligence.d.ts.map +1 -1
- package/dist/cli/commands/context-intelligence.js +12 -33
- package/dist/cli/commands/context-intelligence.js.map +1 -1
- package/dist/cli/commands/index.d.ts +4 -7
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +4 -7
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +40 -81
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts +0 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +1 -7
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/memory.d.ts +7 -1
- package/dist/cli/commands/memory.d.ts.map +1 -1
- package/dist/cli/commands/memory.js +51 -57
- package/dist/cli/commands/memory.js.map +1 -1
- package/dist/cli/commands/misc.d.ts +1 -15
- package/dist/cli/commands/misc.d.ts.map +1 -1
- package/dist/cli/commands/misc.js +36 -215
- package/dist/cli/commands/misc.js.map +1 -1
- package/dist/cli/commands/plans.d.ts +0 -6
- package/dist/cli/commands/plans.d.ts.map +1 -1
- package/dist/cli/commands/plans.js +9 -77
- package/dist/cli/commands/plans.js.map +1 -1
- package/dist/cli/commands/skills.js +6 -6
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/task.js +4 -4
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +122 -52
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/prompts.d.ts.map +1 -1
- package/dist/cli/prompts.js +1 -78
- package/dist/cli/prompts.js.map +1 -1
- package/dist/core/claude/claude-mcp.d.ts +59 -0
- package/dist/core/claude/claude-mcp.d.ts.map +1 -0
- package/dist/core/claude/claude-mcp.js +220 -0
- package/dist/core/claude/claude-mcp.js.map +1 -0
- package/dist/core/claude/claude-md-generator.d.ts +52 -0
- package/dist/core/claude/claude-md-generator.d.ts.map +1 -0
- package/dist/core/claude/claude-md-generator.js +104 -0
- package/dist/core/claude/claude-md-generator.js.map +1 -0
- package/dist/core/claude/claude-settings-manager.d.ts +44 -0
- package/dist/core/claude/claude-settings-manager.d.ts.map +1 -0
- package/dist/core/claude/claude-settings-manager.js +194 -0
- package/dist/core/claude/claude-settings-manager.js.map +1 -0
- package/dist/core/console/cli-bridge.d.ts +113 -0
- package/dist/core/console/cli-bridge.d.ts.map +1 -0
- package/dist/core/console/cli-bridge.js +1094 -0
- package/dist/core/console/cli-bridge.js.map +1 -0
- package/dist/core/detect/detector.d.ts +35 -0
- package/dist/core/detect/detector.d.ts.map +1 -0
- package/dist/core/detect/detector.js +541 -0
- package/dist/core/detect/detector.js.map +1 -0
- package/dist/core/docs/docs-generator.d.ts +9 -0
- package/dist/core/docs/docs-generator.d.ts.map +1 -0
- package/dist/core/docs/docs-generator.js +531 -0
- package/dist/core/docs/docs-generator.js.map +1 -0
- package/dist/core/docs/mcp-reference-generator.d.ts +13 -0
- package/dist/core/docs/mcp-reference-generator.d.ts.map +1 -0
- package/dist/core/docs/mcp-reference-generator.js +66 -0
- package/dist/core/docs/mcp-reference-generator.js.map +1 -0
- package/dist/core/generators/generator.d.ts +54 -0
- package/dist/core/generators/generator.d.ts.map +1 -0
- package/dist/core/generators/generator.js +1041 -0
- package/dist/core/generators/generator.js.map +1 -0
- package/dist/core/generators/gitignore-generator.d.ts +13 -0
- package/dist/core/generators/gitignore-generator.d.ts.map +1 -0
- package/dist/core/generators/gitignore-generator.js +307 -0
- package/dist/core/generators/gitignore-generator.js.map +1 -0
- package/dist/core/generators/minimal-scaffolder.d.ts +8 -0
- package/dist/core/generators/minimal-scaffolder.d.ts.map +1 -0
- package/dist/core/generators/minimal-scaffolder.js +51 -0
- package/dist/core/generators/minimal-scaffolder.js.map +1 -0
- package/dist/core/generators/rules-generator.d.ts +73 -0
- package/dist/core/generators/rules-generator.d.ts.map +1 -0
- package/dist/core/generators/rules-generator.js +202 -0
- package/dist/core/generators/rules-generator.js.map +1 -0
- package/dist/core/generators/workflow-generator.d.ts +15 -0
- package/dist/core/generators/workflow-generator.d.ts.map +1 -0
- package/dist/core/generators/workflow-generator.js +390 -0
- package/dist/core/generators/workflow-generator.js.map +1 -0
- package/dist/core/ide/multi-tool-generator.d.ts +59 -0
- package/dist/core/ide/multi-tool-generator.d.ts.map +1 -0
- package/dist/core/ide/multi-tool-generator.js +157 -0
- package/dist/core/ide/multi-tool-generator.js.map +1 -0
- package/dist/core/ide/opencode-generator.d.ts +72 -0
- package/dist/core/ide/opencode-generator.d.ts.map +1 -0
- package/dist/core/ide/opencode-generator.js +450 -0
- package/dist/core/ide/opencode-generator.js.map +1 -0
- package/dist/core/merger.d.ts +1 -1
- package/dist/core/merger.d.ts.map +1 -1
- package/dist/core/merger.js +5 -5
- package/dist/core/merger.js.map +1 -1
- package/dist/core/migrator.d.ts +0 -1
- package/dist/core/migrator.d.ts.map +1 -1
- package/dist/core/migrator.js +4 -29
- package/dist/core/migrator.js.map +1 -1
- package/dist/core/quality/coverage-checker.d.ts +14 -0
- package/dist/core/quality/coverage-checker.d.ts.map +1 -0
- package/dist/core/quality/coverage-checker.js +176 -0
- package/dist/core/quality/coverage-checker.js.map +1 -0
- package/dist/core/quality/dependency-checker.d.ts +21 -0
- package/dist/core/quality/dependency-checker.d.ts.map +1 -0
- package/dist/core/quality/dependency-checker.js +247 -0
- package/dist/core/quality/dependency-checker.js.map +1 -0
- package/dist/core/quality/doctor.d.ts +19 -0
- package/dist/core/quality/doctor.d.ts.map +1 -0
- package/dist/core/quality/doctor.js +163 -0
- package/dist/core/quality/doctor.js.map +1 -0
- package/dist/core/quality/validator.d.ts +21 -0
- package/dist/core/quality/validator.d.ts.map +1 -0
- package/dist/core/quality/validator.js +177 -0
- package/dist/core/quality/validator.js.map +1 -0
- package/dist/core/skills/skills-manager.d.ts +126 -0
- package/dist/core/skills/skills-manager.d.ts.map +1 -0
- package/dist/core/skills/skills-manager.js +630 -0
- package/dist/core/skills/skills-manager.js.map +1 -0
- package/dist/core/state/config-manager.d.ts +86 -0
- package/dist/core/state/config-manager.d.ts.map +1 -0
- package/dist/core/state/config-manager.js +562 -0
- package/dist/core/state/config-manager.js.map +1 -0
- package/dist/core/state/override-manager.d.ts +23 -0
- package/dist/core/state/override-manager.d.ts.map +1 -0
- package/dist/core/state/override-manager.js +82 -0
- package/dist/core/state/override-manager.js.map +1 -0
- package/dist/core/state/state-writer.d.ts +34 -0
- package/dist/core/state/state-writer.d.ts.map +1 -0
- package/dist/core/state/state-writer.js +78 -0
- package/dist/core/state/state-writer.js.map +1 -0
- package/dist/core/state/version-bumper.d.ts +19 -0
- package/dist/core/state/version-bumper.d.ts.map +1 -0
- package/dist/core/state/version-bumper.js +180 -0
- package/dist/core/state/version-bumper.js.map +1 -0
- package/dist/core/tasks/decision-manager.d.ts +25 -0
- package/dist/core/tasks/decision-manager.d.ts.map +1 -0
- package/dist/core/tasks/decision-manager.js +183 -0
- package/dist/core/tasks/decision-manager.js.map +1 -0
- package/dist/core/tasks/knowledge-manager.d.ts +24 -0
- package/dist/core/tasks/knowledge-manager.d.ts.map +1 -0
- package/dist/core/tasks/knowledge-manager.js +173 -0
- package/dist/core/tasks/knowledge-manager.js.map +1 -0
- package/dist/core/tasks/learn-manager.d.ts +27 -0
- package/dist/core/tasks/learn-manager.d.ts.map +1 -0
- package/dist/core/tasks/learn-manager.js +121 -0
- package/dist/core/tasks/learn-manager.js.map +1 -0
- package/dist/core/tasks/plans-manager.d.ts +46 -0
- package/dist/core/tasks/plans-manager.d.ts.map +1 -0
- package/dist/core/tasks/plans-manager.js +158 -0
- package/dist/core/tasks/plans-manager.js.map +1 -0
- package/dist/core/tasks/task-manager.d.ts +127 -0
- package/dist/core/tasks/task-manager.d.ts.map +1 -0
- package/dist/core/tasks/task-manager.js +607 -0
- package/dist/core/tasks/task-manager.js.map +1 -0
- package/dist/core/workspace/project-worker.d.ts +6 -6
- package/dist/core/workspace/project-worker.d.ts.map +1 -1
- package/dist/core/workspace/project-worker.js +6 -6
- package/dist/core/workspace/project-worker.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -176
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +16 -960
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/file-search.d.ts +43 -0
- package/dist/memory/file-search.d.ts.map +1 -0
- package/dist/memory/file-search.js +228 -0
- package/dist/memory/file-search.js.map +1 -0
- package/dist/memory/file-store.d.ts +99 -0
- package/dist/memory/file-store.d.ts.map +1 -0
- package/dist/memory/file-store.js +615 -0
- package/dist/memory/file-store.js.map +1 -0
- package/dist/memory/legacy-migrator.d.ts +27 -0
- package/dist/memory/legacy-migrator.d.ts.map +1 -0
- package/dist/memory/legacy-migrator.js +185 -0
- package/dist/memory/legacy-migrator.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +25 -24
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +97 -140
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/memory-types.d.ts +1 -1
- package/dist/memory/memory-types.d.ts.map +1 -1
- package/dist/types.d.ts +8 -119
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -6
- package/templates/agents/context-intelligence.md +50 -52
- package/templates/cli/OPENCODE.md +85 -18
- package/templates/commands/rulebook-learn-capture.md +41 -48
- package/templates/commands/rulebook-learn-list.md +13 -13
- package/templates/core/AGENTS_LEAN.md +0 -14
- package/templates/hooks/check-context-and-handoff.sh +8 -10
- package/templates/hooks/enforce-pre-tool.sh +70 -0
- package/templates/hooks/terse-mode-tracker.sh +146 -143
- package/templates/ides/OPENCODE.md +63 -0
- package/templates/skills/cli/opencode/SKILL.md +82 -28
- package/.claude/commands/ralph-config.md +0 -112
- package/.claude/commands/ralph-history.md +0 -110
- package/.claude/commands/ralph-init.md +0 -72
- package/.claude/commands/ralph-pause-resume.md +0 -105
- package/.claude/commands/ralph-run.md +0 -101
- package/.claude/commands/ralph-status.md +0 -76
- package/templates/core/RALPH.md +0 -471
- package/templates/frameworks/ANGULAR.md +0 -36
- package/templates/frameworks/DJANGO.md +0 -83
- package/templates/frameworks/ELECTRON.md +0 -147
- package/templates/frameworks/FLASK.md +0 -38
- package/templates/frameworks/FLUTTER.md +0 -55
- package/templates/frameworks/JQUERY.md +0 -32
- package/templates/frameworks/LARAVEL.md +0 -38
- package/templates/frameworks/NESTJS.md +0 -43
- package/templates/frameworks/NEXTJS.md +0 -127
- package/templates/frameworks/NUXT.md +0 -40
- package/templates/frameworks/RAILS.md +0 -66
- package/templates/frameworks/REACT.md +0 -38
- package/templates/frameworks/REACT_NATIVE.md +0 -47
- package/templates/frameworks/SPRING.md +0 -39
- package/templates/frameworks/SYMFONY.md +0 -36
- package/templates/frameworks/VUE.md +0 -36
- package/templates/frameworks/ZEND.md +0 -35
- package/templates/hooks/enforce-mcp-for-tasks.sh +0 -31
- package/templates/hooks/enforce-no-deferred.sh +0 -21
- package/templates/hooks/enforce-no-shortcuts.sh +0 -31
- package/templates/ides/COPILOT.md +0 -37
- package/templates/ides/CURSOR.md +0 -43
- package/templates/ides/JETBRAINS_AI.md +0 -35
- package/templates/ides/REPLIT.md +0 -36
- package/templates/ides/TABNINE.md +0 -29
- package/templates/ides/VSCODE.md +0 -40
- package/templates/ides/WINDSURF.md +0 -36
- package/templates/ides/ZED.md +0 -32
- package/templates/ides/cursor-mdc/go.mdc +0 -24
- package/templates/ides/cursor-mdc/python.mdc +0 -24
- package/templates/ides/cursor-mdc/quality.mdc +0 -25
- package/templates/ides/cursor-mdc/ralph.mdc +0 -39
- package/templates/ides/cursor-mdc/rulebook.mdc +0 -38
- package/templates/ides/cursor-mdc/rust.mdc +0 -24
- package/templates/ides/cursor-mdc/typescript.mdc +0 -25
- package/templates/ralph/ralph-history.bat +0 -4
- package/templates/ralph/ralph-history.sh +0 -5
- package/templates/ralph/ralph-init.bat +0 -5
- package/templates/ralph/ralph-init.sh +0 -5
- package/templates/ralph/ralph-pause.bat +0 -5
- package/templates/ralph/ralph-pause.sh +0 -5
- package/templates/ralph/ralph-run.bat +0 -5
- package/templates/ralph/ralph-run.sh +0 -5
- package/templates/ralph/ralph-status.bat +0 -4
- package/templates/ralph/ralph-status.sh +0 -5
- package/templates/services/AZURE_BLOB.md +0 -184
- package/templates/services/CASSANDRA.md +0 -239
- package/templates/services/DATADOG.md +0 -26
- package/templates/services/DOCKER.md +0 -124
- package/templates/services/DOCKER_COMPOSE.md +0 -168
- package/templates/services/DYNAMODB.md +0 -308
- package/templates/services/ELASTICSEARCH.md +0 -347
- package/templates/services/GCS.md +0 -178
- package/templates/services/HELM.md +0 -194
- package/templates/services/INFLUXDB.md +0 -265
- package/templates/services/KAFKA.md +0 -341
- package/templates/services/KUBERNETES.md +0 -208
- package/templates/services/MARIADB.md +0 -183
- package/templates/services/MEMCACHED.md +0 -242
- package/templates/services/MINIO.md +0 -201
- package/templates/services/MONGODB.md +0 -268
- package/templates/services/MYSQL.md +0 -358
- package/templates/services/NEO4J.md +0 -247
- package/templates/services/OPENTELEMETRY.md +0 -25
- package/templates/services/ORACLE.md +0 -290
- package/templates/services/PINO.md +0 -24
- package/templates/services/POSTGRESQL.md +0 -326
- package/templates/services/PROMETHEUS.md +0 -33
- package/templates/services/RABBITMQ.md +0 -286
- package/templates/services/REDIS.md +0 -292
- package/templates/services/S3.md +0 -298
- package/templates/services/SENTRY.md +0 -23
- package/templates/services/SQLITE.md +0 -294
- package/templates/services/SQLSERVER.md +0 -294
- package/templates/services/WINSTON.md +0 -30
- package/templates/skills/frameworks/angular/SKILL.md +0 -46
- package/templates/skills/frameworks/django/SKILL.md +0 -93
- package/templates/skills/frameworks/electron/SKILL.md +0 -157
- package/templates/skills/frameworks/flask/SKILL.md +0 -48
- package/templates/skills/frameworks/flutter/SKILL.md +0 -65
- package/templates/skills/frameworks/jquery/SKILL.md +0 -42
- package/templates/skills/frameworks/laravel/SKILL.md +0 -48
- package/templates/skills/frameworks/nestjs/SKILL.md +0 -53
- package/templates/skills/frameworks/nextjs/SKILL.md +0 -137
- package/templates/skills/frameworks/nuxt/SKILL.md +0 -50
- package/templates/skills/frameworks/rails/SKILL.md +0 -76
- package/templates/skills/frameworks/react/SKILL.md +0 -48
- package/templates/skills/frameworks/react-native/SKILL.md +0 -57
- package/templates/skills/frameworks/spring/SKILL.md +0 -49
- package/templates/skills/frameworks/symfony/SKILL.md +0 -46
- package/templates/skills/frameworks/vue/SKILL.md +0 -46
- package/templates/skills/frameworks/zend/SKILL.md +0 -45
- package/templates/skills/services/azure-blob/SKILL.md +0 -194
- package/templates/skills/services/cassandra/SKILL.md +0 -249
- package/templates/skills/services/dynamodb/SKILL.md +0 -318
- package/templates/skills/services/elasticsearch/SKILL.md +0 -357
- package/templates/skills/services/gcs/SKILL.md +0 -188
- package/templates/skills/services/influxdb/SKILL.md +0 -275
- package/templates/skills/services/kafka/SKILL.md +0 -351
- package/templates/skills/services/mariadb/SKILL.md +0 -193
- package/templates/skills/services/memcached/SKILL.md +0 -252
- package/templates/skills/services/minio/SKILL.md +0 -211
- package/templates/skills/services/mongodb/SKILL.md +0 -278
- package/templates/skills/services/mysql/SKILL.md +0 -368
- package/templates/skills/services/neo4j/SKILL.md +0 -257
- package/templates/skills/services/oracle/SKILL.md +0 -300
- package/templates/skills/services/postgresql/SKILL.md +0 -336
- package/templates/skills/services/rabbitmq/SKILL.md +0 -296
- package/templates/skills/services/redis/SKILL.md +0 -302
- package/templates/skills/services/s3/SKILL.md +0 -308
- package/templates/skills/services/sqlite/SKILL.md +0 -304
- package/templates/skills/services/sqlserver/SKILL.md +0 -304
- package/templates/skills/workflows/ralph/SETUP.md +0 -228
- package/templates/skills/workflows/ralph/SKILL.md +0 -309
- package/templates/skills/workflows/ralph/install.sh +0 -87
- package/templates/skills/workflows/ralph/manifest.json +0 -158
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync, } from 'fs';
|
|
5
|
-
import {
|
|
5
|
+
import { dirname, join, resolve } from 'path';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import { ConfigManager } from '../core/config-manager.js';
|
|
7
|
+
import { ConfigManager } from '../core/state/config-manager.js';
|
|
8
8
|
import { BackgroundIndexer } from '../core/indexer/background-indexer.js';
|
|
9
|
-
import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
|
|
10
|
-
import { TaskManager } from '../core/task-manager.js';
|
|
9
|
+
import { SkillsManager, getDefaultTemplatesPath } from '../core/skills/skills-manager.js';
|
|
10
|
+
import { TaskManager } from '../core/tasks/task-manager.js';
|
|
11
11
|
import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
|
|
12
12
|
// --- Timeout guard for MCP tool handlers ---
|
|
13
13
|
// Prevents the MCP server from hanging when a tool handler blocks (SQLite, WASM, fs).
|
|
@@ -272,28 +272,14 @@ export async function startRulebookMcpServer() {
|
|
|
272
272
|
name: 'rulebook-task-management',
|
|
273
273
|
version: '5.2.0',
|
|
274
274
|
});
|
|
275
|
-
// ---
|
|
276
|
-
const { createTelemetryMiddleware } = await import('../core/telemetry.js');
|
|
277
|
-
const telemetryEnabled = configManager
|
|
278
|
-
? (await configManager.loadConfig())?.features?.telemetry === true
|
|
279
|
-
: false;
|
|
280
|
-
const telemetry = createTelemetryMiddleware({
|
|
281
|
-
enabled: telemetryEnabled,
|
|
282
|
-
dir: join(projectRoot, '.rulebook', 'telemetry'),
|
|
283
|
-
});
|
|
284
|
-
// --- Wrap all tool handlers with timeout guard + telemetry ---
|
|
285
|
-
// Intercept registerTool to automatically add timeout protection and
|
|
286
|
-
// optional telemetry recording to every handler.
|
|
275
|
+
// --- Wrap all tool handlers with a timeout guard ---
|
|
287
276
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
288
277
|
server.registerTool = ((name, config, handler) => {
|
|
289
278
|
const wrappedHandler = async (...handlerArgs) => {
|
|
290
|
-
const start = Date.now();
|
|
291
|
-
let success = true;
|
|
292
279
|
try {
|
|
293
280
|
return await withTimeout(handler(...handlerArgs), MCP_TOOL_TIMEOUT_MS, name);
|
|
294
281
|
}
|
|
295
282
|
catch (error) {
|
|
296
|
-
success = false;
|
|
297
283
|
const msg = error instanceof Error ? error.message : String(error);
|
|
298
284
|
console.error(`[rulebook-mcp] ${name} error: ${msg}`);
|
|
299
285
|
return {
|
|
@@ -302,14 +288,6 @@ export async function startRulebookMcpServer() {
|
|
|
302
288
|
],
|
|
303
289
|
};
|
|
304
290
|
}
|
|
305
|
-
finally {
|
|
306
|
-
telemetry.record({
|
|
307
|
-
tool: name,
|
|
308
|
-
latency_ms: Date.now() - start,
|
|
309
|
-
success,
|
|
310
|
-
timestamp: new Date().toISOString(),
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
291
|
};
|
|
314
292
|
return originalRegisterTool(name, config, wrappedHandler);
|
|
315
293
|
});
|
|
@@ -1144,491 +1122,6 @@ export async function startRulebookMcpServer() {
|
|
|
1144
1122
|
};
|
|
1145
1123
|
}
|
|
1146
1124
|
});
|
|
1147
|
-
// Ralph Autonomous Loop Tools (v3.0)
|
|
1148
|
-
const ralphConfig = await configManager.loadConfig();
|
|
1149
|
-
const ralphEnabled = ralphConfig.ralph?.enabled ?? true;
|
|
1150
|
-
if (ralphEnabled) {
|
|
1151
|
-
// Register tool: rulebook_ralph_init
|
|
1152
|
-
server.registerTool('rulebook_ralph_init', {
|
|
1153
|
-
title: 'Initialize Ralph',
|
|
1154
|
-
description: 'Initialize Ralph autonomous loop and create PRD from rulebook tasks',
|
|
1155
|
-
inputSchema: {},
|
|
1156
|
-
}, async () => {
|
|
1157
|
-
try {
|
|
1158
|
-
const { Logger } = await import('../core/logger.js');
|
|
1159
|
-
const { RalphManager } = await import('../core/ralph-manager.js');
|
|
1160
|
-
const { PRDGenerator } = await import('../core/prd-generator.js');
|
|
1161
|
-
const logger = new Logger(projectRoot);
|
|
1162
|
-
const ralphManager = new RalphManager(projectRoot, logger);
|
|
1163
|
-
const prdGenerator = new PRDGenerator(projectRoot, logger);
|
|
1164
|
-
const configData = await configManager.loadConfig();
|
|
1165
|
-
const maxIterations = configData.ralph?.maxIterations || 10;
|
|
1166
|
-
const tool = (configData.ralph?.tool || 'claude');
|
|
1167
|
-
// Generate PRD first, then initialize with correct task count
|
|
1168
|
-
const prd = await prdGenerator.generatePRD(basename(projectRoot) || 'project');
|
|
1169
|
-
const { writeFile } = await import('../utils/file-system.js');
|
|
1170
|
-
const prdPath = join(projectRoot, '.rulebook', 'ralph', 'prd.json');
|
|
1171
|
-
await writeFile(prdPath, JSON.stringify(prd, null, 2));
|
|
1172
|
-
// Initialize after PRD is written so task count is correct
|
|
1173
|
-
await ralphManager.initialize(maxIterations, tool);
|
|
1174
|
-
return {
|
|
1175
|
-
content: [
|
|
1176
|
-
{
|
|
1177
|
-
type: 'text',
|
|
1178
|
-
text: JSON.stringify({
|
|
1179
|
-
success: true,
|
|
1180
|
-
message: `Ralph initialized with ${prd.userStories.length} user stories`,
|
|
1181
|
-
tasks: prd.userStories.length,
|
|
1182
|
-
maxIterations,
|
|
1183
|
-
tool,
|
|
1184
|
-
}),
|
|
1185
|
-
},
|
|
1186
|
-
],
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
|
-
catch (error) {
|
|
1190
|
-
return {
|
|
1191
|
-
content: [
|
|
1192
|
-
{
|
|
1193
|
-
type: 'text',
|
|
1194
|
-
text: JSON.stringify({ success: false, error: String(error) }),
|
|
1195
|
-
},
|
|
1196
|
-
],
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1200
|
-
// Register tool: rulebook_ralph_run
|
|
1201
|
-
server.registerTool('rulebook_ralph_run', {
|
|
1202
|
-
title: 'Run Ralph Loop',
|
|
1203
|
-
description: 'Execute Ralph autonomous iteration loop',
|
|
1204
|
-
inputSchema: {
|
|
1205
|
-
maxIterations: z.number().optional().describe('Maximum iterations'),
|
|
1206
|
-
tool: z.enum(['claude', 'amp', 'gemini']).optional().describe('AI tool to use'),
|
|
1207
|
-
},
|
|
1208
|
-
}, async (args) => {
|
|
1209
|
-
try {
|
|
1210
|
-
const { Logger } = await import('../core/logger.js');
|
|
1211
|
-
const { RalphManager } = await import('../core/ralph-manager.js');
|
|
1212
|
-
const { RalphParser } = await import('../agents/ralph-parser.js');
|
|
1213
|
-
const { spawn } = await import('child_process');
|
|
1214
|
-
const { execSync } = await import('child_process');
|
|
1215
|
-
const logger = new Logger(projectRoot);
|
|
1216
|
-
const ralphManager = new RalphManager(projectRoot, logger);
|
|
1217
|
-
const configData = await configManager.loadConfig();
|
|
1218
|
-
const maxIterations = args.maxIterations || configData.ralph?.maxIterations || 10;
|
|
1219
|
-
const tool = (args.tool || configData.ralph?.tool || 'claude');
|
|
1220
|
-
// ── Concurrency guard: prevent multiple simultaneous Ralph runs ──
|
|
1221
|
-
const lockAcquired = await ralphManager.acquireLock(tool);
|
|
1222
|
-
if (!lockAcquired) {
|
|
1223
|
-
const lockInfo = await ralphManager.getLockInfo();
|
|
1224
|
-
return {
|
|
1225
|
-
content: [
|
|
1226
|
-
{
|
|
1227
|
-
type: 'text',
|
|
1228
|
-
text: JSON.stringify({
|
|
1229
|
-
success: false,
|
|
1230
|
-
error: `Ralph is already running (PID ${lockInfo?.pid}, started ${lockInfo?.startedAt}, task: ${lockInfo?.currentTask || 'starting'}, iteration: ${lockInfo?.iteration || 0}). Wait for it to finish or check ralph_status. Do NOT start another run.`,
|
|
1231
|
-
}),
|
|
1232
|
-
},
|
|
1233
|
-
],
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
// Lock cleanup is handled by the top-level SIGINT handler (line ~858)
|
|
1237
|
-
// which shuts down all managers. Adding per-call SIGINT/SIGTERM listeners
|
|
1238
|
-
// causes accumulation and race conditions with the server shutdown handler.
|
|
1239
|
-
try {
|
|
1240
|
-
// Validate tool is available before starting
|
|
1241
|
-
const toolCmdNames = {
|
|
1242
|
-
claude: 'claude',
|
|
1243
|
-
amp: 'amp',
|
|
1244
|
-
gemini: 'gemini',
|
|
1245
|
-
};
|
|
1246
|
-
const toolCmd = toolCmdNames[tool] || 'claude';
|
|
1247
|
-
try {
|
|
1248
|
-
execSync(`${toolCmd} --version`, { stdio: 'pipe', timeout: 10000 });
|
|
1249
|
-
}
|
|
1250
|
-
catch {
|
|
1251
|
-
return {
|
|
1252
|
-
content: [
|
|
1253
|
-
{
|
|
1254
|
-
type: 'text',
|
|
1255
|
-
text: JSON.stringify({
|
|
1256
|
-
success: false,
|
|
1257
|
-
error: `CLI tool "${toolCmd}" not found or not responding. Install it first: https://docs.anthropic.com/claude-code`,
|
|
1258
|
-
}),
|
|
1259
|
-
},
|
|
1260
|
-
],
|
|
1261
|
-
};
|
|
1262
|
-
}
|
|
1263
|
-
// Resume existing state if available, otherwise initialize fresh
|
|
1264
|
-
const existingState = await ralphManager.getStatus();
|
|
1265
|
-
if (!existingState) {
|
|
1266
|
-
await ralphManager.initialize(maxIterations, tool);
|
|
1267
|
-
}
|
|
1268
|
-
// Helper: run a shell command and return stdout
|
|
1269
|
-
const runCmd = (cmd, cmdArgs) => new Promise((resolve) => {
|
|
1270
|
-
let stdout = '';
|
|
1271
|
-
let stderr = '';
|
|
1272
|
-
const proc = spawn(cmd, cmdArgs, {
|
|
1273
|
-
cwd: projectRoot,
|
|
1274
|
-
shell: true,
|
|
1275
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1276
|
-
});
|
|
1277
|
-
proc.stdout?.on('data', (d) => {
|
|
1278
|
-
stdout += d.toString();
|
|
1279
|
-
});
|
|
1280
|
-
proc.stderr?.on('data', (d) => {
|
|
1281
|
-
stderr += d.toString();
|
|
1282
|
-
});
|
|
1283
|
-
proc.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
|
|
1284
|
-
proc.on('error', (err) => resolve({ code: 1, stdout, stderr: err.message }));
|
|
1285
|
-
});
|
|
1286
|
-
// Helper: build prompt for AI agent
|
|
1287
|
-
const buildPrompt = (task, projectName) => {
|
|
1288
|
-
const criteria = (task.acceptanceCriteria || [])
|
|
1289
|
-
.map((c) => `- ${c}`)
|
|
1290
|
-
.join('\n');
|
|
1291
|
-
return [
|
|
1292
|
-
`You are working on project: ${projectName}`,
|
|
1293
|
-
``,
|
|
1294
|
-
`## Current Task: ${task.title}`,
|
|
1295
|
-
`ID: ${task.id}`,
|
|
1296
|
-
``,
|
|
1297
|
-
`## Description`,
|
|
1298
|
-
task.description,
|
|
1299
|
-
``,
|
|
1300
|
-
`## Acceptance Criteria`,
|
|
1301
|
-
criteria,
|
|
1302
|
-
``,
|
|
1303
|
-
task.notes ? `## Notes\n${task.notes}\n` : '',
|
|
1304
|
-
`## Instructions`,
|
|
1305
|
-
`1. Implement the changes described above`,
|
|
1306
|
-
`2. Ensure all acceptance criteria are met`,
|
|
1307
|
-
`3. Run quality checks: type-check, lint, tests`,
|
|
1308
|
-
`4. Fix any issues found by quality checks`,
|
|
1309
|
-
`5. When done, summarize what was changed`,
|
|
1310
|
-
]
|
|
1311
|
-
.filter(Boolean)
|
|
1312
|
-
.join('\n');
|
|
1313
|
-
};
|
|
1314
|
-
// Helper: execute AI agent with proper error handling
|
|
1315
|
-
const executeAgent = (agentTool, prompt) => new Promise((resolve, reject) => {
|
|
1316
|
-
let output = '';
|
|
1317
|
-
let stderrOutput = '';
|
|
1318
|
-
const toolCmds = {
|
|
1319
|
-
claude: {
|
|
1320
|
-
cmd: 'claude',
|
|
1321
|
-
args: ['-p', '--dangerously-skip-permissions', '--verbose'],
|
|
1322
|
-
stdinPrompt: true,
|
|
1323
|
-
},
|
|
1324
|
-
amp: { cmd: 'amp', args: ['-p', prompt], stdinPrompt: false },
|
|
1325
|
-
gemini: { cmd: 'gemini', args: ['-p', prompt], stdinPrompt: false },
|
|
1326
|
-
};
|
|
1327
|
-
const cfg = toolCmds[agentTool] || toolCmds.claude;
|
|
1328
|
-
let settled = false;
|
|
1329
|
-
const settle = (fn) => {
|
|
1330
|
-
if (!settled) {
|
|
1331
|
-
settled = true;
|
|
1332
|
-
fn();
|
|
1333
|
-
}
|
|
1334
|
-
};
|
|
1335
|
-
const proc = spawn(cfg.cmd, cfg.args, {
|
|
1336
|
-
cwd: projectRoot,
|
|
1337
|
-
shell: true,
|
|
1338
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1339
|
-
});
|
|
1340
|
-
if (cfg.stdinPrompt && proc.stdin) {
|
|
1341
|
-
proc.stdin.write(prompt);
|
|
1342
|
-
proc.stdin.end();
|
|
1343
|
-
}
|
|
1344
|
-
proc.stdout?.on('data', (d) => {
|
|
1345
|
-
output += d.toString();
|
|
1346
|
-
});
|
|
1347
|
-
proc.stderr?.on('data', (d) => {
|
|
1348
|
-
stderrOutput += d.toString();
|
|
1349
|
-
});
|
|
1350
|
-
proc.on('close', (code) => {
|
|
1351
|
-
settle(() => {
|
|
1352
|
-
if (code === 0 || output.length > 0) {
|
|
1353
|
-
resolve(output);
|
|
1354
|
-
}
|
|
1355
|
-
else {
|
|
1356
|
-
reject(new Error(`Agent ${agentTool} exited with code ${code}${stderrOutput ? ': ' + stderrOutput.slice(0, 500) : ''}`));
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
});
|
|
1360
|
-
proc.on('error', (err) => {
|
|
1361
|
-
settle(() => reject(new Error(`Failed to spawn ${agentTool}: ${err.message}`)));
|
|
1362
|
-
});
|
|
1363
|
-
const timeout = setTimeout(() => {
|
|
1364
|
-
proc.kill('SIGTERM');
|
|
1365
|
-
settle(() => resolve(output || `Agent ${agentTool} timed out after 10 minutes`));
|
|
1366
|
-
}, 600000);
|
|
1367
|
-
proc.on('close', () => clearTimeout(timeout));
|
|
1368
|
-
});
|
|
1369
|
-
// Sync task count from PRD
|
|
1370
|
-
await ralphManager.refreshTaskCount();
|
|
1371
|
-
// Load PRD for project name (used in prompts)
|
|
1372
|
-
const prd = await ralphManager.loadPRD();
|
|
1373
|
-
if (!prd || !prd.userStories || prd.userStories.length === 0) {
|
|
1374
|
-
return {
|
|
1375
|
-
content: [
|
|
1376
|
-
{
|
|
1377
|
-
type: 'text',
|
|
1378
|
-
text: JSON.stringify({
|
|
1379
|
-
success: false,
|
|
1380
|
-
error: 'No PRD found or no user stories. Run rulebook_ralph_init first.',
|
|
1381
|
-
}),
|
|
1382
|
-
},
|
|
1383
|
-
],
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
const projectName = prd.project || 'unknown';
|
|
1387
|
-
const totalTasks = prd.userStories.filter((s) => !s.passes).length;
|
|
1388
|
-
let iterationCount = 0;
|
|
1389
|
-
const iterationResults = [];
|
|
1390
|
-
// Log to stderr so MCP callers can see progress
|
|
1391
|
-
const logProgress = (msg) => {
|
|
1392
|
-
process.stderr.write(`[Ralph] ${msg}\n`);
|
|
1393
|
-
};
|
|
1394
|
-
logProgress(`Starting Ralph loop: ${totalTasks} pending tasks, max ${maxIterations} iterations, tool=${tool}`);
|
|
1395
|
-
while (ralphManager.canContinue() && iterationCount < maxIterations) {
|
|
1396
|
-
iterationCount++;
|
|
1397
|
-
const task = await ralphManager.getNextTask();
|
|
1398
|
-
if (!task)
|
|
1399
|
-
break;
|
|
1400
|
-
// Update lock with current progress
|
|
1401
|
-
await ralphManager.updateLockProgress(iterationCount, `${task.id}: ${task.title}`);
|
|
1402
|
-
logProgress(`Iteration ${iterationCount}/${maxIterations} — Task: ${task.id} "${task.title}"`);
|
|
1403
|
-
const startTime = Date.now();
|
|
1404
|
-
// 1. Execute AI agent
|
|
1405
|
-
let agentOutput = '';
|
|
1406
|
-
try {
|
|
1407
|
-
logProgress(` Executing ${tool} agent...`);
|
|
1408
|
-
const prompt = buildPrompt(task, projectName);
|
|
1409
|
-
agentOutput = await executeAgent(tool, prompt);
|
|
1410
|
-
logProgress(` Agent finished (${((Date.now() - startTime) / 1000).toFixed(0)}s)`);
|
|
1411
|
-
}
|
|
1412
|
-
catch (agentErr) {
|
|
1413
|
-
agentOutput = `Error: ${agentErr.message || agentErr}`;
|
|
1414
|
-
logProgress(` Agent error: ${agentErr.message || agentErr}`);
|
|
1415
|
-
}
|
|
1416
|
-
// 2. Run quality gates
|
|
1417
|
-
logProgress(` Running quality gates...`);
|
|
1418
|
-
const [typeCheck, lint, tests] = await Promise.all([
|
|
1419
|
-
runCmd('npm', ['run', 'type-check']).then((r) => r.code === 0),
|
|
1420
|
-
runCmd('npm', ['run', 'lint']).then((r) => r.code === 0),
|
|
1421
|
-
runCmd('npm', ['test']).then((r) => r.code === 0),
|
|
1422
|
-
]);
|
|
1423
|
-
const qualityChecks = { type_check: typeCheck, lint, tests, coverage_met: tests };
|
|
1424
|
-
const allPass = typeCheck && lint && tests;
|
|
1425
|
-
const passCount = Object.values(qualityChecks).filter(Boolean).length;
|
|
1426
|
-
const status = allPass
|
|
1427
|
-
? 'success'
|
|
1428
|
-
: passCount >= 2
|
|
1429
|
-
? 'partial'
|
|
1430
|
-
: 'failed';
|
|
1431
|
-
logProgress(` Quality: type-check=${typeCheck ? 'PASS' : 'FAIL'} lint=${lint ? 'PASS' : 'FAIL'} tests=${tests ? 'PASS' : 'FAIL'} → ${status.toUpperCase()}`);
|
|
1432
|
-
// 3. Git commit if all gates pass
|
|
1433
|
-
let gitCommit;
|
|
1434
|
-
if (allPass) {
|
|
1435
|
-
await runCmd('git', ['add', '-A']);
|
|
1436
|
-
const commitResult = await runCmd('git', [
|
|
1437
|
-
'commit',
|
|
1438
|
-
'-m',
|
|
1439
|
-
`ralph(${task.id}): ${task.title}\n\nIteration ${iterationCount} - Ralph autonomous loop`,
|
|
1440
|
-
]);
|
|
1441
|
-
const hashMatch = commitResult.stdout.match(/\[[\w/.-]+ ([a-f0-9]+)\]/);
|
|
1442
|
-
gitCommit = hashMatch ? hashMatch[1] : undefined;
|
|
1443
|
-
await ralphManager.markStoryComplete(task.id);
|
|
1444
|
-
logProgress(` Committed: ${gitCommit || 'no hash'} — Story ${task.id} COMPLETE`);
|
|
1445
|
-
}
|
|
1446
|
-
const iterDuration = Date.now() - startTime;
|
|
1447
|
-
// 4. Parse output for learnings/errors
|
|
1448
|
-
const parsed = RalphParser.parseAgentOutput(agentOutput, iterationCount, task.id, task.title, tool);
|
|
1449
|
-
// 5. Record iteration and refresh task count for canContinue()
|
|
1450
|
-
await ralphManager.recordIteration({
|
|
1451
|
-
iteration: iterationCount,
|
|
1452
|
-
timestamp: new Date().toISOString(),
|
|
1453
|
-
task_id: task.id,
|
|
1454
|
-
task_title: task.title,
|
|
1455
|
-
status,
|
|
1456
|
-
ai_tool: tool,
|
|
1457
|
-
execution_time_ms: iterDuration,
|
|
1458
|
-
quality_checks: qualityChecks,
|
|
1459
|
-
output_summary: parsed.output_summary || `Iteration ${iterationCount}: ${task.title}`,
|
|
1460
|
-
git_commit: gitCommit,
|
|
1461
|
-
learnings: parsed.learnings,
|
|
1462
|
-
errors: parsed.errors,
|
|
1463
|
-
metadata: {
|
|
1464
|
-
context_loss_count: parsed.metadata.context_loss_count,
|
|
1465
|
-
parsed_completion: parsed.metadata.parsed_completion,
|
|
1466
|
-
},
|
|
1467
|
-
});
|
|
1468
|
-
iterationResults.push({
|
|
1469
|
-
iteration: iterationCount,
|
|
1470
|
-
taskId: task.id,
|
|
1471
|
-
taskTitle: task.title,
|
|
1472
|
-
status,
|
|
1473
|
-
durationMs: iterDuration,
|
|
1474
|
-
});
|
|
1475
|
-
// Refresh task count so canContinue() reflects updated PRD
|
|
1476
|
-
await ralphManager.refreshTaskCount();
|
|
1477
|
-
logProgress(` Iteration ${iterationCount} complete (${(iterDuration / 1000).toFixed(0)}s)\n`);
|
|
1478
|
-
}
|
|
1479
|
-
const stats = await ralphManager.getTaskStats();
|
|
1480
|
-
logProgress(`Ralph loop finished: ${iterationCount} iterations, ${stats.completed}/${stats.total} tasks completed`);
|
|
1481
|
-
return {
|
|
1482
|
-
content: [
|
|
1483
|
-
{
|
|
1484
|
-
type: 'text',
|
|
1485
|
-
text: JSON.stringify({
|
|
1486
|
-
success: true,
|
|
1487
|
-
iterations: iterationCount,
|
|
1488
|
-
completed: stats.completed,
|
|
1489
|
-
total: stats.total,
|
|
1490
|
-
results: iterationResults,
|
|
1491
|
-
}),
|
|
1492
|
-
},
|
|
1493
|
-
],
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
finally {
|
|
1497
|
-
// Always release lock, even on error
|
|
1498
|
-
await ralphManager.releaseLock();
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
catch (error) {
|
|
1502
|
-
return {
|
|
1503
|
-
content: [
|
|
1504
|
-
{
|
|
1505
|
-
type: 'text',
|
|
1506
|
-
text: JSON.stringify({ success: false, error: String(error) }),
|
|
1507
|
-
},
|
|
1508
|
-
],
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
});
|
|
1512
|
-
// Register tool: rulebook_ralph_status
|
|
1513
|
-
server.registerTool('rulebook_ralph_status', {
|
|
1514
|
-
title: 'Ralph Status',
|
|
1515
|
-
description: 'Get current Ralph loop status',
|
|
1516
|
-
inputSchema: {},
|
|
1517
|
-
}, async () => {
|
|
1518
|
-
try {
|
|
1519
|
-
const { Logger } = await import('../core/logger.js');
|
|
1520
|
-
const { RalphManager } = await import('../core/ralph-manager.js');
|
|
1521
|
-
const logger = new Logger(projectRoot);
|
|
1522
|
-
const ralphManager = new RalphManager(projectRoot, logger);
|
|
1523
|
-
const status = await ralphManager.getStatus();
|
|
1524
|
-
if (!status) {
|
|
1525
|
-
return {
|
|
1526
|
-
content: [
|
|
1527
|
-
{
|
|
1528
|
-
type: 'text',
|
|
1529
|
-
text: JSON.stringify({ success: false, error: 'Ralph not initialized' }),
|
|
1530
|
-
},
|
|
1531
|
-
],
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
const stats = await ralphManager.getTaskStats();
|
|
1535
|
-
// Check if Ralph is currently running (lock held by alive process)
|
|
1536
|
-
const running = await ralphManager.isRunning();
|
|
1537
|
-
const lockInfo = running ? await ralphManager.getLockInfo() : null;
|
|
1538
|
-
return {
|
|
1539
|
-
content: [
|
|
1540
|
-
{
|
|
1541
|
-
type: 'text',
|
|
1542
|
-
text: JSON.stringify({
|
|
1543
|
-
success: true,
|
|
1544
|
-
running,
|
|
1545
|
-
...(running && lockInfo
|
|
1546
|
-
? {
|
|
1547
|
-
runningPid: lockInfo.pid,
|
|
1548
|
-
runningTask: lockInfo.currentTask || null,
|
|
1549
|
-
runningIteration: lockInfo.iteration || 0,
|
|
1550
|
-
runningSince: lockInfo.startedAt,
|
|
1551
|
-
}
|
|
1552
|
-
: {}),
|
|
1553
|
-
iteration: status.current_iteration,
|
|
1554
|
-
maxIterations: status.max_iterations,
|
|
1555
|
-
completedTasks: stats.completed,
|
|
1556
|
-
totalTasks: stats.total,
|
|
1557
|
-
paused: status.paused,
|
|
1558
|
-
tool: status.tool,
|
|
1559
|
-
startedAt: status.started_at,
|
|
1560
|
-
}),
|
|
1561
|
-
},
|
|
1562
|
-
],
|
|
1563
|
-
};
|
|
1564
|
-
}
|
|
1565
|
-
catch (error) {
|
|
1566
|
-
return {
|
|
1567
|
-
content: [
|
|
1568
|
-
{
|
|
1569
|
-
type: 'text',
|
|
1570
|
-
text: JSON.stringify({ success: false, error: String(error) }),
|
|
1571
|
-
},
|
|
1572
|
-
],
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
});
|
|
1576
|
-
// Register tool: rulebook_ralph_get_iteration_history
|
|
1577
|
-
server.registerTool('rulebook_ralph_get_iteration_history', {
|
|
1578
|
-
title: 'Ralph Iteration History',
|
|
1579
|
-
description: 'Get Ralph iteration history and statistics',
|
|
1580
|
-
inputSchema: {
|
|
1581
|
-
limit: z.number().optional().describe('Maximum iterations to return'),
|
|
1582
|
-
taskId: z.string().optional().describe('Filter by task ID'),
|
|
1583
|
-
},
|
|
1584
|
-
}, async (args) => {
|
|
1585
|
-
try {
|
|
1586
|
-
const { Logger } = await import('../core/logger.js');
|
|
1587
|
-
const { IterationTracker } = await import('../core/iteration-tracker.js');
|
|
1588
|
-
const logger = new Logger(projectRoot);
|
|
1589
|
-
const tracker = new IterationTracker(projectRoot, logger);
|
|
1590
|
-
const history = await tracker.getHistory(args.limit || 10, args.taskId);
|
|
1591
|
-
const stats = await tracker.getStatistics();
|
|
1592
|
-
return {
|
|
1593
|
-
content: [
|
|
1594
|
-
{
|
|
1595
|
-
type: 'text',
|
|
1596
|
-
text: JSON.stringify({
|
|
1597
|
-
success: true,
|
|
1598
|
-
iterations: history.length,
|
|
1599
|
-
history: history.map((iter) => ({
|
|
1600
|
-
iteration: iter.iteration,
|
|
1601
|
-
taskId: iter.task_id,
|
|
1602
|
-
taskTitle: iter.task_title,
|
|
1603
|
-
status: iter.status,
|
|
1604
|
-
duration: iter.duration_ms,
|
|
1605
|
-
qualityChecks: iter.quality_checks,
|
|
1606
|
-
commit: iter.git_commit,
|
|
1607
|
-
})),
|
|
1608
|
-
statistics: {
|
|
1609
|
-
total: stats.total_iterations,
|
|
1610
|
-
successful: stats.successful_iterations,
|
|
1611
|
-
failed: stats.failed_iterations,
|
|
1612
|
-
successRate: (stats.success_rate * 100).toFixed(1) + '%',
|
|
1613
|
-
avgDuration: stats.average_duration_ms,
|
|
1614
|
-
},
|
|
1615
|
-
}),
|
|
1616
|
-
},
|
|
1617
|
-
],
|
|
1618
|
-
};
|
|
1619
|
-
}
|
|
1620
|
-
catch (error) {
|
|
1621
|
-
return {
|
|
1622
|
-
content: [
|
|
1623
|
-
{
|
|
1624
|
-
type: 'text',
|
|
1625
|
-
text: JSON.stringify({ success: false, error: String(error) }),
|
|
1626
|
-
},
|
|
1627
|
-
],
|
|
1628
|
-
};
|
|
1629
|
-
}
|
|
1630
|
-
});
|
|
1631
|
-
}
|
|
1632
1125
|
// --- Background Indexer Tools ---
|
|
1633
1126
|
// Register tool: rulebook_codebase_search
|
|
1634
1127
|
server.registerTool('rulebook_codebase_search', {
|
|
@@ -1883,7 +1376,7 @@ export async function startRulebookMcpServer() {
|
|
|
1883
1376
|
const root = args.projectId && workspaceManager
|
|
1884
1377
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1885
1378
|
: projectRoot;
|
|
1886
|
-
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1379
|
+
const { DecisionManager } = await import('../core/tasks/decision-manager.js');
|
|
1887
1380
|
const dm = new DecisionManager(root);
|
|
1888
1381
|
const decision = await dm.create(args.title, {
|
|
1889
1382
|
context: args.context,
|
|
@@ -1931,7 +1424,7 @@ export async function startRulebookMcpServer() {
|
|
|
1931
1424
|
const root = args.projectId && workspaceManager
|
|
1932
1425
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1933
1426
|
: projectRoot;
|
|
1934
|
-
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1427
|
+
const { DecisionManager } = await import('../core/tasks/decision-manager.js');
|
|
1935
1428
|
const dm = new DecisionManager(root);
|
|
1936
1429
|
const decisions = await dm.list(args.status);
|
|
1937
1430
|
return {
|
|
@@ -1970,7 +1463,7 @@ export async function startRulebookMcpServer() {
|
|
|
1970
1463
|
const root = args.projectId && workspaceManager
|
|
1971
1464
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1972
1465
|
: projectRoot;
|
|
1973
|
-
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1466
|
+
const { DecisionManager } = await import('../core/tasks/decision-manager.js');
|
|
1974
1467
|
const dm = new DecisionManager(root);
|
|
1975
1468
|
const result = await dm.show(args.id);
|
|
1976
1469
|
if (!result) {
|
|
@@ -2029,7 +1522,7 @@ export async function startRulebookMcpServer() {
|
|
|
2029
1522
|
const root = args.projectId && workspaceManager
|
|
2030
1523
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2031
1524
|
: projectRoot;
|
|
2032
|
-
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1525
|
+
const { DecisionManager } = await import('../core/tasks/decision-manager.js');
|
|
2033
1526
|
const dm = new DecisionManager(root);
|
|
2034
1527
|
const updated = await dm.update(args.id, {
|
|
2035
1528
|
status: args.status,
|
|
@@ -2089,7 +1582,7 @@ export async function startRulebookMcpServer() {
|
|
|
2089
1582
|
const root = args.projectId && workspaceManager
|
|
2090
1583
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2091
1584
|
: projectRoot;
|
|
2092
|
-
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1585
|
+
const { KnowledgeManager } = await import('../core/tasks/knowledge-manager.js');
|
|
2093
1586
|
const km = new KnowledgeManager(root);
|
|
2094
1587
|
const entry = await km.add(args.type, args.title, {
|
|
2095
1588
|
category: args.category,
|
|
@@ -2136,7 +1629,7 @@ export async function startRulebookMcpServer() {
|
|
|
2136
1629
|
const root = args.projectId && workspaceManager
|
|
2137
1630
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2138
1631
|
: projectRoot;
|
|
2139
|
-
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1632
|
+
const { KnowledgeManager } = await import('../core/tasks/knowledge-manager.js');
|
|
2140
1633
|
const km = new KnowledgeManager(root);
|
|
2141
1634
|
const entries = await km.list(args.type, args.category);
|
|
2142
1635
|
return {
|
|
@@ -2175,7 +1668,7 @@ export async function startRulebookMcpServer() {
|
|
|
2175
1668
|
const root = args.projectId && workspaceManager
|
|
2176
1669
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2177
1670
|
: projectRoot;
|
|
2178
|
-
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1671
|
+
const { KnowledgeManager } = await import('../core/tasks/knowledge-manager.js');
|
|
2179
1672
|
const km = new KnowledgeManager(root);
|
|
2180
1673
|
const result = await km.show(args.id);
|
|
2181
1674
|
if (!result) {
|
|
@@ -2230,7 +1723,7 @@ export async function startRulebookMcpServer() {
|
|
|
2230
1723
|
const root = args.projectId && workspaceManager
|
|
2231
1724
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2232
1725
|
: projectRoot;
|
|
2233
|
-
const { LearnManager } = await import('../core/learn-manager.js');
|
|
1726
|
+
const { LearnManager } = await import('../core/tasks/learn-manager.js');
|
|
2234
1727
|
const lm = new LearnManager(root);
|
|
2235
1728
|
const learning = await lm.capture(args.title, args.content, {
|
|
2236
1729
|
tags: args.tags,
|
|
@@ -2272,7 +1765,7 @@ export async function startRulebookMcpServer() {
|
|
|
2272
1765
|
const root = args.projectId && workspaceManager
|
|
2273
1766
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2274
1767
|
: projectRoot;
|
|
2275
|
-
const { LearnManager } = await import('../core/learn-manager.js');
|
|
1768
|
+
const { LearnManager } = await import('../core/tasks/learn-manager.js');
|
|
2276
1769
|
const lm = new LearnManager(root);
|
|
2277
1770
|
const learnings = await lm.list(args.limit);
|
|
2278
1771
|
return {
|
|
@@ -2315,7 +1808,7 @@ export async function startRulebookMcpServer() {
|
|
|
2315
1808
|
const root = args.projectId && workspaceManager
|
|
2316
1809
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2317
1810
|
: projectRoot;
|
|
2318
|
-
const { LearnManager } = await import('../core/learn-manager.js');
|
|
1811
|
+
const { LearnManager } = await import('../core/tasks/learn-manager.js');
|
|
2319
1812
|
const lm = new LearnManager(root);
|
|
2320
1813
|
const result = await lm.promote(args.id, args.target, { title: args.title });
|
|
2321
1814
|
if (!result) {
|
|
@@ -2490,7 +1983,7 @@ export async function startRulebookMcpServer() {
|
|
|
2490
1983
|
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2491
1984
|
: projectRoot;
|
|
2492
1985
|
const { listRules } = await import('../core/rule-engine.js');
|
|
2493
|
-
const { listRulesWithSource } = await import('../core/rules-generator.js');
|
|
1986
|
+
const { listRulesWithSource } = await import('../core/generators/rules-generator.js');
|
|
2494
1987
|
const canonical = await listRules(root);
|
|
2495
1988
|
const languageRules = await listRulesWithSource(root);
|
|
2496
1989
|
return {
|
|
@@ -2521,443 +2014,6 @@ export async function startRulebookMcpServer() {
|
|
|
2521
2014
|
};
|
|
2522
2015
|
}
|
|
2523
2016
|
});
|
|
2524
|
-
// Register tool: rulebook_doctor_run
|
|
2525
|
-
server.registerTool('rulebook_doctor_run', {
|
|
2526
|
-
title: 'Run Doctor',
|
|
2527
|
-
description: 'Run rulebook health checks: file sizes, broken @imports, stale STATE.md, missing files',
|
|
2528
|
-
inputSchema: { projectId: projectIdSchema },
|
|
2529
|
-
}, async (args) => {
|
|
2530
|
-
try {
|
|
2531
|
-
const root = args.projectId && workspaceManager
|
|
2532
|
-
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2533
|
-
: projectRoot;
|
|
2534
|
-
const { runDoctor } = await import('../core/doctor.js');
|
|
2535
|
-
const report = await runDoctor(root);
|
|
2536
|
-
return {
|
|
2537
|
-
content: [{ type: 'text', text: JSON.stringify({ success: true, ...report }) }],
|
|
2538
|
-
};
|
|
2539
|
-
}
|
|
2540
|
-
catch (error) {
|
|
2541
|
-
return {
|
|
2542
|
-
content: [
|
|
2543
|
-
{
|
|
2544
|
-
type: 'text',
|
|
2545
|
-
text: JSON.stringify({
|
|
2546
|
-
success: false,
|
|
2547
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2548
|
-
}),
|
|
2549
|
-
},
|
|
2550
|
-
],
|
|
2551
|
-
};
|
|
2552
|
-
}
|
|
2553
|
-
});
|
|
2554
|
-
// Register tool: rulebook_analysis_create
|
|
2555
|
-
server.registerTool('rulebook_analysis_create', {
|
|
2556
|
-
title: 'Create Analysis',
|
|
2557
|
-
description: 'Scaffold a new structured analysis in docs/analysis/<slug>/',
|
|
2558
|
-
inputSchema: {
|
|
2559
|
-
topic: z.string().describe('Analysis topic'),
|
|
2560
|
-
noTasks: z.boolean().optional().describe('Skip task materialization'),
|
|
2561
|
-
projectId: projectIdSchema,
|
|
2562
|
-
},
|
|
2563
|
-
}, async (args) => {
|
|
2564
|
-
try {
|
|
2565
|
-
const root = args.projectId && workspaceManager
|
|
2566
|
-
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2567
|
-
: projectRoot;
|
|
2568
|
-
const { createAnalysis } = await import('../core/analysis-manager.js');
|
|
2569
|
-
const result = await createAnalysis(root, {
|
|
2570
|
-
topic: args.topic,
|
|
2571
|
-
noTasks: args.noTasks ?? false,
|
|
2572
|
-
});
|
|
2573
|
-
return {
|
|
2574
|
-
content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }) }],
|
|
2575
|
-
};
|
|
2576
|
-
}
|
|
2577
|
-
catch (error) {
|
|
2578
|
-
return {
|
|
2579
|
-
content: [
|
|
2580
|
-
{
|
|
2581
|
-
type: 'text',
|
|
2582
|
-
text: JSON.stringify({
|
|
2583
|
-
success: false,
|
|
2584
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2585
|
-
}),
|
|
2586
|
-
},
|
|
2587
|
-
],
|
|
2588
|
-
};
|
|
2589
|
-
}
|
|
2590
|
-
});
|
|
2591
|
-
// Register tool: rulebook_analysis_list
|
|
2592
|
-
server.registerTool('rulebook_analysis_list', {
|
|
2593
|
-
title: 'List Analyses',
|
|
2594
|
-
description: 'List all structured analyses in docs/analysis/',
|
|
2595
|
-
inputSchema: { projectId: projectIdSchema },
|
|
2596
|
-
}, async (args) => {
|
|
2597
|
-
try {
|
|
2598
|
-
const root = args.projectId && workspaceManager
|
|
2599
|
-
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2600
|
-
: projectRoot;
|
|
2601
|
-
const { listAnalyses } = await import('../core/analysis-manager.js');
|
|
2602
|
-
const analyses = await listAnalyses(root);
|
|
2603
|
-
return {
|
|
2604
|
-
content: [
|
|
2605
|
-
{
|
|
2606
|
-
type: 'text',
|
|
2607
|
-
text: JSON.stringify({ success: true, analyses, count: analyses.length }),
|
|
2608
|
-
},
|
|
2609
|
-
],
|
|
2610
|
-
};
|
|
2611
|
-
}
|
|
2612
|
-
catch (error) {
|
|
2613
|
-
return {
|
|
2614
|
-
content: [
|
|
2615
|
-
{
|
|
2616
|
-
type: 'text',
|
|
2617
|
-
text: JSON.stringify({
|
|
2618
|
-
success: false,
|
|
2619
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2620
|
-
}),
|
|
2621
|
-
},
|
|
2622
|
-
],
|
|
2623
|
-
};
|
|
2624
|
-
}
|
|
2625
|
-
});
|
|
2626
|
-
// Register tool: rulebook_analysis_show
|
|
2627
|
-
server.registerTool('rulebook_analysis_show', {
|
|
2628
|
-
title: 'Show Analysis',
|
|
2629
|
-
description: 'Show the contents of a structured analysis by slug',
|
|
2630
|
-
inputSchema: {
|
|
2631
|
-
slug: z.string().describe('Analysis slug (directory name)'),
|
|
2632
|
-
projectId: projectIdSchema,
|
|
2633
|
-
},
|
|
2634
|
-
}, async (args) => {
|
|
2635
|
-
try {
|
|
2636
|
-
const root = args.projectId && workspaceManager
|
|
2637
|
-
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2638
|
-
: projectRoot;
|
|
2639
|
-
const { showAnalysis } = await import('../core/analysis-manager.js');
|
|
2640
|
-
const analysis = await showAnalysis(root, args.slug);
|
|
2641
|
-
if (!analysis) {
|
|
2642
|
-
return {
|
|
2643
|
-
content: [
|
|
2644
|
-
{
|
|
2645
|
-
type: 'text',
|
|
2646
|
-
text: JSON.stringify({
|
|
2647
|
-
success: false,
|
|
2648
|
-
error: `Analysis "${args.slug}" not found`,
|
|
2649
|
-
}),
|
|
2650
|
-
},
|
|
2651
|
-
],
|
|
2652
|
-
};
|
|
2653
|
-
}
|
|
2654
|
-
return {
|
|
2655
|
-
content: [{ type: 'text', text: JSON.stringify({ success: true, ...analysis }) }],
|
|
2656
|
-
};
|
|
2657
|
-
}
|
|
2658
|
-
catch (error) {
|
|
2659
|
-
return {
|
|
2660
|
-
content: [
|
|
2661
|
-
{
|
|
2662
|
-
type: 'text',
|
|
2663
|
-
text: JSON.stringify({
|
|
2664
|
-
success: false,
|
|
2665
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2666
|
-
}),
|
|
2667
|
-
},
|
|
2668
|
-
],
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
});
|
|
2672
|
-
// Register tool: rulebook_blockers
|
|
2673
|
-
server.registerTool('rulebook_blockers', {
|
|
2674
|
-
title: 'Show Blockers',
|
|
2675
|
-
description: 'Show task blocker chain with cascade impact analysis',
|
|
2676
|
-
inputSchema: {
|
|
2677
|
-
projectId: projectIdSchema,
|
|
2678
|
-
},
|
|
2679
|
-
}, async (args) => {
|
|
2680
|
-
try {
|
|
2681
|
-
const tm = await getTaskMgr(args.projectId);
|
|
2682
|
-
const tasks = await tm.listTasks();
|
|
2683
|
-
// Build blocker chain from task metadata
|
|
2684
|
-
const blockers = [];
|
|
2685
|
-
for (const task of tasks) {
|
|
2686
|
-
const metadata = await tm.getTaskMetadata(task.id);
|
|
2687
|
-
const blocks = Array.isArray(metadata?.blocks) ? metadata.blocks : [];
|
|
2688
|
-
const blockedBy = Array.isArray(metadata?.blockedBy)
|
|
2689
|
-
? metadata.blockedBy
|
|
2690
|
-
: [];
|
|
2691
|
-
if (blocks.length > 0 || blockedBy.length > 0) {
|
|
2692
|
-
blockers.push({
|
|
2693
|
-
taskId: task.id,
|
|
2694
|
-
blocks,
|
|
2695
|
-
blockedBy,
|
|
2696
|
-
cascadeImpact: metadata?.cascadeImpact || blocks.length,
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
// Sort by cascade impact (highest first)
|
|
2701
|
-
blockers.sort((a, b) => b.cascadeImpact - a.cascadeImpact);
|
|
2702
|
-
return {
|
|
2703
|
-
content: [
|
|
2704
|
-
{
|
|
2705
|
-
type: 'text',
|
|
2706
|
-
text: JSON.stringify({ success: true, blockers, count: blockers.length }),
|
|
2707
|
-
},
|
|
2708
|
-
],
|
|
2709
|
-
};
|
|
2710
|
-
}
|
|
2711
|
-
catch (error) {
|
|
2712
|
-
return {
|
|
2713
|
-
content: [
|
|
2714
|
-
{
|
|
2715
|
-
type: 'text',
|
|
2716
|
-
text: JSON.stringify({
|
|
2717
|
-
success: false,
|
|
2718
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2719
|
-
}),
|
|
2720
|
-
},
|
|
2721
|
-
],
|
|
2722
|
-
};
|
|
2723
|
-
}
|
|
2724
|
-
});
|
|
2725
|
-
// ── v5.4.0 compress tools ──────────────────────────────────────────
|
|
2726
|
-
server.registerTool('rulebook_compress', {
|
|
2727
|
-
title: 'Compress Memory File',
|
|
2728
|
-
description: 'Compress a markdown memory file (prose-only rewriter; preserves code, URLs, paths, dates, versions byte-for-byte). Writes a backup to <file>.original.md and replaces the file in place. Returns before/after byte counts and validator result.',
|
|
2729
|
-
inputSchema: {
|
|
2730
|
-
filePath: z
|
|
2731
|
-
.string()
|
|
2732
|
-
.describe('Absolute or project-relative path to the .md file to compress'),
|
|
2733
|
-
dryRun: z
|
|
2734
|
-
.boolean()
|
|
2735
|
-
.optional()
|
|
2736
|
-
.describe('Return the would-be result without writing anything'),
|
|
2737
|
-
projectId: projectIdSchema,
|
|
2738
|
-
},
|
|
2739
|
-
}, async (args) => {
|
|
2740
|
-
try {
|
|
2741
|
-
const { readFile, writeFile, fileExists } = await import('../utils/file-system.js');
|
|
2742
|
-
const { compress } = await import('../core/compress/compressor.js');
|
|
2743
|
-
const path = await import('path');
|
|
2744
|
-
const projectRoot = process.cwd();
|
|
2745
|
-
const abs = path.default.isAbsolute(args.filePath)
|
|
2746
|
-
? args.filePath
|
|
2747
|
-
: path.default.join(projectRoot, args.filePath);
|
|
2748
|
-
if (!(await fileExists(abs))) {
|
|
2749
|
-
return {
|
|
2750
|
-
content: [
|
|
2751
|
-
{
|
|
2752
|
-
type: 'text',
|
|
2753
|
-
text: JSON.stringify({ success: false, error: `File not found: ${abs}` }),
|
|
2754
|
-
},
|
|
2755
|
-
],
|
|
2756
|
-
};
|
|
2757
|
-
}
|
|
2758
|
-
const original = await readFile(abs);
|
|
2759
|
-
const result = compress(original);
|
|
2760
|
-
if (!result.validation.ok) {
|
|
2761
|
-
return {
|
|
2762
|
-
content: [
|
|
2763
|
-
{
|
|
2764
|
-
type: 'text',
|
|
2765
|
-
text: JSON.stringify({
|
|
2766
|
-
success: false,
|
|
2767
|
-
error: 'Validator rejected the compressed output',
|
|
2768
|
-
violations: result.validation.violations.slice(0, 10),
|
|
2769
|
-
}),
|
|
2770
|
-
},
|
|
2771
|
-
],
|
|
2772
|
-
};
|
|
2773
|
-
}
|
|
2774
|
-
const backupPath = abs.replace(/\.md$/i, '.original.md');
|
|
2775
|
-
if (!args.dryRun) {
|
|
2776
|
-
if (!(await fileExists(backupPath))) {
|
|
2777
|
-
await writeFile(backupPath, original);
|
|
2778
|
-
}
|
|
2779
|
-
await writeFile(abs, result.output);
|
|
2780
|
-
}
|
|
2781
|
-
return {
|
|
2782
|
-
content: [
|
|
2783
|
-
{
|
|
2784
|
-
type: 'text',
|
|
2785
|
-
text: JSON.stringify({
|
|
2786
|
-
success: true,
|
|
2787
|
-
filePath: abs,
|
|
2788
|
-
dryRun: !!args.dryRun,
|
|
2789
|
-
originalBytes: result.validation.stats.originalBytes,
|
|
2790
|
-
compressedBytes: result.validation.stats.compressedBytes,
|
|
2791
|
-
savedPct: Math.round((1 - result.validation.stats.ratio) * 100),
|
|
2792
|
-
retries: result.retries,
|
|
2793
|
-
backup: args.dryRun ? null : backupPath,
|
|
2794
|
-
}),
|
|
2795
|
-
},
|
|
2796
|
-
],
|
|
2797
|
-
};
|
|
2798
|
-
}
|
|
2799
|
-
catch (error) {
|
|
2800
|
-
return {
|
|
2801
|
-
content: [
|
|
2802
|
-
{
|
|
2803
|
-
type: 'text',
|
|
2804
|
-
text: JSON.stringify({
|
|
2805
|
-
success: false,
|
|
2806
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2807
|
-
}),
|
|
2808
|
-
},
|
|
2809
|
-
],
|
|
2810
|
-
};
|
|
2811
|
-
}
|
|
2812
|
-
});
|
|
2813
|
-
server.registerTool('rulebook_evals_measure', {
|
|
2814
|
-
title: 'Measure Terse Evals',
|
|
2815
|
-
description: 'Run the offline three-arm evaluation measurement (baseline/terse/rulebook-terse). Reads snapshots committed under evals/snapshots/, uses tiktoken when installed (falls back to UTF-8 byte counts otherwise). Returns per-prompt lift, total lift, and pass/fail against arms.json liftThreshold. Does NOT call the Anthropic API — safe for CI without credentials.',
|
|
2816
|
-
inputSchema: {
|
|
2817
|
-
projectId: projectIdSchema,
|
|
2818
|
-
},
|
|
2819
|
-
}, async () => {
|
|
2820
|
-
try {
|
|
2821
|
-
const path = await import('path');
|
|
2822
|
-
const root = process.cwd();
|
|
2823
|
-
const measurePath = path.default.resolve(root, 'evals/measure.js');
|
|
2824
|
-
const { existsSync } = await import('fs');
|
|
2825
|
-
// Resolve the project-local evals path so rulebook run from a
|
|
2826
|
-
// user project (no evals/ directory) fails cleanly.
|
|
2827
|
-
const measureModulePath = existsSync(measurePath)
|
|
2828
|
-
? measurePath
|
|
2829
|
-
: path.default.resolve(root, 'evals/measure.ts');
|
|
2830
|
-
if (!existsSync(measureModulePath)) {
|
|
2831
|
-
return {
|
|
2832
|
-
content: [
|
|
2833
|
-
{
|
|
2834
|
-
type: 'text',
|
|
2835
|
-
text: JSON.stringify({
|
|
2836
|
-
success: false,
|
|
2837
|
-
error: 'evals/ directory not found in project — this tool targets the Rulebook repo itself, not downstream projects.',
|
|
2838
|
-
}),
|
|
2839
|
-
},
|
|
2840
|
-
],
|
|
2841
|
-
};
|
|
2842
|
-
}
|
|
2843
|
-
// Use a bare URL import so Node resolves the project-local file
|
|
2844
|
-
// regardless of where the MCP server was installed from.
|
|
2845
|
-
const mod = (await import(/* @vite-ignore */ measureModulePath));
|
|
2846
|
-
const report = await mod.measure(path.default.resolve(root, 'evals/snapshots/results.json'), path.default.resolve(root, 'evals/arms.json'));
|
|
2847
|
-
return {
|
|
2848
|
-
content: [{ type: 'text', text: JSON.stringify({ success: true, report }) }],
|
|
2849
|
-
};
|
|
2850
|
-
}
|
|
2851
|
-
catch (error) {
|
|
2852
|
-
return {
|
|
2853
|
-
content: [
|
|
2854
|
-
{
|
|
2855
|
-
type: 'text',
|
|
2856
|
-
text: JSON.stringify({
|
|
2857
|
-
success: false,
|
|
2858
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2859
|
-
}),
|
|
2860
|
-
},
|
|
2861
|
-
],
|
|
2862
|
-
};
|
|
2863
|
-
}
|
|
2864
|
-
});
|
|
2865
|
-
server.registerTool('rulebook_evals_run', {
|
|
2866
|
-
title: 'Run Terse Evals Against Live API',
|
|
2867
|
-
description: 'Regenerate evals/snapshots/results.json by calling the Anthropic API for every (prompt, arm) pair. Requires ANTHROPIC_API_KEY in the environment and @anthropic-ai/sdk in the project. Expensive — run only when SKILL.md or prompts change. Use rulebook_evals_measure for the cheap offline comparison.',
|
|
2868
|
-
inputSchema: {
|
|
2869
|
-
projectId: projectIdSchema,
|
|
2870
|
-
},
|
|
2871
|
-
}, async () => {
|
|
2872
|
-
try {
|
|
2873
|
-
if (!process.env.ANTHROPIC_API_KEY) {
|
|
2874
|
-
return {
|
|
2875
|
-
content: [
|
|
2876
|
-
{
|
|
2877
|
-
type: 'text',
|
|
2878
|
-
text: JSON.stringify({
|
|
2879
|
-
success: false,
|
|
2880
|
-
error: 'ANTHROPIC_API_KEY is not set.',
|
|
2881
|
-
}),
|
|
2882
|
-
},
|
|
2883
|
-
],
|
|
2884
|
-
};
|
|
2885
|
-
}
|
|
2886
|
-
// Delegate to the CLI script — spawning keeps this non-blocking
|
|
2887
|
-
// and isolates the API dependency from the MCP server process.
|
|
2888
|
-
const { spawn } = await import('child_process');
|
|
2889
|
-
const result = await new Promise((resolvePromise) => {
|
|
2890
|
-
const child = spawn('npx', ['tsx', 'evals/llm_run.ts'], {
|
|
2891
|
-
cwd: process.cwd(),
|
|
2892
|
-
shell: true,
|
|
2893
|
-
});
|
|
2894
|
-
let stdout = '';
|
|
2895
|
-
let stderr = '';
|
|
2896
|
-
child.stdout.on('data', (d) => (stdout += String(d)));
|
|
2897
|
-
child.stderr.on('data', (d) => (stderr += String(d)));
|
|
2898
|
-
child.on('close', (code) => resolvePromise({ stdout, stderr, code: code ?? 1 }));
|
|
2899
|
-
});
|
|
2900
|
-
return {
|
|
2901
|
-
content: [
|
|
2902
|
-
{
|
|
2903
|
-
type: 'text',
|
|
2904
|
-
text: JSON.stringify({
|
|
2905
|
-
success: result.code === 0,
|
|
2906
|
-
exitCode: result.code,
|
|
2907
|
-
stdout: result.stdout.slice(-4000),
|
|
2908
|
-
stderr: result.stderr.slice(-2000),
|
|
2909
|
-
}),
|
|
2910
|
-
},
|
|
2911
|
-
],
|
|
2912
|
-
};
|
|
2913
|
-
}
|
|
2914
|
-
catch (error) {
|
|
2915
|
-
return {
|
|
2916
|
-
content: [
|
|
2917
|
-
{
|
|
2918
|
-
type: 'text',
|
|
2919
|
-
text: JSON.stringify({
|
|
2920
|
-
success: false,
|
|
2921
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2922
|
-
}),
|
|
2923
|
-
},
|
|
2924
|
-
],
|
|
2925
|
-
};
|
|
2926
|
-
}
|
|
2927
|
-
});
|
|
2928
|
-
server.registerTool('rulebook_compress_list', {
|
|
2929
|
-
title: 'List Compression Candidates',
|
|
2930
|
-
description: 'List markdown memory files in the project that are compression candidates (CLAUDE.md, AGENTS.md, AGENTS.override.md, .rulebook/PLANS.md, .rulebook/STATE.md, and all .md under .rulebook/knowledge/ + .rulebook/learnings/). Reports current size + backup state.',
|
|
2931
|
-
inputSchema: {
|
|
2932
|
-
projectId: projectIdSchema,
|
|
2933
|
-
},
|
|
2934
|
-
}, async () => {
|
|
2935
|
-
try {
|
|
2936
|
-
const { listCompressCandidates } = await import('../core/compress/discover.js');
|
|
2937
|
-
const candidates = await listCompressCandidates(process.cwd());
|
|
2938
|
-
return {
|
|
2939
|
-
content: [
|
|
2940
|
-
{
|
|
2941
|
-
type: 'text',
|
|
2942
|
-
text: JSON.stringify({ success: true, candidates, count: candidates.length }),
|
|
2943
|
-
},
|
|
2944
|
-
],
|
|
2945
|
-
};
|
|
2946
|
-
}
|
|
2947
|
-
catch (error) {
|
|
2948
|
-
return {
|
|
2949
|
-
content: [
|
|
2950
|
-
{
|
|
2951
|
-
type: 'text',
|
|
2952
|
-
text: JSON.stringify({
|
|
2953
|
-
success: false,
|
|
2954
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2955
|
-
}),
|
|
2956
|
-
},
|
|
2957
|
-
],
|
|
2958
|
-
};
|
|
2959
|
-
}
|
|
2960
|
-
});
|
|
2961
2017
|
const transport = new StdioServerTransport();
|
|
2962
2018
|
await server.connect(transport);
|
|
2963
2019
|
}
|