@hivehub/rulebook 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +539 -0
- package/dist/agents/claude-code.d.ts +69 -0
- package/dist/agents/claude-code.d.ts.map +1 -0
- package/dist/agents/claude-code.js +180 -0
- package/dist/agents/claude-code.js.map +1 -0
- package/dist/agents/cursor-agent.d.ts +184 -0
- package/dist/agents/cursor-agent.d.ts.map +1 -0
- package/dist/agents/cursor-agent.js +299 -0
- package/dist/agents/cursor-agent.js.map +1 -0
- package/dist/agents/gemini-cli.d.ts +69 -0
- package/dist/agents/gemini-cli.d.ts.map +1 -0
- package/dist/agents/gemini-cli.js +180 -0
- package/dist/agents/gemini-cli.js.map +1 -0
- package/dist/cli/commands.d.ts +57 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +1370 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/docs-prompts.d.ts +3 -0
- package/dist/cli/docs-prompts.d.ts.map +1 -0
- package/dist/cli/docs-prompts.js +45 -0
- package/dist/cli/docs-prompts.js.map +1 -0
- package/dist/cli/prompts.d.ts +6 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +376 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/core/agent-manager.d.ts +89 -0
- package/dist/core/agent-manager.d.ts.map +1 -0
- package/dist/core/agent-manager.js +546 -0
- package/dist/core/agent-manager.js.map +1 -0
- package/dist/core/auto-fixer.d.ts +14 -0
- package/dist/core/auto-fixer.d.ts.map +1 -0
- package/dist/core/auto-fixer.js +207 -0
- package/dist/core/auto-fixer.js.map +1 -0
- package/dist/core/changelog-generator.d.ts +44 -0
- package/dist/core/changelog-generator.d.ts.map +1 -0
- package/dist/core/changelog-generator.js +222 -0
- package/dist/core/changelog-generator.js.map +1 -0
- package/dist/core/cli-bridge.d.ts +113 -0
- package/dist/core/cli-bridge.d.ts.map +1 -0
- package/dist/core/cli-bridge.js +1094 -0
- package/dist/core/cli-bridge.js.map +1 -0
- package/dist/core/config-manager.d.ts +65 -0
- package/dist/core/config-manager.d.ts.map +1 -0
- package/dist/core/config-manager.js +266 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/coverage-checker.d.ts +14 -0
- package/dist/core/coverage-checker.d.ts.map +1 -0
- package/dist/core/coverage-checker.js +176 -0
- package/dist/core/coverage-checker.js.map +1 -0
- package/dist/core/custom-templates.d.ts +27 -0
- package/dist/core/custom-templates.d.ts.map +1 -0
- package/dist/core/custom-templates.js +122 -0
- package/dist/core/custom-templates.js.map +1 -0
- package/dist/core/dependency-checker.d.ts +21 -0
- package/dist/core/dependency-checker.d.ts.map +1 -0
- package/dist/core/dependency-checker.js +247 -0
- package/dist/core/dependency-checker.js.map +1 -0
- package/dist/core/detector.d.ts +3 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +1443 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/docs-generator.d.ts +9 -0
- package/dist/core/docs-generator.d.ts.map +1 -0
- package/dist/core/docs-generator.js +531 -0
- package/dist/core/docs-generator.js.map +1 -0
- package/dist/core/generator.d.ts +16 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +561 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/gitignore-generator.d.ts +13 -0
- package/dist/core/gitignore-generator.d.ts.map +1 -0
- package/dist/core/gitignore-generator.js +307 -0
- package/dist/core/gitignore-generator.js.map +1 -0
- package/dist/core/health-scorer.d.ts +22 -0
- package/dist/core/health-scorer.d.ts.map +1 -0
- package/dist/core/health-scorer.js +395 -0
- package/dist/core/health-scorer.js.map +1 -0
- package/dist/core/logger.d.ts +116 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +289 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/merger.d.ts +6 -0
- package/dist/core/merger.d.ts.map +1 -0
- package/dist/core/merger.js +131 -0
- package/dist/core/merger.js.map +1 -0
- package/dist/core/migrator.d.ts +19 -0
- package/dist/core/migrator.d.ts.map +1 -0
- package/dist/core/migrator.js +102 -0
- package/dist/core/migrator.js.map +1 -0
- package/dist/core/minimal-scaffolder.d.ts +8 -0
- package/dist/core/minimal-scaffolder.d.ts.map +1 -0
- package/dist/core/minimal-scaffolder.js +51 -0
- package/dist/core/minimal-scaffolder.js.map +1 -0
- package/dist/core/modern-console-new.d.ts +81 -0
- package/dist/core/modern-console-new.d.ts.map +1 -0
- package/dist/core/modern-console-new.js +340 -0
- package/dist/core/modern-console-new.js.map +1 -0
- package/dist/core/modern-console.d.ts +99 -0
- package/dist/core/modern-console.d.ts.map +1 -0
- package/dist/core/modern-console.js +568 -0
- package/dist/core/modern-console.js.map +1 -0
- package/dist/core/openspec-manager.d.ts +133 -0
- package/dist/core/openspec-manager.d.ts.map +1 -0
- package/dist/core/openspec-manager.js +605 -0
- package/dist/core/openspec-manager.js.map +1 -0
- package/dist/core/openspec-migrator.d.ts +27 -0
- package/dist/core/openspec-migrator.d.ts.map +1 -0
- package/dist/core/openspec-migrator.js +255 -0
- package/dist/core/openspec-migrator.js.map +1 -0
- package/dist/core/task-manager.d.ts +65 -0
- package/dist/core/task-manager.d.ts.map +1 -0
- package/dist/core/task-manager.js +318 -0
- package/dist/core/task-manager.js.map +1 -0
- package/dist/core/test-task-manager.d.ts +49 -0
- package/dist/core/test-task-manager.d.ts.map +1 -0
- package/dist/core/test-task-manager.js +121 -0
- package/dist/core/test-task-manager.js.map +1 -0
- package/dist/core/validator.d.ts +21 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +177 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/core/version-bumper.d.ts +19 -0
- package/dist/core/version-bumper.d.ts.map +1 -0
- package/dist/core/version-bumper.js +180 -0
- package/dist/core/version-bumper.js.map +1 -0
- package/dist/core/watcher.d.ts +9 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +22 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/core/workflow-generator.d.ts +10 -0
- package/dist/core/workflow-generator.d.ts.map +1 -0
- package/dist/core/workflow-generator.js +279 -0
- package/dist/core/workflow-generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/handlers/archive-task.d.ts +17 -0
- package/dist/mcp/handlers/archive-task.d.ts.map +1 -0
- package/dist/mcp/handlers/archive-task.js +36 -0
- package/dist/mcp/handlers/archive-task.js.map +1 -0
- package/dist/mcp/handlers/create-task.d.ts +17 -0
- package/dist/mcp/handlers/create-task.d.ts.map +1 -0
- package/dist/mcp/handlers/create-task.js +56 -0
- package/dist/mcp/handlers/create-task.js.map +1 -0
- package/dist/mcp/handlers/list-tasks.d.ts +22 -0
- package/dist/mcp/handlers/list-tasks.d.ts.map +1 -0
- package/dist/mcp/handlers/list-tasks.js +42 -0
- package/dist/mcp/handlers/list-tasks.js.map +1 -0
- package/dist/mcp/handlers/show-task.d.ts +25 -0
- package/dist/mcp/handlers/show-task.d.ts.map +1 -0
- package/dist/mcp/handlers/show-task.js +43 -0
- package/dist/mcp/handlers/show-task.js.map +1 -0
- package/dist/mcp/handlers/update-task.d.ts +17 -0
- package/dist/mcp/handlers/update-task.d.ts.map +1 -0
- package/dist/mcp/handlers/update-task.js +35 -0
- package/dist/mcp/handlers/update-task.js.map +1 -0
- package/dist/mcp/handlers/validate-task.d.ts +15 -0
- package/dist/mcp/handlers/validate-task.d.ts.map +1 -0
- package/dist/mcp/handlers/validate-task.js +27 -0
- package/dist/mcp/handlers/validate-task.js.map +1 -0
- package/dist/mcp/rulebook-config.d.ts +22 -0
- package/dist/mcp/rulebook-config.d.ts.map +1 -0
- package/dist/mcp/rulebook-config.js +65 -0
- package/dist/mcp/rulebook-config.js.map +1 -0
- package/dist/mcp/rulebook-server.d.ts +4 -0
- package/dist/mcp/rulebook-server.d.ts.map +1 -0
- package/dist/mcp/rulebook-server.js +246 -0
- package/dist/mcp/rulebook-server.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file-system.d.ts +9 -0
- package/dist/utils/file-system.d.ts.map +1 -0
- package/dist/utils/file-system.js +51 -0
- package/dist/utils/file-system.js.map +1 -0
- package/dist/utils/git-hooks.d.ts +8 -0
- package/dist/utils/git-hooks.d.ts.map +1 -0
- package/dist/utils/git-hooks.js +440 -0
- package/dist/utils/git-hooks.js.map +1 -0
- package/dist/utils/rulesignore.d.ts +9 -0
- package/dist/utils/rulesignore.d.ts.map +1 -0
- package/dist/utils/rulesignore.js +42 -0
- package/dist/utils/rulesignore.js.map +1 -0
- package/package.json +106 -0
- package/templates/cli/AIDER.md +49 -0
- package/templates/cli/AMAZON_Q.md +25 -0
- package/templates/cli/AUGGIE.md +32 -0
- package/templates/cli/CLAUDE.md +32 -0
- package/templates/cli/CLAUDE_CODE.md +35 -0
- package/templates/cli/CLINE.md +32 -0
- package/templates/cli/CODEBUDDY.md +20 -0
- package/templates/cli/CODEIUM.md +20 -0
- package/templates/cli/CODEX.md +21 -0
- package/templates/cli/CONTINUE.md +34 -0
- package/templates/cli/CURSOR_CLI.md +28 -0
- package/templates/cli/FACTORY.md +18 -0
- package/templates/cli/GEMINI.md +35 -0
- package/templates/cli/KILOCODE.md +18 -0
- package/templates/cli/OPENCODE.md +18 -0
- package/templates/cli/_GENERIC_TEMPLATE.md +29 -0
- package/templates/commands/rulebook-task-apply.md +67 -0
- package/templates/commands/rulebook-task-archive.md +70 -0
- package/templates/commands/rulebook-task-create.md +93 -0
- package/templates/commands/rulebook-task-list.md +42 -0
- package/templates/commands/rulebook-task-show.md +52 -0
- package/templates/commands/rulebook-task-validate.md +53 -0
- package/templates/core/AGENT_AUTOMATION.md +184 -0
- package/templates/core/DAG.md +304 -0
- package/templates/core/DOCUMENTATION_RULES.md +37 -0
- package/templates/core/QUALITY_ENFORCEMENT.md +68 -0
- package/templates/core/RULEBOOK.md +1874 -0
- package/templates/frameworks/ANGULAR.md +36 -0
- package/templates/frameworks/DJANGO.md +83 -0
- package/templates/frameworks/ELECTRON.md +147 -0
- package/templates/frameworks/FLASK.md +38 -0
- package/templates/frameworks/FLUTTER.md +55 -0
- package/templates/frameworks/JQUERY.md +32 -0
- package/templates/frameworks/LARAVEL.md +38 -0
- package/templates/frameworks/NESTJS.md +43 -0
- package/templates/frameworks/NEXTJS.md +127 -0
- package/templates/frameworks/NUXT.md +40 -0
- package/templates/frameworks/RAILS.md +66 -0
- package/templates/frameworks/REACT.md +38 -0
- package/templates/frameworks/REACT_NATIVE.md +47 -0
- package/templates/frameworks/SPRING.md +39 -0
- package/templates/frameworks/SYMFONY.md +36 -0
- package/templates/frameworks/VUE.md +36 -0
- package/templates/frameworks/ZEND.md +35 -0
- package/templates/git/CI_CD_PATTERNS.md +661 -0
- package/templates/git/GITHUB_ACTIONS.md +728 -0
- package/templates/git/GITLAB_CI.md +730 -0
- package/templates/git/GIT_WORKFLOW.md +1157 -0
- package/templates/git/SECRETS_MANAGEMENT.md +585 -0
- package/templates/hooks/COMMIT_MSG.md +530 -0
- package/templates/hooks/POST_CHECKOUT.md +546 -0
- package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -0
- package/templates/hooks/PRE_COMMIT.md +414 -0
- package/templates/hooks/PRE_PUSH.md +601 -0
- package/templates/hooks/csharp-pre-commit.sh +23 -0
- package/templates/hooks/csharp-pre-push.sh +23 -0
- package/templates/hooks/dart-pre-commit.sh +30 -0
- package/templates/hooks/dart-pre-push.sh +25 -0
- package/templates/hooks/elixir-pre-commit.sh +32 -0
- package/templates/hooks/elixir-pre-push.sh +31 -0
- package/templates/hooks/erlang-pre-commit.sh +30 -0
- package/templates/hooks/erlang-pre-push.sh +37 -0
- package/templates/hooks/go-pre-commit.sh +40 -0
- package/templates/hooks/go-pre-push.sh +31 -0
- package/templates/hooks/haskell-pre-commit.sh +41 -0
- package/templates/hooks/haskell-pre-push.sh +37 -0
- package/templates/hooks/java-pre-commit.sh +34 -0
- package/templates/hooks/java-pre-push.sh +24 -0
- package/templates/hooks/kotlin-pre-commit.sh +32 -0
- package/templates/hooks/kotlin-pre-push.sh +16 -0
- package/templates/hooks/php-pre-commit.sh +36 -0
- package/templates/hooks/php-pre-push.sh +26 -0
- package/templates/hooks/python-pre-commit.sh +51 -0
- package/templates/hooks/python-pre-push.sh +25 -0
- package/templates/hooks/ruby-pre-commit.sh +33 -0
- package/templates/hooks/ruby-pre-push.sh +32 -0
- package/templates/hooks/rust-pre-commit.sh +30 -0
- package/templates/hooks/rust-pre-push.sh +30 -0
- package/templates/hooks/scala-pre-commit.sh +32 -0
- package/templates/hooks/scala-pre-push.sh +24 -0
- package/templates/hooks/swift-pre-commit.sh +25 -0
- package/templates/hooks/swift-pre-push.sh +23 -0
- package/templates/hooks/typescript-pre-commit.sh +37 -0
- package/templates/hooks/typescript-pre-push.sh +36 -0
- package/templates/ides/COPILOT.md +37 -0
- package/templates/ides/CURSOR.md +43 -0
- package/templates/ides/JETBRAINS_AI.md +35 -0
- package/templates/ides/REPLIT.md +36 -0
- package/templates/ides/TABNINE.md +29 -0
- package/templates/ides/VSCODE.md +40 -0
- package/templates/ides/WINDSURF.md +36 -0
- package/templates/ides/ZED.md +32 -0
- package/templates/languages/ADA.md +58 -0
- package/templates/languages/C.md +333 -0
- package/templates/languages/CPP.md +743 -0
- package/templates/languages/CSHARP.md +417 -0
- package/templates/languages/DART.md +332 -0
- package/templates/languages/ELIXIR.md +454 -0
- package/templates/languages/ERLANG.md +361 -0
- package/templates/languages/GO.md +645 -0
- package/templates/languages/HASKELL.md +177 -0
- package/templates/languages/JAVA.md +607 -0
- package/templates/languages/JAVASCRIPT.md +631 -0
- package/templates/languages/JULIA.md +97 -0
- package/templates/languages/KOTLIN.md +511 -0
- package/templates/languages/LISP.md +100 -0
- package/templates/languages/LUA.md +74 -0
- package/templates/languages/OBJECTIVEC.md +90 -0
- package/templates/languages/PHP.md +416 -0
- package/templates/languages/PYTHON.md +682 -0
- package/templates/languages/R.md +350 -0
- package/templates/languages/RUBY.md +421 -0
- package/templates/languages/RUST.md +477 -0
- package/templates/languages/SAS.md +73 -0
- package/templates/languages/SCALA.md +348 -0
- package/templates/languages/SOLIDITY.md +580 -0
- package/templates/languages/SQL.md +137 -0
- package/templates/languages/SWIFT.md +466 -0
- package/templates/languages/TYPESCRIPT.md +591 -0
- package/templates/languages/ZIG.md +265 -0
- package/templates/modules/ATLASSIAN.md +255 -0
- package/templates/modules/CONTEXT7.md +54 -0
- package/templates/modules/FIGMA.md +267 -0
- package/templates/modules/GITHUB_MCP.md +64 -0
- package/templates/modules/GRAFANA.md +328 -0
- package/templates/modules/NOTION.md +247 -0
- package/templates/modules/PLAYWRIGHT.md +90 -0
- package/templates/modules/RULEBOOK_MCP.md +156 -0
- package/templates/modules/SERENA.md +337 -0
- package/templates/modules/SUPABASE.md +223 -0
- package/templates/modules/SYNAP.md +69 -0
- package/templates/modules/VECTORIZER.md +63 -0
- package/templates/services/AZURE_BLOB.md +184 -0
- package/templates/services/CASSANDRA.md +239 -0
- package/templates/services/DYNAMODB.md +308 -0
- package/templates/services/ELASTICSEARCH.md +347 -0
- package/templates/services/GCS.md +178 -0
- package/templates/services/INFLUXDB.md +265 -0
- package/templates/services/KAFKA.md +341 -0
- package/templates/services/MARIADB.md +183 -0
- package/templates/services/MEMCACHED.md +242 -0
- package/templates/services/MINIO.md +201 -0
- package/templates/services/MONGODB.md +268 -0
- package/templates/services/MYSQL.md +358 -0
- package/templates/services/NEO4J.md +247 -0
- package/templates/services/ORACLE.md +290 -0
- package/templates/services/POSTGRESQL.md +326 -0
- package/templates/services/RABBITMQ.md +286 -0
- package/templates/services/REDIS.md +292 -0
- package/templates/services/S3.md +298 -0
- package/templates/services/SQLITE.md +294 -0
- package/templates/services/SQLSERVER.md +294 -0
- package/templates/workflows/codespell.yml +31 -0
- package/templates/workflows/cpp-lint.yml +47 -0
- package/templates/workflows/cpp-publish.yml +119 -0
- package/templates/workflows/cpp-test.yml +77 -0
- package/templates/workflows/dotnet-lint.yml +29 -0
- package/templates/workflows/dotnet-publish.yml +40 -0
- package/templates/workflows/dotnet-test.yml +41 -0
- package/templates/workflows/elixir-lint.yml +45 -0
- package/templates/workflows/elixir-publish.yml +49 -0
- package/templates/workflows/elixir-test.yml +54 -0
- package/templates/workflows/erlang-lint.yml +47 -0
- package/templates/workflows/erlang-test.yml +62 -0
- package/templates/workflows/go-lint.yml +39 -0
- package/templates/workflows/go-publish.yml +95 -0
- package/templates/workflows/go-test.yml +59 -0
- package/templates/workflows/java-lint.yml +60 -0
- package/templates/workflows/java-publish.yml +120 -0
- package/templates/workflows/java-test.yml +85 -0
- package/templates/workflows/kotlin-lint.yml +34 -0
- package/templates/workflows/kotlin-publish.yml +56 -0
- package/templates/workflows/kotlin-test.yml +48 -0
- package/templates/workflows/php-lint.yml +39 -0
- package/templates/workflows/php-publish.yml +50 -0
- package/templates/workflows/php-test.yml +54 -0
- package/templates/workflows/python-lint.yml +47 -0
- package/templates/workflows/python-publish.yml +91 -0
- package/templates/workflows/python-test.yml +59 -0
- package/templates/workflows/rust-lint.yml +54 -0
- package/templates/workflows/rust-publish.yml +66 -0
- package/templates/workflows/rust-test.yml +75 -0
- package/templates/workflows/solidity-lint.yml +41 -0
- package/templates/workflows/solidity-test.yml +47 -0
- package/templates/workflows/swift-lint.yml +32 -0
- package/templates/workflows/swift-publish.yml +58 -0
- package/templates/workflows/swift-test.yml +44 -0
- package/templates/workflows/typescript-lint.yml +61 -0
- package/templates/workflows/typescript-publish.yml +60 -0
- package/templates/workflows/typescript-test.yml +73 -0
- package/templates/workflows/zig-lint.yml +27 -0
- package/templates/workflows/zig-test.yml +40 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { existsSync, readdirSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { fileExists, readFile, writeFile } from '../utils/file-system.js';
|
|
4
|
+
import { createTaskManager } from './task-manager.js';
|
|
5
|
+
const OPENSPEC_DIR = 'openspec';
|
|
6
|
+
const CHANGES_DIR = 'changes';
|
|
7
|
+
const SPECS_DIR = 'specs';
|
|
8
|
+
const TASKS_FILE = 'tasks.md';
|
|
9
|
+
const PROPOSAL_FILE = 'proposal.md';
|
|
10
|
+
const DESIGN_FILE = 'design.md';
|
|
11
|
+
/**
|
|
12
|
+
* Migrate OpenSpec tasks to Rulebook format
|
|
13
|
+
*/
|
|
14
|
+
export async function migrateOpenSpecToRulebook(projectRoot, rulebookDir = 'rulebook') {
|
|
15
|
+
const result = {
|
|
16
|
+
migrated: 0,
|
|
17
|
+
skipped: 0,
|
|
18
|
+
errors: [],
|
|
19
|
+
migratedTasks: [],
|
|
20
|
+
};
|
|
21
|
+
const openspecPath = join(projectRoot, OPENSPEC_DIR);
|
|
22
|
+
const changesPath = join(openspecPath, CHANGES_DIR);
|
|
23
|
+
// Check if OpenSpec directory exists
|
|
24
|
+
if (!existsSync(changesPath)) {
|
|
25
|
+
return result; // No OpenSpec tasks to migrate
|
|
26
|
+
}
|
|
27
|
+
// Get all change directories
|
|
28
|
+
let changeDirs = [];
|
|
29
|
+
try {
|
|
30
|
+
changeDirs = readdirSync(changesPath, { withFileTypes: true })
|
|
31
|
+
.filter((dirent) => dirent.isDirectory())
|
|
32
|
+
.map((dirent) => dirent.name);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
result.errors.push(`Failed to read OpenSpec changes directory: ${error.message}`);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
if (changeDirs.length === 0) {
|
|
39
|
+
return result; // No tasks to migrate
|
|
40
|
+
}
|
|
41
|
+
// Initialize TaskManager
|
|
42
|
+
const taskManager = createTaskManager(projectRoot, rulebookDir);
|
|
43
|
+
await taskManager.initialize();
|
|
44
|
+
// Migrate each task
|
|
45
|
+
for (const changeDir of changeDirs) {
|
|
46
|
+
const taskId = changeDir; // Use directory name as task ID
|
|
47
|
+
const sourceTaskPath = join(changesPath, changeDir);
|
|
48
|
+
const targetTaskPath = join(projectRoot, rulebookDir, 'tasks', taskId);
|
|
49
|
+
try {
|
|
50
|
+
// Check if task already exists in Rulebook
|
|
51
|
+
if (existsSync(targetTaskPath)) {
|
|
52
|
+
result.skipped++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Create target directory
|
|
56
|
+
mkdirSync(targetTaskPath, { recursive: true });
|
|
57
|
+
mkdirSync(join(targetTaskPath, SPECS_DIR), { recursive: true });
|
|
58
|
+
// Migrate proposal.md
|
|
59
|
+
const proposalSource = join(sourceTaskPath, PROPOSAL_FILE);
|
|
60
|
+
if (await fileExists(proposalSource)) {
|
|
61
|
+
const proposalContent = await readFile(proposalSource);
|
|
62
|
+
await writeFile(join(targetTaskPath, PROPOSAL_FILE), proposalContent);
|
|
63
|
+
}
|
|
64
|
+
// Migrate tasks.md
|
|
65
|
+
const tasksSource = join(sourceTaskPath, TASKS_FILE);
|
|
66
|
+
if (await fileExists(tasksSource)) {
|
|
67
|
+
const tasksContent = await readFile(tasksSource);
|
|
68
|
+
await writeFile(join(targetTaskPath, TASKS_FILE), tasksContent);
|
|
69
|
+
}
|
|
70
|
+
// Migrate design.md (if exists)
|
|
71
|
+
const designSource = join(sourceTaskPath, DESIGN_FILE);
|
|
72
|
+
if (await fileExists(designSource)) {
|
|
73
|
+
const designContent = await readFile(designSource);
|
|
74
|
+
await writeFile(join(targetTaskPath, DESIGN_FILE), designContent);
|
|
75
|
+
}
|
|
76
|
+
// Migrate specs
|
|
77
|
+
const specsSource = join(sourceTaskPath, SPECS_DIR);
|
|
78
|
+
if (existsSync(specsSource)) {
|
|
79
|
+
const specDirs = readdirSync(specsSource, { withFileTypes: true })
|
|
80
|
+
.filter((dirent) => dirent.isDirectory())
|
|
81
|
+
.map((dirent) => dirent.name);
|
|
82
|
+
for (const specDir of specDirs) {
|
|
83
|
+
const specSource = join(specsSource, specDir, 'spec.md');
|
|
84
|
+
if (await fileExists(specSource)) {
|
|
85
|
+
const specContent = await readFile(specSource);
|
|
86
|
+
const targetSpecDir = join(targetTaskPath, SPECS_DIR, specDir);
|
|
87
|
+
mkdirSync(targetSpecDir, { recursive: true });
|
|
88
|
+
await writeFile(join(targetSpecDir, 'spec.md'), specContent);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
result.migrated++;
|
|
93
|
+
result.migratedTasks.push(taskId);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
result.errors.push(`Failed to migrate task ${taskId}: ${error.message}`);
|
|
97
|
+
result.skipped++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Migrate OpenSpec archived tasks to Rulebook format
|
|
104
|
+
*/
|
|
105
|
+
export async function migrateOpenSpecArchives(projectRoot, rulebookDir = 'rulebook') {
|
|
106
|
+
const result = {
|
|
107
|
+
migrated: 0,
|
|
108
|
+
skipped: 0,
|
|
109
|
+
errors: [],
|
|
110
|
+
migratedTasks: [],
|
|
111
|
+
};
|
|
112
|
+
const openspecPath = join(projectRoot, OPENSPEC_DIR);
|
|
113
|
+
const archivePath = join(openspecPath, CHANGES_DIR, 'archive');
|
|
114
|
+
// Check if OpenSpec archive directory exists
|
|
115
|
+
if (!existsSync(archivePath)) {
|
|
116
|
+
return result; // No archived tasks to migrate
|
|
117
|
+
}
|
|
118
|
+
// Get all archived task directories
|
|
119
|
+
let archiveDirs = [];
|
|
120
|
+
try {
|
|
121
|
+
archiveDirs = readdirSync(archivePath, { withFileTypes: true })
|
|
122
|
+
.filter((dirent) => dirent.isDirectory())
|
|
123
|
+
.map((dirent) => dirent.name);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
result.errors.push(`Failed to read OpenSpec archive directory: ${error.message}`);
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
if (archiveDirs.length === 0) {
|
|
130
|
+
return result; // No archived tasks to migrate
|
|
131
|
+
}
|
|
132
|
+
// Initialize TaskManager
|
|
133
|
+
const taskManager = createTaskManager(projectRoot, rulebookDir);
|
|
134
|
+
await taskManager.initialize();
|
|
135
|
+
// Migrate each archived task
|
|
136
|
+
for (const archiveDir of archiveDirs) {
|
|
137
|
+
const sourceArchivePath = join(archivePath, archiveDir);
|
|
138
|
+
// Preserve archive date prefix (YYYY-MM-DD-task-id format)
|
|
139
|
+
const archiveName = archiveDir; // Keep original name with date prefix
|
|
140
|
+
const targetArchivePath = join(projectRoot, rulebookDir, 'tasks', 'archive', archiveName);
|
|
141
|
+
try {
|
|
142
|
+
// Check if archive already exists in Rulebook
|
|
143
|
+
if (existsSync(targetArchivePath)) {
|
|
144
|
+
result.skipped++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
// Create target archive directory
|
|
148
|
+
mkdirSync(targetArchivePath, { recursive: true });
|
|
149
|
+
mkdirSync(join(targetArchivePath, SPECS_DIR), { recursive: true });
|
|
150
|
+
// Migrate proposal.md
|
|
151
|
+
const proposalSource = join(sourceArchivePath, PROPOSAL_FILE);
|
|
152
|
+
if (await fileExists(proposalSource)) {
|
|
153
|
+
const proposalContent = await readFile(proposalSource);
|
|
154
|
+
await writeFile(join(targetArchivePath, PROPOSAL_FILE), proposalContent);
|
|
155
|
+
}
|
|
156
|
+
// Migrate tasks.md
|
|
157
|
+
const tasksSource = join(sourceArchivePath, TASKS_FILE);
|
|
158
|
+
if (await fileExists(tasksSource)) {
|
|
159
|
+
const tasksContent = await readFile(tasksSource);
|
|
160
|
+
await writeFile(join(targetArchivePath, TASKS_FILE), tasksContent);
|
|
161
|
+
}
|
|
162
|
+
// Migrate design.md (if exists)
|
|
163
|
+
const designSource = join(sourceArchivePath, DESIGN_FILE);
|
|
164
|
+
if (await fileExists(designSource)) {
|
|
165
|
+
const designContent = await readFile(designSource);
|
|
166
|
+
await writeFile(join(targetArchivePath, DESIGN_FILE), designContent);
|
|
167
|
+
}
|
|
168
|
+
// Migrate specs
|
|
169
|
+
const specsSource = join(sourceArchivePath, SPECS_DIR);
|
|
170
|
+
if (existsSync(specsSource)) {
|
|
171
|
+
const specDirs = readdirSync(specsSource, { withFileTypes: true })
|
|
172
|
+
.filter((dirent) => dirent.isDirectory())
|
|
173
|
+
.map((dirent) => dirent.name);
|
|
174
|
+
for (const specDir of specDirs) {
|
|
175
|
+
const specSource = join(specsSource, specDir, 'spec.md');
|
|
176
|
+
if (await fileExists(specSource)) {
|
|
177
|
+
const specContent = await readFile(specSource);
|
|
178
|
+
const targetSpecDir = join(targetArchivePath, SPECS_DIR, specDir);
|
|
179
|
+
mkdirSync(targetSpecDir, { recursive: true });
|
|
180
|
+
await writeFile(join(targetSpecDir, 'spec.md'), specContent);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
result.migrated++;
|
|
185
|
+
result.migratedTasks.push(archiveName);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
result.errors.push(`Failed to migrate archived task ${archiveName}: ${error.message}`);
|
|
189
|
+
result.skipped++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Remove /rulebook/OPENSPEC.md if it exists
|
|
196
|
+
*/
|
|
197
|
+
export async function removeOpenSpecRulebookFile(projectRoot, rulebookDir = 'rulebook') {
|
|
198
|
+
const openspecRulebookPath = join(projectRoot, rulebookDir, 'OPENSPEC.md');
|
|
199
|
+
if (await fileExists(openspecRulebookPath)) {
|
|
200
|
+
const { unlink } = await import('fs/promises');
|
|
201
|
+
await unlink(openspecRulebookPath);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Remove OpenSpec commands from .cursor/commands/
|
|
208
|
+
*/
|
|
209
|
+
export async function removeOpenSpecCommands(projectRoot) {
|
|
210
|
+
const commandsDir = join(projectRoot, '.cursor', 'commands');
|
|
211
|
+
if (!existsSync(commandsDir)) {
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
const openspecCommands = ['openspec-proposal.md', 'openspec-archive.md', 'openspec-apply.md'];
|
|
215
|
+
let removed = 0;
|
|
216
|
+
for (const command of openspecCommands) {
|
|
217
|
+
const commandPath = join(commandsDir, command);
|
|
218
|
+
if (await fileExists(commandPath)) {
|
|
219
|
+
const { unlink } = await import('fs/promises');
|
|
220
|
+
await unlink(commandPath);
|
|
221
|
+
removed++;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return removed;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Archive OpenSpec directory (move to archive)
|
|
228
|
+
*/
|
|
229
|
+
export async function archiveOpenSpecDirectory(projectRoot) {
|
|
230
|
+
const openspecPath = join(projectRoot, OPENSPEC_DIR);
|
|
231
|
+
if (!existsSync(openspecPath)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const archiveDate = new Date().toISOString().split('T')[0];
|
|
235
|
+
const archivePath = join(projectRoot, `openspec-archive-${archiveDate}`);
|
|
236
|
+
try {
|
|
237
|
+
// Use mv command to move directory
|
|
238
|
+
const { execSync } = await import('child_process');
|
|
239
|
+
execSync(`mv "${openspecPath}" "${archivePath}"`, { cwd: projectRoot });
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Fallback: copy and remove
|
|
244
|
+
const { cpSync, rmSync } = await import('fs');
|
|
245
|
+
try {
|
|
246
|
+
cpSync(openspecPath, archivePath, { recursive: true });
|
|
247
|
+
rmSync(openspecPath, { recursive: true, force: true });
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=openspec-migrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openspec-migrator.js","sourceRoot":"","sources":["../../src/core/openspec-migrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface RulebookTask {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
proposal?: string;
|
|
5
|
+
tasks?: string;
|
|
6
|
+
design?: string;
|
|
7
|
+
specs?: Record<string, string>;
|
|
8
|
+
status: 'pending' | 'in-progress' | 'completed' | 'blocked';
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
archivedAt?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface TaskValidationResult {
|
|
14
|
+
valid: boolean;
|
|
15
|
+
errors: string[];
|
|
16
|
+
warnings: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare class TaskManager {
|
|
19
|
+
private rulebookPath;
|
|
20
|
+
private tasksPath;
|
|
21
|
+
private archivePath;
|
|
22
|
+
constructor(projectRoot: string, rulebookDir?: string);
|
|
23
|
+
/**
|
|
24
|
+
* Initialize Rulebook tasks directory structure
|
|
25
|
+
*/
|
|
26
|
+
initialize(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get the path to a task directory
|
|
29
|
+
*/
|
|
30
|
+
getTaskPath(taskId: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Create a new task
|
|
33
|
+
*/
|
|
34
|
+
createTask(taskId: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* List all tasks
|
|
37
|
+
*/
|
|
38
|
+
listTasks(includeArchived?: boolean): Promise<RulebookTask[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Load a task by ID
|
|
41
|
+
*/
|
|
42
|
+
loadTask(taskId: string, archived?: boolean): Promise<RulebookTask | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Validate task format (OpenSpec-compatible)
|
|
45
|
+
*/
|
|
46
|
+
validateTask(taskId: string): Promise<TaskValidationResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Archive a completed task
|
|
49
|
+
*/
|
|
50
|
+
archiveTask(taskId: string, skipValidation?: boolean): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Update task status
|
|
53
|
+
*/
|
|
54
|
+
updateTaskStatus(taskId: string, status: RulebookTask['status']): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Show task details
|
|
57
|
+
*/
|
|
58
|
+
showTask(taskId: string): Promise<RulebookTask | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Delete a task permanently
|
|
61
|
+
*/
|
|
62
|
+
deleteTask(taskId: string): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
export declare function createTaskManager(projectRoot: string, rulebookDir?: string): TaskManager;
|
|
65
|
+
//# sourceMappingURL=task-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-manager.d.ts","sourceRoot":"","sources":["../../src/core/task-manager.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM,EAAE,WAAW,GAAE,MAAmB;IAMjE;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAInC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C/C;;OAEG;IACG,SAAS,CAAC,eAAe,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyC1E;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA8DvF;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkEjE;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCjF;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAarF;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAiB5D;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWhD;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAmB,GAC/B,WAAW,CAEb"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { writeFile, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { fileExists, readFile as readFileUtil } from '../utils/file-system.js';
|
|
5
|
+
const writeFileAsync = promisify(writeFile);
|
|
6
|
+
const TASKS_DIR = 'tasks';
|
|
7
|
+
const ARCHIVE_DIR = 'archive';
|
|
8
|
+
const SPECS_DIR = 'specs';
|
|
9
|
+
export class TaskManager {
|
|
10
|
+
rulebookPath;
|
|
11
|
+
tasksPath;
|
|
12
|
+
archivePath;
|
|
13
|
+
constructor(projectRoot, rulebookDir = 'rulebook') {
|
|
14
|
+
this.rulebookPath = join(projectRoot, rulebookDir);
|
|
15
|
+
this.tasksPath = join(this.rulebookPath, TASKS_DIR);
|
|
16
|
+
this.archivePath = join(this.tasksPath, ARCHIVE_DIR);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Initialize Rulebook tasks directory structure
|
|
20
|
+
*/
|
|
21
|
+
async initialize() {
|
|
22
|
+
if (!existsSync(this.rulebookPath)) {
|
|
23
|
+
mkdirSync(this.rulebookPath, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
if (!existsSync(this.tasksPath)) {
|
|
26
|
+
mkdirSync(this.tasksPath, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
if (!existsSync(this.archivePath)) {
|
|
29
|
+
mkdirSync(this.archivePath, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the path to a task directory
|
|
34
|
+
*/
|
|
35
|
+
getTaskPath(taskId) {
|
|
36
|
+
return join(this.tasksPath, taskId);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create a new task
|
|
40
|
+
*/
|
|
41
|
+
async createTask(taskId) {
|
|
42
|
+
await this.initialize();
|
|
43
|
+
const taskPath = join(this.tasksPath, taskId);
|
|
44
|
+
if (existsSync(taskPath)) {
|
|
45
|
+
throw new Error(`Task ${taskId} already exists`);
|
|
46
|
+
}
|
|
47
|
+
mkdirSync(taskPath, { recursive: true });
|
|
48
|
+
mkdirSync(join(taskPath, SPECS_DIR), { recursive: true });
|
|
49
|
+
// Create proposal.md template
|
|
50
|
+
const proposalContent = `# Proposal: ${taskId}
|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
[Explain why this change is needed - minimum 20 characters]
|
|
54
|
+
|
|
55
|
+
## What Changes
|
|
56
|
+
[Describe what will change]
|
|
57
|
+
|
|
58
|
+
## Impact
|
|
59
|
+
- Affected specs: [list]
|
|
60
|
+
- Affected code: [list]
|
|
61
|
+
- Breaking change: YES/NO
|
|
62
|
+
- User benefit: [describe]
|
|
63
|
+
`;
|
|
64
|
+
await writeFileAsync(join(taskPath, 'proposal.md'), proposalContent);
|
|
65
|
+
// Create tasks.md template
|
|
66
|
+
const tasksContent = `## 1. Implementation
|
|
67
|
+
- [ ] 1.1 First task
|
|
68
|
+
- [ ] 1.2 Second task
|
|
69
|
+
|
|
70
|
+
## 2. Testing
|
|
71
|
+
- [ ] 2.1 Write tests
|
|
72
|
+
- [ ] 2.2 Verify coverage
|
|
73
|
+
|
|
74
|
+
## 3. Documentation
|
|
75
|
+
- [ ] 3.1 Update README
|
|
76
|
+
- [ ] 3.2 Update CHANGELOG
|
|
77
|
+
`;
|
|
78
|
+
await writeFileAsync(join(taskPath, 'tasks.md'), tasksContent);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* List all tasks
|
|
82
|
+
*/
|
|
83
|
+
async listTasks(includeArchived = false) {
|
|
84
|
+
await this.initialize();
|
|
85
|
+
const tasks = [];
|
|
86
|
+
// List active tasks
|
|
87
|
+
if (existsSync(this.tasksPath)) {
|
|
88
|
+
const entries = readdirSync(this.tasksPath, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
if (entry.isDirectory() && entry.name !== ARCHIVE_DIR) {
|
|
91
|
+
const task = await this.loadTask(entry.name);
|
|
92
|
+
if (task) {
|
|
93
|
+
tasks.push(task);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// List archived tasks if requested
|
|
99
|
+
if (includeArchived && existsSync(this.archivePath)) {
|
|
100
|
+
const archiveEntries = readdirSync(this.archivePath, { withFileTypes: true });
|
|
101
|
+
for (const entry of archiveEntries) {
|
|
102
|
+
if (entry.isDirectory()) {
|
|
103
|
+
const task = await this.loadTask(entry.name, true);
|
|
104
|
+
if (task) {
|
|
105
|
+
// Extract date from archive name (YYYY-MM-DD-task-id format)
|
|
106
|
+
const dateMatch = entry.name.match(/^(\d{4}-\d{2}-\d{2})-/);
|
|
107
|
+
if (dateMatch) {
|
|
108
|
+
task.archivedAt = dateMatch[1];
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
task.archivedAt = new Date().toISOString().split('T')[0];
|
|
112
|
+
}
|
|
113
|
+
tasks.push(task);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return tasks;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Load a task by ID
|
|
122
|
+
*/
|
|
123
|
+
async loadTask(taskId, archived = false) {
|
|
124
|
+
const basePath = archived ? this.archivePath : this.tasksPath;
|
|
125
|
+
const taskPath = join(basePath, taskId);
|
|
126
|
+
if (!existsSync(taskPath)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const proposalPath = join(taskPath, 'proposal.md');
|
|
130
|
+
const tasksPath = join(taskPath, 'tasks.md');
|
|
131
|
+
const designPath = join(taskPath, 'design.md');
|
|
132
|
+
const specsPath = join(taskPath, SPECS_DIR);
|
|
133
|
+
const task = {
|
|
134
|
+
id: taskId,
|
|
135
|
+
title: taskId,
|
|
136
|
+
status: 'pending',
|
|
137
|
+
createdAt: new Date().toISOString(),
|
|
138
|
+
updatedAt: new Date().toISOString(),
|
|
139
|
+
specs: {},
|
|
140
|
+
};
|
|
141
|
+
// Load proposal
|
|
142
|
+
if (await fileExists(proposalPath)) {
|
|
143
|
+
task.proposal = await readFileUtil(proposalPath);
|
|
144
|
+
}
|
|
145
|
+
// Load tasks
|
|
146
|
+
if (await fileExists(tasksPath)) {
|
|
147
|
+
task.tasks = await readFileUtil(tasksPath);
|
|
148
|
+
}
|
|
149
|
+
// Load design
|
|
150
|
+
if (await fileExists(designPath)) {
|
|
151
|
+
task.design = await readFileUtil(designPath);
|
|
152
|
+
}
|
|
153
|
+
// Load specs
|
|
154
|
+
if (existsSync(specsPath)) {
|
|
155
|
+
const specEntries = readdirSync(specsPath, { withFileTypes: true });
|
|
156
|
+
for (const specEntry of specEntries) {
|
|
157
|
+
if (specEntry.isDirectory()) {
|
|
158
|
+
const specFile = join(specsPath, specEntry.name, 'spec.md');
|
|
159
|
+
if (await fileExists(specFile)) {
|
|
160
|
+
task.specs[specEntry.name] = await readFileUtil(specFile);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Get file stats for dates
|
|
166
|
+
try {
|
|
167
|
+
const stats = statSync(taskPath);
|
|
168
|
+
task.createdAt = stats.birthtime.toISOString();
|
|
169
|
+
task.updatedAt = stats.mtime.toISOString();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Use defaults if stats fail
|
|
173
|
+
}
|
|
174
|
+
return task;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Validate task format (OpenSpec-compatible)
|
|
178
|
+
*/
|
|
179
|
+
async validateTask(taskId) {
|
|
180
|
+
const task = await this.loadTask(taskId);
|
|
181
|
+
if (!task) {
|
|
182
|
+
return {
|
|
183
|
+
valid: false,
|
|
184
|
+
errors: [`Task ${taskId} not found`],
|
|
185
|
+
warnings: [],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const errors = [];
|
|
189
|
+
const warnings = [];
|
|
190
|
+
// Validate proposal
|
|
191
|
+
if (!task.proposal) {
|
|
192
|
+
errors.push('Missing proposal.md');
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// Check Purpose section (minimum 20 characters)
|
|
196
|
+
const purposeMatch = task.proposal.match(/## Why\s*\n([\s\S]*?)(?=\n##|$)/);
|
|
197
|
+
if (!purposeMatch || purposeMatch[1].trim().length < 20) {
|
|
198
|
+
errors.push('Purpose section (## Why) must have at least 20 characters');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Validate specs
|
|
202
|
+
if (!task.specs || Object.keys(task.specs).length === 0) {
|
|
203
|
+
warnings.push('No spec files found (specs/*/spec.md)');
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
for (const [module, specContent] of Object.entries(task.specs)) {
|
|
207
|
+
// Check for requirements with SHALL/MUST
|
|
208
|
+
const requirementMatches = specContent.match(/### Requirement:.*/g) || [];
|
|
209
|
+
for (const req of requirementMatches) {
|
|
210
|
+
const reqText = specContent.substring(specContent.indexOf(req));
|
|
211
|
+
const reqBody = reqText.split('\n').slice(1).join('\n').split('####')[0];
|
|
212
|
+
if (!reqBody.match(/\b(SHALL|MUST)\b/i)) {
|
|
213
|
+
errors.push(`Requirement in ${module}/spec.md missing SHALL or MUST keyword: ${req}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Check for scenarios with 4 hashtags (not 3)
|
|
217
|
+
// Only check at start of line (not in text content)
|
|
218
|
+
const scenario3Matches = specContent.match(/^### Scenario:/gm) || [];
|
|
219
|
+
if (scenario3Matches.length > 0) {
|
|
220
|
+
errors.push(`Scenarios in ${module}/spec.md must use 4 hashtags (####), not 3 (###)`);
|
|
221
|
+
}
|
|
222
|
+
// Check for Given/When/Then structure
|
|
223
|
+
const scenarios = specContent.match(/#### Scenario:[\s\S]*?(?=####|##|$)/g) || [];
|
|
224
|
+
for (const scenario of scenarios) {
|
|
225
|
+
const hasGiven = /Given/i.test(scenario);
|
|
226
|
+
const hasWhen = /When/i.test(scenario);
|
|
227
|
+
const hasThen = /Then/i.test(scenario);
|
|
228
|
+
if (!hasGiven || !hasWhen || !hasThen) {
|
|
229
|
+
warnings.push(`Scenario in ${module}/spec.md should use Given/When/Then structure`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
valid: errors.length === 0,
|
|
236
|
+
errors,
|
|
237
|
+
warnings,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Archive a completed task
|
|
242
|
+
*/
|
|
243
|
+
async archiveTask(taskId, skipValidation = false) {
|
|
244
|
+
const task = await this.loadTask(taskId);
|
|
245
|
+
if (!task) {
|
|
246
|
+
throw new Error(`Task ${taskId} not found`);
|
|
247
|
+
}
|
|
248
|
+
// Validate before archiving (unless skipped)
|
|
249
|
+
if (!skipValidation) {
|
|
250
|
+
const validation = await this.validateTask(taskId);
|
|
251
|
+
if (!validation.valid) {
|
|
252
|
+
throw new Error(`Task validation failed:\n${validation.errors.join('\n')}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const taskPath = join(this.tasksPath, taskId);
|
|
256
|
+
const archiveDate = new Date().toISOString().split('T')[0];
|
|
257
|
+
const archiveName = `${archiveDate}-${taskId}`;
|
|
258
|
+
const archiveTaskPath = join(this.archivePath, archiveName);
|
|
259
|
+
// Ensure archive directory exists
|
|
260
|
+
if (!existsSync(this.archivePath)) {
|
|
261
|
+
mkdirSync(this.archivePath, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
if (existsSync(archiveTaskPath)) {
|
|
264
|
+
throw new Error(`Archive ${archiveName} already exists`);
|
|
265
|
+
}
|
|
266
|
+
// Move task to archive using cross-platform filesystem operations
|
|
267
|
+
const { renameSync } = await import('fs');
|
|
268
|
+
renameSync(taskPath, archiveTaskPath);
|
|
269
|
+
// TODO: Apply spec deltas to main specifications
|
|
270
|
+
// This would require parsing the spec deltas and merging them into /rulebook/specs/
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Update task status
|
|
274
|
+
*/
|
|
275
|
+
async updateTaskStatus(taskId, status) {
|
|
276
|
+
const task = await this.loadTask(taskId);
|
|
277
|
+
if (!task) {
|
|
278
|
+
throw new Error(`Task ${taskId} not found`);
|
|
279
|
+
}
|
|
280
|
+
task.status = status;
|
|
281
|
+
task.updatedAt = new Date().toISOString();
|
|
282
|
+
// Status is updated in memory only
|
|
283
|
+
// In the future, we could persist this to a status.md file or update tasks.md
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Show task details
|
|
287
|
+
*/
|
|
288
|
+
async showTask(taskId) {
|
|
289
|
+
// Try active tasks first
|
|
290
|
+
let task = await this.loadTask(taskId, false);
|
|
291
|
+
if (task) {
|
|
292
|
+
return task;
|
|
293
|
+
}
|
|
294
|
+
// Try archived tasks
|
|
295
|
+
task = await this.loadTask(taskId, true);
|
|
296
|
+
if (task) {
|
|
297
|
+
task.archivedAt = taskId.split('-').slice(0, 3).join('-'); // Extract date from task ID
|
|
298
|
+
return task;
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Delete a task permanently
|
|
304
|
+
*/
|
|
305
|
+
async deleteTask(taskId) {
|
|
306
|
+
const taskPath = join(this.tasksPath, taskId);
|
|
307
|
+
if (!existsSync(taskPath)) {
|
|
308
|
+
throw new Error(`Task ${taskId} not found`);
|
|
309
|
+
}
|
|
310
|
+
// Remove task directory recursively
|
|
311
|
+
const { rmSync } = await import('fs');
|
|
312
|
+
rmSync(taskPath, { recursive: true, force: true });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export function createTaskManager(projectRoot, rulebookDir = 'rulebook') {
|
|
316
|
+
return new TaskManager(projectRoot, rulebookDir);
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=task-manager.js.map
|