cc-devflow 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +83 -0
- package/.claude/agents/architecture-designer.md +443 -0
- package/.claude/agents/bug-analyzer.md +382 -0
- package/.claude/agents/checklist-agent.md +175 -0
- package/.claude/agents/clarify-analyst.md +50 -0
- package/.claude/agents/code-reviewer.md +71 -0
- package/.claude/agents/codex-analyzer.md +39 -0
- package/.claude/agents/compatibility-checker.md +580 -0
- package/.claude/agents/consistency-checker.md +532 -0
- package/.claude/agents/impact-analyzer.md +441 -0
- package/.claude/agents/planner.md +230 -0
- package/.claude/agents/prd-writer.md +320 -0
- package/.claude/agents/project-guidelines-generator.md +1329 -0
- package/.claude/agents/qa-tester.md +313 -0
- package/.claude/agents/release-manager.md +295 -0
- package/.claude/agents/security-reviewer.md +314 -0
- package/.claude/agents/style-guide-generator.md +458 -0
- package/.claude/agents/tech-architect.md +516 -0
- package/.claude/agents/ui-designer.md +485 -0
- package/.claude/commands/code-review-high.md +58 -0
- package/.claude/commands/core-architecture.md +429 -0
- package/.claude/commands/core-guidelines.md +486 -0
- package/.claude/commands/core-roadmap.md +439 -0
- package/.claude/commands/core-style.md +293 -0
- package/.claude/commands/flow-archive.md +245 -0
- package/.claude/commands/flow-checklist.md +260 -0
- package/.claude/commands/flow-clarify.md +136 -0
- package/.claude/commands/flow-constitution.md +82 -0
- package/.claude/commands/flow-dev.md +134 -0
- package/.claude/commands/flow-epic.md +150 -0
- package/.claude/commands/flow-fix.md +104 -0
- package/.claude/commands/flow-ideate.md +214 -0
- package/.claude/commands/flow-init.md +313 -0
- package/.claude/commands/flow-new.md +394 -0
- package/.claude/commands/flow-prd.md +131 -0
- package/.claude/commands/flow-qa.md +93 -0
- package/.claude/commands/flow-release.md +92 -0
- package/.claude/commands/flow-restart.md +98 -0
- package/.claude/commands/flow-status.md +64 -0
- package/.claude/commands/flow-tech.md +142 -0
- package/.claude/commands/flow-ui.md +189 -0
- package/.claude/commands/flow-update.md +111 -0
- package/.claude/commands/flow-upgrade.md +115 -0
- package/.claude/commands/flow-verify.md +96 -0
- package/.claude/commands/problem-analyzer.md +60 -0
- package/.claude/config/quality-rules.yml +161 -0
- package/.claude/docs/SPEC_KIT_CONSTITUTION_ANALYSIS.md +426 -0
- package/.claude/docs/design/consistency-conflict-detection-algorithms.md +658 -0
- package/.claude/docs/design/intent-driven-input-design.md +380 -0
- package/.claude/docs/design/prd-version-management-design.md +437 -0
- package/.claude/docs/guides/INIT_TROUBLESHOOTING.md +117 -0
- package/.claude/docs/guides/NEW_TROUBLESHOOTING.md +151 -0
- package/.claude/docs/guides/ROADMAP_TROUBLESHOOTING.md +188 -0
- package/.claude/docs/guides/TASK_COMPLETION_MARKING.md +338 -0
- package/.claude/docs/templates/ARCHITECTURE_TEMPLATE.md +633 -0
- package/.claude/docs/templates/BACKLOG_TEMPLATE.md +261 -0
- package/.claude/docs/templates/CHECKLIST_TEMPLATE.md +52 -0
- package/.claude/docs/templates/CLARIFICATION_REPORT_TEMPLATE.md +206 -0
- package/.claude/docs/templates/CODE_REVIEW_TEMPLATE.md +71 -0
- package/.claude/docs/templates/EPIC_TEMPLATE.md +805 -0
- package/.claude/docs/templates/INIT_FLOW_TEMPLATE.md +213 -0
- package/.claude/docs/templates/INTENT_CLARIFICATION_TEMPLATE.md +57 -0
- package/.claude/docs/templates/NEW_ORCHESTRATION_TEMPLATE.md +148 -0
- package/.claude/docs/templates/PRD_TEMPLATE.md +562 -0
- package/.claude/docs/templates/RESEARCH_TEMPLATE.md +276 -0
- package/.claude/docs/templates/REVIEW-HIGH.md +57 -0
- package/.claude/docs/templates/ROADMAP_DIALOGUE_TEMPLATE.md +198 -0
- package/.claude/docs/templates/ROADMAP_TEMPLATE.md +310 -0
- package/.claude/docs/templates/STYLE_TEMPLATE.md +1266 -0
- package/.claude/docs/templates/TASKS_TEMPLATE.md +523 -0
- package/.claude/docs/templates/TECH_DESIGN_TEMPLATE.md +1019 -0
- package/.claude/docs/templates/UI_PROTOTYPE_TEMPLATE.md +1436 -0
- package/.claude/guides/agent-guides/agent-coordination-guide.md +459 -0
- package/.claude/guides/project-guidelines-system.md +463 -0
- package/.claude/guides/technical-guides/datetime-handling-guide.md +563 -0
- package/.claude/guides/technical-guides/git-github-guide.md +642 -0
- package/.claude/guides/technical-guides/test-execution-guide.md +618 -0
- package/.claude/guides/workflow-guides/bug-fix-orchestrator.md +217 -0
- package/.claude/guides/workflow-guides/flow-orchestrator.md +282 -0
- package/.claude/hooks/checklist-gate.js +397 -0
- package/.claude/hooks/error-handling-reminder.sh +12 -0
- package/.claude/hooks/error-handling-reminder.ts +459 -0
- package/.claude/hooks/post-tool-use-tracker.sh +280 -0
- package/.claude/hooks/pre-tool-use-guardrail.sh +36 -0
- package/.claude/hooks/pre-tool-use-guardrail.ts +342 -0
- package/.claude/hooks/skill-activation-prompt.sh +36 -0
- package/.claude/hooks/skill-activation-prompt.ts +214 -0
- package/.claude/hooks/state/skills-used-test-guard.json +3 -0
- package/.claude/rules/devflow-conventions.md +305 -0
- package/.claude/rules/project-constitution.md +748 -0
- package/.claude/schemas/constitution.schema.json +43 -0
- package/.claude/scripts/analyze-upgrade-impact.sh +200 -0
- package/.claude/scripts/archive-requirement.sh +351 -0
- package/.claude/scripts/calculate-checklist-completion.sh +243 -0
- package/.claude/scripts/calculate-quarter.sh +206 -0
- package/.claude/scripts/check-dependencies.sh +409 -0
- package/.claude/scripts/check-prerequisites.sh +232 -0
- package/.claude/scripts/check-task-status.sh +264 -0
- package/.claude/scripts/checklist-errors.sh +131 -0
- package/.claude/scripts/common.sh +570 -0
- package/.claude/scripts/consolidate-research.sh +182 -0
- package/.claude/scripts/create-requirement.sh +426 -0
- package/.claude/scripts/export-contracts.sh +117 -0
- package/.claude/scripts/extract-data-model.sh +78 -0
- package/.claude/scripts/generate-clarification-questions.sh +377 -0
- package/.claude/scripts/generate-clarification-report.sh +463 -0
- package/.claude/scripts/generate-quickstart.sh +146 -0
- package/.claude/scripts/generate-research-tasks.sh +157 -0
- package/.claude/scripts/generate-status-report.sh +523 -0
- package/.claude/scripts/generate-tech-analysis.sh +46 -0
- package/.claude/scripts/locate-requirement-in-roadmap.sh +233 -0
- package/.claude/scripts/manage-constitution.sh +602 -0
- package/.claude/scripts/mark-task-complete.sh +198 -0
- package/.claude/scripts/populate-research-tasks.sh +259 -0
- package/.claude/scripts/recover-workflow.sh +460 -0
- package/.claude/scripts/run-clarify-scan.sh +601 -0
- package/.claude/scripts/run-high-review.sh +62 -0
- package/.claude/scripts/run-problem-analysis.sh +68 -0
- package/.claude/scripts/setup-epic.sh +173 -0
- package/.claude/scripts/sync-roadmap-progress.sh +300 -0
- package/.claude/scripts/sync-task-marks.sh +199 -0
- package/.claude/scripts/test-clarify-scan.sh +515 -0
- package/.claude/scripts/update-agent-context.sh +806 -0
- package/.claude/scripts/validate-constitution.sh +567 -0
- package/.claude/scripts/validate-hooks.sh +487 -0
- package/.claude/scripts/validate-research.sh +332 -0
- package/.claude/scripts/validate-scope-boundary.sh +493 -0
- package/.claude/scripts/verify-setup.sh +37 -0
- package/.claude/settings.json +76 -0
- package/.claude/skills/_reference-implementations/README.md +96 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/SKILL.md +302 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/architecture-overview.md +451 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/async-and-errors.md +307 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/complete-examples.md +638 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/configuration.md +275 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/database-patterns.md +224 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/middleware-guide.md +213 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/routing-and-controllers.md +756 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/sentry-and-monitoring.md +336 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/services-and-repositories.md +789 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/testing-guide.md +235 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/validation-patterns.md +754 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/SKILL.md +399 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/common-patterns.md +331 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/complete-examples.md +872 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/component-patterns.md +502 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/data-fetching.md +767 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/file-organization.md +502 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/loading-and-error-states.md +501 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/performance.md +406 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/routing-guide.md +364 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/styling-guide.md +428 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/typescript-standards.md +418 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +229 -0
- package/.claude/skills/constitution-guardian/SKILL.md +306 -0
- package/.claude/skills/devflow-constitution-quick-ref/SKILL.md +374 -0
- package/.claude/skills/devflow-file-standards/SKILL.md +353 -0
- package/.claude/skills/devflow-tdd-enforcer/SKILL.md +192 -0
- package/.claude/skills/skill-developer/ADVANCED.md +197 -0
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
- package/.claude/skills/skill-developer/SKILL.md +426 -0
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
- package/.claude/skills/skill-rules.json +213 -0
- package/.claude/tests/README.md +300 -0
- package/.claude/tests/TODO.md +69 -0
- package/.claude/tests/__pycache__/test_analyze_upgrade_impact.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_consolidate_research.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_export_contracts.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_extract_data_model.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_generate_quickstart.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_generate_research_tasks.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/constitution/run_all_constitution_tests.sh +111 -0
- package/.claude/tests/constitution/test_agent_assignment.sh +207 -0
- package/.claude/tests/constitution/test_article_coverage.sh +201 -0
- package/.claude/tests/constitution/test_template_completeness.sh +150 -0
- package/.claude/tests/constitution/test_version_consistency.sh +120 -0
- package/.claude/tests/fixtures/spec_delta_full.md +16 -0
- package/.claude/tests/fixtures/tasks_progress_sample.md +5 -0
- package/.claude/tests/run-all-tests.sh +229 -0
- package/.claude/tests/scripts/run.sh +30 -0
- package/.claude/tests/scripts/test-framework.sh +128 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh +511 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak +504 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak2 +505 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak3 +506 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak4 +507 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak5 +508 -0
- package/.claude/tests/scripts/test_check_task_status.sh +499 -0
- package/.claude/tests/scripts/test_common.sh +244 -0
- package/.claude/tests/scripts/test_generate_status_report.sh +71 -0
- package/.claude/tests/scripts/test_mark_task_complete.sh +441 -0
- package/.claude/tests/scripts/test_mark_task_complete.sh.backup +410 -0
- package/.claude/tests/scripts/test_recover_workflow.sh +304 -0
- package/.claude/tests/scripts/test_setup_epic.sh +437 -0
- package/.claude/tests/scripts/test_sync_task_marks.sh +196 -0
- package/.claude/tests/scripts/test_validate_constitution.sh +74 -0
- package/.claude/tests/scripts/test_validate_research.sh +462 -0
- package/.claude/tests/slugify.bats +82 -0
- package/.claude/tests/test-framework.sh +732 -0
- package/.claude/tests/test_analyze_upgrade_impact.py +34 -0
- package/.claude/tests/test_consolidate_research.py +48 -0
- package/.claude/tests/test_export_contracts.py +43 -0
- package/.claude/tests/test_extract_data_model.py +33 -0
- package/.claude/tests/test_generate_quickstart.py +50 -0
- package/.claude/tests/test_generate_research_tasks.py +52 -0
- package/.claude/tsc-cache/6e64f818-6398-49ca-8623-581a9af85c44/edited-files.log +1 -0
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +1 -0
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/edited-files.log +1 -0
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +1 -0
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/edited-files.log +1 -0
- package/CHANGELOG.md +507 -0
- package/LICENSE +21 -0
- package/README.md +534 -0
- package/README.zh-CN.md +530 -0
- package/bin/adapt.js +240 -0
- package/bin/cc-devflow-cli.js +185 -0
- package/bin/cc-devflow.js +78 -0
- package/config/adapters.yml +5 -0
- package/config/schema/adapters.schema.json +44 -0
- package/docs/CLAUDE.md +26 -0
- package/docs/commands/README.md +61 -0
- package/docs/commands/README.zh-CN.md +55 -0
- package/docs/commands/core-roadmap.md +106 -0
- package/docs/commands/core-roadmap.zh-CN.md +102 -0
- package/docs/commands/core-style.md +405 -0
- package/docs/commands/core-style.zh-CN.md +405 -0
- package/docs/commands/flow-init.md +134 -0
- package/docs/commands/flow-init.zh-CN.md +163 -0
- package/docs/commands/flow-new.md +274 -0
- package/docs/commands/flow-new.zh-CN.md +270 -0
- package/docs/guides/getting-started.md +204 -0
- package/docs/guides/getting-started.zh-CN.md +152 -0
- package/lib/adapters/adapter-interface.js +57 -0
- package/lib/adapters/claude-adapter.js +74 -0
- package/lib/adapters/codex-adapter.js +40 -0
- package/lib/adapters/config-validator.js +68 -0
- package/lib/adapters/logger.js +42 -0
- package/lib/adapters/registry.js +153 -0
- package/lib/compiler/CLAUDE.md +92 -0
- package/lib/compiler/__tests__/drift.test.js +215 -0
- package/lib/compiler/__tests__/errors.test.js +184 -0
- package/lib/compiler/__tests__/incremental.test.js +174 -0
- package/lib/compiler/__tests__/integration.test.js +174 -0
- package/lib/compiler/__tests__/manifest.test.js +233 -0
- package/lib/compiler/__tests__/parser.test.js +456 -0
- package/lib/compiler/__tests__/schemas.test.js +301 -0
- package/lib/compiler/__tests__/skills-registry.test.js +125 -0
- package/lib/compiler/__tests__/transformer.test.js +286 -0
- package/lib/compiler/emitters/antigravity-emitter.js +171 -0
- package/lib/compiler/emitters/base-emitter.js +73 -0
- package/lib/compiler/emitters/codex-emitter.js +52 -0
- package/lib/compiler/emitters/cursor-emitter.js +31 -0
- package/lib/compiler/emitters/index.js +50 -0
- package/lib/compiler/emitters/qwen-emitter.js +39 -0
- package/lib/compiler/errors.js +119 -0
- package/lib/compiler/index.js +256 -0
- package/lib/compiler/manifest.js +242 -0
- package/lib/compiler/parser.js +258 -0
- package/lib/compiler/platforms.js +113 -0
- package/lib/compiler/resource-copier.js +320 -0
- package/lib/compiler/rules-emitters/__tests__/antigravity-rules-emitter.test.js +191 -0
- package/lib/compiler/rules-emitters/__tests__/codex-rules-emitter.test.js +109 -0
- package/lib/compiler/rules-emitters/__tests__/cursor-rules-emitter.test.js +123 -0
- package/lib/compiler/rules-emitters/__tests__/qwen-rules-emitter.test.js +123 -0
- package/lib/compiler/rules-emitters/antigravity-rules-emitter.js +253 -0
- package/lib/compiler/rules-emitters/base-rules-emitter.js +83 -0
- package/lib/compiler/rules-emitters/codex-rules-emitter.js +116 -0
- package/lib/compiler/rules-emitters/cursor-rules-emitter.js +98 -0
- package/lib/compiler/rules-emitters/index.js +71 -0
- package/lib/compiler/rules-emitters/qwen-rules-emitter.js +70 -0
- package/lib/compiler/schemas.js +144 -0
- package/lib/compiler/skills-registry.js +225 -0
- package/lib/compiler/transformer.js +236 -0
- package/package.json +50 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const logger = require('./logger');
|
|
2
|
+
|
|
3
|
+
class AdapterRegistry {
|
|
4
|
+
constructor() {
|
|
5
|
+
if (AdapterRegistry.instance) {
|
|
6
|
+
return AdapterRegistry.instance;
|
|
7
|
+
}
|
|
8
|
+
this._adapters = new Map();
|
|
9
|
+
this._config = { policies: { allow_shell: false, allow_network: false } };
|
|
10
|
+
this._cachedSelection = null;
|
|
11
|
+
AdapterRegistry.instance = this;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static getInstance() {
|
|
15
|
+
if (!AdapterRegistry.instance) {
|
|
16
|
+
AdapterRegistry.instance = new AdapterRegistry();
|
|
17
|
+
}
|
|
18
|
+
return AdapterRegistry.instance;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reset singleton for testing.
|
|
23
|
+
*/
|
|
24
|
+
reset() {
|
|
25
|
+
this._adapters.clear();
|
|
26
|
+
this._config = { policies: { allow_shell: false, allow_network: false } };
|
|
27
|
+
this._cachedSelection = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setConfig(config) {
|
|
31
|
+
const next = config && typeof config === 'object' ? config : {};
|
|
32
|
+
const policies = next.policies && typeof next.policies === 'object' ? next.policies : {};
|
|
33
|
+
this._config = {
|
|
34
|
+
...next,
|
|
35
|
+
policies: {
|
|
36
|
+
allow_shell: false,
|
|
37
|
+
allow_network: false,
|
|
38
|
+
...policies
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
this._cachedSelection = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getAdapter(name) {
|
|
45
|
+
return this._adapters.get(name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
register(adapter) {
|
|
49
|
+
// If it's a class, instantiate it? Or assume instance?
|
|
50
|
+
// Tech design says register(adapter). Let's support instances.
|
|
51
|
+
if (!adapter.name) throw new Error("Adapter must have a name");
|
|
52
|
+
this._adapters.set(adapter.name, adapter);
|
|
53
|
+
this._cachedSelection = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async detectEnvironment() {
|
|
57
|
+
// 1. Check override
|
|
58
|
+
if (this._config.preferred) {
|
|
59
|
+
const preferred = this._adapters.get(this._config.preferred);
|
|
60
|
+
if (preferred) {
|
|
61
|
+
this._cachedSelection = preferred;
|
|
62
|
+
return preferred;
|
|
63
|
+
}
|
|
64
|
+
logger.warn('Preferred adapter not found', { preferred: this._config.preferred });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Detection loop
|
|
68
|
+
// Sort adapters? For now iteration order.
|
|
69
|
+
// Spec-kit pattern: could iterate AGENT_CONFIG keys, but we need registered instances to detect?
|
|
70
|
+
// Or does registry instantiate them?
|
|
71
|
+
// Current task T007 implies "Implement Singleton, register(), and detectEnvironment()".
|
|
72
|
+
// We will iterate registered adapters.
|
|
73
|
+
|
|
74
|
+
if (this._cachedSelection) return this._cachedSelection;
|
|
75
|
+
|
|
76
|
+
const detected = [];
|
|
77
|
+
for (const adapter of this._adapters.values()) {
|
|
78
|
+
const isDetected = await adapter.detect();
|
|
79
|
+
if (isDetected) detected.push(adapter);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (detected.length === 0) return null;
|
|
83
|
+
|
|
84
|
+
detected.sort((a, b) => {
|
|
85
|
+
const aPriority = typeof a.priority === 'number' ? a.priority : 0;
|
|
86
|
+
const bPriority = typeof b.priority === 'number' ? b.priority : 0;
|
|
87
|
+
if (aPriority !== bPriority) return bPriority - aPriority;
|
|
88
|
+
return String(a.name).localeCompare(String(b.name));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (detected.length > 1) {
|
|
92
|
+
logger.warn('Multiple adapters detected', {
|
|
93
|
+
candidates: detected.map((a) => a.name),
|
|
94
|
+
selected: detected[0].name,
|
|
95
|
+
reason: 'priority/order'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this._cachedSelection = detected[0];
|
|
100
|
+
return detected[0];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Execute command via selected/specific adapter, strictly enforcing security policies.
|
|
105
|
+
*/
|
|
106
|
+
async executeCommand(command, args = [], options = {}) {
|
|
107
|
+
// Mock selection for now, or use detected
|
|
108
|
+
let adapter = options.adapter ? this.getAdapter(options.adapter) : await this.detectEnvironment();
|
|
109
|
+
if (!adapter) throw new Error("No adapter selected/detected");
|
|
110
|
+
|
|
111
|
+
// Security Check
|
|
112
|
+
const policies = this._config.policies || {};
|
|
113
|
+
const adapterCapabilities = adapter.capabilities || [];
|
|
114
|
+
const requiredCapabilities = Array.isArray(options.requiredCapabilities)
|
|
115
|
+
? options.requiredCapabilities
|
|
116
|
+
: [];
|
|
117
|
+
|
|
118
|
+
if (requiredCapabilities.includes('shell') && !policies.allow_shell) {
|
|
119
|
+
throw new Error('Capability denied: shell');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (requiredCapabilities.includes('network') && !policies.allow_network) {
|
|
123
|
+
throw new Error('Capability denied: network');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const capability of requiredCapabilities) {
|
|
127
|
+
if (!adapterCapabilities.includes(capability)) {
|
|
128
|
+
throw new Error(`Adapter missing capability: ${capability}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return adapter.execute(command, args, options);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// spec-kit pattern: Metadata-driven configuration for supported agents
|
|
137
|
+
// This can be used to auto-register or suggest adapters.
|
|
138
|
+
AdapterRegistry.AGENT_CONFIG = {
|
|
139
|
+
"claude": {
|
|
140
|
+
"name": "Claude Code",
|
|
141
|
+
"folder": ".claude",
|
|
142
|
+
"install_url": "https://docs.anthropic.com/en/docs/claude-code/setup",
|
|
143
|
+
"requires_cli": true
|
|
144
|
+
},
|
|
145
|
+
"codex": {
|
|
146
|
+
"name": "Codex CLI",
|
|
147
|
+
"folder": ".codex",
|
|
148
|
+
"install_url": "https://github.com/openai/codex",
|
|
149
|
+
"requires_cli": true
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
module.exports = AdapterRegistry;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Command Emitter - Compiler Module Architecture
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Multi-platform command compiler that transforms `.claude/commands/*.md` (SSOT) into native formats for Codex, Cursor, Qwen, and Antigravity platforms.
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
lib/compiler/
|
|
10
|
+
├── parser.js # Parse .md files, extract frontmatter + detect placeholders
|
|
11
|
+
├── transformer.js # Expand {SCRIPT:*}, {AGENT_SCRIPT}, map $ARGUMENTS
|
|
12
|
+
├── manifest.js # Incremental compilation, drift detection
|
|
13
|
+
├── schemas.js # Zod validation schemas (CommandIR, Manifest)
|
|
14
|
+
├── errors.js # Custom error types (MissingFrontmatter, UnknownAlias, etc.)
|
|
15
|
+
├── skills-registry.js # Generate skills registry from .claude/skills/
|
|
16
|
+
├── index.js # Compiler entry point, orchestrates pipeline
|
|
17
|
+
└── emitters/
|
|
18
|
+
├── base-emitter.js # Abstract base class
|
|
19
|
+
├── codex-emitter.js # .codex/prompts/*.md (YAML frontmatter)
|
|
20
|
+
├── cursor-emitter.js # .cursor/commands/*.md (pure Markdown)
|
|
21
|
+
├── qwen-emitter.js # .qwen/commands/*.toml (TOML format)
|
|
22
|
+
├── antigravity-emitter.js # .agent/workflows/*.md (12K limit, auto-split)
|
|
23
|
+
└── index.js # Emitter factory
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Data Flow
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
.claude/commands/*.md
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
┌─────────────────┐
|
|
33
|
+
│ Parser │ gray-matter → CommandIR
|
|
34
|
+
│ (parser.js) │ - Extract frontmatter
|
|
35
|
+
│ │ - Detect placeholders
|
|
36
|
+
│ │ - Compute SHA-256 hash
|
|
37
|
+
└────────┬────────┘
|
|
38
|
+
│ CommandIR[]
|
|
39
|
+
▼
|
|
40
|
+
┌─────────────────┐
|
|
41
|
+
│ Transformer │ Platform-specific transforms
|
|
42
|
+
│(transformer.js) │ - {SCRIPT:alias} → "bash <path>"
|
|
43
|
+
│ │ - $ARGUMENTS → {{args}} / [arguments]
|
|
44
|
+
│ │ - {AGENT_SCRIPT} + __AGENT__ substitution
|
|
45
|
+
└────────┬────────┘
|
|
46
|
+
│ TransformedContent
|
|
47
|
+
▼
|
|
48
|
+
┌─────────────────┐
|
|
49
|
+
│ Emitters │ Platform format + file write
|
|
50
|
+
│ (emitters/*.js) │ - Codex: MD + YAML frontmatter
|
|
51
|
+
│ │ - Cursor: pure MD
|
|
52
|
+
│ │ - Qwen: TOML
|
|
53
|
+
│ │ - Antigravity: MD + YAML (12K limit)
|
|
54
|
+
└────────┬────────┘
|
|
55
|
+
│
|
|
56
|
+
▼
|
|
57
|
+
┌─────────────────┐
|
|
58
|
+
│ Manifest │ devflow/.generated/manifest.json
|
|
59
|
+
│ (manifest.js) │ - Track source/target hashes
|
|
60
|
+
│ │ - Enable incremental compilation
|
|
61
|
+
│ │ - Detect drift
|
|
62
|
+
└─────────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## CLI Usage
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm run adapt # Compile all platforms
|
|
69
|
+
npm run adapt -- --platform codex # Compile single platform
|
|
70
|
+
npm run adapt -- --check # Drift detection only
|
|
71
|
+
npm run adapt -- --skills # Generate skills-registry.json
|
|
72
|
+
npm run adapt -- --verbose # Detailed output
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Key Schemas
|
|
76
|
+
|
|
77
|
+
- **CommandIR**: Intermediate representation after parsing
|
|
78
|
+
- **ManifestEntry**: Single compilation record (source, target, hash, platform)
|
|
79
|
+
- **Manifest**: Complete compilation history
|
|
80
|
+
|
|
81
|
+
## Dependencies
|
|
82
|
+
|
|
83
|
+
- `gray-matter`: Frontmatter parsing
|
|
84
|
+
- `@iarna/toml`: TOML serialization (Qwen)
|
|
85
|
+
- `js-yaml`: YAML serialization (Codex, Antigravity)
|
|
86
|
+
- `zod`: Schema validation
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
**Created**: 2025-12-18
|
|
91
|
+
**REQ**: REQ-005 (RM-007)
|
|
92
|
+
**Version**: 1.0.0
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T011: Drift Detection Tests
|
|
3
|
+
*
|
|
4
|
+
* 测试漂移检测:
|
|
5
|
+
* 1. 无漂移时返回 exit code 0
|
|
6
|
+
* 2. 手动修改时返回 exit code 2 并输出 diff
|
|
7
|
+
* 3. 目标文件缺失时返回 exit code 2
|
|
8
|
+
* 4. 规则入口文件包含在漂移检测中
|
|
9
|
+
*
|
|
10
|
+
* Reference: contracts/cli-spec.yaml#check_output
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
describe('Drift Detection', () => {
|
|
18
|
+
let tempDir;
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
// 创建临时目录用于测试
|
|
22
|
+
tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'drift-test-'));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
// 清理临时目录
|
|
27
|
+
if (tempDir) {
|
|
28
|
+
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('checkDrift()', () => {
|
|
33
|
+
test('should return empty array when no drift', async () => {
|
|
34
|
+
const { checkDrift, hashContent } = require('../manifest');
|
|
35
|
+
|
|
36
|
+
// 创建测试文件
|
|
37
|
+
const targetPath = path.join(tempDir, 'test.md');
|
|
38
|
+
const content = 'test content';
|
|
39
|
+
await fs.promises.writeFile(targetPath, content);
|
|
40
|
+
|
|
41
|
+
// 创建 manifest 记录
|
|
42
|
+
const manifest = {
|
|
43
|
+
entries: [
|
|
44
|
+
{
|
|
45
|
+
source: 'source.md',
|
|
46
|
+
target: targetPath,
|
|
47
|
+
hash: hashContent(content), // 使用相同 hash
|
|
48
|
+
platform: 'codex'
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const drift = await checkDrift(manifest);
|
|
54
|
+
|
|
55
|
+
expect(drift).toEqual([]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should detect drift when target file modified', async () => {
|
|
59
|
+
const { checkDrift, hashContent } = require('../manifest');
|
|
60
|
+
|
|
61
|
+
// 创建测试文件
|
|
62
|
+
const targetPath = path.join(tempDir, 'test.md');
|
|
63
|
+
const originalContent = 'original content';
|
|
64
|
+
const modifiedContent = 'modified content';
|
|
65
|
+
|
|
66
|
+
await fs.promises.writeFile(targetPath, modifiedContent);
|
|
67
|
+
|
|
68
|
+
// manifest 记录原始内容的 hash
|
|
69
|
+
const manifest = {
|
|
70
|
+
entries: [
|
|
71
|
+
{
|
|
72
|
+
source: 'source.md',
|
|
73
|
+
target: targetPath,
|
|
74
|
+
hash: hashContent(originalContent),
|
|
75
|
+
platform: 'codex'
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const drift = await checkDrift(manifest);
|
|
81
|
+
|
|
82
|
+
expect(drift.length).toBe(1);
|
|
83
|
+
expect(drift[0].issue).toBe('target file modified since last compile');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('should detect drift when target file missing', async () => {
|
|
87
|
+
const { checkDrift, hashContent } = require('../manifest');
|
|
88
|
+
|
|
89
|
+
const missingPath = path.join(tempDir, 'nonexistent.md');
|
|
90
|
+
|
|
91
|
+
const manifest = {
|
|
92
|
+
entries: [
|
|
93
|
+
{
|
|
94
|
+
source: 'source.md',
|
|
95
|
+
target: missingPath,
|
|
96
|
+
hash: hashContent('some content'),
|
|
97
|
+
platform: 'codex'
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const drift = await checkDrift(manifest);
|
|
103
|
+
|
|
104
|
+
expect(drift.length).toBe(1);
|
|
105
|
+
expect(drift[0].issue).toBe('target file missing');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should return empty array for null manifest', async () => {
|
|
109
|
+
const { checkDrift } = require('../manifest');
|
|
110
|
+
|
|
111
|
+
const drift = await checkDrift(null);
|
|
112
|
+
|
|
113
|
+
expect(drift).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('should return empty array for manifest without entries', async () => {
|
|
117
|
+
const { checkDrift } = require('../manifest');
|
|
118
|
+
|
|
119
|
+
const drift = await checkDrift({ entries: null });
|
|
120
|
+
|
|
121
|
+
expect(drift).toEqual([]);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('Rules Entry Drift Detection', () => {
|
|
126
|
+
test('should include rules entry files in drift check', async () => {
|
|
127
|
+
// 规则入口文件应该和普通命令文件一样被检测
|
|
128
|
+
// 这需要在 Phase 4 扩展 checkDrift 函数
|
|
129
|
+
|
|
130
|
+
const { checkDrift, hashContent, addRulesEntry, createManifest } = require('../manifest');
|
|
131
|
+
|
|
132
|
+
const manifest = createManifest();
|
|
133
|
+
|
|
134
|
+
// 添加规则入口记录
|
|
135
|
+
const rulesPath = path.join(tempDir, 'devflow.mdc');
|
|
136
|
+
const originalContent = 'original rules';
|
|
137
|
+
|
|
138
|
+
addRulesEntry(manifest, 'cursor', {
|
|
139
|
+
path: rulesPath,
|
|
140
|
+
hash: hashContent(originalContent),
|
|
141
|
+
timestamp: new Date().toISOString()
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 创建被修改的文件
|
|
145
|
+
await fs.promises.writeFile(rulesPath, 'modified rules');
|
|
146
|
+
|
|
147
|
+
// 注意:当前 checkDrift 只检查 entries,不检查 rulesEntry
|
|
148
|
+
// 这个测试会在 Phase 4 (T025) 实现后通过
|
|
149
|
+
|
|
150
|
+
// 占位断言
|
|
151
|
+
expect(manifest.rulesEntry.cursor).toBeDefined();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('Exit Codes', () => {
|
|
156
|
+
test('should indicate exit code 0 for no drift', async () => {
|
|
157
|
+
const { checkDrift } = require('../manifest');
|
|
158
|
+
|
|
159
|
+
const drift = await checkDrift({ entries: [] });
|
|
160
|
+
|
|
161
|
+
// exit code 由调用方根据 drift 数组长度决定
|
|
162
|
+
const exitCode = drift.length > 0 ? 2 : 0;
|
|
163
|
+
expect(exitCode).toBe(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('should indicate exit code 2 for drift detected', async () => {
|
|
167
|
+
const { checkDrift, hashContent } = require('../manifest');
|
|
168
|
+
|
|
169
|
+
const targetPath = path.join(tempDir, 'drifted.md');
|
|
170
|
+
await fs.promises.writeFile(targetPath, 'modified');
|
|
171
|
+
|
|
172
|
+
const manifest = {
|
|
173
|
+
entries: [
|
|
174
|
+
{
|
|
175
|
+
source: 'source.md',
|
|
176
|
+
target: targetPath,
|
|
177
|
+
hash: hashContent('original'),
|
|
178
|
+
platform: 'codex'
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const drift = await checkDrift(manifest);
|
|
184
|
+
|
|
185
|
+
const exitCode = drift.length > 0 ? 2 : 0;
|
|
186
|
+
expect(exitCode).toBe(2);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('Drift Report Format', () => {
|
|
191
|
+
test('should include source and target in drift report', async () => {
|
|
192
|
+
const { checkDrift, hashContent } = require('../manifest');
|
|
193
|
+
|
|
194
|
+
const targetPath = path.join(tempDir, 'report-test.md');
|
|
195
|
+
await fs.promises.writeFile(targetPath, 'modified');
|
|
196
|
+
|
|
197
|
+
const manifest = {
|
|
198
|
+
entries: [
|
|
199
|
+
{
|
|
200
|
+
source: 'original-source.md',
|
|
201
|
+
target: targetPath,
|
|
202
|
+
hash: hashContent('original content'),
|
|
203
|
+
platform: 'codex'
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const drift = await checkDrift(manifest);
|
|
209
|
+
|
|
210
|
+
expect(drift[0]).toHaveProperty('source', 'original-source.md');
|
|
211
|
+
expect(drift[0]).toHaveProperty('target', targetPath);
|
|
212
|
+
expect(drift[0]).toHaveProperty('issue');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T010: Error Types Tests
|
|
3
|
+
* Tests for compiler error classes
|
|
4
|
+
* Expected: All tests FAIL (errors not implemented)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================
|
|
8
|
+
// Error imports - will fail until lib/compiler/errors.js exists
|
|
9
|
+
// ============================================================
|
|
10
|
+
let errors;
|
|
11
|
+
try {
|
|
12
|
+
errors = require('../errors.js');
|
|
13
|
+
} catch (e) {
|
|
14
|
+
errors = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('Compiler Errors', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
if (!errors) {
|
|
20
|
+
throw new Error('errors.js not implemented');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ----------------------------------------------------------
|
|
25
|
+
// CompilerError (Base Class) Tests
|
|
26
|
+
// ----------------------------------------------------------
|
|
27
|
+
describe('CompilerError', () => {
|
|
28
|
+
it('should be an instance of Error', () => {
|
|
29
|
+
const err = new errors.CompilerError('test message');
|
|
30
|
+
expect(err).toBeInstanceOf(Error);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should set name to CompilerError', () => {
|
|
34
|
+
const err = new errors.CompilerError('test');
|
|
35
|
+
expect(err.name).toBe('CompilerError');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should preserve message', () => {
|
|
39
|
+
const err = new errors.CompilerError('custom message');
|
|
40
|
+
expect(err.message).toBe('custom message');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should have proper stack trace', () => {
|
|
44
|
+
const err = new errors.CompilerError('test');
|
|
45
|
+
expect(err.stack).toBeDefined();
|
|
46
|
+
expect(err.stack).toContain('CompilerError');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ----------------------------------------------------------
|
|
51
|
+
// MissingFrontmatterError Tests
|
|
52
|
+
// ----------------------------------------------------------
|
|
53
|
+
describe('MissingFrontmatterError', () => {
|
|
54
|
+
it('should extend CompilerError', () => {
|
|
55
|
+
const err = new errors.MissingFrontmatterError('test.md');
|
|
56
|
+
expect(err).toBeInstanceOf(errors.CompilerError);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should set name to MissingFrontmatterError', () => {
|
|
60
|
+
const err = new errors.MissingFrontmatterError('test.md');
|
|
61
|
+
expect(err.name).toBe('MissingFrontmatterError');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should include file path in message', () => {
|
|
65
|
+
const err = new errors.MissingFrontmatterError('/path/to/file.md');
|
|
66
|
+
expect(err.message).toContain('/path/to/file.md');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should store filePath property', () => {
|
|
70
|
+
const err = new errors.MissingFrontmatterError('/path/to/file.md');
|
|
71
|
+
expect(err.filePath).toBe('/path/to/file.md');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ----------------------------------------------------------
|
|
76
|
+
// InvalidFrontmatterError Tests
|
|
77
|
+
// ----------------------------------------------------------
|
|
78
|
+
describe('InvalidFrontmatterError', () => {
|
|
79
|
+
it('should extend CompilerError', () => {
|
|
80
|
+
const err = new errors.InvalidFrontmatterError('test.md', 'missing name');
|
|
81
|
+
expect(err).toBeInstanceOf(errors.CompilerError);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should set name to InvalidFrontmatterError', () => {
|
|
85
|
+
const err = new errors.InvalidFrontmatterError('test.md', 'reason');
|
|
86
|
+
expect(err.name).toBe('InvalidFrontmatterError');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should include file path and reason in message', () => {
|
|
90
|
+
const err = new errors.InvalidFrontmatterError('/path/file.md', 'missing required field: name');
|
|
91
|
+
expect(err.message).toContain('/path/file.md');
|
|
92
|
+
expect(err.message).toContain('missing required field: name');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should store filePath and reason properties', () => {
|
|
96
|
+
const err = new errors.InvalidFrontmatterError('/path/file.md', 'bad format');
|
|
97
|
+
expect(err.filePath).toBe('/path/file.md');
|
|
98
|
+
expect(err.reason).toBe('bad format');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ----------------------------------------------------------
|
|
103
|
+
// UnknownAliasError Tests
|
|
104
|
+
// ----------------------------------------------------------
|
|
105
|
+
describe('UnknownAliasError', () => {
|
|
106
|
+
it('should extend CompilerError', () => {
|
|
107
|
+
const err = new errors.UnknownAliasError('test.md', 'unknown_alias');
|
|
108
|
+
expect(err).toBeInstanceOf(errors.CompilerError);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should set name to UnknownAliasError', () => {
|
|
112
|
+
const err = new errors.UnknownAliasError('test.md', 'alias');
|
|
113
|
+
expect(err.name).toBe('UnknownAliasError');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should include file path and alias in message', () => {
|
|
117
|
+
const err = new errors.UnknownAliasError('/path/file.md', 'prereq');
|
|
118
|
+
expect(err.message).toContain('/path/file.md');
|
|
119
|
+
expect(err.message).toContain('prereq');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should store filePath and alias properties', () => {
|
|
123
|
+
const err = new errors.UnknownAliasError('/path/file.md', 'my_alias');
|
|
124
|
+
expect(err.filePath).toBe('/path/file.md');
|
|
125
|
+
expect(err.alias).toBe('my_alias');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ----------------------------------------------------------
|
|
130
|
+
// WriteError Tests
|
|
131
|
+
// ----------------------------------------------------------
|
|
132
|
+
describe('WriteError', () => {
|
|
133
|
+
it('should extend CompilerError', () => {
|
|
134
|
+
const err = new errors.WriteError('test.md', 'permission denied');
|
|
135
|
+
expect(err).toBeInstanceOf(errors.CompilerError);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should set name to WriteError', () => {
|
|
139
|
+
const err = new errors.WriteError('test.md', 'error');
|
|
140
|
+
expect(err.name).toBe('WriteError');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should include file path and cause in message', () => {
|
|
144
|
+
const err = new errors.WriteError('/output/file.md', 'EACCES: permission denied');
|
|
145
|
+
expect(err.message).toContain('/output/file.md');
|
|
146
|
+
expect(err.message).toContain('EACCES');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should store filePath and cause properties', () => {
|
|
150
|
+
const err = new errors.WriteError('/output/file.md', 'disk full');
|
|
151
|
+
expect(err.filePath).toBe('/output/file.md');
|
|
152
|
+
expect(err.cause).toBe('disk full');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ----------------------------------------------------------
|
|
157
|
+
// ContentTooLargeError Tests
|
|
158
|
+
// ----------------------------------------------------------
|
|
159
|
+
describe('ContentTooLargeError', () => {
|
|
160
|
+
it('should extend CompilerError', () => {
|
|
161
|
+
const err = new errors.ContentTooLargeError('test.md', 15000, 12000);
|
|
162
|
+
expect(err).toBeInstanceOf(errors.CompilerError);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should set name to ContentTooLargeError', () => {
|
|
166
|
+
const err = new errors.ContentTooLargeError('test.md', 15000, 12000);
|
|
167
|
+
expect(err.name).toBe('ContentTooLargeError');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should include file path, actual and limit in message', () => {
|
|
171
|
+
const err = new errors.ContentTooLargeError('/path/file.md', 15000, 12000);
|
|
172
|
+
expect(err.message).toContain('/path/file.md');
|
|
173
|
+
expect(err.message).toContain('15000');
|
|
174
|
+
expect(err.message).toContain('12000');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should store filePath, actualSize and limit properties', () => {
|
|
178
|
+
const err = new errors.ContentTooLargeError('/path/file.md', 15000, 12000);
|
|
179
|
+
expect(err.filePath).toBe('/path/file.md');
|
|
180
|
+
expect(err.actualSize).toBe(15000);
|
|
181
|
+
expect(err.limit).toBe(12000);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|