aiwf 0.3.23 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +146 -0
- package/CHANGELOG.md +49 -1
- package/README.ko.md +118 -309
- package/README.md +68 -254
- package/docs/modernization/BROWNFIELD-GREENFIELD.ko.md +105 -0
- package/docs/modernization/CLAUDE-PLAN-REVIEW-2026-10-03.ko.md +77 -0
- package/docs/modernization/CLI-PRODUCTIVITY-REVIEWED-2026-10-03.ko.md +177 -0
- package/docs/modernization/CLI-PRODUCTIVITY.ko.md +193 -0
- package/docs/modernization/CORE-PACKAGE-PLAN.md +7 -0
- package/docs/modernization/DEEP-REVERSE-ENGINEERING.ko.md +88 -0
- package/docs/modernization/DELEGATION-OPTIONAL.ko.md +57 -0
- package/docs/modernization/DIRECTION.ko.md +56 -0
- package/docs/modernization/FULL-TEST-2026-10-03.md +49 -0
- package/docs/modernization/LEGACY-REMOVAL-PLAN.md +9 -0
- package/docs/modernization/PILOT-RESULT-2026-10-03.ko.md +85 -0
- package/docs/modernization/PILOT-UC-001.ko.md +79 -0
- package/docs/modernization/PLAN.md +28 -0
- package/docs/modernization/SKILLS.ko.md +105 -0
- package/docs/modernization/SPRINTABLE.ko.md +47 -0
- package/docs/modernization/SYNC-DOCS-VALIDATION-2026-10-04.ko.md +38 -0
- package/docs/modernization/VALIDATION.md +79 -0
- package/docs/modernization/evidence/example-review-packet.json +115 -0
- package/docs/modernization/evidence/example-spec-pin.json +48 -0
- package/docs/modernization/evidence/full-test-20261003/claude-lint.md +22 -0
- package/docs/modernization/evidence/full-test-20261003/claude-review-retry.md +56 -0
- package/docs/modernization/evidence/full-test-20261003/codex-review-packet.json +159 -0
- package/docs/modernization/evidence/full-test-20261003/src/expense.mjs +40 -0
- package/docs/modernization/evidence/full-test-20261003/summary.json +106 -0
- package/docs/modernization/evidence/full-test-20261003/tests/expense.test.mjs +61 -0
- package/docs/modernization/evidence/local-pilot-20261003/README.ko.md +40 -0
- package/docs/modernization/evidence/local-pilot-20261003/execution.json +1055 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.stdout.txt +6 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.stdout.txt +6 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.json +23 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.stdout.txt +535 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.stdout.txt +273 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.stdout.txt +7 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-service.stdout.txt +148 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-check.stdout.txt +67 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.json +128 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.stdout.txt +128 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.json +54 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.stdout.txt +60 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-service.stdout.txt +82 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-check.stdout.txt +79 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-packet-refused.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-packet-refused.stdout.txt +7 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin-refresh.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin-refresh.stdout.txt +66 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin.json +60 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-check.stdout.txt +73 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.json +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.stdout.txt +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-service.stdout.txt +148 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/node-version.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/node-version.stdout.txt +1 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/python-version.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/python-version.stdout.txt +1 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.json +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.stdout.txt +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-service.stdout.txt +266 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/entity_model.md +33 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/glossary.md +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/plans/UC-001.md +20 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/requirements.md +10 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/test_cases/TC-001-submit-expense.md +37 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/test_cases/TC-002-description-limit.md +42 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/use_cases/UC-001-submit-expense.md +76 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/use_cases.puml +12 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/vision.md +21 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/src/expense.mjs +48 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/tests/expense.test.mjs +116 -0
- package/docs/modernization/evidence/local-pilot-20261003/scope.json +31 -0
- package/docs/modernization/reviews/2026-10-03-claude-cli/CONTRACTS.ko.md +122 -0
- package/docs/modernization/reviews/2026-10-03-claude-cli/DIRECTION.ko.md +84 -0
- package/examples/spec-workflow/README.md +54 -0
- package/examples/spec-workflow/docs/entity_model.md +33 -0
- package/examples/spec-workflow/docs/glossary.md +9 -0
- package/examples/spec-workflow/docs/requirements.md +10 -0
- package/examples/spec-workflow/docs/test_cases/TC-001-submit-expense.md +37 -0
- package/examples/spec-workflow/docs/use_cases/UC-001-submit-expense.md +63 -0
- package/examples/spec-workflow/docs/use_cases.puml +12 -0
- package/examples/spec-workflow/docs/vision.md +21 -0
- package/package.json +37 -105
- package/plugins/aiwf-angular-jpa/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-angular-jpa/LICENSE +201 -0
- package/plugins/aiwf-angular-jpa/NOTICE +16 -0
- package/plugins/aiwf-angular-jpa/README.md +54 -0
- package/plugins/aiwf-angular-jpa/UPSTREAM.json +35 -0
- package/plugins/aiwf-angular-jpa/agents/uc-coverage.md +263 -0
- package/plugins/aiwf-angular-jpa/rules/mcp-servers.md +64 -0
- package/plugins/aiwf-angular-jpa/skills/coverage-check/SKILL.md +190 -0
- package/plugins/aiwf-angular-jpa/skills/flyway-migration/SKILL.md +119 -0
- package/plugins/aiwf-angular-jpa/skills/implement/SKILL.md +528 -0
- package/plugins/aiwf-angular-jpa/skills/implement/references/module-layout.md +95 -0
- package/plugins/aiwf-angular-jpa/skills/playwright-test/SKILL.md +360 -0
- package/plugins/aiwf-angular-jpa/skills/spring-boot-test/SKILL.md +504 -0
- package/plugins/aiwf-angular-jpa/skills/vitest-test/SKILL.md +309 -0
- package/plugins/aiwf-blazor-dotnet/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-blazor-dotnet/LICENSE +201 -0
- package/plugins/aiwf-blazor-dotnet/NOTICE +16 -0
- package/plugins/aiwf-blazor-dotnet/README.md +53 -0
- package/plugins/aiwf-blazor-dotnet/UPSTREAM.json +31 -0
- package/plugins/aiwf-blazor-dotnet/rules/mcp-servers.md +25 -0
- package/plugins/aiwf-blazor-dotnet/skills/bunit-test/SKILL.md +110 -0
- package/plugins/aiwf-blazor-dotnet/skills/dotnet-test/SKILL.md +108 -0
- package/plugins/aiwf-blazor-dotnet/skills/ef-migration/SKILL.md +43 -0
- package/plugins/aiwf-blazor-dotnet/skills/implement/SKILL.md +149 -0
- package/plugins/aiwf-blazor-dotnet/skills/playwright-test/SKILL.md +130 -0
- package/plugins/aiwf-core/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-core/LICENSE +201 -0
- package/plugins/aiwf-core/NOTICE +20 -0
- package/plugins/aiwf-core/README.md +38 -0
- package/plugins/aiwf-core/UPSTREAM.json +50 -0
- package/plugins/aiwf-core/skills/entity-model/SKILL.md +160 -0
- package/plugins/aiwf-core/skills/entity-model/references/REFERENCE.md +36 -0
- package/plugins/aiwf-core/skills/requirements/SKILL.md +157 -0
- package/plugins/aiwf-core/skills/requirements/references/REFERENCE.md +70 -0
- package/plugins/aiwf-core/skills/requirements/references/glossary.md +7 -0
- package/plugins/aiwf-core/skills/reverse-engineer/SKILL.md +509 -0
- package/plugins/aiwf-core/skills/reverse-engineer/references/stack-signals.md +210 -0
- package/plugins/aiwf-core/skills/spec-review/SKILL.md +197 -0
- package/plugins/aiwf-core/skills/spec-review/references/lint-codes.md +63 -0
- package/plugins/aiwf-core/skills/spec-review/references/review-checklist.md +189 -0
- package/plugins/aiwf-core/skills/spec-review/scripts/spec_lint.py +1216 -0
- package/plugins/aiwf-core/skills/test-case/SKILL.md +138 -0
- package/plugins/aiwf-core/skills/test-case/references/example-process.bpmn +118 -0
- package/plugins/aiwf-core/skills/test-case/references/example.md +38 -0
- package/plugins/aiwf-core/skills/test-case/references/test-case.md +35 -0
- package/plugins/aiwf-core/skills/test-case/scripts/bpmn_paths.py +542 -0
- package/plugins/aiwf-core/skills/use-case-diagram/SKILL.md +99 -0
- package/plugins/aiwf-core/skills/use-case-spec/SKILL.md +257 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/clarify-checklist.md +70 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/example.md +88 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/format-spec.md +246 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/use-case.md +50 -0
- package/plugins/aiwf-core/skills/use-case-spec/scripts/validate_use_case.py +941 -0
- package/plugins/aiwf-delegate-claude/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-delegate-claude/LICENSE +21 -0
- package/plugins/aiwf-delegate-claude/NOTICE +4 -0
- package/plugins/aiwf-delegate-claude/README.md +10 -0
- package/plugins/aiwf-delegate-claude/plugin.json +13 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/LICENSE +21 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/NOTICE +4 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/SKILL.md +49 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/agents/openai.yaml +2 -0
- package/plugins/aiwf-delegate-codex/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-delegate-codex/LICENSE +21 -0
- package/plugins/aiwf-delegate-codex/NOTICE +4 -0
- package/plugins/aiwf-delegate-codex/README.md +10 -0
- package/plugins/aiwf-delegate-codex/plugin.json +13 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/LICENSE +21 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/NOTICE +4 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/SKILL.md +47 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/agents/openai.yaml +2 -0
- package/plugins/aiwf-nestjs-nextjs/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-nestjs-nextjs/LICENSE +201 -0
- package/plugins/aiwf-nestjs-nextjs/NOTICE +16 -0
- package/plugins/aiwf-nestjs-nextjs/README.md +53 -0
- package/plugins/aiwf-nestjs-nextjs/UPSTREAM.json +32 -0
- package/plugins/aiwf-nestjs-nextjs/rules/mcp-servers.md +59 -0
- package/plugins/aiwf-nestjs-nextjs/skills/drizzle-migration/SKILL.md +222 -0
- package/plugins/aiwf-nestjs-nextjs/skills/implement/SKILL.md +393 -0
- package/plugins/aiwf-nestjs-nextjs/skills/implement/references/project-layout.md +158 -0
- package/plugins/aiwf-nestjs-nextjs/skills/nest-test/SKILL.md +300 -0
- package/plugins/aiwf-nestjs-nextjs/skills/playwright-test/SKILL.md +245 -0
- package/plugins/aiwf-nestjs-nextjs/skills/react-test/SKILL.md +217 -0
- package/plugins/aiwf-spec/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-spec/LICENSE +201 -0
- package/plugins/aiwf-spec/NOTICE +20 -0
- package/plugins/aiwf-spec/README.md +36 -0
- package/plugins/aiwf-spec/skills/sync-docs/SKILL.md +47 -0
- package/plugins/aiwf-spec/skills/workflow/SKILL.md +42 -0
- package/plugins/aiwf-vaadin-jooq/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-vaadin-jooq/LICENSE +201 -0
- package/plugins/aiwf-vaadin-jooq/NOTICE +16 -0
- package/plugins/aiwf-vaadin-jooq/README.md +56 -0
- package/plugins/aiwf-vaadin-jooq/UPSTREAM.json +43 -0
- package/plugins/aiwf-vaadin-jooq/agents/uc-coverage.md +260 -0
- package/plugins/aiwf-vaadin-jooq/rules/mcp-servers.md +44 -0
- package/plugins/aiwf-vaadin-jooq/skills/browserless-test/SKILL.md +407 -0
- package/plugins/aiwf-vaadin-jooq/skills/browserless-test/references/UC001ManagePersonsTest.java +111 -0
- package/plugins/aiwf-vaadin-jooq/skills/coverage-check/SKILL.md +190 -0
- package/plugins/aiwf-vaadin-jooq/skills/flyway-migration/SKILL.md +70 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/SKILL.md +350 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/references/UC001ManagePersonsServiceTest.java +81 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/references/UC001ManagePersonsViewTest.tsx +87 -0
- package/plugins/aiwf-vaadin-jooq/skills/implement/SKILL.md +196 -0
- package/plugins/aiwf-vaadin-jooq/skills/implement-hilla/SKILL.md +216 -0
- package/plugins/aiwf-vaadin-jooq/skills/karibu-test/SKILL.md +298 -0
- package/plugins/aiwf-vaadin-jooq/skills/karibu-test/references/UC001ManagePersonsTest.java +93 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/SKILL.md +237 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/ExampleViewIT.java +143 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/TC001CustomerOnboardingIT.java +144 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/dramafinder-api.md +190 -0
- package/scripts/check-dependencies.js +26 -96
- package/scripts/install-spec-skills.mjs +126 -0
- package/scripts/validate-spec-plugin.mjs +145 -0
- package/src/cli/spec-cli.js +292 -0
- package/src/lib/spec-workflow.js +982 -0
- package/docs/ADR_MANAGEMENT_GUIDE.ko.md +0 -602
- package/docs/ADR_MANAGEMENT_GUIDE.md +0 -602
- package/docs/AI-WORKFLOW.ko.md +0 -299
- package/docs/AI-WORKFLOW.md +0 -401
- package/docs/API_REFERENCE_FULL.ko.md +0 -1135
- package/docs/API_REFERENCE_FULL.md +0 -1135
- package/docs/ARCHITECTURE.ko.md +0 -314
- package/docs/ARCHITECTURE.md +0 -314
- package/docs/CLI_USAGE_GUIDE.ko.md +0 -634
- package/docs/CLI_USAGE_GUIDE.md +0 -640
- package/docs/CODE_CLEANUP_GUIDE.ko.md +0 -415
- package/docs/CODE_CLEANUP_GUIDE.md +0 -415
- package/docs/COMMANDS_GUIDE.ko.md +0 -1037
- package/docs/COMMANDS_GUIDE.md +0 -1037
- package/docs/CONTRIBUTING.ko.md +0 -408
- package/docs/CONTRIBUTING.md +0 -408
- package/docs/DEVELOPMENT_GUIDE.ko.md +0 -440
- package/docs/DEVELOPMENT_GUIDE.md +0 -727
- package/docs/EXAMPLES.ko.md +0 -695
- package/docs/EXAMPLES.md +0 -693
- package/docs/GETTING_STARTED.ko.md +0 -219
- package/docs/GETTING_STARTED.md +0 -476
- package/docs/MODULE_MANAGEMENT_GUIDE.ko.md +0 -289
- package/docs/MODULE_MANAGEMENT_GUIDE.md +0 -289
- package/docs/PERFORMANCE_ARCHITECTURE.md +0 -494
- package/docs/PERFORMANCE_GUIDELINES.ko.md +0 -388
- package/docs/PERFORMANCE_GUIDELINES.md +0 -553
- package/docs/PRD.ko.md +0 -148
- package/docs/PRD.md +0 -150
- package/docs/ROADMAP_v0.4.0.md +0 -286
- package/docs/STATE_MANAGEMENT_GUIDE.ko.md +0 -278
- package/docs/STATE_MANAGEMENT_GUIDE.md +0 -278
- package/docs/TROUBLESHOOTING.ko.md +0 -366
- package/docs/TROUBLESHOOTING.md +0 -722
- package/docs/VALIDATOR_API.ko.md +0 -324
- package/docs/VALIDATOR_API.md +0 -324
- package/docs/YOLO_SYSTEM_GUIDE.ko.md +0 -542
- package/docs/YOLO_SYSTEM_GUIDE.md +0 -542
- package/docs/designs/AI_PERSONA_SYSTEM_DESIGN.md +0 -516
- package/docs/designs/API_DOCUMENTATION.md +0 -932
- package/docs/designs/API_REFERENCE.md +0 -979
- package/docs/designs/Enhanced_Installation_Flow_Design.md +0 -498
- package/docs/designs/aiwf-metadata-system-prd.md +0 -127
- package/docs/designs/offline-template-cache.md +0 -323
- package/docs/designs/persona-aware-compression.md +0 -168
- package/docs/guides/ai-personas-guide-ko.md +0 -239
- package/docs/guides/ai-personas-guide.md +0 -239
- package/docs/guides/checkpoint-system-guide-ko.md +0 -356
- package/docs/guides/checkpoint-system-guide.md +0 -356
- package/docs/guides/context-compression-guide-ko.md +0 -313
- package/docs/guides/context-compression-guide.md +0 -313
- package/docs/guides/independent-sprint-guide-ko.md +0 -321
- package/docs/guides/independent-sprint-guide.md +0 -321
- package/rules/global/aiwf-code-style-guide.md +0 -30
- package/rules/global/aiwf-coding-principles.md +0 -33
- package/rules/global/aiwf-development-process.md +0 -41
- package/rules/global/aiwf-global-rules.md +0 -84
- package/rules/manual/aiwf-generate-plan-docs.md +0 -280
- package/scripts/run-integration-tests.js +0 -317
- package/scripts/update-file-lists.js +0 -267
- package/scripts/validate-commands.js +0 -254
- package/src/cli/index.js +0 -184
- package/src/commands/sprint-independent.js +0 -393
- package/src/commands/state.js +0 -1164
- package/src/commands/yolo-config.js +0 -502
- package/src/config/file-lists.js +0 -147
- package/src/config/yolo-config-template.yaml +0 -168
- package/src/lib/backup-manager.js +0 -271
- package/src/lib/file-downloader.js +0 -304
- package/src/lib/installer.js +0 -1270
- package/src/lib/rollback-manager.js +0 -418
- package/src/lib/validator.js +0 -376
- package/src/utils/checkpoint-manager.js +0 -435
- package/src/utils/language-utils.js +0 -331
- package/src/utils/messages.js +0 -190
- package/src/utils/paths.js +0 -112
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const plugins = resolve(dirname(fileURLToPath(import.meta.url)), '../plugins');
|
|
7
|
+
export const supportedStacks = ['vaadin-jooq', 'angular-jpa', 'blazor-dotnet', 'nestjs-nextjs'];
|
|
8
|
+
export const supportedDelegates = ['claude', 'codex'];
|
|
9
|
+
|
|
10
|
+
function markdownFiles(directory) {
|
|
11
|
+
return readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
|
|
12
|
+
const file = join(directory, entry.name);
|
|
13
|
+
return entry.isDirectory() ? markdownFiles(file) : entry.name.endsWith('.md') ? [file] : [];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function rejectSymlink(path) {
|
|
18
|
+
if (existsSync(path) && lstatSync(path).isSymbolicLink()) {
|
|
19
|
+
throw new Error(`Refusing symbolic link: ${path}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function installSpecSkills(project, { dryRun = false, stack, delegates = [] } = {}) {
|
|
24
|
+
if (stack !== undefined && !supportedStacks.includes(stack)) {
|
|
25
|
+
throw new Error(`Unknown stack: ${stack}; choose ${supportedStacks.join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(delegates) || delegates.some(target => !supportedDelegates.includes(target))) {
|
|
28
|
+
throw new Error(`Unknown delegate: choose ${supportedDelegates.join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
delegates = [...new Set(delegates)];
|
|
31
|
+
const root = resolve(project);
|
|
32
|
+
rejectSymlink(root);
|
|
33
|
+
if (!existsSync(root) || !lstatSync(root).isDirectory()) {
|
|
34
|
+
throw new Error(`Project directory does not exist: ${root}`);
|
|
35
|
+
}
|
|
36
|
+
const target = join(root, '.agents', 'skills');
|
|
37
|
+
for (const path of [join(root, '.agents'), target]) {
|
|
38
|
+
rejectSymlink(path);
|
|
39
|
+
if (existsSync(path) && !lstatSync(path).isDirectory()) {
|
|
40
|
+
throw new Error(`Expected directory: ${path}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const bundles = [
|
|
44
|
+
{ plugin: join(plugins, 'aiwf-core'), prefix: 'aiwf-' },
|
|
45
|
+
{ plugin: join(plugins, 'aiwf-spec'), prefix: 'aiwf-' }
|
|
46
|
+
];
|
|
47
|
+
if (stack) { bundles.push({ plugin: join(plugins, `aiwf-${stack}`), prefix: `aiwf-${stack}-` }); }
|
|
48
|
+
for (const target of delegates) {
|
|
49
|
+
bundles.push({ plugin: join(plugins, `aiwf-delegate-${target}`), prefix: 'aiwf-' });
|
|
50
|
+
}
|
|
51
|
+
const entries = bundles.flatMap(bundle => readdirSync(join(bundle.plugin, 'skills')).sort()
|
|
52
|
+
.map(name => ({ ...bundle, name, installedName: `${bundle.prefix}${name}` })));
|
|
53
|
+
const destinations = entries.map(entry => join(target, entry.installedName));
|
|
54
|
+
for (const destination of destinations) {
|
|
55
|
+
// lstat detects dangling symlinks too.
|
|
56
|
+
try {
|
|
57
|
+
lstatSync(destination);
|
|
58
|
+
throw new Error(`Skill already exists; no files overwritten: ${destination}`);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error.code !== 'ENOENT') { throw error; }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (dryRun) { return { dry_run: true, destinations }; }
|
|
64
|
+
mkdirSync(target, { recursive: true });
|
|
65
|
+
for (let index = 0; index < entries.length; index++) {
|
|
66
|
+
const entry = entries[index];
|
|
67
|
+
const destination = destinations[index];
|
|
68
|
+
cpSync(join(entry.plugin, 'skills', entry.name), destination, { recursive: true, errorOnExist: true, force: false });
|
|
69
|
+
for (const notice of ['LICENSE', 'NOTICE']) { cpSync(join(entry.plugin, notice), join(destination, notice)); }
|
|
70
|
+
for (const resources of ['rules', 'agents']) {
|
|
71
|
+
if (existsSync(join(entry.plugin, resources))) {
|
|
72
|
+
cpSync(join(entry.plugin, resources), join(destination, resources), { recursive: true, errorOnExist: true, force: false });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Stack-local names take precedence; shared core commands retain their core names.
|
|
76
|
+
const mapping = new Map(entries.filter(item => item.prefix === 'aiwf-').map(item => [item.name, item.installedName]));
|
|
77
|
+
for (const item of entries.filter(item => item.plugin === entry.plugin)) {
|
|
78
|
+
mapping.set(item.name, item.installedName);
|
|
79
|
+
}
|
|
80
|
+
const commands = new RegExp(`(?<![.\\w-])/(${[...mapping.keys()].join('|')})(?![\\w-])`, 'g');
|
|
81
|
+
const namedSkills = new RegExp('`(' + [...mapping.keys()].join('|') + ')`', 'g');
|
|
82
|
+
for (const file of markdownFiles(destination)) {
|
|
83
|
+
const original = readFileSync(file, 'utf8');
|
|
84
|
+
let text = original;
|
|
85
|
+
if (file === join(destination, 'SKILL.md')) {
|
|
86
|
+
text = text.replace(/^name: .+$/m, `name: ${entry.installedName}`);
|
|
87
|
+
}
|
|
88
|
+
text = text.replace(commands, (_, name) => `/${mapping.get(name)}`)
|
|
89
|
+
.replace(namedSkills, (_, name) => '`' + mapping.get(name) + '`');
|
|
90
|
+
if (text !== original) {
|
|
91
|
+
text += '\n<!-- AIWF installation modification: prefixed skill names and command references throughout bundled Markdown. -->\n';
|
|
92
|
+
writeFileSync(file, text);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { dry_run: false, installed: destinations };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
100
|
+
try {
|
|
101
|
+
const args = process.argv.slice(2);
|
|
102
|
+
let project;
|
|
103
|
+
let dryRun = false;
|
|
104
|
+
let stack;
|
|
105
|
+
const delegates = [];
|
|
106
|
+
for (let index = 0; index < args.length; index++) {
|
|
107
|
+
if (args[index] === '--project' && args[index + 1] && !args[index + 1].startsWith('--') && !project) {
|
|
108
|
+
project = args[++index];
|
|
109
|
+
} else if (args[index] === '--dry-run' && !dryRun) {
|
|
110
|
+
dryRun = true;
|
|
111
|
+
} else if (args[index] === '--stack' && args[index + 1] && !args[index + 1].startsWith('--') && !stack) {
|
|
112
|
+
stack = args[++index];
|
|
113
|
+
} else if (args[index] === '--delegate' && args[index + 1] && !args[index + 1].startsWith('--')) {
|
|
114
|
+
delegates.push(args[++index]);
|
|
115
|
+
} else if (args[index] === '--help') {
|
|
116
|
+
console.log(`node scripts/install-spec-skills.mjs --project <existing-project> [--stack <${supportedStacks.join('|')}>] [--delegate <${supportedDelegates.join('|')}> ...] [--dry-run]`);
|
|
117
|
+
process.exit(0);
|
|
118
|
+
} else { throw new Error(`Unknown or incomplete argument: ${args[index]}`); }
|
|
119
|
+
}
|
|
120
|
+
if (!project) { throw new Error('--project is required'); }
|
|
121
|
+
console.log(JSON.stringify(installSpecSkills(project, { dryRun, stack, delegates }), null, 2));
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(error.message);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { supportedStacks } from './install-spec-skills.mjs';
|
|
8
|
+
|
|
9
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
+
const read = path => readFileSync(path, 'utf8');
|
|
11
|
+
const digest = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const market = JSON.parse(read(join(root, '.claude-plugin/marketplace.json')));
|
|
13
|
+
const pkg = JSON.parse(read(join(root, 'package.json')));
|
|
14
|
+
let verified = 0;
|
|
15
|
+
let modified = 0;
|
|
16
|
+
let skills = 0;
|
|
17
|
+
let importedSkills = 0;
|
|
18
|
+
let commit;
|
|
19
|
+
|
|
20
|
+
// AIWF-owned skill directories in aiwf-spec are authored here, not imported, so they
|
|
21
|
+
// carry no upstream provenance. Keep this list narrow: only the named skill roots are
|
|
22
|
+
// exempt, never arbitrary untracked resources in the plugin.
|
|
23
|
+
const aiwfOwnedSkillRoots = ['skills/workflow/', 'skills/sync-docs/'];
|
|
24
|
+
const isAiwfOwned = relative => aiwfOwnedSkillRoots.some(directory => relative.startsWith(directory));
|
|
25
|
+
|
|
26
|
+
function files(directory) {
|
|
27
|
+
return readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
|
|
28
|
+
const file = join(directory, entry.name);
|
|
29
|
+
return entry.isDirectory() ? files(file) : [file];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const name of ['aiwf-core', 'aiwf-spec', ...supportedStacks.map(stack => `aiwf-${stack}`)]) {
|
|
34
|
+
const plugin = join(root, 'plugins', name);
|
|
35
|
+
const provenance = name === 'aiwf-spec' ? { upstream_sha256: {}, modified_sha256: {} }
|
|
36
|
+
: JSON.parse(read(join(plugin, 'UPSTREAM.json')));
|
|
37
|
+
if (name !== 'aiwf-spec') {
|
|
38
|
+
assert.equal(provenance.repository, 'https://github.com/AI-Unified-Process/marketplace');
|
|
39
|
+
assert.match(provenance.commit, /^[0-9a-f]{40}$/);
|
|
40
|
+
commit ??= provenance.commit;
|
|
41
|
+
assert.equal(provenance.commit, commit, `Mixed upstream versions: ${name}`);
|
|
42
|
+
}
|
|
43
|
+
const allowedModifications = name === 'aiwf-core' ? ['NOTICE'] : [];
|
|
44
|
+
for (const [path, expected] of Object.entries(provenance.modified_sha256)) {
|
|
45
|
+
assert.ok(allowedModifications.includes(path), `Upstream sources must stay unchanged: ${name}/${path}`);
|
|
46
|
+
assert.equal(digest(readFileSync(join(plugin, path))), expected, `Modified resource changed: ${name}/${path}`);
|
|
47
|
+
modified++;
|
|
48
|
+
}
|
|
49
|
+
for (const [path, expected] of Object.entries(provenance.upstream_sha256)) {
|
|
50
|
+
const bytes = readFileSync(join(plugin, path));
|
|
51
|
+
if (Object.hasOwn(provenance.modified_sha256, path)) {
|
|
52
|
+
assert.equal(path, 'NOTICE');
|
|
53
|
+
assert.match(bytes.toString(), /derived from the AI Unified Process Marketplace by Simon Martinelli/);
|
|
54
|
+
} else {
|
|
55
|
+
assert.equal(digest(bytes), expected, `Upstream resource changed: ${name}/${path}`);
|
|
56
|
+
verified++;
|
|
57
|
+
}
|
|
58
|
+
if (path.endsWith('/SKILL.md')) { importedSkills++; }
|
|
59
|
+
}
|
|
60
|
+
for (const directory of ['skills', 'rules', 'agents']) {
|
|
61
|
+
if (!existsSync(join(plugin, directory))) { continue; }
|
|
62
|
+
for (const file of files(join(plugin, directory))) {
|
|
63
|
+
const relative = file.slice(plugin.length + 1);
|
|
64
|
+
if (name === 'aiwf-spec' && isAiwfOwned(relative)) { continue; }
|
|
65
|
+
assert.ok(Object.hasOwn(provenance.upstream_sha256, relative), `Resource missing provenance: ${name}/${relative}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const names = readdirSync(join(plugin, 'skills')).sort();
|
|
69
|
+
if (name === 'aiwf-core') { assert.equal(names.length, 7); }
|
|
70
|
+
if (name === 'aiwf-spec') { assert.deepEqual(names, ['sync-docs', 'workflow']); }
|
|
71
|
+
skills += names.length;
|
|
72
|
+
for (const skill of names) {
|
|
73
|
+
const file = join(plugin, 'skills', skill, 'SKILL.md');
|
|
74
|
+
const text = read(file);
|
|
75
|
+
assert.match(text, new RegExp(`^---\\nname: ${skill}\\n`, 'm'), `Skill name mismatch: ${name}/${skill}`);
|
|
76
|
+
assert.match(text, /^description: .+/m);
|
|
77
|
+
assert.match(text, /Apache(-| License, Version )2\.0/);
|
|
78
|
+
for (const markdown of files(dirname(file)).filter(path => path.endsWith('.md'))) {
|
|
79
|
+
const prose = read(markdown).replace(/^```[^\n]*\n[\s\S]*?^```\s*$/gm, '');
|
|
80
|
+
for (const match of prose.matchAll(/\]\(([^\s)]+)\)/g)) {
|
|
81
|
+
const ref = match[1].split('#')[0];
|
|
82
|
+
// Project-artifact links in template examples are not bundled resources.
|
|
83
|
+
if (!/^(references|scripts)\//.test(ref)) { continue; }
|
|
84
|
+
assert.ok(existsSync(resolve(dirname(markdown), ref)), `Broken bundled reference: ${name}/${skill}: ${ref}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const entry = market.plugins.find(p => p.name === name);
|
|
89
|
+
assert.ok(entry, `Marketplace entry missing: ${name}`);
|
|
90
|
+
assert.equal(entry.source, `./plugins/${name}`);
|
|
91
|
+
const manifest = JSON.parse(read(join(plugin, '.claude-plugin/plugin.json')));
|
|
92
|
+
assert.equal(manifest.name, entry.name);
|
|
93
|
+
assert.equal(manifest.version, entry.version);
|
|
94
|
+
assert.ok(pkg.files.includes(`plugins/${name}/`), `Package excludes plugin: ${name}`);
|
|
95
|
+
}
|
|
96
|
+
assert.equal(importedSkills, 31);
|
|
97
|
+
const delegationTargets = ['claude', 'codex'];
|
|
98
|
+
for (const target of delegationTargets) {
|
|
99
|
+
const name = `aiwf-delegate-${target}`;
|
|
100
|
+
const plugin = join(root, 'plugins', name);
|
|
101
|
+
const expectedSkill = `delegate-${target}`;
|
|
102
|
+
assert.deepEqual(readdirSync(join(plugin, 'skills')), [expectedSkill]);
|
|
103
|
+
const skill = read(join(plugin, 'skills', expectedSkill, 'SKILL.md'));
|
|
104
|
+
assert.match(skill, new RegExp(`^name: ${expectedSkill}$`, 'm'));
|
|
105
|
+
assert.match(skill, /^disable-model-invocation: true$/m);
|
|
106
|
+
assert.match(read(join(plugin, 'skills', expectedSkill, 'agents/openai.yaml')), /allow_implicit_invocation: false/);
|
|
107
|
+
for (const requiredPolicy of [
|
|
108
|
+
/current user request contains the literal token `--cross-cli`/,
|
|
109
|
+
/does not consent to starting a separate CLI process/,
|
|
110
|
+
/For cross-CLI work, default to read-only/,
|
|
111
|
+
/explicitly asks for file changes, with the files or directory scope stated/,
|
|
112
|
+
/Native subagents follow the current host's normal permission policy/,
|
|
113
|
+
/If the native subagent tool is unavailable, report that route as unavailable and stop, even when `--cross-cli` is present/,
|
|
114
|
+
/Do not substitute this host's native agents, another provider, or do the task yourself/,
|
|
115
|
+
/Do not substitute `npx`, a bundled or renamed binary, or another provider's CLI/,
|
|
116
|
+
/same operating-system account and inherits the current working directory and environment/,
|
|
117
|
+
/not a security-isolation boundary/,
|
|
118
|
+
/Do not expose environment secrets in prompts or output/,
|
|
119
|
+
/instead of retrying through another tool, broader access, or `sudo`/
|
|
120
|
+
]) {
|
|
121
|
+
assert.match(skill, requiredPolicy, `Missing delegation safety policy: ${name} ${requiredPolicy}`);
|
|
122
|
+
}
|
|
123
|
+
if (target === 'claude') {
|
|
124
|
+
assert.match(skill, /--permission-mode plan/);
|
|
125
|
+
assert.match(skill, /Never add `--dangerously-skip-permissions`/);
|
|
126
|
+
} else {
|
|
127
|
+
assert.match(skill, /Codex `exec` defaults to a read-only sandbox/);
|
|
128
|
+
assert.match(skill, /Add `--sandbox workspace-write` only if the current user request explicitly authorizes writes/);
|
|
129
|
+
assert.match(skill, /Never add `--full-auto`, `--danger-full-access`/);
|
|
130
|
+
}
|
|
131
|
+
const entry = market.plugins.find(item => item.name === name);
|
|
132
|
+
assert.ok(entry, `Marketplace entry missing: ${name}`);
|
|
133
|
+
assert.equal(entry.source, `./plugins/${name}`);
|
|
134
|
+
assert.equal(entry.version, '0.1.0');
|
|
135
|
+
for (const manifestPath of ['.claude-plugin/plugin.json', 'plugin.json']) {
|
|
136
|
+
const manifest = JSON.parse(read(join(plugin, manifestPath)));
|
|
137
|
+
assert.equal(manifest.name, name);
|
|
138
|
+
assert.equal(manifest.version, entry.version);
|
|
139
|
+
}
|
|
140
|
+
assert.ok(pkg.files.includes(`plugins/${name}/`), `Package excludes plugin: ${name}`);
|
|
141
|
+
skills++;
|
|
142
|
+
}
|
|
143
|
+
assert.equal(skills, 35);
|
|
144
|
+
assert.equal(pkg.bin['aiwf-spec'], './src/cli/spec-cli.js');
|
|
145
|
+
console.log(`AIWF: ${importedSkills} unchanged upstream skills + ${skills - importedSkills} AIWF skills (workflow, sync-docs and optional delegates); ${verified} unchanged upstream resources, ${modified} attributed NOTICE modification; references and manifests validated.`);
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aiwf-spec - bounded AIWF specification workflow CLI.
|
|
5
|
+
*
|
|
6
|
+
* Commands: init, pin, check, packet. Built-in Node modules only.
|
|
7
|
+
* This CLI records local snapshots and evidence; it never claims approval.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
initSpec,
|
|
15
|
+
pinSpec,
|
|
16
|
+
checkSpec,
|
|
17
|
+
createReviewPacket,
|
|
18
|
+
writeReviewPacket
|
|
19
|
+
} from '../lib/spec-workflow.js';
|
|
20
|
+
|
|
21
|
+
const EXIT_OK = 0;
|
|
22
|
+
const EXIT_FAILURE = 1;
|
|
23
|
+
const EXIT_USAGE = 2;
|
|
24
|
+
|
|
25
|
+
export class UsageError extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'UsageError';
|
|
29
|
+
this.code = 'usage_error';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const COMMANDS = {
|
|
34
|
+
init: {
|
|
35
|
+
summary: 'Create minimal Draft AIUP-compatible spec files (never overwrites).',
|
|
36
|
+
string: ['root', 'name'],
|
|
37
|
+
boolean: ['json'],
|
|
38
|
+
required: ['root', 'name']
|
|
39
|
+
},
|
|
40
|
+
pin: {
|
|
41
|
+
summary: 'Snapshot the spec into .aiwf/spec-pin.json.',
|
|
42
|
+
string: ['root'],
|
|
43
|
+
boolean: ['json', 'refresh'],
|
|
44
|
+
required: ['root']
|
|
45
|
+
},
|
|
46
|
+
check: {
|
|
47
|
+
summary: 'Compare the pinned spec against the current working tree (read-only).',
|
|
48
|
+
string: ['root'],
|
|
49
|
+
boolean: ['json'],
|
|
50
|
+
required: ['root']
|
|
51
|
+
},
|
|
52
|
+
packet: {
|
|
53
|
+
summary: 'Build a local review packet from a pinned spec and an evidence file.',
|
|
54
|
+
string: ['root', 'evidence', 'output'],
|
|
55
|
+
boolean: ['json', 'force'],
|
|
56
|
+
required: ['root', 'evidence']
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function camel(key) {
|
|
61
|
+
return key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Strict option parser: unknown options, missing values, and stray positionals are rejected.
|
|
66
|
+
*/
|
|
67
|
+
export function parseOptions(args, config) {
|
|
68
|
+
const options = {};
|
|
69
|
+
const positionals = [];
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
72
|
+
const arg = args[i];
|
|
73
|
+
if (arg === '-h' || arg === '--help') {
|
|
74
|
+
options.help = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (arg.startsWith('--')) {
|
|
78
|
+
let key = arg.slice(2);
|
|
79
|
+
let value;
|
|
80
|
+
const eq = key.indexOf('=');
|
|
81
|
+
if (eq !== -1) {
|
|
82
|
+
value = key.slice(eq + 1);
|
|
83
|
+
key = key.slice(0, eq);
|
|
84
|
+
}
|
|
85
|
+
const name = camel(key);
|
|
86
|
+
if (config.boolean.includes(name)) {
|
|
87
|
+
if (value !== undefined) {
|
|
88
|
+
throw new UsageError(`Option --${key} does not take a value.`);
|
|
89
|
+
}
|
|
90
|
+
options[name] = true;
|
|
91
|
+
} else if (config.string.includes(name)) {
|
|
92
|
+
if (value === undefined) {
|
|
93
|
+
value = args[i + 1];
|
|
94
|
+
i += 1;
|
|
95
|
+
}
|
|
96
|
+
if (value === undefined || value === '' || value.startsWith('--')) {
|
|
97
|
+
throw new UsageError(`Missing value for option --${key}.`);
|
|
98
|
+
}
|
|
99
|
+
options[name] = value;
|
|
100
|
+
} else {
|
|
101
|
+
throw new UsageError(`Unknown option: --${key}`);
|
|
102
|
+
}
|
|
103
|
+
} else if (arg.startsWith('-') && arg.length > 1) {
|
|
104
|
+
throw new UsageError(`Unknown option: ${arg}`);
|
|
105
|
+
} else {
|
|
106
|
+
positionals.push(arg);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (positionals.length > 0) {
|
|
111
|
+
throw new UsageError(`Unexpected argument: ${positionals[0]}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return options;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function printHelp(stream = process.stdout) {
|
|
118
|
+
const lines = [
|
|
119
|
+
'aiwf-spec - bounded AIWF specification workflow',
|
|
120
|
+
'',
|
|
121
|
+
'Usage: aiwf-spec <command> [options]',
|
|
122
|
+
'',
|
|
123
|
+
'Commands:'
|
|
124
|
+
];
|
|
125
|
+
for (const [name, config] of Object.entries(COMMANDS)) {
|
|
126
|
+
lines.push(` ${name.padEnd(8)} ${config.summary}`);
|
|
127
|
+
}
|
|
128
|
+
lines.push(
|
|
129
|
+
'',
|
|
130
|
+
'Options:',
|
|
131
|
+
' --root <path> Project root directory (required for all commands).',
|
|
132
|
+
' --name <title> Product title for init.',
|
|
133
|
+
' --evidence <file> Evidence JSON file for packet.',
|
|
134
|
+
' --output <path> Packet output path (default .aiwf/review-packet.json).',
|
|
135
|
+
' --refresh Overwrite an existing, differing pin (pin).',
|
|
136
|
+
' --force Overwrite an existing review packet (packet).',
|
|
137
|
+
' --json Emit machine-readable JSON on stdout.',
|
|
138
|
+
' -h, --help Show this help.',
|
|
139
|
+
'',
|
|
140
|
+
'Notes:',
|
|
141
|
+
' Snapshots cover docs/vision.md, requirements.md, glossary.md, entity_model.md,',
|
|
142
|
+
' use_cases.puml, *.md under use_cases/test_cases/architecture/plans,',
|
|
143
|
+
' *.bpmn under docs/processes, and docs/.spec-lint-baseline.json when present.',
|
|
144
|
+
' Only unresolved TODO / NEEDS CLARIFICATION markers block pinning; Draft and',
|
|
145
|
+
' Reviewed status words are recorded as written and are not approvals.',
|
|
146
|
+
' Structural and semantic lint is not performed here; it remains an upstream step.',
|
|
147
|
+
'',
|
|
148
|
+
'Exit codes: 0 success, 1 spec/pin failure, 2 usage error.'
|
|
149
|
+
);
|
|
150
|
+
stream.write(`${lines.join('\n')}\n`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function emitJson(payload) {
|
|
154
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function emitError(command, error, json) {
|
|
158
|
+
const code = error.code || 'error';
|
|
159
|
+
if (json) {
|
|
160
|
+
emitJson({ command, error: { code, message: error.message } });
|
|
161
|
+
} else {
|
|
162
|
+
process.stderr.write(`${code}: ${error.message}\n`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function runInit(options) {
|
|
167
|
+
const result = initSpec(options.root, options.name);
|
|
168
|
+
if (options.json) {
|
|
169
|
+
return { code: EXIT_OK, payload: result };
|
|
170
|
+
}
|
|
171
|
+
process.stdout.write(
|
|
172
|
+
`Initialized spec at ${result.root}\nCreated: ${result.created.length}, preserved: ${result.preserved.length}\n`
|
|
173
|
+
);
|
|
174
|
+
return { code: EXIT_OK };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function runPin(options) {
|
|
178
|
+
const result = pinSpec(options.root, { refresh: Boolean(options.refresh) });
|
|
179
|
+
if (options.json) {
|
|
180
|
+
return { code: EXIT_OK, payload: result };
|
|
181
|
+
}
|
|
182
|
+
const verb = result.unchanged ? 'Pin unchanged' : 'Pinned';
|
|
183
|
+
process.stdout.write(`${verb} ${result.pin.files.length} files (digest ${result.digest}).\n`);
|
|
184
|
+
return { code: EXIT_OK };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function runCheck(options) {
|
|
188
|
+
const result = checkSpec(options.root);
|
|
189
|
+
const code = result.in_sync ? EXIT_OK : EXIT_FAILURE;
|
|
190
|
+
if (options.json) {
|
|
191
|
+
return { code, payload: result };
|
|
192
|
+
}
|
|
193
|
+
process.stdout.write(`status: ${result.status}\n`);
|
|
194
|
+
if (result.added.length > 0) {
|
|
195
|
+
process.stdout.write(`added: ${result.added.join(', ')}\n`);
|
|
196
|
+
}
|
|
197
|
+
if (result.changed.length > 0) {
|
|
198
|
+
process.stdout.write(`changed: ${result.changed.join(', ')}\n`);
|
|
199
|
+
}
|
|
200
|
+
if (result.deleted.length > 0) {
|
|
201
|
+
process.stdout.write(`deleted: ${result.deleted.join(', ')}\n`);
|
|
202
|
+
}
|
|
203
|
+
if (result.draft_markers.length > 0) {
|
|
204
|
+
process.stdout.write(`draft markers: ${result.draft_markers.map(m => m.path).join(', ')}\n`);
|
|
205
|
+
}
|
|
206
|
+
return { code };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function runPacket(options) {
|
|
210
|
+
const packet = createReviewPacket(options.root, options.evidence, { cwd: process.cwd() });
|
|
211
|
+
const written = writeReviewPacket(options.root, packet, {
|
|
212
|
+
output: options.output,
|
|
213
|
+
force: Boolean(options.force),
|
|
214
|
+
cwd: process.cwd()
|
|
215
|
+
});
|
|
216
|
+
if (options.json) {
|
|
217
|
+
return { code: EXIT_OK, payload: packet };
|
|
218
|
+
}
|
|
219
|
+
process.stdout.write(`Wrote review packet: ${written.output} (status: ${packet.status})\n`);
|
|
220
|
+
return { code: EXIT_OK };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const RUNNERS = {
|
|
224
|
+
init: runInit,
|
|
225
|
+
pin: runPin,
|
|
226
|
+
check: runCheck,
|
|
227
|
+
packet: runPacket
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Execute the CLI. Returns the process exit code and writes all output.
|
|
232
|
+
*/
|
|
233
|
+
export function run(argv = process.argv.slice(2)) {
|
|
234
|
+
const [command, ...rest] = argv;
|
|
235
|
+
|
|
236
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
237
|
+
printHelp();
|
|
238
|
+
return EXIT_OK;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const config = COMMANDS[command];
|
|
242
|
+
if (!config) {
|
|
243
|
+
process.stderr.write(`Unknown command: ${command}\n`);
|
|
244
|
+
printHelp(process.stderr);
|
|
245
|
+
return EXIT_USAGE;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let options;
|
|
249
|
+
try {
|
|
250
|
+
options = parseOptions(rest, config);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
process.stderr.write(`${error.message}\n`);
|
|
253
|
+
return EXIT_USAGE;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (options.help) {
|
|
257
|
+
printHelp();
|
|
258
|
+
return EXIT_OK;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const required of config.required) {
|
|
262
|
+
if (options[required] === undefined) {
|
|
263
|
+
process.stderr.write(`Missing required option: --${required.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`)}\n`);
|
|
264
|
+
return EXIT_USAGE;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const result = RUNNERS[command](options);
|
|
270
|
+
if (result.payload !== undefined) {
|
|
271
|
+
emitJson(result.payload);
|
|
272
|
+
}
|
|
273
|
+
return result.code;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
emitError(command, error, Boolean(options.json));
|
|
276
|
+
return EXIT_FAILURE;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function main(argv = process.argv.slice(2)) {
|
|
281
|
+
const code = run(argv);
|
|
282
|
+
process.exitCode = code;
|
|
283
|
+
return code;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const isDirect = process.argv[1] && existsSync(process.argv[1])
|
|
287
|
+
&& import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;
|
|
288
|
+
if (isDirect) {
|
|
289
|
+
main();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export default { run, main, parseOptions, COMMANDS };
|