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,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T025: Parser Module
|
|
3
|
+
*
|
|
4
|
+
* Parses .claude/commands/*.md files:
|
|
5
|
+
* - Extracts YAML frontmatter using gray-matter
|
|
6
|
+
* - Validates frontmatter with Zod schema
|
|
7
|
+
* - Detects placeholders ({SCRIPT:*}, {TEMPLATE:*}, {GUIDE:*}, {AGENT_SCRIPT}, $ARGUMENTS)
|
|
8
|
+
* - Computes SHA-256 content hash
|
|
9
|
+
* - Returns CommandIR structure
|
|
10
|
+
*/
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const crypto = require('crypto');
|
|
14
|
+
const matter = require('gray-matter');
|
|
15
|
+
|
|
16
|
+
const { FrontmatterSchema, CommandIRSchema } = require('./schemas.js');
|
|
17
|
+
const {
|
|
18
|
+
MissingFrontmatterError,
|
|
19
|
+
InvalidFrontmatterError,
|
|
20
|
+
UnknownAliasError,
|
|
21
|
+
UnknownTemplateAliasError,
|
|
22
|
+
UnknownGuideAliasError
|
|
23
|
+
} = require('./errors.js');
|
|
24
|
+
|
|
25
|
+
// ============================================================
|
|
26
|
+
// SECURITY CONFIGURATION (FINDING-002, FINDING-003)
|
|
27
|
+
// ============================================================
|
|
28
|
+
const MAX_FILE_SIZE = 1024 * 1024; // 1MB limit (FINDING-003: Resource Exhaustion)
|
|
29
|
+
|
|
30
|
+
// ============================================================
|
|
31
|
+
// Placeholder Detection Patterns
|
|
32
|
+
// ============================================================
|
|
33
|
+
const SCRIPT_PATTERN = /\{SCRIPT:([^}]+)\}/g;
|
|
34
|
+
const TEMPLATE_PATTERN = /\{TEMPLATE:([^}]+)\}/g;
|
|
35
|
+
const GUIDE_PATTERN = /\{GUIDE:([^}]+)\}/g;
|
|
36
|
+
const AGENT_SCRIPT_PATTERN = /\{AGENT_SCRIPT\}/g;
|
|
37
|
+
const ARGUMENTS_PATTERN = /\$ARGUMENTS/g;
|
|
38
|
+
|
|
39
|
+
// ============================================================
|
|
40
|
+
// hashContent - 计算 SHA-256 哈希
|
|
41
|
+
// ============================================================
|
|
42
|
+
function hashContent(content) {
|
|
43
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ============================================================
|
|
47
|
+
// detectPlaceholders - 检测正文中的占位符
|
|
48
|
+
// ============================================================
|
|
49
|
+
function detectPlaceholders(body) {
|
|
50
|
+
const placeholders = [];
|
|
51
|
+
|
|
52
|
+
// 检测 {SCRIPT:alias}
|
|
53
|
+
let match;
|
|
54
|
+
const scriptPattern = new RegExp(SCRIPT_PATTERN.source, 'g');
|
|
55
|
+
while ((match = scriptPattern.exec(body)) !== null) {
|
|
56
|
+
placeholders.push({
|
|
57
|
+
type: 'SCRIPT',
|
|
58
|
+
raw: match[0],
|
|
59
|
+
alias: match[1],
|
|
60
|
+
position: { start: match.index, end: match.index + match[0].length }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 检测 {TEMPLATE:alias}
|
|
65
|
+
const templatePattern = new RegExp(TEMPLATE_PATTERN.source, 'g');
|
|
66
|
+
while ((match = templatePattern.exec(body)) !== null) {
|
|
67
|
+
placeholders.push({
|
|
68
|
+
type: 'TEMPLATE',
|
|
69
|
+
raw: match[0],
|
|
70
|
+
alias: match[1],
|
|
71
|
+
position: { start: match.index, end: match.index + match[0].length }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 检测 {GUIDE:alias}
|
|
76
|
+
const guidePattern = new RegExp(GUIDE_PATTERN.source, 'g');
|
|
77
|
+
while ((match = guidePattern.exec(body)) !== null) {
|
|
78
|
+
placeholders.push({
|
|
79
|
+
type: 'GUIDE',
|
|
80
|
+
raw: match[0],
|
|
81
|
+
alias: match[1],
|
|
82
|
+
position: { start: match.index, end: match.index + match[0].length }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 检测 {AGENT_SCRIPT}
|
|
87
|
+
const agentPattern = new RegExp(AGENT_SCRIPT_PATTERN.source, 'g');
|
|
88
|
+
while ((match = agentPattern.exec(body)) !== null) {
|
|
89
|
+
placeholders.push({
|
|
90
|
+
type: 'AGENT_SCRIPT',
|
|
91
|
+
raw: match[0],
|
|
92
|
+
position: { start: match.index, end: match.index + match[0].length }
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 检测 $ARGUMENTS
|
|
97
|
+
const argsPattern = new RegExp(ARGUMENTS_PATTERN.source, 'g');
|
|
98
|
+
while ((match = argsPattern.exec(body)) !== null) {
|
|
99
|
+
placeholders.push({
|
|
100
|
+
type: 'ARGUMENTS',
|
|
101
|
+
raw: match[0],
|
|
102
|
+
position: { start: match.index, end: match.index + match[0].length }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return placeholders;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ============================================================
|
|
110
|
+
// validateScriptAliases - 验证 SCRIPT alias 是否在 frontmatter 中定义
|
|
111
|
+
// ============================================================
|
|
112
|
+
function validateScriptAliases(placeholders, scripts, filePath) {
|
|
113
|
+
const scriptPlaceholders = placeholders.filter(p => p.type === 'SCRIPT');
|
|
114
|
+
|
|
115
|
+
for (const placeholder of scriptPlaceholders) {
|
|
116
|
+
if (!scripts || !scripts[placeholder.alias]) {
|
|
117
|
+
throw new UnknownAliasError(filePath, placeholder.alias);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ============================================================
|
|
123
|
+
// validateTemplateAliases - 验证 TEMPLATE alias 是否在 frontmatter 中定义
|
|
124
|
+
// ============================================================
|
|
125
|
+
function validateTemplateAliases(placeholders, templates, filePath) {
|
|
126
|
+
const templatePlaceholders = placeholders.filter(p => p.type === 'TEMPLATE');
|
|
127
|
+
|
|
128
|
+
for (const placeholder of templatePlaceholders) {
|
|
129
|
+
if (!templates || !templates[placeholder.alias]) {
|
|
130
|
+
throw new UnknownTemplateAliasError(filePath, placeholder.alias);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================
|
|
136
|
+
// validateGuideAliases - 验证 GUIDE alias 是否在 frontmatter 中定义
|
|
137
|
+
// ============================================================
|
|
138
|
+
function validateGuideAliases(placeholders, guides, filePath) {
|
|
139
|
+
const guidePlaceholders = placeholders.filter(p => p.type === 'GUIDE');
|
|
140
|
+
|
|
141
|
+
for (const placeholder of guidePlaceholders) {
|
|
142
|
+
if (!guides || !guides[placeholder.alias]) {
|
|
143
|
+
throw new UnknownGuideAliasError(filePath, placeholder.alias);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================
|
|
149
|
+
// parseCommand - 解析单个命令文件
|
|
150
|
+
// ============================================================
|
|
151
|
+
function parseCommand(filePath) {
|
|
152
|
+
// 读取文件内容
|
|
153
|
+
const absolutePath = path.resolve(filePath);
|
|
154
|
+
|
|
155
|
+
// ✅ SECURITY FIX (FINDING-003): Check file size before reading
|
|
156
|
+
const stats = fs.statSync(absolutePath);
|
|
157
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
158
|
+
throw new Error(`File too large: ${filePath} (${stats.size} bytes > ${MAX_FILE_SIZE} bytes)`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
162
|
+
const hash = hashContent(content);
|
|
163
|
+
|
|
164
|
+
// ✅ SECURITY FIX (FINDING-002): Enable YAML safe mode (CORE_SCHEMA)
|
|
165
|
+
const yaml = require('js-yaml');
|
|
166
|
+
const parsed = matter(content, {
|
|
167
|
+
engines: {
|
|
168
|
+
yaml: (s) => yaml.load(s, { schema: yaml.CORE_SCHEMA })
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// 检查是否有 frontmatter
|
|
173
|
+
if (!parsed.data || Object.keys(parsed.data).length === 0) {
|
|
174
|
+
throw new MissingFrontmatterError(filePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 验证 frontmatter schema
|
|
178
|
+
const frontmatterResult = FrontmatterSchema.safeParse(parsed.data);
|
|
179
|
+
if (!frontmatterResult.success) {
|
|
180
|
+
const errorMessages = frontmatterResult.error.issues
|
|
181
|
+
.map(issue => `${issue.path.join('.')}: ${issue.message}`)
|
|
182
|
+
.join(', ');
|
|
183
|
+
throw new InvalidFrontmatterError(filePath, errorMessages);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const frontmatter = frontmatterResult.data;
|
|
187
|
+
const body = parsed.content;
|
|
188
|
+
|
|
189
|
+
// 检测占位符
|
|
190
|
+
const placeholders = detectPlaceholders(body);
|
|
191
|
+
|
|
192
|
+
// 验证 SCRIPT alias 存在性
|
|
193
|
+
validateScriptAliases(placeholders, frontmatter.scripts, filePath);
|
|
194
|
+
|
|
195
|
+
// 验证 TEMPLATE alias 存在性
|
|
196
|
+
validateTemplateAliases(placeholders, frontmatter.templates, filePath);
|
|
197
|
+
|
|
198
|
+
// 验证 GUIDE alias 存在性
|
|
199
|
+
validateGuideAliases(placeholders, frontmatter.guides, filePath);
|
|
200
|
+
|
|
201
|
+
// 构建 CommandIR
|
|
202
|
+
const ir = {
|
|
203
|
+
source: {
|
|
204
|
+
path: absolutePath,
|
|
205
|
+
filename: path.basename(filePath, '.md'),
|
|
206
|
+
hash
|
|
207
|
+
},
|
|
208
|
+
frontmatter,
|
|
209
|
+
body,
|
|
210
|
+
placeholders
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// 最终验证 CommandIR
|
|
214
|
+
const irResult = CommandIRSchema.safeParse(ir);
|
|
215
|
+
if (!irResult.success) {
|
|
216
|
+
throw new InvalidFrontmatterError(filePath, 'Invalid CommandIR structure');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return irResult.data;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ============================================================
|
|
223
|
+
// parseAllCommands - 批量解析命令目录
|
|
224
|
+
// ============================================================
|
|
225
|
+
async function parseAllCommands(dirPath) {
|
|
226
|
+
const absoluteDir = path.resolve(dirPath);
|
|
227
|
+
|
|
228
|
+
// 读取目录中的所有文件
|
|
229
|
+
const files = fs.readdirSync(absoluteDir);
|
|
230
|
+
|
|
231
|
+
// 过滤 .md 文件
|
|
232
|
+
const mdFiles = files.filter(file => file.endsWith('.md'));
|
|
233
|
+
|
|
234
|
+
// 解析每个文件
|
|
235
|
+
const results = [];
|
|
236
|
+
for (const file of mdFiles) {
|
|
237
|
+
const filePath = path.join(absoluteDir, file);
|
|
238
|
+
try {
|
|
239
|
+
const ir = parseCommand(filePath);
|
|
240
|
+
results.push(ir);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
// 重新抛出以便调用者处理
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = {
|
|
251
|
+
parseCommand,
|
|
252
|
+
parseAllCommands,
|
|
253
|
+
hashContent,
|
|
254
|
+
detectPlaceholders,
|
|
255
|
+
validateScriptAliases,
|
|
256
|
+
validateTemplateAliases,
|
|
257
|
+
validateGuideAliases
|
|
258
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T001: Platform Configuration Registry
|
|
3
|
+
*
|
|
4
|
+
* 定义四个目标平台的编译配置:
|
|
5
|
+
* - cursor: Cursor IDE (.mdc format)
|
|
6
|
+
* - codex: Codex CLI (SKILL.md format)
|
|
7
|
+
* - qwen: Qwen Code (.toml format)
|
|
8
|
+
* - antigravity: Antigravity (.md with 12K limit)
|
|
9
|
+
*
|
|
10
|
+
* Reference: data-model.md#PlatformConfig
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================================
|
|
14
|
+
// PLATFORM_CONFIG - 平台配置注册表
|
|
15
|
+
// ============================================================
|
|
16
|
+
const PLATFORM_CONFIG = {
|
|
17
|
+
cursor: {
|
|
18
|
+
name: 'Cursor IDE',
|
|
19
|
+
folder: '.cursor/',
|
|
20
|
+
rulesEntry: {
|
|
21
|
+
path: 'rules/devflow.mdc',
|
|
22
|
+
format: 'mdc'
|
|
23
|
+
},
|
|
24
|
+
commandsDir: 'commands/',
|
|
25
|
+
commandExt: '.md',
|
|
26
|
+
argumentPattern: '$ARGUMENTS',
|
|
27
|
+
hasHooks: false,
|
|
28
|
+
limits: {}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
codex: {
|
|
32
|
+
name: 'Codex CLI',
|
|
33
|
+
folder: '.codex/',
|
|
34
|
+
rulesEntry: {
|
|
35
|
+
path: 'skills/cc-devflow/SKILL.md',
|
|
36
|
+
format: 'markdown'
|
|
37
|
+
},
|
|
38
|
+
commandsDir: 'prompts/',
|
|
39
|
+
commandExt: '.md',
|
|
40
|
+
argumentPattern: '$ARGUMENTS',
|
|
41
|
+
hasHooks: false,
|
|
42
|
+
limits: {}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
qwen: {
|
|
46
|
+
name: 'Qwen Code',
|
|
47
|
+
folder: '.qwen/',
|
|
48
|
+
rulesEntry: {
|
|
49
|
+
path: 'commands/devflow.toml',
|
|
50
|
+
format: 'toml'
|
|
51
|
+
},
|
|
52
|
+
commandsDir: 'commands/',
|
|
53
|
+
commandExt: '.toml',
|
|
54
|
+
argumentPattern: '{{args}}',
|
|
55
|
+
hasHooks: false,
|
|
56
|
+
limits: {}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
antigravity: {
|
|
60
|
+
name: 'Antigravity',
|
|
61
|
+
folder: '.agent/',
|
|
62
|
+
rulesEntry: {
|
|
63
|
+
path: 'rules/rules.md',
|
|
64
|
+
format: 'markdown'
|
|
65
|
+
},
|
|
66
|
+
commandsDir: 'workflows/',
|
|
67
|
+
commandExt: '.md',
|
|
68
|
+
argumentPattern: '$ARGUMENTS',
|
|
69
|
+
hasHooks: false,
|
|
70
|
+
limits: {
|
|
71
|
+
maxFileChars: 12000
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ============================================================
|
|
77
|
+
// PLATFORMS - 平台名称列表
|
|
78
|
+
// ============================================================
|
|
79
|
+
const PLATFORMS = Object.keys(PLATFORM_CONFIG);
|
|
80
|
+
|
|
81
|
+
// ============================================================
|
|
82
|
+
// getPlatformConfig - 获取平台配置
|
|
83
|
+
// ============================================================
|
|
84
|
+
function getPlatformConfig(platform) {
|
|
85
|
+
const config = PLATFORM_CONFIG[platform];
|
|
86
|
+
if (!config) {
|
|
87
|
+
throw new Error(`Unknown platform: ${platform}. Valid: ${PLATFORMS.join(', ')}`);
|
|
88
|
+
}
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================
|
|
93
|
+
// isValidPlatform - 验证平台名有效性
|
|
94
|
+
// ============================================================
|
|
95
|
+
function isValidPlatform(platform) {
|
|
96
|
+
return PLATFORMS.includes(platform);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================
|
|
100
|
+
// getRulesEntryPath - 获取规则入口文件完整路径
|
|
101
|
+
// ============================================================
|
|
102
|
+
function getRulesEntryPath(platform) {
|
|
103
|
+
const config = getPlatformConfig(platform);
|
|
104
|
+
return `${config.folder}${config.rulesEntry.path}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
PLATFORM_CONFIG,
|
|
109
|
+
PLATFORMS,
|
|
110
|
+
getPlatformConfig,
|
|
111
|
+
isValidPlatform,
|
|
112
|
+
getRulesEntryPath
|
|
113
|
+
};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T050: Resource Copier Module
|
|
3
|
+
*
|
|
4
|
+
* 资源文件复制与路径重写:
|
|
5
|
+
* - 收集 IR 中引用的 scripts/templates/guides 文件
|
|
6
|
+
* - 复制到各平台对应目录
|
|
7
|
+
* - 提供路径映射表供 transformer 重写内容中的路径
|
|
8
|
+
*
|
|
9
|
+
* 目录映射规则:
|
|
10
|
+
* .claude/scripts/ → .{platform}/scripts/
|
|
11
|
+
* .claude/docs/templates/ → .{platform}/docs/templates/
|
|
12
|
+
* .claude/docs/guides/ → .{platform}/docs/guides/
|
|
13
|
+
*/
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const crypto = require('crypto');
|
|
17
|
+
|
|
18
|
+
// ============================================================
|
|
19
|
+
// Platform Directory Mapping
|
|
20
|
+
// ============================================================
|
|
21
|
+
const PLATFORM_DIRS = {
|
|
22
|
+
codex: '.codex',
|
|
23
|
+
cursor: '.cursor',
|
|
24
|
+
qwen: '.qwen',
|
|
25
|
+
antigravity: '.agent'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ============================================================
|
|
29
|
+
// Patterns for inline .claude/ path detection
|
|
30
|
+
// ============================================================
|
|
31
|
+
const INLINE_CLAUDE_PATH_PATTERN = /\.claude\/(scripts|docs\/templates|docs\/guides)\/[^\s"'`<>)}\]]+/g;
|
|
32
|
+
|
|
33
|
+
// ============================================================
|
|
34
|
+
// collectReferencedResources - 收集 IR 中引用的资源文件
|
|
35
|
+
// 包括 frontmatter 定义的和内容中直接硬编码的
|
|
36
|
+
// ============================================================
|
|
37
|
+
function collectReferencedResources(irs) {
|
|
38
|
+
const resources = {
|
|
39
|
+
scripts: new Set(),
|
|
40
|
+
templates: new Set(),
|
|
41
|
+
guides: new Set()
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const ir of irs) {
|
|
45
|
+
const fm = ir.frontmatter;
|
|
46
|
+
|
|
47
|
+
// 1. 收集 frontmatter 中定义的 scripts
|
|
48
|
+
if (fm.scripts) {
|
|
49
|
+
for (const scriptPath of Object.values(fm.scripts)) {
|
|
50
|
+
resources.scripts.add(scriptPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. 收集 frontmatter 中定义的 templates
|
|
55
|
+
if (fm.templates) {
|
|
56
|
+
for (const templatePath of Object.values(fm.templates)) {
|
|
57
|
+
resources.templates.add(templatePath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. 收集 frontmatter 中定义的 guides
|
|
62
|
+
if (fm.guides) {
|
|
63
|
+
for (const guidePath of Object.values(fm.guides)) {
|
|
64
|
+
resources.guides.add(guidePath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 4. 收集 agent_scripts 中的脚本路径
|
|
69
|
+
if (fm.agent_scripts) {
|
|
70
|
+
if (fm.agent_scripts.sh) {
|
|
71
|
+
// 提取脚本路径(去掉 __AGENT__ 等参数)
|
|
72
|
+
const shPath = extractScriptPath(fm.agent_scripts.sh);
|
|
73
|
+
if (shPath && shPath.startsWith('.claude/scripts/')) {
|
|
74
|
+
resources.scripts.add(shPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 5. 扫描内容中直接硬编码的 .claude/ 路径
|
|
80
|
+
const inlinePaths = scanInlineClaudePaths(ir.body);
|
|
81
|
+
for (const inlinePath of inlinePaths) {
|
|
82
|
+
if (inlinePath.startsWith('.claude/scripts/')) {
|
|
83
|
+
resources.scripts.add(inlinePath);
|
|
84
|
+
} else if (inlinePath.startsWith('.claude/docs/templates/')) {
|
|
85
|
+
resources.templates.add(inlinePath);
|
|
86
|
+
} else if (inlinePath.startsWith('.claude/docs/guides/')) {
|
|
87
|
+
resources.guides.add(inlinePath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
scripts: Array.from(resources.scripts),
|
|
94
|
+
templates: Array.from(resources.templates),
|
|
95
|
+
guides: Array.from(resources.guides)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================
|
|
100
|
+
// extractScriptPath - 从命令字符串中提取脚本路径
|
|
101
|
+
// 例如:".claude/scripts/foo.sh __AGENT__" → ".claude/scripts/foo.sh"
|
|
102
|
+
// ============================================================
|
|
103
|
+
function extractScriptPath(commandString) {
|
|
104
|
+
if (!commandString) return null;
|
|
105
|
+
// 匹配 .claude/ 开头的路径
|
|
106
|
+
const match = commandString.match(/^(\.claude\/[^\s]+)/);
|
|
107
|
+
return match ? match[1] : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============================================================
|
|
111
|
+
// scanInlineClaudePaths - 扫描内容中直接硬编码的 .claude/ 路径
|
|
112
|
+
// ============================================================
|
|
113
|
+
function scanInlineClaudePaths(content) {
|
|
114
|
+
const paths = new Set();
|
|
115
|
+
const pattern = new RegExp(INLINE_CLAUDE_PATH_PATTERN.source, 'g');
|
|
116
|
+
|
|
117
|
+
let match;
|
|
118
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
119
|
+
// 清理路径末尾可能的标点符号
|
|
120
|
+
let cleanPath = match[0];
|
|
121
|
+
// 移除末尾的常见标点
|
|
122
|
+
cleanPath = cleanPath.replace(/[,;:.\s]+$/, '');
|
|
123
|
+
paths.add(cleanPath);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return Array.from(paths);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================
|
|
130
|
+
// mapPathToPlatform - 将 .claude/ 路径映射到平台路径
|
|
131
|
+
// ============================================================
|
|
132
|
+
function mapPathToPlatform(sourcePath, platform) {
|
|
133
|
+
const platformDir = PLATFORM_DIRS[platform];
|
|
134
|
+
if (!platformDir) {
|
|
135
|
+
throw new Error(`Unknown platform: ${platform}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 将 .claude/xxx 替换为 .{platform}/xxx
|
|
139
|
+
if (sourcePath.startsWith('.claude/')) {
|
|
140
|
+
return sourcePath.replace(/^\.claude\//, `${platformDir}/`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 如果不是 .claude 开头,保持原样
|
|
144
|
+
return sourcePath;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============================================================
|
|
148
|
+
// copyResourceFile - 复制单个资源文件
|
|
149
|
+
// ============================================================
|
|
150
|
+
async function copyResourceFile(sourcePath, targetPath, options = {}) {
|
|
151
|
+
const { verbose = false, dryRun = false } = options;
|
|
152
|
+
|
|
153
|
+
// 解析绝对路径
|
|
154
|
+
const absoluteSource = path.resolve(process.cwd(), sourcePath);
|
|
155
|
+
const absoluteTarget = path.resolve(process.cwd(), targetPath);
|
|
156
|
+
|
|
157
|
+
// 检查源文件是否存在
|
|
158
|
+
if (!fs.existsSync(absoluteSource)) {
|
|
159
|
+
if (verbose) {
|
|
160
|
+
console.warn(`Warning: Source file not found: ${sourcePath}`);
|
|
161
|
+
}
|
|
162
|
+
return { success: false, error: 'Source not found' };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (dryRun) {
|
|
166
|
+
if (verbose) {
|
|
167
|
+
console.log(`[DRY-RUN] Would copy: ${sourcePath} → ${targetPath}`);
|
|
168
|
+
}
|
|
169
|
+
return { success: true, dryRun: true };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// 确保目标目录存在
|
|
174
|
+
const targetDir = path.dirname(absoluteTarget);
|
|
175
|
+
await fs.promises.mkdir(targetDir, { recursive: true, mode: 0o755 });
|
|
176
|
+
|
|
177
|
+
// 复制文件
|
|
178
|
+
await fs.promises.copyFile(absoluteSource, absoluteTarget);
|
|
179
|
+
|
|
180
|
+
// 设置权限
|
|
181
|
+
await fs.promises.chmod(absoluteTarget, 0o644);
|
|
182
|
+
|
|
183
|
+
if (verbose) {
|
|
184
|
+
console.log(`Copied: ${sourcePath} → ${targetPath}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return { success: true, source: sourcePath, target: targetPath };
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (verbose) {
|
|
190
|
+
console.error(`Error copying ${sourcePath}: ${error.message}`);
|
|
191
|
+
}
|
|
192
|
+
return { success: false, error: error.message };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ============================================================
|
|
197
|
+
// copyResourcesForPlatform - 为单个平台复制所有资源
|
|
198
|
+
// ============================================================
|
|
199
|
+
async function copyResourcesForPlatform(resources, platform, options = {}) {
|
|
200
|
+
const { verbose = false, dryRun = false } = options;
|
|
201
|
+
const results = {
|
|
202
|
+
platform,
|
|
203
|
+
copied: 0,
|
|
204
|
+
skipped: 0,
|
|
205
|
+
errors: [],
|
|
206
|
+
pathMap: {} // 源路径 → 目标路径 映射
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// 合并所有资源
|
|
210
|
+
const allResources = [
|
|
211
|
+
...resources.scripts,
|
|
212
|
+
...resources.templates,
|
|
213
|
+
...resources.guides
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
for (const sourcePath of allResources) {
|
|
217
|
+
const targetPath = mapPathToPlatform(sourcePath, platform);
|
|
218
|
+
results.pathMap[sourcePath] = targetPath;
|
|
219
|
+
|
|
220
|
+
// 检查目标文件是否已存在且相同
|
|
221
|
+
const absoluteSource = path.resolve(process.cwd(), sourcePath);
|
|
222
|
+
const absoluteTarget = path.resolve(process.cwd(), targetPath);
|
|
223
|
+
|
|
224
|
+
if (fs.existsSync(absoluteTarget) && fs.existsSync(absoluteSource)) {
|
|
225
|
+
const sourceHash = hashFile(absoluteSource);
|
|
226
|
+
const targetHash = hashFile(absoluteTarget);
|
|
227
|
+
if (sourceHash === targetHash) {
|
|
228
|
+
results.skipped++;
|
|
229
|
+
if (verbose) {
|
|
230
|
+
console.log(`Skipped (unchanged): ${targetPath}`);
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const copyResult = await copyResourceFile(sourcePath, targetPath, { verbose, dryRun });
|
|
237
|
+
|
|
238
|
+
if (copyResult.success) {
|
|
239
|
+
results.copied++;
|
|
240
|
+
} else {
|
|
241
|
+
results.errors.push({ source: sourcePath, error: copyResult.error });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return results;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ============================================================
|
|
249
|
+
// copyResourcesForAllPlatforms - 为所有平台复制资源
|
|
250
|
+
// ============================================================
|
|
251
|
+
async function copyResourcesForAllPlatforms(irs, platforms, options = {}) {
|
|
252
|
+
const resources = collectReferencedResources(irs);
|
|
253
|
+
const results = {
|
|
254
|
+
totalCopied: 0,
|
|
255
|
+
totalSkipped: 0,
|
|
256
|
+
platforms: {},
|
|
257
|
+
allPathMaps: {} // platform → pathMap
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
for (const platform of platforms) {
|
|
261
|
+
const platformResult = await copyResourcesForPlatform(resources, platform, options);
|
|
262
|
+
results.platforms[platform] = platformResult;
|
|
263
|
+
results.allPathMaps[platform] = platformResult.pathMap;
|
|
264
|
+
results.totalCopied += platformResult.copied;
|
|
265
|
+
results.totalSkipped += platformResult.skipped;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return results;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================
|
|
272
|
+
// rewritePathsInContent - 重写内容中的 .claude/ 路径
|
|
273
|
+
// ============================================================
|
|
274
|
+
function rewritePathsInContent(content, pathMap) {
|
|
275
|
+
let result = content;
|
|
276
|
+
|
|
277
|
+
// 按路径长度降序排序,避免短路径替换长路径的部分
|
|
278
|
+
const sortedPaths = Object.keys(pathMap).sort((a, b) => b.length - a.length);
|
|
279
|
+
|
|
280
|
+
for (const sourcePath of sortedPaths) {
|
|
281
|
+
const targetPath = pathMap[sourcePath];
|
|
282
|
+
// 全局替换
|
|
283
|
+
result = result.split(sourcePath).join(targetPath);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ============================================================
|
|
290
|
+
// hashFile - 计算文件哈希
|
|
291
|
+
// ============================================================
|
|
292
|
+
function hashFile(filePath) {
|
|
293
|
+
try {
|
|
294
|
+
const content = fs.readFileSync(filePath);
|
|
295
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ============================================================
|
|
302
|
+
// clearResourceCache - 清除资源缓存(用于测试)
|
|
303
|
+
// ============================================================
|
|
304
|
+
function clearResourceCache() {
|
|
305
|
+
// 目前没有缓存,预留接口
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = {
|
|
309
|
+
PLATFORM_DIRS,
|
|
310
|
+
INLINE_CLAUDE_PATH_PATTERN,
|
|
311
|
+
collectReferencedResources,
|
|
312
|
+
scanInlineClaudePaths,
|
|
313
|
+
extractScriptPath,
|
|
314
|
+
mapPathToPlatform,
|
|
315
|
+
copyResourceFile,
|
|
316
|
+
copyResourcesForPlatform,
|
|
317
|
+
copyResourcesForAllPlatforms,
|
|
318
|
+
rewritePathsInContent,
|
|
319
|
+
clearResourceCache
|
|
320
|
+
};
|