@hivehub/rulebook 4.1.0 → 4.2.1
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/continue.md +33 -33
- package/.claude/commands/ralph-config.md +112 -112
- package/.claude/commands/ralph-history.md +110 -110
- package/.claude/commands/ralph-init.md +72 -72
- package/.claude/commands/ralph-pause-resume.md +105 -105
- package/.claude/commands/ralph-run.md +101 -101
- package/.claude/commands/ralph-status.md +76 -76
- package/.claude/commands/rulebook-memory-save.md +48 -48
- package/.claude/commands/rulebook-memory-search.md +47 -47
- package/.claude/commands/rulebook-task-apply.md +67 -67
- package/.claude/commands/rulebook-task-archive.md +70 -70
- package/.claude/commands/rulebook-task-create.md +93 -93
- package/.claude/commands/rulebook-task-list.md +42 -42
- package/.claude/commands/rulebook-task-show.md +52 -52
- package/.claude/commands/rulebook-task-validate.md +53 -53
- package/.claude-plugin/marketplace.json +28 -28
- package/.claude-plugin/plugin.json +8 -8
- package/README.md +137 -1
- package/dist/cli/commands.d.ts +18 -6
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +727 -406
- package/dist/cli/commands.js.map +1 -1
- package/dist/core/claude-mcp.d.ts +4 -2
- package/dist/core/claude-mcp.d.ts.map +1 -1
- package/dist/core/claude-mcp.js +14 -9
- package/dist/core/claude-mcp.js.map +1 -1
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +13 -0
- package/dist/core/generator.js.map +1 -1
- package/dist/core/indexer/background-indexer.d.ts.map +1 -1
- package/dist/core/indexer/background-indexer.js +26 -5
- package/dist/core/indexer/background-indexer.js.map +1 -1
- package/dist/core/indexer/file-parser.d.ts.map +1 -1
- package/dist/core/indexer/file-parser.js +1 -1
- package/dist/core/indexer/file-parser.js.map +1 -1
- package/dist/core/indexer/indexer-types.d.ts.map +1 -1
- package/dist/core/workspace/legacy-migrator.d.ts +29 -0
- package/dist/core/workspace/legacy-migrator.d.ts.map +1 -0
- package/dist/core/workspace/legacy-migrator.js +142 -0
- package/dist/core/workspace/legacy-migrator.js.map +1 -0
- package/dist/core/workspace/project-worker.d.ts +49 -0
- package/dist/core/workspace/project-worker.d.ts.map +1 -0
- package/dist/core/workspace/project-worker.js +108 -0
- package/dist/core/workspace/project-worker.js.map +1 -0
- package/dist/core/workspace/workspace-manager.d.ts +90 -0
- package/dist/core/workspace/workspace-manager.d.ts.map +1 -0
- package/dist/core/workspace/workspace-manager.js +347 -0
- package/dist/core/workspace/workspace-manager.js.map +1 -0
- package/dist/core/workspace/workspace-types.d.ts +37 -0
- package/dist/core/workspace/workspace-types.d.ts.map +1 -0
- package/dist/core/workspace/workspace-types.js +8 -0
- package/dist/core/workspace/workspace-types.js.map +1 -0
- package/dist/index.js +43 -7
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +367 -100
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/memory-manager.js +2 -2
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/memory-search.js.map +1 -1
- package/dist/memory/memory-store.d.ts.map +1 -1
- package/dist/memory/memory-store.js +1 -1
- package/dist/memory/memory-store.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -21
- package/templates/agents/implementer.md +35 -35
- package/templates/agents/researcher.md +34 -34
- package/templates/agents/team-lead.md +34 -34
- package/templates/agents/tester.md +42 -42
- package/templates/ci/rulebook-review.yml +26 -26
- package/templates/cli/AIDER.md +49 -49
- package/templates/cli/AMAZON_Q.md +25 -25
- package/templates/cli/AUGGIE.md +32 -32
- package/templates/cli/CLAUDE.md +117 -117
- package/templates/cli/CLINE.md +99 -99
- package/templates/cli/CODEBUDDY.md +20 -20
- package/templates/cli/CODEIUM.md +20 -20
- package/templates/cli/CODEX.md +21 -21
- package/templates/cli/CONTINUE.md +34 -34
- package/templates/cli/CURSOR_CLI.md +62 -62
- package/templates/cli/FACTORY.md +18 -18
- package/templates/cli/GEMINI.md +35 -35
- package/templates/cli/KILOCODE.md +18 -18
- package/templates/cli/OPENCODE.md +18 -18
- package/templates/cli/_GENERIC_TEMPLATE.md +29 -29
- package/templates/commands/rulebook-memory-save.md +48 -48
- package/templates/commands/rulebook-memory-search.md +47 -47
- package/templates/commands/rulebook-task-apply.md +67 -67
- package/templates/commands/rulebook-task-archive.md +94 -94
- package/templates/commands/rulebook-task-create.md +93 -93
- package/templates/commands/rulebook-task-list.md +42 -42
- package/templates/commands/rulebook-task-show.md +52 -52
- package/templates/commands/rulebook-task-validate.md +53 -53
- package/templates/core/AGENTS_LEAN.md +25 -25
- package/templates/core/AGENTS_OVERRIDE.md +16 -16
- package/templates/core/AGENT_AUTOMATION.md +288 -288
- package/templates/core/DAG.md +304 -304
- package/templates/core/DOCUMENTATION_RULES.md +36 -36
- package/templates/core/MULTI_AGENT.md +74 -74
- package/templates/core/PLANS.md +28 -28
- package/templates/core/QUALITY_ENFORCEMENT.md +68 -68
- package/templates/core/RALPH.md +471 -471
- package/templates/core/RULEBOOK.md +1935 -1935
- package/templates/core/WORKSPACE.md +69 -0
- package/templates/frameworks/ANGULAR.md +36 -36
- package/templates/frameworks/DJANGO.md +83 -83
- package/templates/frameworks/ELECTRON.md +147 -147
- package/templates/frameworks/FLASK.md +38 -38
- package/templates/frameworks/FLUTTER.md +55 -55
- package/templates/frameworks/JQUERY.md +32 -32
- package/templates/frameworks/LARAVEL.md +38 -38
- package/templates/frameworks/NESTJS.md +43 -43
- package/templates/frameworks/NEXTJS.md +127 -127
- package/templates/frameworks/NUXT.md +40 -40
- package/templates/frameworks/RAILS.md +66 -66
- package/templates/frameworks/REACT.md +38 -38
- package/templates/frameworks/REACT_NATIVE.md +47 -47
- package/templates/frameworks/SPRING.md +39 -39
- package/templates/frameworks/SYMFONY.md +36 -36
- package/templates/frameworks/VUE.md +36 -36
- package/templates/frameworks/ZEND.md +35 -35
- package/templates/git/CI_CD_PATTERNS.md +661 -661
- package/templates/git/GITHUB_ACTIONS.md +728 -728
- package/templates/git/GITLAB_CI.md +730 -730
- package/templates/git/GIT_WORKFLOW.md +1157 -1157
- package/templates/git/SECRETS_MANAGEMENT.md +585 -585
- package/templates/hooks/COMMIT_MSG.md +530 -530
- package/templates/hooks/POST_CHECKOUT.md +546 -546
- package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -619
- package/templates/hooks/PRE_COMMIT.md +414 -414
- package/templates/hooks/PRE_PUSH.md +601 -601
- package/templates/ides/CONTINUE_RULES.md +16 -16
- package/templates/ides/COPILOT.md +37 -37
- package/templates/ides/COPILOT_INSTRUCTIONS.md +23 -23
- package/templates/ides/CURSOR.md +43 -43
- package/templates/ides/GEMINI_RULES.md +17 -17
- package/templates/ides/JETBRAINS_AI.md +35 -35
- package/templates/ides/REPLIT.md +36 -36
- package/templates/ides/TABNINE.md +29 -29
- package/templates/ides/VSCODE.md +40 -40
- package/templates/ides/WINDSURF.md +36 -36
- package/templates/ides/WINDSURF_RULES.md +14 -14
- package/templates/ides/ZED.md +32 -32
- package/templates/ides/cursor-mdc/go.mdc +24 -24
- package/templates/ides/cursor-mdc/python.mdc +24 -24
- package/templates/ides/cursor-mdc/quality.mdc +25 -25
- package/templates/ides/cursor-mdc/ralph.mdc +39 -39
- package/templates/ides/cursor-mdc/rulebook.mdc +38 -38
- package/templates/ides/cursor-mdc/rust.mdc +24 -24
- package/templates/ides/cursor-mdc/typescript.mdc +25 -25
- package/templates/languages/C.md +333 -333
- package/templates/languages/CPP.md +743 -743
- package/templates/languages/CSHARP.md +417 -417
- package/templates/languages/ELIXIR.md +454 -454
- package/templates/languages/ERLANG.md +361 -361
- package/templates/languages/GO.md +645 -645
- package/templates/languages/HASKELL.md +177 -177
- package/templates/languages/JAVA.md +607 -607
- package/templates/languages/JAVASCRIPT.md +631 -631
- package/templates/languages/JULIA.md +97 -97
- package/templates/languages/KOTLIN.md +511 -511
- package/templates/languages/LISP.md +100 -100
- package/templates/languages/LUA.md +74 -74
- package/templates/languages/OBJECTIVEC.md +90 -90
- package/templates/languages/PHP.md +416 -416
- package/templates/languages/PYTHON.md +682 -682
- package/templates/languages/RUBY.md +421 -421
- package/templates/languages/RUST.md +477 -477
- package/templates/languages/SAS.md +73 -73
- package/templates/languages/SCALA.md +348 -348
- package/templates/languages/SOLIDITY.md +580 -580
- package/templates/languages/SQL.md +137 -137
- package/templates/languages/SWIFT.md +466 -466
- package/templates/languages/TYPESCRIPT.md +591 -591
- package/templates/languages/ZIG.md +265 -265
- package/templates/modules/ATLASSIAN.md +255 -255
- package/templates/modules/CONTEXT7.md +54 -54
- package/templates/modules/FIGMA.md +267 -267
- package/templates/modules/GITHUB_MCP.md +64 -64
- package/templates/modules/GRAFANA.md +328 -328
- package/templates/modules/MEMORY.md +126 -126
- package/templates/modules/NOTION.md +247 -247
- package/templates/modules/PLAYWRIGHT.md +90 -90
- package/templates/modules/RULEBOOK_MCP.md +156 -156
- package/templates/modules/SERENA.md +337 -337
- package/templates/modules/SUPABASE.md +223 -223
- package/templates/modules/SYNAP.md +69 -69
- package/templates/modules/VECTORIZER.md +63 -63
- package/templates/modules/sequential-thinking.md +42 -42
- package/templates/ralph/ralph-history.bat +4 -4
- package/templates/ralph/ralph-history.sh +5 -5
- package/templates/ralph/ralph-init.bat +5 -5
- package/templates/ralph/ralph-init.sh +5 -5
- package/templates/ralph/ralph-pause.bat +5 -5
- package/templates/ralph/ralph-pause.sh +5 -5
- package/templates/ralph/ralph-run.bat +5 -5
- package/templates/ralph/ralph-run.sh +5 -5
- package/templates/ralph/ralph-status.bat +4 -4
- package/templates/ralph/ralph-status.sh +5 -5
- package/templates/services/AZURE_BLOB.md +184 -184
- package/templates/services/CASSANDRA.md +239 -239
- package/templates/services/DATADOG.md +26 -26
- package/templates/services/DOCKER.md +124 -124
- package/templates/services/DOCKER_COMPOSE.md +168 -168
- package/templates/services/DYNAMODB.md +308 -308
- package/templates/services/ELASTICSEARCH.md +347 -347
- package/templates/services/GCS.md +178 -178
- package/templates/services/HELM.md +194 -194
- package/templates/services/INFLUXDB.md +265 -265
- package/templates/services/KAFKA.md +341 -341
- package/templates/services/KUBERNETES.md +208 -208
- package/templates/services/MARIADB.md +183 -183
- package/templates/services/MEMCACHED.md +242 -242
- package/templates/services/MINIO.md +201 -201
- package/templates/services/MONGODB.md +268 -268
- package/templates/services/MYSQL.md +358 -358
- package/templates/services/NEO4J.md +247 -247
- package/templates/services/OPENTELEMETRY.md +25 -25
- package/templates/services/ORACLE.md +290 -290
- package/templates/services/PINO.md +24 -24
- package/templates/services/POSTGRESQL.md +326 -326
- package/templates/services/PROMETHEUS.md +33 -33
- package/templates/services/RABBITMQ.md +286 -286
- package/templates/services/REDIS.md +292 -292
- package/templates/services/S3.md +298 -298
- package/templates/services/SENTRY.md +23 -23
- package/templates/services/SQLITE.md +294 -294
- package/templates/services/SQLSERVER.md +294 -294
- package/templates/services/WINSTON.md +30 -30
- package/templates/skills/cli/aider/SKILL.md +59 -59
- package/templates/skills/cli/amazon-q/SKILL.md +35 -35
- package/templates/skills/cli/auggie/SKILL.md +42 -42
- package/templates/skills/cli/claude/SKILL.md +42 -42
- package/templates/skills/cli/cline/SKILL.md +42 -42
- package/templates/skills/cli/codebuddy/SKILL.md +30 -30
- package/templates/skills/cli/codeium/SKILL.md +30 -30
- package/templates/skills/cli/codex/SKILL.md +31 -31
- package/templates/skills/cli/continue/SKILL.md +44 -44
- package/templates/skills/cli/cursor-cli/SKILL.md +38 -38
- package/templates/skills/cli/factory/SKILL.md +28 -28
- package/templates/skills/cli/gemini/SKILL.md +45 -45
- package/templates/skills/cli/kilocode/SKILL.md +28 -28
- package/templates/skills/cli/opencode/SKILL.md +28 -28
- package/templates/skills/core/agent-automation/SKILL.md +194 -194
- package/templates/skills/core/dag/SKILL.md +314 -314
- package/templates/skills/core/documentation-rules/SKILL.md +46 -46
- package/templates/skills/core/quality-enforcement/SKILL.md +78 -78
- package/templates/skills/core/rulebook/SKILL.md +176 -176
- package/templates/skills/frameworks/angular/SKILL.md +46 -46
- package/templates/skills/frameworks/django/SKILL.md +93 -93
- package/templates/skills/frameworks/electron/SKILL.md +157 -157
- package/templates/skills/frameworks/flask/SKILL.md +48 -48
- package/templates/skills/frameworks/flutter/SKILL.md +65 -65
- package/templates/skills/frameworks/jquery/SKILL.md +42 -42
- package/templates/skills/frameworks/laravel/SKILL.md +48 -48
- package/templates/skills/frameworks/nestjs/SKILL.md +53 -53
- package/templates/skills/frameworks/nextjs/SKILL.md +137 -137
- package/templates/skills/frameworks/nuxt/SKILL.md +50 -50
- package/templates/skills/frameworks/rails/SKILL.md +76 -76
- package/templates/skills/frameworks/react/SKILL.md +48 -48
- package/templates/skills/frameworks/react-native/SKILL.md +57 -57
- package/templates/skills/frameworks/spring/SKILL.md +49 -49
- package/templates/skills/frameworks/symfony/SKILL.md +46 -46
- package/templates/skills/frameworks/vue/SKILL.md +46 -46
- package/templates/skills/frameworks/zend/SKILL.md +45 -45
- package/templates/skills/ides/copilot/SKILL.md +47 -47
- package/templates/skills/ides/cursor/SKILL.md +53 -53
- package/templates/skills/ides/jetbrains-ai/SKILL.md +45 -45
- package/templates/skills/ides/replit/SKILL.md +46 -46
- package/templates/skills/ides/tabnine/SKILL.md +39 -39
- package/templates/skills/ides/vscode/SKILL.md +50 -50
- package/templates/skills/ides/windsurf/SKILL.md +46 -46
- package/templates/skills/ides/zed/SKILL.md +42 -42
- package/templates/skills/languages/c/SKILL.md +343 -343
- package/templates/skills/languages/cpp/SKILL.md +753 -753
- package/templates/skills/languages/csharp/SKILL.md +427 -427
- package/templates/skills/languages/elixir/SKILL.md +464 -464
- package/templates/skills/languages/erlang/SKILL.md +371 -371
- package/templates/skills/languages/go/SKILL.md +655 -655
- package/templates/skills/languages/haskell/SKILL.md +187 -187
- package/templates/skills/languages/java/SKILL.md +617 -617
- package/templates/skills/languages/javascript/SKILL.md +641 -641
- package/templates/skills/languages/julia/SKILL.md +107 -107
- package/templates/skills/languages/kotlin/SKILL.md +521 -521
- package/templates/skills/languages/lisp/SKILL.md +110 -110
- package/templates/skills/languages/lua/SKILL.md +84 -84
- package/templates/skills/languages/objectivec/SKILL.md +100 -100
- package/templates/skills/languages/php/SKILL.md +426 -426
- package/templates/skills/languages/python/SKILL.md +692 -692
- package/templates/skills/languages/ruby/SKILL.md +431 -431
- package/templates/skills/languages/rust/SKILL.md +487 -487
- package/templates/skills/languages/sas/SKILL.md +83 -83
- package/templates/skills/languages/scala/SKILL.md +358 -358
- package/templates/skills/languages/solidity/SKILL.md +590 -590
- package/templates/skills/languages/sql/SKILL.md +147 -147
- package/templates/skills/languages/swift/SKILL.md +476 -476
- package/templates/skills/languages/typescript/SKILL.md +302 -302
- package/templates/skills/languages/zig/SKILL.md +275 -275
- package/templates/skills/modules/atlassian/SKILL.md +265 -265
- package/templates/skills/modules/context7/SKILL.md +64 -64
- package/templates/skills/modules/figma/SKILL.md +277 -277
- package/templates/skills/modules/github-mcp/SKILL.md +74 -74
- package/templates/skills/modules/grafana/SKILL.md +338 -338
- package/templates/skills/modules/memory/SKILL.md +73 -73
- package/templates/skills/modules/notion/SKILL.md +257 -257
- package/templates/skills/modules/playwright/SKILL.md +100 -100
- package/templates/skills/modules/rulebook-mcp/SKILL.md +166 -166
- package/templates/skills/modules/serena/SKILL.md +347 -347
- package/templates/skills/modules/supabase/SKILL.md +233 -233
- package/templates/skills/modules/synap/SKILL.md +79 -79
- package/templates/skills/modules/vectorizer/SKILL.md +73 -73
- package/templates/skills/services/azure-blob/SKILL.md +194 -194
- package/templates/skills/services/cassandra/SKILL.md +249 -249
- package/templates/skills/services/dynamodb/SKILL.md +318 -318
- package/templates/skills/services/elasticsearch/SKILL.md +357 -357
- package/templates/skills/services/gcs/SKILL.md +188 -188
- package/templates/skills/services/influxdb/SKILL.md +275 -275
- package/templates/skills/services/kafka/SKILL.md +351 -351
- package/templates/skills/services/mariadb/SKILL.md +193 -193
- package/templates/skills/services/memcached/SKILL.md +252 -252
- package/templates/skills/services/minio/SKILL.md +211 -211
- package/templates/skills/services/mongodb/SKILL.md +278 -278
- package/templates/skills/services/mysql/SKILL.md +368 -368
- package/templates/skills/services/neo4j/SKILL.md +257 -257
- package/templates/skills/services/oracle/SKILL.md +300 -300
- package/templates/skills/services/postgresql/SKILL.md +336 -336
- package/templates/skills/services/rabbitmq/SKILL.md +296 -296
- package/templates/skills/services/redis/SKILL.md +302 -302
- package/templates/skills/services/s3/SKILL.md +308 -308
- package/templates/skills/services/sqlite/SKILL.md +304 -304
- package/templates/skills/services/sqlserver/SKILL.md +304 -304
- package/templates/skills/workflows/ralph/SKILL.md +309 -309
- package/templates/skills/workflows/ralph/install.sh +87 -87
- package/templates/skills/workflows/ralph/manifest.json +158 -158
package/dist/cli/commands.js
CHANGED
|
@@ -10,9 +10,11 @@ import { parseRulesIgnore } from '../utils/rulesignore.js';
|
|
|
10
10
|
import { installGitHooks } from '../utils/git-hooks.js';
|
|
11
11
|
import { scaffoldMinimalProject } from '../core/minimal-scaffolder.js';
|
|
12
12
|
import path from 'path';
|
|
13
|
-
import { readFileSync } from 'fs';
|
|
13
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
|
|
16
|
+
import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
|
|
17
|
+
import { migrateLegacyMcpConfigs } from '../core/workspace/legacy-migrator.js';
|
|
16
18
|
const FRAMEWORK_LABELS = {
|
|
17
19
|
nestjs: 'NestJS',
|
|
18
20
|
spring: 'Spring Boot',
|
|
@@ -900,19 +902,68 @@ export async function configCommand(options) {
|
|
|
900
902
|
process.exit(1);
|
|
901
903
|
}
|
|
902
904
|
}
|
|
905
|
+
/**
|
|
906
|
+
* Resolve a TaskManager for a specific project.
|
|
907
|
+
*
|
|
908
|
+
* Resolution order:
|
|
909
|
+
* 1. Explicit --project flag → find workspace config from cwd, route to named project
|
|
910
|
+
* 2. Auto-detect → walk up from cwd to find workspace, match cwd to a project
|
|
911
|
+
* 3. Fallback → single-project from cwd (no workspace)
|
|
912
|
+
*/
|
|
913
|
+
async function resolveTaskManager(cwd, options) {
|
|
914
|
+
const { createTaskManager } = await import('../core/task-manager.js');
|
|
915
|
+
const { createConfigManager } = await import('../core/config-manager.js');
|
|
916
|
+
const { isAbsolute, resolve } = await import('path');
|
|
917
|
+
// Helper: build TaskManager from a resolved project root
|
|
918
|
+
const buildFromProjectRoot = async (projectRoot, label) => {
|
|
919
|
+
const configManager = createConfigManager(projectRoot);
|
|
920
|
+
const config = await configManager.loadConfig();
|
|
921
|
+
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
922
|
+
return { taskManager: createTaskManager(projectRoot, rulebookDir), projectLabel: label };
|
|
923
|
+
};
|
|
924
|
+
// 1. Explicit --project flag
|
|
925
|
+
if (options?.project) {
|
|
926
|
+
const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
|
|
927
|
+
if (!ws) {
|
|
928
|
+
console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
|
|
929
|
+
process.exit(1);
|
|
930
|
+
}
|
|
931
|
+
const project = ws.config.projects.find((p) => p.name === options.project);
|
|
932
|
+
if (!project) {
|
|
933
|
+
console.error(chalk.red(`Project "${options.project}" not found in workspace.`));
|
|
934
|
+
console.error(chalk.gray(`Available: ${ws.config.projects.map((p) => p.name).join(', ')}`));
|
|
935
|
+
process.exit(1);
|
|
936
|
+
}
|
|
937
|
+
const projectRoot = isAbsolute(project.path)
|
|
938
|
+
? project.path
|
|
939
|
+
: resolve(ws.root, project.path);
|
|
940
|
+
return buildFromProjectRoot(projectRoot, project.name);
|
|
941
|
+
}
|
|
942
|
+
// 2. Auto-detect: walk up from cwd to find workspace and match project
|
|
943
|
+
const resolved = WorkspaceManager.resolveProjectFromCwd(cwd);
|
|
944
|
+
if (resolved) {
|
|
945
|
+
const project = resolved.config.projects.find((p) => p.name === resolved.projectName);
|
|
946
|
+
if (project) {
|
|
947
|
+
const projectRoot = isAbsolute(project.path)
|
|
948
|
+
? project.path
|
|
949
|
+
: resolve(resolved.root, project.path);
|
|
950
|
+
return buildFromProjectRoot(projectRoot, project.name);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
// 3. Fallback: single-project from cwd
|
|
954
|
+
const configManager = createConfigManager(cwd);
|
|
955
|
+
const config = await configManager.loadConfig();
|
|
956
|
+
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
957
|
+
return { taskManager: createTaskManager(cwd, rulebookDir) };
|
|
958
|
+
}
|
|
903
959
|
// Task management commands using Rulebook task system
|
|
904
|
-
export async function taskCreateCommand(taskId) {
|
|
960
|
+
export async function taskCreateCommand(taskId, wsOptions) {
|
|
905
961
|
try {
|
|
906
962
|
const cwd = process.cwd();
|
|
907
|
-
const {
|
|
908
|
-
const { createConfigManager } = await import('../core/config-manager.js');
|
|
909
|
-
const configManager = createConfigManager(cwd);
|
|
910
|
-
const config = await configManager.loadConfig();
|
|
911
|
-
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
912
|
-
const taskManager = createTaskManager(cwd, rulebookDir);
|
|
963
|
+
const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
|
|
913
964
|
await taskManager.createTask(taskId);
|
|
914
|
-
|
|
915
|
-
console.log(chalk.
|
|
965
|
+
const prefix = projectLabel ? `[${projectLabel}] ` : '';
|
|
966
|
+
console.log(chalk.green(`✅ ${prefix}Task ${taskId} created successfully`));
|
|
916
967
|
console.log(chalk.yellow('\n⚠️ Remember to:'));
|
|
917
968
|
console.log(chalk.gray(' 1. Fill in proposal.md (minimum 20 characters in "Why" section)'));
|
|
918
969
|
console.log(chalk.gray(' 3. Add tasks to tasks.md'));
|
|
@@ -924,21 +975,66 @@ export async function taskCreateCommand(taskId) {
|
|
|
924
975
|
process.exit(1);
|
|
925
976
|
}
|
|
926
977
|
}
|
|
927
|
-
export async function taskListCommand(includeArchived = false) {
|
|
978
|
+
export async function taskListCommand(includeArchived = false, wsOptions) {
|
|
928
979
|
try {
|
|
929
980
|
const cwd = process.cwd();
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
981
|
+
// --all-projects: list tasks from every workspace project
|
|
982
|
+
if (wsOptions?.allProjects) {
|
|
983
|
+
const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
|
|
984
|
+
if (!ws) {
|
|
985
|
+
console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
|
|
986
|
+
process.exit(1);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
console.log(chalk.bold.blue(`\n📋 Workspace Tasks (${ws.config.name})\n`));
|
|
990
|
+
const { createTaskManager } = await import('../core/task-manager.js');
|
|
991
|
+
const { createConfigManager } = await import('../core/config-manager.js');
|
|
992
|
+
const { isAbsolute, resolve } = await import('path');
|
|
993
|
+
let totalTasks = 0;
|
|
994
|
+
for (const project of ws.config.projects) {
|
|
995
|
+
const projectRoot = isAbsolute(project.path) ? project.path : resolve(ws.root, project.path);
|
|
996
|
+
try {
|
|
997
|
+
const configManager = createConfigManager(projectRoot);
|
|
998
|
+
const config = await configManager.loadConfig();
|
|
999
|
+
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
1000
|
+
const taskManager = createTaskManager(projectRoot, rulebookDir);
|
|
1001
|
+
const tasks = await taskManager.listTasks(includeArchived);
|
|
1002
|
+
if (tasks.length > 0) {
|
|
1003
|
+
console.log(chalk.bold.cyan(` [${project.name}]`));
|
|
1004
|
+
for (const task of tasks) {
|
|
1005
|
+
const statusColor = task.status === 'completed'
|
|
1006
|
+
? chalk.green
|
|
1007
|
+
: task.status === 'in-progress'
|
|
1008
|
+
? chalk.yellow
|
|
1009
|
+
: task.status === 'blocked'
|
|
1010
|
+
? chalk.red
|
|
1011
|
+
: chalk.gray;
|
|
1012
|
+
console.log(` ${statusColor(task.status.padEnd(12))} ${chalk.white(task.id)} - ${chalk.gray(task.title)}`);
|
|
1013
|
+
}
|
|
1014
|
+
console.log('');
|
|
1015
|
+
totalTasks += tasks.length;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
catch {
|
|
1019
|
+
console.log(chalk.bold.cyan(` [${project.name}]`));
|
|
1020
|
+
console.log(chalk.gray(' No tasks or no .rulebook config'));
|
|
1021
|
+
console.log('');
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
console.log(chalk.gray(`${totalTasks} task(s) across ${ws.config.projects.length} project(s)`));
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
// Single project (optionally targeted via --project)
|
|
1028
|
+
const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
|
|
936
1029
|
const tasks = await taskManager.listTasks(includeArchived);
|
|
937
1030
|
if (tasks.length === 0) {
|
|
938
1031
|
console.log(chalk.gray('No tasks found'));
|
|
939
1032
|
return;
|
|
940
1033
|
}
|
|
941
|
-
|
|
1034
|
+
const header = projectLabel
|
|
1035
|
+
? `\n📋 Rulebook Tasks [${projectLabel}]\n`
|
|
1036
|
+
: '\n📋 Rulebook Tasks\n';
|
|
1037
|
+
console.log(chalk.bold.blue(header));
|
|
942
1038
|
const activeTasks = tasks.filter((t) => !t.archivedAt);
|
|
943
1039
|
const archivedTasks = tasks.filter((t) => t.archivedAt);
|
|
944
1040
|
if (activeTasks.length > 0) {
|
|
@@ -968,15 +1064,10 @@ export async function taskListCommand(includeArchived = false) {
|
|
|
968
1064
|
process.exit(1);
|
|
969
1065
|
}
|
|
970
1066
|
}
|
|
971
|
-
export async function taskShowCommand(taskId) {
|
|
1067
|
+
export async function taskShowCommand(taskId, wsOptions) {
|
|
972
1068
|
try {
|
|
973
1069
|
const cwd = process.cwd();
|
|
974
|
-
const {
|
|
975
|
-
const { createConfigManager } = await import('../core/config-manager.js');
|
|
976
|
-
const configManager = createConfigManager(cwd);
|
|
977
|
-
const config = await configManager.loadConfig();
|
|
978
|
-
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
979
|
-
const taskManager = createTaskManager(cwd, rulebookDir);
|
|
1070
|
+
const { taskManager } = await resolveTaskManager(cwd, wsOptions);
|
|
980
1071
|
const task = await taskManager.showTask(taskId);
|
|
981
1072
|
if (!task) {
|
|
982
1073
|
console.error(chalk.red(`❌ Task ${taskId} not found`));
|
|
@@ -1010,15 +1101,10 @@ export async function taskShowCommand(taskId) {
|
|
|
1010
1101
|
process.exit(1);
|
|
1011
1102
|
}
|
|
1012
1103
|
}
|
|
1013
|
-
export async function taskValidateCommand(taskId) {
|
|
1104
|
+
export async function taskValidateCommand(taskId, wsOptions) {
|
|
1014
1105
|
try {
|
|
1015
1106
|
const cwd = process.cwd();
|
|
1016
|
-
const {
|
|
1017
|
-
const { createConfigManager } = await import('../core/config-manager.js');
|
|
1018
|
-
const configManager = createConfigManager(cwd);
|
|
1019
|
-
const config = await configManager.loadConfig();
|
|
1020
|
-
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
1021
|
-
const taskManager = createTaskManager(cwd, rulebookDir);
|
|
1107
|
+
const { taskManager } = await resolveTaskManager(cwd, wsOptions);
|
|
1022
1108
|
const validation = await taskManager.validateTask(taskId);
|
|
1023
1109
|
if (validation.valid) {
|
|
1024
1110
|
console.log(chalk.green(`✅ Task ${taskId} is valid`));
|
|
@@ -1049,17 +1135,13 @@ export async function taskValidateCommand(taskId) {
|
|
|
1049
1135
|
process.exit(1);
|
|
1050
1136
|
}
|
|
1051
1137
|
}
|
|
1052
|
-
export async function taskArchiveCommand(taskId, skipValidation = false) {
|
|
1138
|
+
export async function taskArchiveCommand(taskId, skipValidation = false, wsOptions) {
|
|
1053
1139
|
try {
|
|
1054
1140
|
const cwd = process.cwd();
|
|
1055
|
-
const {
|
|
1056
|
-
const { createConfigManager } = await import('../core/config-manager.js');
|
|
1057
|
-
const configManager = createConfigManager(cwd);
|
|
1058
|
-
const config = await configManager.loadConfig();
|
|
1059
|
-
const rulebookDir = config.rulebookDir || '.rulebook';
|
|
1060
|
-
const taskManager = createTaskManager(cwd, rulebookDir);
|
|
1141
|
+
const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
|
|
1061
1142
|
await taskManager.archiveTask(taskId, skipValidation);
|
|
1062
|
-
|
|
1143
|
+
const prefix = projectLabel ? `[${projectLabel}] ` : '';
|
|
1144
|
+
console.log(chalk.green(`✅ ${prefix}Task ${taskId} archived successfully`));
|
|
1063
1145
|
}
|
|
1064
1146
|
catch (error) {
|
|
1065
1147
|
console.error(chalk.red(`❌ Failed to archive task: ${error.message}`));
|
|
@@ -1070,23 +1152,59 @@ export async function taskArchiveCommand(taskId, skipValidation = false) {
|
|
|
1070
1152
|
* Initialize MCP configuration in .rulebook file
|
|
1071
1153
|
* Adds mcp block to .rulebook and creates/updates .cursor/mcp.json
|
|
1072
1154
|
*/
|
|
1073
|
-
export async function mcpInitCommand() {
|
|
1155
|
+
export async function mcpInitCommand(options) {
|
|
1074
1156
|
const { findRulebookConfig } = await import('../mcp/rulebook-server.js');
|
|
1075
1157
|
const { existsSync, readFileSync, writeFileSync, statSync } = await import('fs');
|
|
1076
1158
|
const { join, dirname } = await import('path');
|
|
1077
1159
|
const { createConfigManager } = await import('../core/config-manager.js');
|
|
1078
1160
|
try {
|
|
1079
|
-
// Find or create .rulebook file/directory
|
|
1080
1161
|
const cwd = process.cwd();
|
|
1162
|
+
// --- Workspace mode ---
|
|
1163
|
+
if (options?.workspace) {
|
|
1164
|
+
const wsConfig = WorkspaceManager.findWorkspaceConfig(cwd);
|
|
1165
|
+
if (!wsConfig) {
|
|
1166
|
+
console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
|
|
1167
|
+
process.exit(1);
|
|
1168
|
+
}
|
|
1169
|
+
const mcpArgs = ['-y', '@hivehub/rulebook@latest', 'mcp-server', '--workspace'];
|
|
1170
|
+
const mcpEntry = { command: 'npx', args: mcpArgs };
|
|
1171
|
+
// Write to .cursor/mcp.json if .cursor exists
|
|
1172
|
+
const cursorDir = join(cwd, '.cursor');
|
|
1173
|
+
if (existsSync(cursorDir)) {
|
|
1174
|
+
const mcpJsonPath = join(cursorDir, 'mcp.json');
|
|
1175
|
+
let mcpConfig = { mcpServers: {} };
|
|
1176
|
+
if (existsSync(mcpJsonPath)) {
|
|
1177
|
+
mcpConfig = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
|
|
1178
|
+
}
|
|
1179
|
+
mcpConfig.mcpServers = mcpConfig.mcpServers ?? {};
|
|
1180
|
+
mcpConfig.mcpServers.rulebook = mcpEntry;
|
|
1181
|
+
writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
1182
|
+
console.log(chalk.green('✓ Workspace MCP initialized'));
|
|
1183
|
+
console.log(chalk.gray(` • Updated .cursor/mcp.json with --workspace flag`));
|
|
1184
|
+
}
|
|
1185
|
+
// Write to .mcp.json (Claude Code format)
|
|
1186
|
+
const mcpJsonPath = join(cwd, '.mcp.json');
|
|
1187
|
+
let mcpConfig = { mcpServers: {} };
|
|
1188
|
+
if (existsSync(mcpJsonPath)) {
|
|
1189
|
+
mcpConfig = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
|
|
1190
|
+
}
|
|
1191
|
+
mcpConfig.mcpServers = mcpConfig.mcpServers ?? {};
|
|
1192
|
+
mcpConfig.mcpServers.rulebook = mcpEntry;
|
|
1193
|
+
writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
1194
|
+
console.log(chalk.green('✓ Workspace MCP initialized'));
|
|
1195
|
+
console.log(chalk.gray(` • Workspace: ${wsConfig.name} (${wsConfig.projects.length} projects)`));
|
|
1196
|
+
console.log(chalk.gray(` • Updated .mcp.json with --workspace flag`));
|
|
1197
|
+
console.log(chalk.gray(` • MCP server will manage all projects automatically`));
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
// --- Single-project mode (original behavior) ---
|
|
1081
1201
|
let rulebookPath = findRulebookConfig(cwd);
|
|
1082
1202
|
if (!rulebookPath) {
|
|
1083
|
-
// Create new .rulebook directory via ConfigManager
|
|
1084
1203
|
rulebookPath = join(cwd, '.rulebook');
|
|
1085
1204
|
const configManager = createConfigManager(cwd);
|
|
1086
1205
|
await configManager.initializeConfig();
|
|
1087
1206
|
}
|
|
1088
1207
|
const projectRoot = dirname(rulebookPath);
|
|
1089
|
-
// Resolve config file path (handle .rulebook as directory or file)
|
|
1090
1208
|
let configFilePath = rulebookPath;
|
|
1091
1209
|
if (existsSync(rulebookPath)) {
|
|
1092
1210
|
const stats = statSync(rulebookPath);
|
|
@@ -1094,13 +1212,11 @@ export async function mcpInitCommand() {
|
|
|
1094
1212
|
configFilePath = join(rulebookPath, 'rulebook.json');
|
|
1095
1213
|
}
|
|
1096
1214
|
}
|
|
1097
|
-
// Load existing config
|
|
1098
1215
|
let config = {};
|
|
1099
1216
|
if (existsSync(configFilePath)) {
|
|
1100
1217
|
const raw = readFileSync(configFilePath, 'utf8');
|
|
1101
1218
|
config = JSON.parse(raw);
|
|
1102
1219
|
}
|
|
1103
|
-
// Add/update mcp block
|
|
1104
1220
|
config.mcp = config.mcp ?? {};
|
|
1105
1221
|
if (config.mcp.enabled === undefined)
|
|
1106
1222
|
config.mcp.enabled = true;
|
|
@@ -1108,9 +1224,7 @@ export async function mcpInitCommand() {
|
|
|
1108
1224
|
config.mcp.tasksDir = '.rulebook/tasks';
|
|
1109
1225
|
if (!config.mcp.archiveDir)
|
|
1110
1226
|
config.mcp.archiveDir = '.rulebook/archive';
|
|
1111
|
-
// Save updated config
|
|
1112
1227
|
writeFileSync(configFilePath, JSON.stringify(config, null, 2) + '\n');
|
|
1113
|
-
// Create/update .cursor/mcp.json if .cursor directory exists
|
|
1114
1228
|
const cursorDir = join(projectRoot, '.cursor');
|
|
1115
1229
|
if (existsSync(cursorDir)) {
|
|
1116
1230
|
const mcpJsonPath = join(cursorDir, 'mcp.json');
|
|
@@ -1181,144 +1295,195 @@ export async function tasksCommand(options) {
|
|
|
1181
1295
|
export async function updateCommand(options) {
|
|
1182
1296
|
try {
|
|
1183
1297
|
const cwd = process.cwd();
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1298
|
+
// Auto-detect workspace: if inside a workspace, update ALL projects
|
|
1299
|
+
const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
|
|
1300
|
+
if (ws && ws.config.projects.length > 1) {
|
|
1301
|
+
console.log(chalk.bold.blue('\n🔄 Rulebook Workspace Update\n'));
|
|
1302
|
+
console.log(chalk.gray(`Workspace "${ws.config.name}" detected — updating ${ws.config.projects.length} projects\n`));
|
|
1303
|
+
const { isAbsolute, resolve, join } = await import('path');
|
|
1304
|
+
const fsPromises = await import('fs/promises');
|
|
1305
|
+
let updatedCount = 0;
|
|
1306
|
+
// Build workspace project list for the template
|
|
1307
|
+
const projectListMd = ws.config.projects
|
|
1308
|
+
.map((p) => {
|
|
1309
|
+
const root = isAbsolute(p.path) ? p.path : resolve(ws.root, p.path);
|
|
1310
|
+
return `- **${p.name}** → \`${root}\``;
|
|
1311
|
+
})
|
|
1312
|
+
.join('\n');
|
|
1313
|
+
// Load WORKSPACE.md template
|
|
1314
|
+
const { getDefaultTemplatesPath } = await import('../core/skills-manager.js');
|
|
1315
|
+
let workspaceTplContent = '';
|
|
1316
|
+
try {
|
|
1317
|
+
const tplPath = join(getDefaultTemplatesPath(), 'core', 'WORKSPACE.md');
|
|
1318
|
+
workspaceTplContent = await fsPromises.readFile(tplPath, 'utf-8');
|
|
1195
1319
|
}
|
|
1320
|
+
catch {
|
|
1321
|
+
// Template not available — skip
|
|
1322
|
+
}
|
|
1323
|
+
for (const project of ws.config.projects) {
|
|
1324
|
+
const projectRoot = isAbsolute(project.path)
|
|
1325
|
+
? project.path
|
|
1326
|
+
: resolve(ws.root, project.path);
|
|
1327
|
+
console.log(chalk.bold.cyan(`\n━━━ [${project.name}] ${projectRoot} ━━━\n`));
|
|
1328
|
+
try {
|
|
1329
|
+
await updateSingleProject(projectRoot, options);
|
|
1330
|
+
// Inject WORKSPACE.md spec into this project's .rulebook/specs/
|
|
1331
|
+
if (workspaceTplContent) {
|
|
1332
|
+
const specsDir = join(projectRoot, '.rulebook', 'specs');
|
|
1333
|
+
await fsPromises.mkdir(specsDir, { recursive: true });
|
|
1334
|
+
const rendered = workspaceTplContent
|
|
1335
|
+
.replace('{{DEFAULT_PROJECT}}', ws.config.defaultProject ?? ws.config.projects[0]?.name ?? '')
|
|
1336
|
+
.replace('{{WORKSPACE_PROJECTS}}', projectListMd);
|
|
1337
|
+
await fsPromises.writeFile(join(specsDir, 'WORKSPACE.md'), rendered, 'utf-8');
|
|
1338
|
+
}
|
|
1339
|
+
updatedCount++;
|
|
1340
|
+
}
|
|
1341
|
+
catch (error) {
|
|
1342
|
+
console.error(chalk.red(` ❌ Failed to update ${project.name}: ${error.message}`));
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
console.log(chalk.bold.green(`\n✅ Workspace update complete — ${updatedCount}/${ws.config.projects.length} projects updated\n`));
|
|
1346
|
+
return;
|
|
1196
1347
|
}
|
|
1197
|
-
//
|
|
1198
|
-
|
|
1199
|
-
|
|
1348
|
+
// Single project update
|
|
1349
|
+
console.log(chalk.bold.blue('\n🔄 Rulebook Update Tool\n'));
|
|
1350
|
+
console.log(chalk.gray('This will update your AGENTS.md and .rulebook to the latest version\n'));
|
|
1351
|
+
await updateSingleProject(cwd, options);
|
|
1352
|
+
}
|
|
1353
|
+
catch (error) {
|
|
1354
|
+
console.error(chalk.red('\n❌ Update failed:'), error);
|
|
1355
|
+
process.exit(1);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/** Update a single project at the given root directory. */
|
|
1359
|
+
async function updateSingleProject(cwd, options) {
|
|
1360
|
+
// Detect project
|
|
1361
|
+
const spinner = ora('Detecting project structure...').start();
|
|
1362
|
+
const detection = await detectProject(cwd);
|
|
1363
|
+
spinner.succeed('Project detection complete');
|
|
1364
|
+
// Show detected languages
|
|
1365
|
+
if (detection.languages.length > 0) {
|
|
1366
|
+
console.log(chalk.green('\n✓ Detected languages:'));
|
|
1367
|
+
for (const lang of detection.languages) {
|
|
1368
|
+
console.log(` - ${lang.language} (${(lang.confidence * 100).toFixed(0)}% confidence)`);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
// Check for existing AGENTS.md
|
|
1372
|
+
if (!detection.existingAgents) {
|
|
1373
|
+
console.log(chalk.yellow('\n⚠ No AGENTS.md found. Use "rulebook init" instead.'));
|
|
1374
|
+
process.exit(0);
|
|
1375
|
+
}
|
|
1376
|
+
console.log(chalk.green(`\n✓ Found existing AGENTS.md with ${detection.existingAgents.blocks.length} blocks`));
|
|
1377
|
+
// Get existing blocks to preserve user customizations
|
|
1378
|
+
const existingBlocks = detection.existingAgents.blocks.map((b) => b.name);
|
|
1379
|
+
console.log(chalk.gray(` Existing blocks: ${existingBlocks.join(', ')}`));
|
|
1380
|
+
let inquirerModule = null;
|
|
1381
|
+
if (!options.yes) {
|
|
1382
|
+
inquirerModule = (await import('inquirer')).default;
|
|
1383
|
+
const { confirm } = await inquirerModule.prompt([
|
|
1384
|
+
{
|
|
1385
|
+
type: 'confirm',
|
|
1386
|
+
name: 'confirm',
|
|
1387
|
+
message: 'Update AGENTS.md and .rulebook with latest templates?',
|
|
1388
|
+
default: true,
|
|
1389
|
+
},
|
|
1390
|
+
]);
|
|
1391
|
+
if (!confirm) {
|
|
1392
|
+
console.log(chalk.yellow('\nUpdate cancelled'));
|
|
1200
1393
|
process.exit(0);
|
|
1201
1394
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1395
|
+
}
|
|
1396
|
+
const hasPreCommit = detection.gitHooks?.preCommitExists ?? false;
|
|
1397
|
+
const hasPrePush = detection.gitHooks?.prePushExists ?? false;
|
|
1398
|
+
const missingHooks = !hasPreCommit || !hasPrePush;
|
|
1399
|
+
let installHooksOnUpdate = false;
|
|
1400
|
+
let hooksInstalledOnUpdate = false;
|
|
1401
|
+
if (missingHooks) {
|
|
1402
|
+
if (options.yes) {
|
|
1403
|
+
console.log(chalk.yellow('\n⚠ Git hooks are missing. Re-run "rulebook update" without --yes to install automated hooks or install them manually.'));
|
|
1404
|
+
}
|
|
1405
|
+
else {
|
|
1406
|
+
if (!inquirerModule) {
|
|
1407
|
+
inquirerModule = (await import('inquirer')).default;
|
|
1408
|
+
}
|
|
1409
|
+
const { installHooks } = await inquirerModule.prompt([
|
|
1210
1410
|
{
|
|
1211
1411
|
type: 'confirm',
|
|
1212
|
-
name: '
|
|
1213
|
-
message:
|
|
1412
|
+
name: 'installHooks',
|
|
1413
|
+
message: `Install Git hooks for automated quality checks? Missing: ${hasPreCommit ? '' : 'pre-commit '}${hasPrePush ? '' : 'pre-push'}`.trim(),
|
|
1214
1414
|
default: true,
|
|
1215
1415
|
},
|
|
1216
1416
|
]);
|
|
1217
|
-
|
|
1218
|
-
console.log(chalk.yellow('\nUpdate cancelled'));
|
|
1219
|
-
process.exit(0);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
const hasPreCommit = detection.gitHooks?.preCommitExists ?? false;
|
|
1223
|
-
const hasPrePush = detection.gitHooks?.prePushExists ?? false;
|
|
1224
|
-
const missingHooks = !hasPreCommit || !hasPrePush;
|
|
1225
|
-
let installHooksOnUpdate = false;
|
|
1226
|
-
let hooksInstalledOnUpdate = false;
|
|
1227
|
-
if (missingHooks) {
|
|
1228
|
-
if (options.yes) {
|
|
1229
|
-
console.log(chalk.yellow('\n⚠ Git hooks are missing. Re-run "rulebook update" without --yes to install automated hooks or install them manually.'));
|
|
1230
|
-
}
|
|
1231
|
-
else {
|
|
1232
|
-
if (!inquirerModule) {
|
|
1233
|
-
inquirerModule = (await import('inquirer')).default;
|
|
1234
|
-
}
|
|
1235
|
-
const { installHooks } = await inquirerModule.prompt([
|
|
1236
|
-
{
|
|
1237
|
-
type: 'confirm',
|
|
1238
|
-
name: 'installHooks',
|
|
1239
|
-
message: `Install Git hooks for automated quality checks? Missing: ${hasPreCommit ? '' : 'pre-commit '}${hasPrePush ? '' : 'pre-push'}`.trim(),
|
|
1240
|
-
default: true,
|
|
1241
|
-
},
|
|
1242
|
-
]);
|
|
1243
|
-
installHooksOnUpdate = installHooks;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
if (missingHooks && !installHooksOnUpdate && !options.yes) {
|
|
1247
|
-
console.log(chalk.gray('\nℹ Git hooks were not installed during update. Re-run "rulebook update" later or install them manually if you change your mind.'));
|
|
1417
|
+
installHooksOnUpdate = installHooks;
|
|
1248
1418
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
else {
|
|
1321
|
-
// Directory doesn't exist, create it and generate commands
|
|
1419
|
+
}
|
|
1420
|
+
if (missingHooks && !installHooksOnUpdate && !options.yes) {
|
|
1421
|
+
console.log(chalk.gray('\nℹ Git hooks were not installed during update. Re-run "rulebook update" later or install them manually if you change your mind.'));
|
|
1422
|
+
}
|
|
1423
|
+
const agentsPath = path.join(cwd, 'AGENTS.md');
|
|
1424
|
+
// Load existing config using ConfigManager
|
|
1425
|
+
const { createConfigManager } = await import('../core/config-manager.js');
|
|
1426
|
+
const configManager = createConfigManager(cwd);
|
|
1427
|
+
const existingConfig = await configManager.loadConfig();
|
|
1428
|
+
let existingMode;
|
|
1429
|
+
let existingLightMode;
|
|
1430
|
+
if (existingConfig) {
|
|
1431
|
+
if (existingConfig && (existingConfig.mode === 'minimal' || existingConfig.mode === 'full')) {
|
|
1432
|
+
existingMode = existingConfig.mode;
|
|
1433
|
+
}
|
|
1434
|
+
if (existingConfig && existingConfig.lightMode !== undefined) {
|
|
1435
|
+
existingLightMode = existingConfig.lightMode;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
const minimalMode = options.minimal ?? existingMode === 'minimal';
|
|
1439
|
+
const lightMode = options.light !== undefined ? options.light : (existingLightMode ?? false);
|
|
1440
|
+
const leanMode = options.lean ?? existingConfig?.agentsMode === 'lean';
|
|
1441
|
+
// Build config from detected project
|
|
1442
|
+
const config = {
|
|
1443
|
+
languages: detection.languages.map((l) => l.language),
|
|
1444
|
+
modules: minimalMode ? [] : detection.modules.filter((m) => m.detected).map((m) => m.module),
|
|
1445
|
+
frameworks: detection.frameworks.filter((f) => f.detected).map((f) => f.framework),
|
|
1446
|
+
ides: [], // Preserve existing IDE choices
|
|
1447
|
+
projectType: 'application',
|
|
1448
|
+
coverageThreshold: 95,
|
|
1449
|
+
strictDocs: true,
|
|
1450
|
+
generateWorkflows: false, // Don't regenerate workflows on update
|
|
1451
|
+
includeGitWorkflow: true,
|
|
1452
|
+
gitPushMode: 'manual',
|
|
1453
|
+
installGitHooks: installHooksOnUpdate,
|
|
1454
|
+
minimal: minimalMode,
|
|
1455
|
+
lightMode: lightMode,
|
|
1456
|
+
...(leanMode ? { agentsMode: 'lean' } : {}),
|
|
1457
|
+
};
|
|
1458
|
+
if (minimalMode) {
|
|
1459
|
+
config.ides = [];
|
|
1460
|
+
config.generateWorkflows = true;
|
|
1461
|
+
}
|
|
1462
|
+
let minimalArtifacts = [];
|
|
1463
|
+
if (minimalMode) {
|
|
1464
|
+
minimalArtifacts = await scaffoldMinimalProject(cwd, {
|
|
1465
|
+
projectName: path.basename(cwd),
|
|
1466
|
+
description: 'Essential project scaffolding refreshed via Rulebook minimal mode.',
|
|
1467
|
+
license: 'MIT',
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
// Generate Rulebook commands if Cursor is detected
|
|
1471
|
+
// This ensures commands are available for all Cursor projects
|
|
1472
|
+
const cursorRulesPath = path.join(cwd, '.cursorrules');
|
|
1473
|
+
const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
|
|
1474
|
+
const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
|
|
1475
|
+
// Deprecated notice: .cursorrules is superseded by .cursor/rules/*.mdc in Cursor v0.45+
|
|
1476
|
+
if (existsSync(cursorRulesPath)) {
|
|
1477
|
+
console.log(chalk.yellow(' ⚠ .cursorrules is deprecated as of Cursor v0.45. Use .cursor/rules/*.mdc instead.'));
|
|
1478
|
+
}
|
|
1479
|
+
if (usesCursor) {
|
|
1480
|
+
// Check if commands already exist to avoid duplicate generation
|
|
1481
|
+
const existingCommandsDir = path.join(cwd, '.cursor', 'commands');
|
|
1482
|
+
if (existsSync(existingCommandsDir)) {
|
|
1483
|
+
const { readdir } = await import('fs/promises');
|
|
1484
|
+
const existingFiles = await readdir(existingCommandsDir);
|
|
1485
|
+
const hasRulebookCommands = existingFiles.some((file) => file.startsWith('rulebook-task-'));
|
|
1486
|
+
if (!hasRulebookCommands) {
|
|
1322
1487
|
const { generateCursorCommands } = await import('../core/workflow-generator.js');
|
|
1323
1488
|
const generatedCommands = await generateCursorCommands(cwd);
|
|
1324
1489
|
if (generatedCommands.length > 0) {
|
|
@@ -1326,256 +1491,260 @@ export async function updateCommand(options) {
|
|
|
1326
1491
|
}
|
|
1327
1492
|
}
|
|
1328
1493
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
let detectedSkills = [];
|
|
1336
|
-
try {
|
|
1337
|
-
const { SkillsManager, getDefaultTemplatesPath } = await import('../core/skills-manager.js');
|
|
1338
|
-
const skillsManager = new SkillsManager(getDefaultTemplatesPath(), cwd);
|
|
1339
|
-
// Build a RulebookConfig-like object for skill detection
|
|
1340
|
-
const rulebookConfigForSkills = {
|
|
1341
|
-
languages: config.languages,
|
|
1342
|
-
frameworks: config.frameworks,
|
|
1343
|
-
modules: config.modules,
|
|
1344
|
-
services: config.services,
|
|
1345
|
-
};
|
|
1346
|
-
detectedSkills = await skillsManager.autoDetectSkills(rulebookConfigForSkills);
|
|
1347
|
-
// Merge with existing skills (keep existing, add new detected)
|
|
1348
|
-
const mergedSkills = [...new Set([...existingSkills, ...detectedSkills])];
|
|
1349
|
-
if (detectedSkills.length > existingSkills.length) {
|
|
1350
|
-
const newSkills = detectedSkills.filter((s) => !existingSkills.includes(s));
|
|
1351
|
-
if (newSkills.length > 0) {
|
|
1352
|
-
console.log(chalk.green('\n✓ New skills detected:'));
|
|
1353
|
-
for (const skillId of newSkills) {
|
|
1354
|
-
console.log(chalk.gray(` - ${skillId}`));
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1494
|
+
else {
|
|
1495
|
+
// Directory doesn't exist, create it and generate commands
|
|
1496
|
+
const { generateCursorCommands } = await import('../core/workflow-generator.js');
|
|
1497
|
+
const generatedCommands = await generateCursorCommands(cwd);
|
|
1498
|
+
if (generatedCommands.length > 0) {
|
|
1499
|
+
console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
|
|
1357
1500
|
}
|
|
1358
|
-
detectedSkills = mergedSkills;
|
|
1359
|
-
}
|
|
1360
|
-
catch {
|
|
1361
|
-
// Skills system not available or error - preserve existing skills
|
|
1362
|
-
detectedSkills = existingSkills;
|
|
1363
1501
|
}
|
|
1364
|
-
|
|
1502
|
+
}
|
|
1503
|
+
// Migration already done via configManager.loadConfig() -> migrateConfig() -> migrateDirectoryStructure()
|
|
1504
|
+
// No need to call it again here
|
|
1505
|
+
// Load existing config to preserve skills and ralph settings (already loaded above)
|
|
1506
|
+
const existingSkills = existingConfig.skills?.enabled || [];
|
|
1507
|
+
const existingRalph = existingConfig.ralph;
|
|
1508
|
+
// Auto-detect skills based on project detection (v2.0)
|
|
1509
|
+
let detectedSkills = [];
|
|
1510
|
+
try {
|
|
1511
|
+
const { SkillsManager, getDefaultTemplatesPath } = await import('../core/skills-manager.js');
|
|
1512
|
+
const skillsManager = new SkillsManager(getDefaultTemplatesPath(), cwd);
|
|
1513
|
+
// Build a RulebookConfig-like object for skill detection
|
|
1514
|
+
const rulebookConfigForSkills = {
|
|
1365
1515
|
languages: config.languages,
|
|
1366
1516
|
frameworks: config.frameworks,
|
|
1367
1517
|
modules: config.modules,
|
|
1368
1518
|
services: config.services,
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
const { hasFlatLayout, migrateFlatToSpecs } = await import('../core/migrator.js');
|
|
1380
|
-
const rulebookDirForMigration = config.rulebookDir || '.rulebook';
|
|
1381
|
-
if (await hasFlatLayout(cwd, rulebookDirForMigration)) {
|
|
1382
|
-
const migrationSpinner = ora('Migrating rulebook files to specs/ subdirectory...').start();
|
|
1383
|
-
const { migratedFiles } = await migrateFlatToSpecs(cwd, rulebookDirForMigration);
|
|
1384
|
-
if (migratedFiles.length > 0) {
|
|
1385
|
-
migrationSpinner.succeed(`Migrated ${migratedFiles.length} file(s) to /${rulebookDirForMigration}/specs/`);
|
|
1386
|
-
}
|
|
1387
|
-
else {
|
|
1388
|
-
migrationSpinner.info('No files to migrate');
|
|
1519
|
+
};
|
|
1520
|
+
detectedSkills = await skillsManager.autoDetectSkills(rulebookConfigForSkills);
|
|
1521
|
+
// Merge with existing skills (keep existing, add new detected)
|
|
1522
|
+
const mergedSkills = [...new Set([...existingSkills, ...detectedSkills])];
|
|
1523
|
+
if (detectedSkills.length > existingSkills.length) {
|
|
1524
|
+
const newSkills = detectedSkills.filter((s) => !existingSkills.includes(s));
|
|
1525
|
+
if (newSkills.length > 0) {
|
|
1526
|
+
console.log(chalk.green('\n✓ New skills detected:'));
|
|
1527
|
+
for (const skillId of newSkills) {
|
|
1528
|
+
console.log(chalk.gray(` - ${skillId}`));
|
|
1389
1529
|
}
|
|
1390
1530
|
}
|
|
1391
1531
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
try {
|
|
1421
|
-
await installGitHooks({ languages: hookLanguages, cwd });
|
|
1422
|
-
hookSpinner.succeed('Git hooks installed successfully');
|
|
1423
|
-
hooksInstalledOnUpdate = true;
|
|
1424
|
-
}
|
|
1425
|
-
catch (error) {
|
|
1426
|
-
hookSpinner.fail('Failed to install Git hooks');
|
|
1427
|
-
console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
|
|
1428
|
-
console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook update" later to retry or install manually.'));
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
const gitHooksActiveAfterUpdate = hooksInstalledOnUpdate || (hasPreCommit && hasPrePush);
|
|
1432
|
-
config.installGitHooks = gitHooksActiveAfterUpdate;
|
|
1433
|
-
// Update .rulebook config
|
|
1434
|
-
const configSpinner = ora('Updating .rulebook configuration...').start();
|
|
1435
|
-
const rulebookFeatures = {
|
|
1436
|
-
watcher: false,
|
|
1437
|
-
agent: false,
|
|
1438
|
-
logging: true,
|
|
1439
|
-
telemetry: false,
|
|
1440
|
-
notifications: false,
|
|
1441
|
-
dryRun: false,
|
|
1442
|
-
gitHooks: gitHooksActiveAfterUpdate,
|
|
1443
|
-
repl: false,
|
|
1444
|
-
templates: true,
|
|
1445
|
-
context: minimalMode ? false : true,
|
|
1446
|
-
health: true,
|
|
1447
|
-
plugins: false,
|
|
1448
|
-
parallel: minimalMode ? false : true,
|
|
1449
|
-
smartContinue: minimalMode ? false : true,
|
|
1450
|
-
};
|
|
1451
|
-
const rulebookConfig = {
|
|
1452
|
-
version: getRulebookVersion(),
|
|
1453
|
-
installedAt: detection.existingAgents.content?.match(/Generated at: (.+)/)?.[1] ||
|
|
1454
|
-
new Date().toISOString(),
|
|
1455
|
-
updatedAt: new Date().toISOString(),
|
|
1456
|
-
projectId: path.basename(cwd),
|
|
1457
|
-
mode: minimalMode ? 'minimal' : 'full',
|
|
1458
|
-
features: rulebookFeatures,
|
|
1459
|
-
coverageThreshold: existingConfig.coverageThreshold ?? 95,
|
|
1460
|
-
language: existingConfig.language ?? 'en',
|
|
1461
|
-
outputLanguage: existingConfig.outputLanguage ?? 'en',
|
|
1462
|
-
cliTools: existingConfig.cliTools ?? [],
|
|
1463
|
-
maxParallelTasks: existingConfig.maxParallelTasks ?? 5,
|
|
1464
|
-
timeouts: existingConfig.timeouts ?? {
|
|
1465
|
-
taskExecution: 3600000,
|
|
1466
|
-
cliResponse: 180000,
|
|
1467
|
-
testRun: 600000,
|
|
1468
|
-
},
|
|
1469
|
-
...(existingConfig.memory ? { memory: existingConfig.memory } : {}),
|
|
1470
|
-
...(existingConfig.ralph ? { ralph: existingConfig.ralph } : {}),
|
|
1471
|
-
...(existingConfig.skills ? { skills: existingConfig.skills } : {}),
|
|
1472
|
-
...(leanMode
|
|
1473
|
-
? { agentsMode: 'lean' }
|
|
1474
|
-
: existingConfig.agentsMode
|
|
1475
|
-
? { agentsMode: existingConfig.agentsMode }
|
|
1476
|
-
: {}),
|
|
1477
|
-
};
|
|
1478
|
-
await configManager.saveConfig(rulebookConfig);
|
|
1479
|
-
configSpinner.succeed('.rulebook configuration updated');
|
|
1480
|
-
// Auto-setup Claude Code integration (MCP + skills)
|
|
1481
|
-
const claudeSpinner = ora('Checking Claude Code integration...').start();
|
|
1482
|
-
try {
|
|
1483
|
-
const { setupClaudeCodeIntegration } = await import('../core/claude-mcp.js');
|
|
1484
|
-
const result = await setupClaudeCodeIntegration(cwd);
|
|
1485
|
-
if (result.detected) {
|
|
1486
|
-
claudeSpinner.succeed('Claude Code integration updated');
|
|
1487
|
-
if (result.mcpConfigured) {
|
|
1488
|
-
console.log(chalk.gray(' • MCP server added to .mcp.json'));
|
|
1489
|
-
}
|
|
1490
|
-
if (result.skillsInstalled.length > 0) {
|
|
1491
|
-
console.log(chalk.gray(` • ${result.skillsInstalled.length} skills updated in .claude/commands/`));
|
|
1492
|
-
}
|
|
1493
|
-
if (result.agentTeamsEnabled) {
|
|
1494
|
-
console.log(chalk.gray(' • Multi-agent teams enabled in .claude/settings.json'));
|
|
1495
|
-
}
|
|
1496
|
-
if (result.agentDefinitionsInstalled.length > 0) {
|
|
1497
|
-
console.log(chalk.gray(` • ${result.agentDefinitionsInstalled.length} agent definitions updated in .claude/agents/`));
|
|
1498
|
-
}
|
|
1532
|
+
detectedSkills = mergedSkills;
|
|
1533
|
+
}
|
|
1534
|
+
catch {
|
|
1535
|
+
// Skills system not available or error - preserve existing skills
|
|
1536
|
+
detectedSkills = existingSkills;
|
|
1537
|
+
}
|
|
1538
|
+
await configManager.updateConfig({
|
|
1539
|
+
languages: config.languages,
|
|
1540
|
+
frameworks: config.frameworks,
|
|
1541
|
+
modules: config.modules,
|
|
1542
|
+
services: config.services,
|
|
1543
|
+
modular: config.modular ?? true,
|
|
1544
|
+
rulebookDir: config.rulebookDir || '.rulebook',
|
|
1545
|
+
skills: detectedSkills.length > 0 ? { enabled: detectedSkills } : undefined,
|
|
1546
|
+
ralph: existingRalph,
|
|
1547
|
+
memory: existingConfig.memory,
|
|
1548
|
+
});
|
|
1549
|
+
// Ensure .rulebook is in .gitignore with exceptions for specs/tasks
|
|
1550
|
+
await configManager.ensureGitignore();
|
|
1551
|
+
// Migrate flat layout to specs/ subdirectory if needed
|
|
1552
|
+
{
|
|
1553
|
+
const { hasFlatLayout, migrateFlatToSpecs } = await import('../core/migrator.js');
|
|
1554
|
+
const rulebookDirForMigration = config.rulebookDir || '.rulebook';
|
|
1555
|
+
if (await hasFlatLayout(cwd, rulebookDirForMigration)) {
|
|
1556
|
+
const migrationSpinner = ora('Migrating rulebook files to specs/ subdirectory...').start();
|
|
1557
|
+
const { migratedFiles } = await migrateFlatToSpecs(cwd, rulebookDirForMigration);
|
|
1558
|
+
if (migratedFiles.length > 0) {
|
|
1559
|
+
migrationSpinner.succeed(`Migrated ${migratedFiles.length} file(s) to /${rulebookDirForMigration}/specs/`);
|
|
1499
1560
|
}
|
|
1500
1561
|
else {
|
|
1501
|
-
|
|
1562
|
+
migrationSpinner.info('No files to migrate');
|
|
1502
1563
|
}
|
|
1503
1564
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1565
|
+
}
|
|
1566
|
+
// Merge with existing AGENTS.md (with migration support)
|
|
1567
|
+
const mergeSpinner = ora('Updating AGENTS.md with latest templates...').start();
|
|
1568
|
+
config.modular = config.modular ?? true; // Enable modular by default
|
|
1569
|
+
const mergedContent = await mergeFullAgents(detection.existingAgents, config, cwd);
|
|
1570
|
+
await writeFile(agentsPath, mergedContent);
|
|
1571
|
+
mergeSpinner.succeed('AGENTS.md updated');
|
|
1572
|
+
// Show multi-tool config feedback (update command)
|
|
1573
|
+
if (detection.geminiCli?.detected) {
|
|
1574
|
+
console.log(chalk.gray(' • Gemini CLI config updated: GEMINI.md'));
|
|
1575
|
+
}
|
|
1576
|
+
if (detection.continueDev?.detected) {
|
|
1577
|
+
console.log(chalk.gray(' • Continue.dev rules updated in .continue/rules/'));
|
|
1578
|
+
}
|
|
1579
|
+
if (detection.windsurf?.detected) {
|
|
1580
|
+
console.log(chalk.gray(' • Windsurf rules updated: .windsurfrules'));
|
|
1581
|
+
}
|
|
1582
|
+
if (detection.githubCopilot?.detected) {
|
|
1583
|
+
console.log(chalk.gray(' • GitHub Copilot instructions updated in .github/'));
|
|
1584
|
+
}
|
|
1585
|
+
if (installHooksOnUpdate) {
|
|
1586
|
+
const hookLanguages = detection.languages.length > 0
|
|
1587
|
+
? detection.languages
|
|
1588
|
+
: config.languages.map((language) => ({
|
|
1589
|
+
language: language,
|
|
1590
|
+
confidence: 1,
|
|
1591
|
+
indicators: [],
|
|
1592
|
+
}));
|
|
1593
|
+
const hookSpinner = ora('Installing Git hooks (pre-commit & pre-push)...').start();
|
|
1508
1594
|
try {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1595
|
+
await installGitHooks({ languages: hookLanguages, cwd });
|
|
1596
|
+
hookSpinner.succeed('Git hooks installed successfully');
|
|
1597
|
+
hooksInstalledOnUpdate = true;
|
|
1598
|
+
}
|
|
1599
|
+
catch (error) {
|
|
1600
|
+
hookSpinner.fail('Failed to install Git hooks');
|
|
1601
|
+
console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
|
|
1602
|
+
console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook update" later to retry or install manually.'));
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const gitHooksActiveAfterUpdate = hooksInstalledOnUpdate || (hasPreCommit && hasPrePush);
|
|
1606
|
+
config.installGitHooks = gitHooksActiveAfterUpdate;
|
|
1607
|
+
// Update .rulebook config
|
|
1608
|
+
const configSpinner = ora('Updating .rulebook configuration...').start();
|
|
1609
|
+
const rulebookFeatures = {
|
|
1610
|
+
watcher: false,
|
|
1611
|
+
agent: false,
|
|
1612
|
+
logging: true,
|
|
1613
|
+
telemetry: false,
|
|
1614
|
+
notifications: false,
|
|
1615
|
+
dryRun: false,
|
|
1616
|
+
gitHooks: gitHooksActiveAfterUpdate,
|
|
1617
|
+
repl: false,
|
|
1618
|
+
templates: true,
|
|
1619
|
+
context: minimalMode ? false : true,
|
|
1620
|
+
health: true,
|
|
1621
|
+
plugins: false,
|
|
1622
|
+
parallel: minimalMode ? false : true,
|
|
1623
|
+
smartContinue: minimalMode ? false : true,
|
|
1624
|
+
};
|
|
1625
|
+
const rulebookConfig = {
|
|
1626
|
+
version: getRulebookVersion(),
|
|
1627
|
+
installedAt: detection.existingAgents.content?.match(/Generated at: (.+)/)?.[1] ||
|
|
1628
|
+
new Date().toISOString(),
|
|
1629
|
+
updatedAt: new Date().toISOString(),
|
|
1630
|
+
projectId: path.basename(cwd),
|
|
1631
|
+
mode: minimalMode ? 'minimal' : 'full',
|
|
1632
|
+
features: rulebookFeatures,
|
|
1633
|
+
coverageThreshold: existingConfig.coverageThreshold ?? 95,
|
|
1634
|
+
language: existingConfig.language ?? 'en',
|
|
1635
|
+
outputLanguage: existingConfig.outputLanguage ?? 'en',
|
|
1636
|
+
cliTools: existingConfig.cliTools ?? [],
|
|
1637
|
+
maxParallelTasks: existingConfig.maxParallelTasks ?? 5,
|
|
1638
|
+
timeouts: existingConfig.timeouts ?? {
|
|
1639
|
+
taskExecution: 3600000,
|
|
1640
|
+
cliResponse: 180000,
|
|
1641
|
+
testRun: 600000,
|
|
1642
|
+
},
|
|
1643
|
+
...(existingConfig.memory ? { memory: existingConfig.memory } : {}),
|
|
1644
|
+
...(existingConfig.ralph ? { ralph: existingConfig.ralph } : {}),
|
|
1645
|
+
...(existingConfig.skills ? { skills: existingConfig.skills } : {}),
|
|
1646
|
+
...(leanMode
|
|
1647
|
+
? { agentsMode: 'lean' }
|
|
1648
|
+
: existingConfig.agentsMode
|
|
1649
|
+
? { agentsMode: existingConfig.agentsMode }
|
|
1650
|
+
: {}),
|
|
1651
|
+
};
|
|
1652
|
+
await configManager.saveConfig(rulebookConfig);
|
|
1653
|
+
configSpinner.succeed('.rulebook configuration updated');
|
|
1654
|
+
// Auto-setup Claude Code integration (MCP + skills)
|
|
1655
|
+
const claudeSpinner = ora('Checking Claude Code integration...').start();
|
|
1656
|
+
try {
|
|
1657
|
+
const { setupClaudeCodeIntegration } = await import('../core/claude-mcp.js');
|
|
1658
|
+
const result = await setupClaudeCodeIntegration(cwd);
|
|
1659
|
+
if (result.detected) {
|
|
1660
|
+
claudeSpinner.succeed('Claude Code integration updated');
|
|
1661
|
+
if (result.mcpConfigured) {
|
|
1662
|
+
console.log(chalk.gray(' • MCP server added to .mcp.json'));
|
|
1663
|
+
}
|
|
1664
|
+
if (result.skillsInstalled.length > 0) {
|
|
1665
|
+
console.log(chalk.gray(` • ${result.skillsInstalled.length} skills updated in .claude/commands/`));
|
|
1666
|
+
}
|
|
1667
|
+
if (result.agentTeamsEnabled) {
|
|
1668
|
+
console.log(chalk.gray(' • Multi-agent teams enabled in .claude/settings.json'));
|
|
1669
|
+
}
|
|
1670
|
+
if (result.agentDefinitionsInstalled.length > 0) {
|
|
1671
|
+
console.log(chalk.gray(` • ${result.agentDefinitionsInstalled.length} agent definitions updated in .claude/agents/`));
|
|
1513
1672
|
}
|
|
1514
1673
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
// Ensure PLANS.md exists (create if missing, never overwrite)
|
|
1519
|
-
try {
|
|
1520
|
-
const { initPlans } = await import('../core/plans-manager.js');
|
|
1521
|
-
await initPlans(cwd);
|
|
1522
|
-
}
|
|
1523
|
-
catch {
|
|
1524
|
-
// Non-blocking
|
|
1525
|
-
}
|
|
1526
|
-
// Migrate memory directory if old structure exists
|
|
1527
|
-
try {
|
|
1528
|
-
await migrateMemoryDirectory();
|
|
1529
|
-
}
|
|
1530
|
-
catch {
|
|
1531
|
-
// Silently skip if migration fails
|
|
1532
|
-
}
|
|
1533
|
-
// Install plugin in Claude Code
|
|
1534
|
-
try {
|
|
1535
|
-
await setupClaudeCodePlugin();
|
|
1536
|
-
}
|
|
1537
|
-
catch {
|
|
1538
|
-
// Silently skip if plugin installation fails
|
|
1674
|
+
else {
|
|
1675
|
+
claudeSpinner.info('Claude Code not detected (skipped)');
|
|
1539
1676
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1677
|
+
}
|
|
1678
|
+
catch {
|
|
1679
|
+
claudeSpinner.info('Claude Code integration skipped');
|
|
1680
|
+
}
|
|
1681
|
+
// Install/update Ralph shell scripts
|
|
1682
|
+
try {
|
|
1683
|
+
const { installRalphScripts } = await import('../core/ralph-scripts.js');
|
|
1684
|
+
const scripts = await installRalphScripts(cwd);
|
|
1685
|
+
if (scripts.length > 0) {
|
|
1686
|
+
console.log(chalk.gray(` • ${scripts.length} Ralph scripts updated in .rulebook/scripts/`));
|
|
1547
1687
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1688
|
+
}
|
|
1689
|
+
catch {
|
|
1690
|
+
// Skip if Ralph scripts installation fails
|
|
1691
|
+
}
|
|
1692
|
+
// Ensure PLANS.md exists (create if missing, never overwrite)
|
|
1693
|
+
try {
|
|
1694
|
+
const { initPlans } = await import('../core/plans-manager.js');
|
|
1695
|
+
await initPlans(cwd);
|
|
1696
|
+
}
|
|
1697
|
+
catch {
|
|
1698
|
+
// Non-blocking
|
|
1699
|
+
}
|
|
1700
|
+
// Migrate memory directory if old structure exists
|
|
1701
|
+
try {
|
|
1702
|
+
await migrateMemoryDirectory();
|
|
1703
|
+
}
|
|
1704
|
+
catch {
|
|
1705
|
+
// Silently skip if migration fails
|
|
1706
|
+
}
|
|
1707
|
+
// Install plugin in Claude Code
|
|
1708
|
+
try {
|
|
1709
|
+
await setupClaudeCodePlugin();
|
|
1710
|
+
}
|
|
1711
|
+
catch {
|
|
1712
|
+
// Silently skip if plugin installation fails
|
|
1713
|
+
}
|
|
1714
|
+
// Clean up any accidental duplicate directories
|
|
1715
|
+
try {
|
|
1716
|
+
const fsPromises = await import('fs/promises');
|
|
1717
|
+
const accidentalDir = path.join(cwd, '.rulebook', '.rulebook');
|
|
1718
|
+
if (existsSync(accidentalDir)) {
|
|
1719
|
+
await fsPromises.rm(accidentalDir, { recursive: true, force: true });
|
|
1574
1720
|
}
|
|
1575
1721
|
}
|
|
1576
|
-
catch
|
|
1577
|
-
|
|
1578
|
-
|
|
1722
|
+
catch {
|
|
1723
|
+
// Ignore cleanup errors
|
|
1724
|
+
}
|
|
1725
|
+
// Success message
|
|
1726
|
+
console.log(chalk.bold.green('\n✅ Update complete!\n'));
|
|
1727
|
+
console.log(chalk.white('Updated components:'));
|
|
1728
|
+
console.log(chalk.green(' ✓ AGENTS.md - Merged with latest templates'));
|
|
1729
|
+
console.log(chalk.green(` ✓ .rulebook - Updated to v${getRulebookVersion()}`));
|
|
1730
|
+
console.log(chalk.white('\nWhat was updated:'));
|
|
1731
|
+
console.log(chalk.gray(` - ${detection.languages.length} language templates`));
|
|
1732
|
+
console.log(chalk.gray(` - ${detection.modules.filter((m) => m.detected).length} MCP modules`));
|
|
1733
|
+
console.log(chalk.gray(' - Git workflow rules'));
|
|
1734
|
+
console.log(chalk.gray(' - Rulebook task management'));
|
|
1735
|
+
console.log(chalk.gray(' - Pre-commit command standardization'));
|
|
1736
|
+
console.log(chalk.yellow('\n⚠ Review the updated AGENTS.md to ensure your custom rules are preserved'));
|
|
1737
|
+
console.log(chalk.white('\nNext steps:'));
|
|
1738
|
+
console.log(chalk.gray(' 1. Review AGENTS.md changes'));
|
|
1739
|
+
console.log(chalk.gray(' 2. Test that your project still builds'));
|
|
1740
|
+
console.log(chalk.gray(' 3. Run quality checks (lint, test, build)'));
|
|
1741
|
+
console.log(chalk.gray(' 4. Commit the updated files\n'));
|
|
1742
|
+
if (minimalMode && minimalArtifacts.length > 0) {
|
|
1743
|
+
console.log(chalk.green('Essentials ensured:'));
|
|
1744
|
+
for (const artifact of minimalArtifacts) {
|
|
1745
|
+
console.log(chalk.gray(` - ${path.relative(cwd, artifact)}`));
|
|
1746
|
+
}
|
|
1747
|
+
console.log('');
|
|
1579
1748
|
}
|
|
1580
1749
|
}
|
|
1581
1750
|
// ============================================
|
|
@@ -3336,4 +3505,156 @@ export async function ralphImportIssuesCommand(options) {
|
|
|
3336
3505
|
process.exit(1);
|
|
3337
3506
|
}
|
|
3338
3507
|
}
|
|
3508
|
+
// ============================================
|
|
3509
|
+
// Workspace Commands (v4.2)
|
|
3510
|
+
// ============================================
|
|
3511
|
+
/** Resolve the workspace config path inside .rulebook/ */
|
|
3512
|
+
function getWorkspaceConfigPath(cwd) {
|
|
3513
|
+
return path.join(cwd, '.rulebook', 'workspace.json');
|
|
3514
|
+
}
|
|
3515
|
+
export async function workspaceInitCommand() {
|
|
3516
|
+
const cwd = process.cwd();
|
|
3517
|
+
const configPath = getWorkspaceConfigPath(cwd);
|
|
3518
|
+
if (existsSync(configPath)) {
|
|
3519
|
+
console.log(chalk.yellow('Workspace already initialized at .rulebook/workspace.json'));
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
const spinner = ora('Detecting workspace structure...').start();
|
|
3523
|
+
// Try auto-discovery first
|
|
3524
|
+
let config = WorkspaceManager.findWorkspaceConfig(cwd);
|
|
3525
|
+
if (config) {
|
|
3526
|
+
spinner.succeed(`Detected workspace: ${config.name} (${config.projects.length} projects)`);
|
|
3527
|
+
console.log('\n Projects found:');
|
|
3528
|
+
for (const p of config.projects) {
|
|
3529
|
+
console.log(` - ${chalk.cyan(p.name)} → ${p.path}`);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
else {
|
|
3533
|
+
spinner.info('No workspace structure detected. Creating empty workspace config.');
|
|
3534
|
+
config = {
|
|
3535
|
+
name: path.basename(cwd),
|
|
3536
|
+
version: '1.0.0',
|
|
3537
|
+
projects: [],
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
// Ensure .rulebook/ directory exists
|
|
3541
|
+
const rulebookDir = path.join(cwd, '.rulebook');
|
|
3542
|
+
if (!existsSync(rulebookDir)) {
|
|
3543
|
+
const { mkdirSync } = await import('fs');
|
|
3544
|
+
mkdirSync(rulebookDir, { recursive: true });
|
|
3545
|
+
}
|
|
3546
|
+
// Write config
|
|
3547
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
3548
|
+
console.log(chalk.green(`\n Created: .rulebook/workspace.json`));
|
|
3549
|
+
// Check for legacy .mcp.json files
|
|
3550
|
+
const migration = await migrateLegacyMcpConfigs(cwd);
|
|
3551
|
+
if (migration.migratedFiles.length > 0) {
|
|
3552
|
+
console.log(chalk.yellow(`\n Migrated ${migration.migratedFiles.length} legacy .mcp.json files (backups at *.mcp.json.bak)`));
|
|
3553
|
+
}
|
|
3554
|
+
console.log(chalk.dim('\n Use `rulebook workspace add <path>` to add more projects'));
|
|
3555
|
+
console.log(chalk.dim(' Use `rulebook mcp init --workspace` to configure MCP for workspace'));
|
|
3556
|
+
}
|
|
3557
|
+
export async function workspaceAddCommand(projectPath) {
|
|
3558
|
+
const cwd = process.cwd();
|
|
3559
|
+
const configPath = getWorkspaceConfigPath(cwd);
|
|
3560
|
+
if (!existsSync(configPath)) {
|
|
3561
|
+
console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
|
|
3562
|
+
process.exit(1);
|
|
3563
|
+
}
|
|
3564
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
3565
|
+
const resolvedPath = path.resolve(cwd, projectPath);
|
|
3566
|
+
const name = path.basename(resolvedPath);
|
|
3567
|
+
// Check for duplicates
|
|
3568
|
+
if (config.projects.some((p) => p.name === name)) {
|
|
3569
|
+
console.error(chalk.red(`Project "${name}" already exists in workspace.`));
|
|
3570
|
+
process.exit(1);
|
|
3571
|
+
}
|
|
3572
|
+
// Use relative path if within workspace, absolute otherwise
|
|
3573
|
+
const isSubpath = resolvedPath.startsWith(cwd);
|
|
3574
|
+
const storedPath = isSubpath ? path.relative(cwd, resolvedPath) : resolvedPath;
|
|
3575
|
+
const project = {
|
|
3576
|
+
name,
|
|
3577
|
+
path: storedPath.startsWith('.') ? storedPath : `./${storedPath}`,
|
|
3578
|
+
};
|
|
3579
|
+
config.projects.push(project);
|
|
3580
|
+
if (!config.defaultProject) {
|
|
3581
|
+
config.defaultProject = name;
|
|
3582
|
+
}
|
|
3583
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
3584
|
+
console.log(chalk.green(`Added project "${name}" → ${project.path}`));
|
|
3585
|
+
}
|
|
3586
|
+
export async function workspaceRemoveCommand(projectName) {
|
|
3587
|
+
const cwd = process.cwd();
|
|
3588
|
+
const configPath = getWorkspaceConfigPath(cwd);
|
|
3589
|
+
if (!existsSync(configPath)) {
|
|
3590
|
+
console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
|
|
3591
|
+
process.exit(1);
|
|
3592
|
+
}
|
|
3593
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
3594
|
+
const idx = config.projects.findIndex((p) => p.name === projectName);
|
|
3595
|
+
if (idx === -1) {
|
|
3596
|
+
console.error(chalk.red(`Project "${projectName}" not found in workspace.`));
|
|
3597
|
+
process.exit(1);
|
|
3598
|
+
}
|
|
3599
|
+
config.projects.splice(idx, 1);
|
|
3600
|
+
if (config.defaultProject === projectName) {
|
|
3601
|
+
config.defaultProject = config.projects[0]?.name;
|
|
3602
|
+
}
|
|
3603
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
3604
|
+
console.log(chalk.green(`Removed project "${projectName}" from workspace.`));
|
|
3605
|
+
}
|
|
3606
|
+
export async function workspaceListCommand() {
|
|
3607
|
+
const cwd = process.cwd();
|
|
3608
|
+
const config = WorkspaceManager.findWorkspaceConfig(cwd);
|
|
3609
|
+
if (!config) {
|
|
3610
|
+
console.log(chalk.yellow('No workspace found. Run `rulebook workspace init` to create one.'));
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
console.log(chalk.bold(`\nWorkspace: ${config.name}`));
|
|
3614
|
+
console.log(chalk.dim(` Version: ${config.version}`));
|
|
3615
|
+
if (config.defaultProject) {
|
|
3616
|
+
console.log(chalk.dim(` Default: ${config.defaultProject}`));
|
|
3617
|
+
}
|
|
3618
|
+
console.log();
|
|
3619
|
+
for (const p of config.projects) {
|
|
3620
|
+
const isDefault = p.name === config.defaultProject;
|
|
3621
|
+
const marker = isDefault ? chalk.green(' (default)') : '';
|
|
3622
|
+
const disabled = p.enabled === false ? chalk.red(' [disabled]') : '';
|
|
3623
|
+
console.log(` ${chalk.cyan(p.name)}${marker}${disabled}`);
|
|
3624
|
+
console.log(` ${chalk.dim(p.path)}`);
|
|
3625
|
+
}
|
|
3626
|
+
console.log(chalk.dim(`\n ${config.projects.length} project(s) total`));
|
|
3627
|
+
}
|
|
3628
|
+
export async function workspaceStatusCommand() {
|
|
3629
|
+
const cwd = process.cwd();
|
|
3630
|
+
const config = WorkspaceManager.findWorkspaceConfig(cwd);
|
|
3631
|
+
if (!config) {
|
|
3632
|
+
console.log(chalk.yellow('No workspace found. Run `rulebook workspace init` to create one.'));
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
const manager = new WorkspaceManager(config, cwd);
|
|
3636
|
+
const spinner = ora('Checking workspace status...').start();
|
|
3637
|
+
try {
|
|
3638
|
+
const status = await manager.getStatus();
|
|
3639
|
+
spinner.stop();
|
|
3640
|
+
console.log(chalk.bold(`\nWorkspace: ${status.name}`));
|
|
3641
|
+
console.log(` Projects: ${status.totalProjects} | Active workers: ${status.activeWorkers}`);
|
|
3642
|
+
console.log();
|
|
3643
|
+
for (const p of status.projects) {
|
|
3644
|
+
const configBadge = p.hasRulebookConfig ? chalk.green('.rulebook') : chalk.dim('no config');
|
|
3645
|
+
const memBadge = p.memoryEnabled ? chalk.blue('memory') : '';
|
|
3646
|
+
const taskBadge = p.taskCount > 0 ? chalk.yellow(`${p.taskCount} tasks`) : '';
|
|
3647
|
+
const badges = [configBadge, memBadge, taskBadge].filter(Boolean).join(' ');
|
|
3648
|
+
console.log(` ${chalk.cyan(p.name)} ${badges}`);
|
|
3649
|
+
console.log(` ${chalk.dim(p.path)}`);
|
|
3650
|
+
}
|
|
3651
|
+
console.log();
|
|
3652
|
+
}
|
|
3653
|
+
catch (error) {
|
|
3654
|
+
spinner.fail(`Failed: ${String(error)}`);
|
|
3655
|
+
}
|
|
3656
|
+
finally {
|
|
3657
|
+
await manager.shutdownAll();
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3339
3660
|
//# sourceMappingURL=commands.js.map
|