add-coder 0.3.26 → 0.3.28
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/README.md +282 -155
- package/dist/index.js +481 -209
- package/package.json +15 -4
- package/templates/.add-coder-src-hash.json +186 -185
- package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
- package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
- package/templates/adapters/claude/hooks/notification.ts +24 -0
- package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
- package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
- package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
- package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
- package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
- package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
- package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
- package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
- package/templates/adapters/claude/hooks/session-end.ts +10 -0
- package/templates/adapters/claude/hooks/session-start.ts +24 -0
- package/templates/adapters/claude/hooks/stop-check.ts +15 -0
- package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
- package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
- package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
- package/templates/adapters/claude/settings.json +13 -13
- package/templates/adapters/codex/config.toml.example +12 -0
- package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/codex/hooks/notification.ts +26 -0
- package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
- package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
- package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
- package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
- package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
- package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
- package/templates/adapters/codex/hooks/session-end.ts +19 -0
- package/templates/adapters/codex/hooks/session-start.ts +45 -0
- package/templates/adapters/codex/hooks/stop-check.ts +59 -0
- package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
- package/templates/adapters/codex/hooks.json +67 -1
- package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
- package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
- package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
- package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
- package/templates/adapters/qoder/hooks/notification.ts +34 -0
- package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
- package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
- package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
- package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
- package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
- package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
- package/templates/adapters/qoder/hooks/session-end.ts +27 -0
- package/templates/adapters/qoder/hooks/session-start.ts +83 -0
- package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
- package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
- package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
- package/templates/adapters/qoder/settings.json +13 -13
- package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/trae/hooks/notification.ts +22 -0
- package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
- package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
- package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
- package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
- package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
- package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
- package/templates/adapters/trae/hooks/session-end.ts +16 -0
- package/templates/adapters/trae/hooks/session-start.ts +15 -0
- package/templates/adapters/trae/hooks/stop-check.ts +20 -0
- package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
- package/templates/adapters/trae/hooks.json +1 -1
- package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
- package/templates/adapters/vscode/hooks/notification.ts +22 -0
- package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
- package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
- package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
- package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
- package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
- package/templates/adapters/vscode/hooks/session-end.ts +16 -0
- package/templates/adapters/vscode/hooks/session-start.ts +24 -0
- package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
- package/templates/core/docs/ADD-governance-claude-code.md +4 -4
- package/templates/core/docs/ADD-governance-codex.md +6 -6
- package/templates/core/docs/ADD-governance-qoder-cn.md +5 -5
- package/templates/core/docs/ADD-governance-trae.md +3 -3
- package/templates/core/docs/ADD-governance-vscode-copilot.md +22 -23
- package/templates/core/governance/audit-bridge.ts +47 -0
- package/templates/core/governance/common.ts +299 -0
- package/templates/core/governance/context-inject.ts +53 -0
- package/templates/core/governance/doc-format-guard.ts +440 -0
- package/templates/core/governance/notification-router.ts +62 -0
- package/templates/core/governance/notify.ts +66 -0
- package/templates/core/governance/permission-gate-router.ts +42 -0
- package/templates/core/governance/post-tool-failure-router.ts +56 -0
- package/templates/core/governance/post-tool-router.ts +212 -0
- package/templates/core/governance/pre-compact-guard.ts +41 -0
- package/templates/core/governance/pre-tool-guard.ts +309 -0
- package/templates/core/governance/preload-templates.ts +241 -0
- package/templates/core/governance/prompt-router.ts +222 -0
- package/templates/core/governance/review-checklist-guard.ts +186 -0
- package/templates/core/governance/rules.ts +720 -0
- package/templates/core/governance/session-end.ts +88 -0
- package/templates/core/governance/session-start-guard.ts +99 -0
- package/templates/core/governance/state-detect.ts +6 -0
- package/templates/core/governance/stop-router.ts +140 -0
- package/templates/core/governance/subagent-guard-router.ts +35 -0
- package/templates/core/governance/subagent-stop-router.ts +93 -0
- package/templates/core/governance/subagent-stop.ts +75 -0
- package/templates/core/governance/vocabulary.ts +109 -0
- package/templates/core/hooks/doc-format-guard.ts +21 -0
- package/templates/core/hooks/notification.ts +11 -0
- package/templates/core/hooks/permission-gate.ts +7 -0
- package/templates/core/hooks/post-tool-failure.ts +7 -0
- package/templates/core/hooks/post-tool-use.ts +23 -0
- package/templates/core/hooks/pre-compact.ts +9 -0
- package/templates/core/hooks/pre-tool-use.ts +30 -0
- package/templates/core/hooks/prompt-submit.ts +11 -0
- package/templates/core/hooks/review-checklist.ts +4 -0
- package/templates/core/hooks/session-end.ts +10 -0
- package/templates/core/hooks/session-start.ts +9 -0
- package/templates/core/hooks/stop-check.ts +14 -0
- package/templates/core/hooks/subagent-guard.ts +8 -0
- package/templates/core/hooks/subagent-stop.ts +9 -0
- package/templates/core/prisma/add.prisma +140 -82
- package/templates/core/rules/project_rules.md +27 -24
- package/templates/core/scripts/db-ensure.sh +8 -0
- package/templates/core/scripts/gen-plan-index.sh +4 -15
- package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
- package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
- package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
- package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
- package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
- package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
- package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
- package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
- package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
- package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
- package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
- package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
- package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
- package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
- package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
- package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
- package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
- package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
- package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
- package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
- package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
- package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
- package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
- package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +86 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
- package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
- package/templates/core/scripts/mcp-server/tools/plan.ts +152 -17
- package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
- package/templates/core/scripts/mcp-server.ts +60 -1
- package/templates/core/scripts/plan-status-bridge.ts +26 -0
- package/templates/core/skills/add-paradigm/SKILL.md +2 -2
- package/templates/core/templates/add-route-template.schema.json +24 -56
- package/templates/core/templates/collab-contract-template.md +1 -1
- package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
- package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
- package/templates/core/templates/handoff-single-round.schema.json +1 -1
- package/templates/core/templates/hitl-approval-widget.html +222 -37
- package/templates/core/templates/spec-template.schema.json +9 -46
- package/templates/core/templates/standard-plan-template.schema.json +1 -1
- package/templates/core/templates/tasks-template.schema.json +13 -54
- package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -202
- package/templates/adapters/claude/hooks/lib/common.sh +0 -261
- package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/claude/hooks/lib/notify.sh +0 -34
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/claude/hooks/notification.sh +0 -45
- package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
- package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
- package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
- package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
- package/templates/adapters/claude/hooks/session-end.sh +0 -21
- package/templates/adapters/claude/hooks/session-start.sh +0 -36
- package/templates/adapters/claude/hooks/stop-check.sh +0 -45
- package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
- package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
- package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
- package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -261
- package/templates/adapters/codex/hooks/lib/common.sh +0 -261
- package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/codex/hooks/lib/notify.sh +0 -34
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/codex/hooks/notification.sh +0 -45
- package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
- package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/codex/hooks/post-tool-use.sh +0 -138
- package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
- package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -217
- package/templates/adapters/codex/hooks/prompt-submit.sh +0 -87
- package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
- package/templates/adapters/codex/hooks/session-end.sh +0 -38
- package/templates/adapters/codex/hooks/session-start.sh +0 -53
- package/templates/adapters/codex/hooks/stop-check.sh +0 -45
- package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -260
- package/templates/adapters/qoder/hooks/lib/common.sh +0 -261
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/qoder/hooks/lib/notify.sh +0 -34
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/qoder/hooks/notification.sh +0 -25
- package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
- package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
- package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
- package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
- package/templates/adapters/qoder/hooks/session-end.sh +0 -24
- package/templates/adapters/qoder/hooks/session-start.sh +0 -45
- package/templates/adapters/qoder/hooks/stop-check.sh +0 -48
- package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
- package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
- package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -261
- package/templates/adapters/trae/hooks/lib/common.sh +0 -261
- package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/trae/hooks/lib/notify.sh +0 -34
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/trae/hooks/notification.sh +0 -45
- package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
- package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
- package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
- package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -217
- package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
- package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
- package/templates/adapters/trae/hooks/session-end.sh +0 -38
- package/templates/adapters/trae/hooks/session-start.sh +0 -53
- package/templates/adapters/trae/hooks/stop-check.sh +0 -45
- package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -201
- package/templates/adapters/vscode/hooks/lib/common.sh +0 -261
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/vscode/hooks/lib/notify.sh +0 -34
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/vscode/hooks/notification.sh +0 -45
- package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
- package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
- package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
- package/templates/adapters/vscode/hooks/session-end.sh +0 -13
- package/templates/adapters/vscode/hooks/session-start.sh +0 -35
- package/templates/adapters/vscode/hooks/stop-check.sh +0 -53
- package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
- package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
- package/templates/core/hooks/doc-format-guard.sh +0 -261
- package/templates/core/hooks/lib/common.sh +0 -261
- package/templates/core/hooks/lib/context-inject.sh +0 -96
- package/templates/core/hooks/lib/notify.sh +0 -34
- package/templates/core/hooks/lib/preload-templates.sh +0 -187
- package/templates/core/hooks/lib/session-end.sh +0 -76
- package/templates/core/hooks/lib/state-detect.sh +0 -117
- package/templates/core/hooks/lib/subagent-stop.sh +0 -90
- package/templates/core/hooks/lib/vocabulary.sh +0 -56
- package/templates/core/hooks/notification.sh +0 -45
- package/templates/core/hooks/permission-gate.sh +0 -18
- package/templates/core/hooks/post-tool-failure.sh +0 -10
- package/templates/core/hooks/post-tool-use.sh +0 -138
- package/templates/core/hooks/pre-compact.sh +0 -37
- package/templates/core/hooks/pre-tool-use.sh +0 -217
- package/templates/core/hooks/prompt-submit.sh +0 -87
- package/templates/core/hooks/review-checklist.sh +0 -10
- package/templates/core/hooks/session-end.sh +0 -38
- package/templates/core/hooks/session-start.sh +0 -53
- package/templates/core/hooks/stop-check.sh +0 -45
- package/templates/core/hooks/subagent-guard.sh +0 -15
- package/templates/core/hooks/subagent-stop.sh +0 -47
- package/templates/shared/hooks-lib/common.sh +0 -22
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -33,6 +33,13 @@ function detectIDE(projectRoot) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// src/caijuehub/strategies/adapter.strategy.ts
|
|
36
|
+
var MAGIC_DIR_MAP = {
|
|
37
|
+
claude: ".claude",
|
|
38
|
+
qoder: ".qoder",
|
|
39
|
+
vscode: ".vscode",
|
|
40
|
+
trae: ".trae",
|
|
41
|
+
codex: ".codex"
|
|
42
|
+
};
|
|
36
43
|
function resolveAdapters(target) {
|
|
37
44
|
return [target];
|
|
38
45
|
}
|
|
@@ -45,6 +52,15 @@ function resolveAdapters2(target) {
|
|
|
45
52
|
return resolveAdapters(target);
|
|
46
53
|
}
|
|
47
54
|
|
|
55
|
+
// src/shared/paths.ts
|
|
56
|
+
import { findUpSync } from "find-up";
|
|
57
|
+
var ADD_DIR = ".add";
|
|
58
|
+
function magicDirFor(adapter) {
|
|
59
|
+
const m = MAGIC_DIR_MAP[adapter];
|
|
60
|
+
if (!m) throw new Error(`\u672A\u77E5 adapter: ${adapter}`);
|
|
61
|
+
return m;
|
|
62
|
+
}
|
|
63
|
+
|
|
48
64
|
// src/cli/config-loader.ts
|
|
49
65
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
50
66
|
import { join as join2 } from "path";
|
|
@@ -372,8 +388,8 @@ function parseSchemaBlocks(content) {
|
|
|
372
388
|
return blocks;
|
|
373
389
|
}
|
|
374
390
|
function diffPrisma(basePath, targetPath) {
|
|
375
|
-
const baseContent = existsSync4(basePath) ? readFileSync3(basePath, "utf-8") : "";
|
|
376
|
-
const targetContent = existsSync4(targetPath) ? readFileSync3(targetPath, "utf-8") : "";
|
|
391
|
+
const baseContent = (existsSync4(basePath) ? readFileSync3(basePath, "utf-8") : "").replace(/\r\n/g, "\n");
|
|
392
|
+
const targetContent = (existsSync4(targetPath) ? readFileSync3(targetPath, "utf-8") : "").replace(/\r\n/g, "\n");
|
|
377
393
|
if (!baseContent) return { hasDiff: false, baseSchema: basePath, targetPath, missing: [], fieldDiffs: [] };
|
|
378
394
|
const baseBlocks = parseSchemaBlocks(baseContent);
|
|
379
395
|
const targetBlocks = parseSchemaBlocks(targetContent);
|
|
@@ -425,11 +441,9 @@ function diffPrisma(basePath, targetPath) {
|
|
|
425
441
|
|
|
426
442
|
// src/core/renderer.ts
|
|
427
443
|
import { readFileSync as readFileSync4, readdirSync, statSync, existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
428
|
-
import { join as join4, relative
|
|
429
|
-
import { fileURLToPath } from "url";
|
|
444
|
+
import { join as join4, relative } from "path";
|
|
430
445
|
import { parse } from "smol-toml";
|
|
431
|
-
var
|
|
432
|
-
var __dirname = dirname2(__filename);
|
|
446
|
+
var __dirname = import.meta.dirname;
|
|
433
447
|
var PLACEHOLDERS = {
|
|
434
448
|
"{{projectName}}": "projectName",
|
|
435
449
|
"{{projectRoot}}": "projectRoot",
|
|
@@ -500,9 +514,14 @@ function render(content, config) {
|
|
|
500
514
|
result = renderStackPlaceholders(result, config);
|
|
501
515
|
return result;
|
|
502
516
|
}
|
|
503
|
-
var TEMPLATES_ROOT =
|
|
517
|
+
var TEMPLATES_ROOT = [
|
|
518
|
+
// Source mode: src/core/renderer.ts → ../../templates
|
|
519
|
+
join4(__dirname, "../../templates"),
|
|
520
|
+
// Bundled mode: dist/index.js → ../templates
|
|
521
|
+
join4(__dirname, "../templates")
|
|
522
|
+
].find(existsSync5) ?? join4(__dirname, "../templates");
|
|
504
523
|
var CORE_DIR = join4(TEMPLATES_ROOT, "core");
|
|
505
|
-
var CORE_TARGET =
|
|
524
|
+
var CORE_TARGET = ADD_DIR;
|
|
506
525
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["prisma", "profiles"]);
|
|
507
526
|
function loadProfileRegistry() {
|
|
508
527
|
try {
|
|
@@ -558,7 +577,7 @@ function renderCore(config, dryRun) {
|
|
|
558
577
|
const src = join4(CORE_DIR, "rules", "profiles", profile.file);
|
|
559
578
|
if (existsSync5(src)) {
|
|
560
579
|
const rendered = render(readFileSync4(src, "utf-8"), config);
|
|
561
|
-
files.set(join4(
|
|
580
|
+
files.set(join4(ADD_DIR, "rules", "profiles", profile.file), rendered);
|
|
562
581
|
}
|
|
563
582
|
}
|
|
564
583
|
}
|
|
@@ -567,7 +586,17 @@ function renderCore(config, dryRun) {
|
|
|
567
586
|
}
|
|
568
587
|
return files;
|
|
569
588
|
}
|
|
570
|
-
function
|
|
589
|
+
function renderCoreForTargets(config, dryRun, targets, renderTarget = renderCore) {
|
|
590
|
+
const files = /* @__PURE__ */ new Map();
|
|
591
|
+
for (const target of new Set(targets)) {
|
|
592
|
+
const targetFiles = renderTarget({ ...config, magicDir: target }, dryRun);
|
|
593
|
+
for (const [relPath, content] of targetFiles) {
|
|
594
|
+
files.set(relPath.replace(/^\.add/, target), content);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return files;
|
|
598
|
+
}
|
|
599
|
+
function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun, includeCoreHooksLib = true) {
|
|
571
600
|
const files = /* @__PURE__ */ new Map();
|
|
572
601
|
const adapterDir = join4(TEMPLATES_ROOT, "adapters", magicPath.replace(".", ""));
|
|
573
602
|
const coreHooksLib = join4(TEMPLATES_ROOT, "core", "hooks", "lib");
|
|
@@ -591,8 +620,10 @@ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
|
|
|
591
620
|
}
|
|
592
621
|
}
|
|
593
622
|
}
|
|
623
|
+
if (includeCoreHooksLib) {
|
|
624
|
+
walk(coreHooksLib, "", join4(magicPath, "hooks", "lib"));
|
|
625
|
+
}
|
|
594
626
|
walk(adapterDir, "");
|
|
595
|
-
walk(coreHooksLib, "", join4(magicPath, "hooks", "lib"));
|
|
596
627
|
if (dryRun) {
|
|
597
628
|
console.log(`[dry-run] ${magicPath} adapter: ${files.size} files`);
|
|
598
629
|
}
|
|
@@ -601,34 +632,161 @@ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
|
|
|
601
632
|
|
|
602
633
|
// src/adapters/claude/renderer.ts
|
|
603
634
|
function renderAdapter(config, targetDir, dryRun, magicDir) {
|
|
604
|
-
return renderAdapterBase(config, magicDir, magicDir === "
|
|
635
|
+
return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
|
|
605
636
|
}
|
|
606
637
|
|
|
607
638
|
// src/adapters/qoder/renderer.ts
|
|
608
639
|
function renderAdapter2(config, targetDir, dryRun, magicDir) {
|
|
609
|
-
return renderAdapterBase(config, magicDir, magicDir === "
|
|
640
|
+
return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
|
|
610
641
|
}
|
|
611
642
|
|
|
612
643
|
// src/adapters/vscode/renderer.ts
|
|
613
644
|
function renderAdapter3(config, targetDir, dryRun, magicDir) {
|
|
614
|
-
return renderAdapterBase(config, magicDir, magicDir === "
|
|
645
|
+
return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
|
|
615
646
|
}
|
|
616
647
|
|
|
617
648
|
// src/adapters/trae/renderer.ts
|
|
618
649
|
function renderAdapter4(config, targetDir, dryRun, magicDir) {
|
|
619
|
-
return renderAdapterBase(config, magicDir, magicDir === "
|
|
650
|
+
return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
|
|
620
651
|
}
|
|
621
652
|
|
|
622
653
|
// src/adapters/codex/renderer.ts
|
|
654
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
655
|
+
import { join as join5, relative as relative2 } from "path";
|
|
656
|
+
var __dirname2 = import.meta.dirname;
|
|
657
|
+
var CODEX_SERVER_ID = "add_coder";
|
|
658
|
+
function normalizeServerId(name) {
|
|
659
|
+
return name.replace(/-/g, "_");
|
|
660
|
+
}
|
|
661
|
+
function codexCommandFor(platform) {
|
|
662
|
+
return platform === "win32" ? "cmd" : "npx";
|
|
663
|
+
}
|
|
664
|
+
function codexLaunchFor(platform, targetDir, magicDir) {
|
|
665
|
+
const serverPath = join5(targetDir, magicDir, "scripts", "mcp-server.ts");
|
|
666
|
+
const command = codexCommandFor(platform);
|
|
667
|
+
const args = platform === "win32" ? ["/c", "npx.cmd", "tsx", serverPath] : ["tsx", serverPath];
|
|
668
|
+
return { command, args, cwd: targetDir };
|
|
669
|
+
}
|
|
670
|
+
function tomlString(value) {
|
|
671
|
+
return JSON.stringify(value);
|
|
672
|
+
}
|
|
673
|
+
function tomlArray(values) {
|
|
674
|
+
return `[${values.map(tomlString).join(", ")}]`;
|
|
675
|
+
}
|
|
676
|
+
function renderCodexMcpTemplate(template, targetDir, magicDir, platform = process.platform) {
|
|
677
|
+
const launch = codexLaunchFor(platform, targetDir, magicDir);
|
|
678
|
+
return template.replaceAll("{{codexCommandToml}}", tomlString(launch.command)).replaceAll("{{codexArgsToml}}", tomlArray(launch.args)).replaceAll("{{codexProjectRootToml}}", tomlString(launch.cwd));
|
|
679
|
+
}
|
|
680
|
+
function mergeCodexProjectConfig(existing, snippet) {
|
|
681
|
+
const serverTable = /^\s*\[mcp_servers\.(?:add_coder|["']add_coder["'])\]\s*(?:#.*)?$/m;
|
|
682
|
+
if (serverTable.test(existing)) return existing;
|
|
683
|
+
const normalizedSnippet = snippet.trim();
|
|
684
|
+
if (!existing) return `${normalizedSnippet}
|
|
685
|
+
`;
|
|
686
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
687
|
+
return `${existing}${separator}${normalizedSnippet}
|
|
688
|
+
`;
|
|
689
|
+
}
|
|
690
|
+
function findCoreSkillsDir() {
|
|
691
|
+
const candidates = [
|
|
692
|
+
join5(__dirname2, "../../../templates/core/skills"),
|
|
693
|
+
join5(__dirname2, "../templates/core/skills")
|
|
694
|
+
];
|
|
695
|
+
const found = candidates.find(existsSync6);
|
|
696
|
+
if (!found) {
|
|
697
|
+
throw new Error(`[codex adapter] core skills \u771F\u6E90\u7F3A\u5931: ${candidates.join(" | ")}`);
|
|
698
|
+
}
|
|
699
|
+
return found;
|
|
700
|
+
}
|
|
701
|
+
function renderCodexSkills(config, skillsRoot = findCoreSkillsDir()) {
|
|
702
|
+
const files = /* @__PURE__ */ new Map();
|
|
703
|
+
function walk(dir) {
|
|
704
|
+
for (const name of readdirSync2(dir).sort()) {
|
|
705
|
+
const full = join5(dir, name);
|
|
706
|
+
if (statSync2(full).isDirectory()) {
|
|
707
|
+
walk(full);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const targetRel = join5(".agents", "skills", relative2(skillsRoot, full));
|
|
711
|
+
files.set(targetRel, render(readFileSync5(full, "utf-8"), config));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
walk(skillsRoot);
|
|
715
|
+
return files;
|
|
716
|
+
}
|
|
623
717
|
function renderAdapter5(config, targetDir, dryRun, magicDir) {
|
|
624
|
-
|
|
718
|
+
const files = renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
|
|
719
|
+
const examplePath = join5(magicDir, "config.toml.example");
|
|
720
|
+
const projectConfigPath = join5(magicDir, "config.toml");
|
|
721
|
+
const example = files.get(examplePath);
|
|
722
|
+
if (!example) {
|
|
723
|
+
throw new Error(`[codex adapter] MCP \u914D\u7F6E\u6A21\u677F\u7F3A\u5931: ${examplePath}`);
|
|
724
|
+
}
|
|
725
|
+
const snippet = renderCodexMcpTemplate(example, targetDir, magicDir);
|
|
726
|
+
files.set(examplePath, snippet);
|
|
727
|
+
const existingPath = join5(targetDir, projectConfigPath);
|
|
728
|
+
const existing = existsSync6(existingPath) ? readFileSync5(existingPath, "utf-8") : "";
|
|
729
|
+
files.set(projectConfigPath, mergeCodexProjectConfig(existing, snippet));
|
|
730
|
+
for (const [relPath, content] of renderCodexSkills(config)) {
|
|
731
|
+
files.set(relPath, content);
|
|
732
|
+
}
|
|
733
|
+
if (!files.has(join5(".agents", "skills", "add-paradigm", "SKILL.md"))) {
|
|
734
|
+
throw new Error("[codex adapter] add-paradigm \u672A\u6620\u5C04\u5230 .agents/skills");
|
|
735
|
+
}
|
|
736
|
+
if (!files.has(join5(".agents", "skills", "session-init", "SKILL.md"))) {
|
|
737
|
+
throw new Error("[codex adapter] session-init \u672A\u6620\u5C04\u5230 .agents/skills");
|
|
738
|
+
}
|
|
739
|
+
if (normalizeServerId("add-coder") !== CODEX_SERVER_ID || !snippet.includes(`[mcp_servers.${CODEX_SERVER_ID}]`)) {
|
|
740
|
+
throw new Error(`[codex adapter] MCP server id \u6F02\u79FB: ${CODEX_SERVER_ID}`);
|
|
741
|
+
}
|
|
742
|
+
return files;
|
|
625
743
|
}
|
|
626
744
|
|
|
627
745
|
// src/lib/utils.ts
|
|
628
746
|
import { createInterface as createInterface2 } from "readline";
|
|
629
|
-
import { existsSync as
|
|
747
|
+
import { existsSync as existsSync7 } from "fs";
|
|
630
748
|
import { resolve as resolve2 } from "path";
|
|
631
|
-
|
|
749
|
+
var pipeQueue = null;
|
|
750
|
+
var pipeReady = null;
|
|
751
|
+
function collectPipeAnswers(input) {
|
|
752
|
+
return new Promise((resolveAnswers) => {
|
|
753
|
+
const answers = [];
|
|
754
|
+
let buffer = "";
|
|
755
|
+
let settled = false;
|
|
756
|
+
const finish = () => {
|
|
757
|
+
if (settled) return;
|
|
758
|
+
settled = true;
|
|
759
|
+
if (buffer.trim()) answers.push(buffer.trim());
|
|
760
|
+
resolveAnswers(answers);
|
|
761
|
+
};
|
|
762
|
+
input.setEncoding("utf-8");
|
|
763
|
+
input.on("data", (chunk) => {
|
|
764
|
+
buffer += chunk;
|
|
765
|
+
const lines = buffer.split("\n");
|
|
766
|
+
buffer = lines.pop() ?? "";
|
|
767
|
+
answers.push(...lines.filter((line) => line.trim() !== ""));
|
|
768
|
+
});
|
|
769
|
+
input.once("end", finish);
|
|
770
|
+
input.once("error", finish);
|
|
771
|
+
input.resume();
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
function takePipeAnswer(queue) {
|
|
775
|
+
return (queue.shift() ?? "").trim().toLowerCase();
|
|
776
|
+
}
|
|
777
|
+
function ensurePipeQueue() {
|
|
778
|
+
if (pipeReady) return pipeReady;
|
|
779
|
+
if (process.stdin.isTTY) return pipeReady = Promise.resolve();
|
|
780
|
+
pipeReady = collectPipeAnswers(process.stdin).then((answers) => {
|
|
781
|
+
pipeQueue = answers;
|
|
782
|
+
});
|
|
783
|
+
return pipeReady;
|
|
784
|
+
}
|
|
785
|
+
async function ask(q) {
|
|
786
|
+
await ensurePipeQueue();
|
|
787
|
+
if (pipeQueue) {
|
|
788
|
+
return takePipeAnswer(pipeQueue);
|
|
789
|
+
}
|
|
632
790
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
633
791
|
return new Promise((r) => {
|
|
634
792
|
rl.question(q, (a) => {
|
|
@@ -638,16 +796,15 @@ function ask(q) {
|
|
|
638
796
|
});
|
|
639
797
|
}
|
|
640
798
|
function detectPm(projectRoot) {
|
|
641
|
-
return
|
|
799
|
+
return existsSync7(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
642
800
|
}
|
|
643
801
|
|
|
644
802
|
// src/cli/prisma-injector.ts
|
|
645
|
-
import { resolve as resolve6
|
|
646
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
803
|
+
import { resolve as resolve6 } from "path";
|
|
647
804
|
|
|
648
805
|
// src/caijuehub/strategies/prisma.strategy.ts
|
|
649
|
-
import { copyFileSync, existsSync as
|
|
650
|
-
import { resolve as resolve5, join as
|
|
806
|
+
import { copyFileSync, existsSync as existsSync10, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
807
|
+
import { resolve as resolve5, join as join7, dirname as dirname2 } from "path";
|
|
651
808
|
|
|
652
809
|
// src/lib/run-command.ts
|
|
653
810
|
import { spawnSync } from "child_process";
|
|
@@ -678,32 +835,32 @@ function commandExists(cmd, platform) {
|
|
|
678
835
|
}
|
|
679
836
|
|
|
680
837
|
// src/lib/db-backup.ts
|
|
681
|
-
import { existsSync as
|
|
682
|
-
import { join as
|
|
838
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync3, rmSync } from "fs";
|
|
839
|
+
import { join as join6, resolve as resolve3 } from "path";
|
|
683
840
|
var ADD_TABLES = ["AddUser", "DevOperation", "AuditLog", "HitlRecord", "PlanRecord", "ReviewRecord", "CollabContract"];
|
|
684
841
|
function ensureGitignoreRule(projectRoot) {
|
|
685
842
|
const gitignorePath = resolve3(projectRoot, ".gitignore");
|
|
686
|
-
if (!
|
|
687
|
-
const content =
|
|
688
|
-
if (content.includes(
|
|
843
|
+
if (!existsSync8(gitignorePath)) return;
|
|
844
|
+
const content = readFileSync6(gitignorePath, "utf-8");
|
|
845
|
+
if (content.includes(`${ADD_DIR}/backups/`)) return;
|
|
689
846
|
writeFileSync3(gitignorePath, `${content}${content.endsWith("\n") ? "" : "\n"}
|
|
690
847
|
# add-coder \u6570\u636E\u5E93\u540C\u6B65\u5907\u4EFD
|
|
691
|
-
|
|
848
|
+
${ADD_DIR}/backups/
|
|
692
849
|
`, "utf-8");
|
|
693
|
-
console.log(
|
|
850
|
+
console.log(`\u{1F4CB} \u5DF2\u6CE8\u5165\u5BBF\u4E3B .gitignore: ${ADD_DIR}/backups/`);
|
|
694
851
|
}
|
|
695
852
|
async function backupBeforeSync(projectRoot, opts) {
|
|
696
853
|
const dir = resolve3(projectRoot, opts.backupDir);
|
|
697
854
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
698
|
-
const bakDir =
|
|
855
|
+
const bakDir = join6(dir, ts);
|
|
699
856
|
ensureGitignoreRule(projectRoot);
|
|
700
857
|
if (opts.datasource === "sqlite" || opts.dbUrl.startsWith("file:")) {
|
|
701
858
|
const dbFile = opts.dbUrl.replace(/^file:/, "");
|
|
702
|
-
if (!
|
|
859
|
+
if (!existsSync8(dbFile)) return handleBackupFailure(opts, `sqlite \u6587\u4EF6\u4E0D\u5B58\u5728: ${dbFile}`);
|
|
703
860
|
mkdirSync3(bakDir, { recursive: true });
|
|
704
|
-
const copy =
|
|
705
|
-
writeFileSync3(copy,
|
|
706
|
-
writeFileSync3(
|
|
861
|
+
const copy = join6(bakDir, "dev.db");
|
|
862
|
+
writeFileSync3(copy, readFileSync6(dbFile), "utf-8");
|
|
863
|
+
writeFileSync3(join6(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["dev.db"] }, null, 2), "utf-8");
|
|
707
864
|
console.log(`\u2705 \u5907\u4EFD\u5B8C\u6210: ${bakDir}`);
|
|
708
865
|
pruneBackups(dir, opts.backupKeep);
|
|
709
866
|
return bakDir;
|
|
@@ -712,8 +869,8 @@ async function backupBeforeSync(projectRoot, opts) {
|
|
|
712
869
|
return handleBackupFailure(opts, "pg_dump \u672A\u5B89\u88C5\uFF08postgresql-client \u7F3A\u5931\uFF09");
|
|
713
870
|
}
|
|
714
871
|
mkdirSync3(bakDir, { recursive: true });
|
|
715
|
-
const schemaFile =
|
|
716
|
-
const tablesFile =
|
|
872
|
+
const schemaFile = join6(bakDir, "schema.sql");
|
|
873
|
+
const tablesFile = join6(bakDir, "add-tables.sql");
|
|
717
874
|
const r1 = runCommand("pg_dump", ["--schema-only", "--no-owner", `--dbname=${opts.dbUrl}`], { timeout: 6e4 });
|
|
718
875
|
if (r1.status !== 0) {
|
|
719
876
|
return handleBackupFailure(opts, `pg_dump schema \u5931\u8D25: ${r1.stderr.trim().split("\n").slice(0, 3).join(" | ")}`);
|
|
@@ -724,7 +881,7 @@ async function backupBeforeSync(projectRoot, opts) {
|
|
|
724
881
|
return handleBackupFailure(opts, `pg_dump ADD \u8868\u5931\u8D25: ${r2.stderr.trim().split("\n").slice(0, 3).join(" | ")}`);
|
|
725
882
|
}
|
|
726
883
|
writeFileSync3(tablesFile, r2.stdout, "utf-8");
|
|
727
|
-
writeFileSync3(
|
|
884
|
+
writeFileSync3(join6(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["schema.sql", "add-tables.sql"] }, null, 2), "utf-8");
|
|
728
885
|
console.log(`\u2705 \u5907\u4EFD\u5B8C\u6210: ${bakDir}`);
|
|
729
886
|
pruneBackups(dir, opts.backupKeep);
|
|
730
887
|
return bakDir;
|
|
@@ -742,17 +899,17 @@ async function handleBackupFailure(opts, reason) {
|
|
|
742
899
|
return null;
|
|
743
900
|
}
|
|
744
901
|
function pruneBackups(dir, backupKeep) {
|
|
745
|
-
if (!
|
|
746
|
-
const entries =
|
|
902
|
+
if (!existsSync8(dir)) return;
|
|
903
|
+
const entries = readdirSync3(dir).filter((n) => /^\d{4}-\d{2}-\d{2}T/.test(n)).sort();
|
|
747
904
|
const excess = entries.length - backupKeep;
|
|
748
905
|
for (let i = 0; i < excess; i++) {
|
|
749
|
-
rmSync(
|
|
906
|
+
rmSync(join6(dir, entries[i]), { recursive: true, force: true });
|
|
750
907
|
console.log(`\u{1F9F9} \u6E05\u7406\u65E7\u5907\u4EFD: ${entries[i]}`);
|
|
751
908
|
}
|
|
752
909
|
}
|
|
753
910
|
|
|
754
911
|
// src/lib/ports-contract.ts
|
|
755
|
-
import { existsSync as
|
|
912
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
|
|
756
913
|
import { resolve as resolve4, basename } from "path";
|
|
757
914
|
|
|
758
915
|
// src/caijuehub/strategies/ports.strategy.ts
|
|
@@ -773,10 +930,10 @@ import { createConnection } from "net";
|
|
|
773
930
|
var PORTS_EXAMPLE_REL = "../templates/core/templates/ports.example.md";
|
|
774
931
|
function ensurePortsContract(projectRoot, config, dryRun = false) {
|
|
775
932
|
const target = resolve4(projectRoot, "docs", "ports.md");
|
|
776
|
-
if (
|
|
933
|
+
if (existsSync9(target)) return;
|
|
777
934
|
let content;
|
|
778
935
|
try {
|
|
779
|
-
content =
|
|
936
|
+
content = readFileSync7(resolve4(import.meta.dirname, PORTS_EXAMPLE_REL), "utf-8");
|
|
780
937
|
} catch {
|
|
781
938
|
console.warn(`\u26A0\uFE0F \u7AEF\u53E3\u5951\u7EA6\u6A21\u677F\u7F3A\u5931\uFF08\u8DF3\u8FC7\u751F\u6210 docs/ports.md\uFF09: ${PORTS_EXAMPLE_REL}`);
|
|
782
939
|
return;
|
|
@@ -809,22 +966,22 @@ function parseContractPorts(content) {
|
|
|
809
966
|
}
|
|
810
967
|
function readRegisteredPorts(projectRoot) {
|
|
811
968
|
const p = resolve4(projectRoot, "docs", "ports.md");
|
|
812
|
-
if (!
|
|
813
|
-
return new Set(parseContractPorts(
|
|
969
|
+
if (!existsSync9(p)) return /* @__PURE__ */ new Set();
|
|
970
|
+
return new Set(parseContractPorts(readFileSync7(p, "utf-8")));
|
|
814
971
|
}
|
|
815
972
|
function readCrossProjectPorts(projectRoot) {
|
|
816
973
|
const ports = /* @__PURE__ */ new Set();
|
|
817
974
|
const parent = resolve4(projectRoot, "..");
|
|
818
975
|
let names = [];
|
|
819
976
|
try {
|
|
820
|
-
names =
|
|
977
|
+
names = readdirSync4(parent, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name !== basename(projectRoot)).map((d) => d.name);
|
|
821
978
|
} catch {
|
|
822
979
|
return ports;
|
|
823
980
|
}
|
|
824
981
|
for (const name of names) {
|
|
825
982
|
const p = resolve4(parent, name, "docs", "ports.md");
|
|
826
|
-
if (
|
|
827
|
-
parseContractPorts(
|
|
983
|
+
if (existsSync9(p)) {
|
|
984
|
+
parseContractPorts(readFileSync7(p, "utf-8")).forEach((x) => ports.add(x));
|
|
828
985
|
}
|
|
829
986
|
}
|
|
830
987
|
return ports;
|
|
@@ -843,11 +1000,11 @@ function scanPodmanPorts() {
|
|
|
843
1000
|
}
|
|
844
1001
|
function registerPort(projectRoot, port, svc) {
|
|
845
1002
|
const contract = resolve4(projectRoot, "docs", "ports.md");
|
|
846
|
-
if (!
|
|
1003
|
+
if (!existsSync9(contract)) return;
|
|
847
1004
|
const env = svc.envKey ? ` \`${svc.envKey}\`` : "";
|
|
848
1005
|
const line = `| ${port} | PostgreSQL\uFF08${svc.containerName}\uFF09 | ${svc.name}\uFF08add-coder \u81EA\u52A8\u767B\u8BB0\uFF09 | \u{1F7E2} \u4F7F\u7528\u4E2D | .env.development${env} |
|
|
849
1006
|
`;
|
|
850
|
-
writeFileSync4(contract,
|
|
1007
|
+
writeFileSync4(contract, readFileSync7(contract, "utf-8") + line, "utf-8");
|
|
851
1008
|
console.log(`\u{1F4CB} \u7AEF\u53E3\u5951\u7EA6\u767B\u8BB0: ${port}\uFF08${svc.containerName}\uFF09\u2192 docs/ports.md`);
|
|
852
1009
|
}
|
|
853
1010
|
async function allocatePortsWithContract(projectRoot, services) {
|
|
@@ -897,13 +1054,13 @@ var PRISMA_CONFIG = {
|
|
|
897
1054
|
var ADD_TABLES2 = ["AddUser", "DevOperation", "AuditLog", "HitlRecord", "PlanRecord", "ReviewRecord", "CollabContract"];
|
|
898
1055
|
function readEnvValue(projectRoot, key) {
|
|
899
1056
|
const envPath = resolve5(projectRoot, ".env.development");
|
|
900
|
-
if (!
|
|
901
|
-
const m =
|
|
1057
|
+
if (!existsSync10(envPath)) return "";
|
|
1058
|
+
const m = readFileSync8(envPath, "utf-8").match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
902
1059
|
return m?.[1]?.trim() || "";
|
|
903
1060
|
}
|
|
904
1061
|
function appendEnvValue(projectRoot, key, value) {
|
|
905
1062
|
const envPath = resolve5(projectRoot, ".env.development");
|
|
906
|
-
const existing =
|
|
1063
|
+
const existing = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
|
|
907
1064
|
if (new RegExp(`^${key}=`, "m").test(existing)) return;
|
|
908
1065
|
writeFileSync5(envPath, `${existing}${existing.endsWith("\n") ? "" : "\n"}${key}=${value}
|
|
909
1066
|
`, "utf-8");
|
|
@@ -956,8 +1113,8 @@ async function ensureSplitDb(projectRoot, opts) {
|
|
|
956
1113
|
}
|
|
957
1114
|
appendEnvValue(projectRoot, "ADD_DATABASE_URL", `postgresql://${dbUser}:${dbPass}@127.0.0.1:${port}/${dbName}?schema=public`);
|
|
958
1115
|
const addPrismaPath = resolve5(projectRoot, "prisma", "add.prisma");
|
|
959
|
-
if (
|
|
960
|
-
const content =
|
|
1116
|
+
if (existsSync10(addPrismaPath)) {
|
|
1117
|
+
const content = readFileSync8(addPrismaPath, "utf-8");
|
|
961
1118
|
if (!content.includes("datasource db")) {
|
|
962
1119
|
writeFileSync5(addPrismaPath, `datasource db {
|
|
963
1120
|
provider = "postgresql"
|
|
@@ -1023,9 +1180,9 @@ async function provisionDevUrl(projectRoot) {
|
|
|
1023
1180
|
}
|
|
1024
1181
|
function resolveAtlasBin(projectRoot) {
|
|
1025
1182
|
const pkgBin = resolve5(projectRoot, "node_modules", "add-coder", "node_modules", ".bin", "atlas");
|
|
1026
|
-
if (
|
|
1183
|
+
if (existsSync10(pkgBin)) return pkgBin;
|
|
1027
1184
|
const local = resolve5(projectRoot, "node_modules", ".bin", "atlas");
|
|
1028
|
-
if (
|
|
1185
|
+
if (existsSync10(local)) return local;
|
|
1029
1186
|
return commandExists("atlas") ? "atlas" : null;
|
|
1030
1187
|
}
|
|
1031
1188
|
function hasNonAddTableChanges(diffSql) {
|
|
@@ -1061,8 +1218,8 @@ function buildExcludeArgs(projectRoot, splitDb) {
|
|
|
1061
1218
|
async function runAtlasSync(projectRoot, targetUrl, splitDb, sync, opts) {
|
|
1062
1219
|
const pm = detectPm(projectRoot);
|
|
1063
1220
|
const schemaTo = splitDb ? resolve5(projectRoot, "prisma", "add.prisma") : resolve5(projectRoot, "prisma");
|
|
1064
|
-
const baselinePath =
|
|
1065
|
-
mkdirSync5(
|
|
1221
|
+
const baselinePath = join7(projectRoot, PRISMA_CONFIG.sync.backupDir, "baseline.sql");
|
|
1222
|
+
mkdirSync5(dirname2(baselinePath), { recursive: true });
|
|
1066
1223
|
const diffR = runCommand(pm, prismaDiffArgs(pm, schemaTo, null), { cwd: projectRoot, timeout: 6e4 });
|
|
1067
1224
|
if (diffR.status !== 0) {
|
|
1068
1225
|
console.warn(`\u26A0\uFE0F baseline \u751F\u6210\u5931\u8D25\uFF08${diffR.stderr.trim().slice(0, 150)}\uFF09\uFF0C\u964D\u7EA7 prisma-diff`);
|
|
@@ -1120,8 +1277,8 @@ async function runAtlasSync(projectRoot, targetUrl, splitDb, sync, opts) {
|
|
|
1120
1277
|
function runPrismaDiffSync(projectRoot, targetUrl, splitDb) {
|
|
1121
1278
|
const pm = detectPm(projectRoot);
|
|
1122
1279
|
const schemaTo = splitDb ? resolve5(projectRoot, "prisma", "add.prisma") : resolve5(projectRoot, "prisma");
|
|
1123
|
-
const diffPath =
|
|
1124
|
-
mkdirSync5(
|
|
1280
|
+
const diffPath = join7(projectRoot, PRISMA_CONFIG.sync.backupDir, "diff.sql");
|
|
1281
|
+
mkdirSync5(dirname2(diffPath), { recursive: true });
|
|
1125
1282
|
const r = runCommand(pm, prismaDiffArgs(pm, schemaTo, targetUrl), { cwd: projectRoot, timeout: 6e4 });
|
|
1126
1283
|
if (r.status !== 0) throw new Error(`prisma migrate diff \u5931\u8D25: ${r.stderr.trim().slice(0, 200)}`);
|
|
1127
1284
|
writeFileSync5(diffPath, r.stdout, "utf-8");
|
|
@@ -1198,10 +1355,10 @@ function runPrismaInit(projectRoot, provider, schemaPath) {
|
|
|
1198
1355
|
console.error(`\u2717 prisma init \u65E0\u6CD5\u6267\u884C: ${e instanceof Error ? e.message : String(e)}`);
|
|
1199
1356
|
initResult = { status: null, stdout: "", stderr: "" };
|
|
1200
1357
|
}
|
|
1201
|
-
if (initResult.status !== 0 || !
|
|
1358
|
+
if (initResult.status !== 0 || !existsSync10(schemaPath)) {
|
|
1202
1359
|
console.error(`\u26A0\uFE0F prisma init \u672A\u5B8C\u6210\uFF08\u9000\u51FA\u7801: ${initResult.status}\uFF09\uFF0C\u56DE\u9000\u624B\u52A8\u521B\u5EFA schema.prisma\u2014\u2014db push \u5C06\u9A8C\u8BC1\u5176\u53EF\u7528\u6027`);
|
|
1203
1360
|
const prismaDir = resolve5(projectRoot, "prisma");
|
|
1204
|
-
if (!
|
|
1361
|
+
if (!existsSync10(prismaDir)) mkdirSync5(prismaDir, { recursive: true });
|
|
1205
1362
|
const content = `generator client {
|
|
1206
1363
|
provider = "prisma-client-js"
|
|
1207
1364
|
}
|
|
@@ -1212,7 +1369,7 @@ datasource db {
|
|
|
1212
1369
|
`;
|
|
1213
1370
|
writeFileSync5(schemaPath, content, "utf-8");
|
|
1214
1371
|
const devEnvPath = resolve5(projectRoot, ".env.development");
|
|
1215
|
-
if (!
|
|
1372
|
+
if (!existsSync10(devEnvPath)) {
|
|
1216
1373
|
const defaultUrl = provider === "sqlite" ? 'DATABASE_URL="file:./data/dev.db"' : '# \u8BF7\u7F16\u8F91\u4E3A\u4F60\u7684\u6570\u636E\u5E93\u8FDE\u63A5\u4FE1\u606F\nDATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DB?schema=public"';
|
|
1217
1374
|
writeFileSync5(devEnvPath, defaultUrl + "\n", "utf-8");
|
|
1218
1375
|
console.log("\u5DF2\u521B\u5EFA .env.development");
|
|
@@ -1224,16 +1381,16 @@ datasource db {
|
|
|
1224
1381
|
function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
|
|
1225
1382
|
const envPath = resolve5(projectRoot, ".env");
|
|
1226
1383
|
const devEnvPath = resolve5(projectRoot, ".env.development");
|
|
1227
|
-
if (
|
|
1228
|
-
const envContent =
|
|
1384
|
+
if (existsSync10(envPath)) {
|
|
1385
|
+
const envContent = readFileSync8(envPath, "utf-8");
|
|
1229
1386
|
const dbUrl = envContent.match(/DATABASE_URL=.*/);
|
|
1230
1387
|
if (dbUrl) {
|
|
1231
|
-
const existing =
|
|
1388
|
+
const existing = existsSync10(devEnvPath) ? readFileSync8(devEnvPath, "utf-8") : "";
|
|
1232
1389
|
if (!existing.includes("DATABASE_URL=")) {
|
|
1233
1390
|
writeFileSync5(devEnvPath, `${existing}${existing ? "\n" : ""}${dbUrl[0]}
|
|
1234
1391
|
`, "utf-8");
|
|
1235
1392
|
}
|
|
1236
|
-
if (
|
|
1393
|
+
if (existsSync10(envPath)) unlinkSync(envPath);
|
|
1237
1394
|
console.log("\u5DF2\u5C06 DATABASE_URL \u8FC1\u79FB\u5230 .env.development");
|
|
1238
1395
|
}
|
|
1239
1396
|
}
|
|
@@ -1242,8 +1399,8 @@ function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
|
|
|
1242
1399
|
patchGeneratorOutput(schemaPath);
|
|
1243
1400
|
}
|
|
1244
1401
|
function patchGeneratorOutput(schemaPath) {
|
|
1245
|
-
if (!
|
|
1246
|
-
let content =
|
|
1402
|
+
if (!existsSync10(schemaPath)) return;
|
|
1403
|
+
let content = readFileSync8(schemaPath, "utf-8");
|
|
1247
1404
|
const genBlock = content.match(/generator\s+\w+\s*\{[\s\S]*?\}/);
|
|
1248
1405
|
if (!genBlock) {
|
|
1249
1406
|
content += `
|
|
@@ -1268,7 +1425,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
1268
1425
|
const schemaPath = resolve5(prismaDir, "schema.prisma");
|
|
1269
1426
|
const destPath = resolve5(prismaDir, "add.prisma");
|
|
1270
1427
|
let justInited = false;
|
|
1271
|
-
if (!
|
|
1428
|
+
if (!existsSync10(prismaDir) || !existsSync10(schemaPath)) {
|
|
1272
1429
|
if (C.onMissing === "skip") {
|
|
1273
1430
|
console.log("\u8DF3\u8FC7\uFF1A\u7F3A\u5C11 Prisma");
|
|
1274
1431
|
return true;
|
|
@@ -1287,7 +1444,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
1287
1444
|
postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath);
|
|
1288
1445
|
justInited = true;
|
|
1289
1446
|
}
|
|
1290
|
-
if (
|
|
1447
|
+
if (existsSync10(destPath) && !justInited) {
|
|
1291
1448
|
if (options.dryRun) {
|
|
1292
1449
|
console.log("[dry-run] \u5DF2\u6709 add.prisma");
|
|
1293
1450
|
return true;
|
|
@@ -1304,8 +1461,8 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
1304
1461
|
console.log("\u8986\u76D6");
|
|
1305
1462
|
} else if (choice === "d") {
|
|
1306
1463
|
copyFileSync(destPath, destPath + ".bak");
|
|
1307
|
-
console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" +
|
|
1308
|
-
console.log("=== \u6A21\u677F ===\n" +
|
|
1464
|
+
console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" + readFileSync8(destPath, "utf-8"));
|
|
1465
|
+
console.log("=== \u6A21\u677F ===\n" + readFileSync8(addPrismaTemplate, "utf-8"));
|
|
1309
1466
|
if (await ask("\u786E\u8BA4\u8986\u76D6\uFF1F[y/N] ") !== "y") {
|
|
1310
1467
|
console.log("\u5DF2\u8DF3\u8FC7");
|
|
1311
1468
|
return true;
|
|
@@ -1353,22 +1510,22 @@ ${detail}` : ""}`);
|
|
|
1353
1510
|
}
|
|
1354
1511
|
|
|
1355
1512
|
// src/cli/prisma-injector.ts
|
|
1356
|
-
var
|
|
1357
|
-
var
|
|
1358
|
-
var ADD_PRISMA_TEMPLATE = resolve6(__dirname2, "../templates/core/prisma/add.prisma");
|
|
1513
|
+
var __dirname3 = import.meta.dirname;
|
|
1514
|
+
var ADD_PRISMA_TEMPLATE = resolve6(__dirname3, "../templates/core/prisma/add.prisma");
|
|
1359
1515
|
async function injectPrisma2(projectRoot, options = {}) {
|
|
1360
1516
|
return injectPrisma(projectRoot, ADD_PRISMA_TEMPLATE, options);
|
|
1361
1517
|
}
|
|
1362
1518
|
|
|
1363
1519
|
// src/cli/commands/init.ts
|
|
1364
|
-
import { readFileSync as
|
|
1520
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, readdirSync as readdirSync5 } from "fs";
|
|
1365
1521
|
import { createHash } from "crypto";
|
|
1366
|
-
import { resolve as resolve8 } from "path";
|
|
1522
|
+
import { resolve as resolve8, dirname as dirname3, join as pathJoin } from "path";
|
|
1523
|
+
import { homedir as homedir2 } from "os";
|
|
1367
1524
|
import { createConnection as createConnection2 } from "net";
|
|
1368
1525
|
|
|
1369
1526
|
// src/lib/model-predownload.ts
|
|
1370
|
-
import { existsSync as
|
|
1371
|
-
import { join as
|
|
1527
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
1528
|
+
import { join as join8, resolve as resolve7 } from "path";
|
|
1372
1529
|
import { homedir } from "os";
|
|
1373
1530
|
import { parse as parse2 } from "smol-toml";
|
|
1374
1531
|
var DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -1377,13 +1534,13 @@ var TOML_CANDIDATES = [
|
|
|
1377
1534
|
resolve7(import.meta.dirname, "../caijuehub/dps-scoring-rules.toml")
|
|
1378
1535
|
];
|
|
1379
1536
|
function resolveEmbeddingModel() {
|
|
1380
|
-
const tomlPath = TOML_CANDIDATES.find((p) =>
|
|
1537
|
+
const tomlPath = TOML_CANDIDATES.find((p) => existsSync11(p));
|
|
1381
1538
|
if (!tomlPath) {
|
|
1382
1539
|
throw new Error(
|
|
1383
1540
|
`dps-scoring-rules.toml \u672A\u627E\u5230\uFF08\u671F\u671B\u8DEF\u5F84: ${TOML_CANDIDATES.join(" \u6216 ")}\uFF09`
|
|
1384
1541
|
);
|
|
1385
1542
|
}
|
|
1386
|
-
const cfg = parse2(
|
|
1543
|
+
const cfg = parse2(readFileSync9(tomlPath, "utf-8"));
|
|
1387
1544
|
const model = cfg.embedding?.model;
|
|
1388
1545
|
if (typeof model !== "string" || model.length === 0) {
|
|
1389
1546
|
throw new Error(`dps-scoring-rules.toml [embedding] model \u672A\u914D\u7F6E\uFF08${tomlPath}\uFF09`);
|
|
@@ -1393,8 +1550,8 @@ function resolveEmbeddingModel() {
|
|
|
1393
1550
|
function resolveCacheDir() {
|
|
1394
1551
|
const hubCache = process.env.HF_HUB_CACHE;
|
|
1395
1552
|
if (hubCache) return hubCache;
|
|
1396
|
-
const home = process.env.HF_HOME ||
|
|
1397
|
-
return
|
|
1553
|
+
const home = process.env.HF_HOME || join8(homedir(), ".cache", "huggingface");
|
|
1554
|
+
return join8(home, "hub");
|
|
1398
1555
|
}
|
|
1399
1556
|
function modelCacheName(model) {
|
|
1400
1557
|
const parts = model.split("/");
|
|
@@ -1404,7 +1561,7 @@ function modelCacheName(model) {
|
|
|
1404
1561
|
}
|
|
1405
1562
|
function isModelCached(model) {
|
|
1406
1563
|
const cacheDir = resolveCacheDir();
|
|
1407
|
-
return
|
|
1564
|
+
return existsSync11(join8(cacheDir, modelCacheName(model), "snapshots"));
|
|
1408
1565
|
}
|
|
1409
1566
|
async function ensureEmbeddingModel(options) {
|
|
1410
1567
|
const force = options?.force ?? false;
|
|
@@ -1417,8 +1574,8 @@ async function ensureEmbeddingModel(options) {
|
|
|
1417
1574
|
const { pipeline, env } = await import("@huggingface/transformers");
|
|
1418
1575
|
env.cacheDir = resolveCacheDir();
|
|
1419
1576
|
const cacheDir = env.cacheDir;
|
|
1420
|
-
const snapshotsDir =
|
|
1421
|
-
if (!force &&
|
|
1577
|
+
const snapshotsDir = join8(cacheDir, modelCacheName(model), "snapshots");
|
|
1578
|
+
if (!force && existsSync11(snapshotsDir)) {
|
|
1422
1579
|
return { status: "already-cached", model, cacheDir };
|
|
1423
1580
|
}
|
|
1424
1581
|
env.remoteHost = "https://hf-mirror.com";
|
|
@@ -1450,8 +1607,17 @@ var ADAPTER_RENDERERS = {
|
|
|
1450
1607
|
trae: renderAdapter4,
|
|
1451
1608
|
codex: renderAdapter5
|
|
1452
1609
|
};
|
|
1453
|
-
|
|
1610
|
+
function coreTargetsForAdapter(target, magicDir) {
|
|
1611
|
+
return target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
|
|
1612
|
+
}
|
|
1613
|
+
function needsClaudeAgentHost(adapters) {
|
|
1614
|
+
return adapters.includes("vscode") || adapters.includes("trae");
|
|
1615
|
+
}
|
|
1454
1616
|
async function initCommand(options) {
|
|
1617
|
+
if (options.printMcpConfig || options.writeUserConfig) {
|
|
1618
|
+
await handleCodexMcpConfig(options);
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1455
1621
|
const ctx = await prepare(options);
|
|
1456
1622
|
writeComposeEnv(ctx);
|
|
1457
1623
|
if (!options.dryRun) {
|
|
@@ -1474,9 +1640,54 @@ async function initCommand(options) {
|
|
|
1474
1640
|
}
|
|
1475
1641
|
}
|
|
1476
1642
|
}
|
|
1643
|
+
async function handleCodexMcpConfig(options) {
|
|
1644
|
+
const projectRoot = process.cwd();
|
|
1645
|
+
const magicDir = magicDirFor(options.adapter ?? "codex");
|
|
1646
|
+
const config = await loadConfig(projectRoot, options.config, { force: true });
|
|
1647
|
+
config.projectRoot = projectRoot;
|
|
1648
|
+
config.magicDir = magicDir;
|
|
1649
|
+
const files = renderAdapter5(config, projectRoot, !!options.dryRun, magicDir);
|
|
1650
|
+
const tomlEntry = [...files.entries()].find(
|
|
1651
|
+
([rel]) => rel.endsWith("config.toml.example") || rel.endsWith("config.toml")
|
|
1652
|
+
);
|
|
1653
|
+
if (!tomlEntry) {
|
|
1654
|
+
console.error("\u2717 config.toml \u6E32\u67D3\u4EA7\u7269\u7F3A\u5931\uFF08\u68C0\u67E5 templates/adapters/codex/config.toml.example\uFF09");
|
|
1655
|
+
process.exit(1);
|
|
1656
|
+
}
|
|
1657
|
+
const toml = tomlEntry[1];
|
|
1658
|
+
if (options.printMcpConfig) {
|
|
1659
|
+
console.log("\n# \u5C06\u4EE5\u4E0B\u7247\u6BB5\u7C98\u8D34\u5230 ~/.codex/config.toml\uFF08\u6216\u4F7F\u7528 --write-user-config \u81EA\u52A8\u5199\u5165\uFF09");
|
|
1660
|
+
console.log(toml);
|
|
1661
|
+
}
|
|
1662
|
+
if (options.writeUserConfig) {
|
|
1663
|
+
const userConfigPath = pathJoin(homedir2(), ".codex", "config.toml");
|
|
1664
|
+
if (options.dryRun) {
|
|
1665
|
+
console.log(`[dry-run] \u5C06\u5199\u5165 ${userConfigPath}`);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
if (existsSync12(userConfigPath) && readFileSync10(userConfigPath, "utf-8").includes("mcp_servers.add_coder")) {
|
|
1669
|
+
console.log(`\u26A0\uFE0F ${userConfigPath} \u5DF2\u5B58\u5728 mcp_servers.add_coder \u914D\u7F6E\uFF0C\u8DF3\u8FC7\uFF08\u907F\u514D\u91CD\u590D\uFF09`);
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
const answer = await ask(`\u5199\u5165 ${userConfigPath}\uFF1F\uFF08y/N\uFF09 \u2192 `);
|
|
1673
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
1674
|
+
console.log("\u5DF2\u53D6\u6D88\u5199\u5165");
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
if (existsSync12(userConfigPath)) {
|
|
1678
|
+
const backup = `${userConfigPath}.bak-${Date.now()}`;
|
|
1679
|
+
copyFileSync2(userConfigPath, backup);
|
|
1680
|
+
console.log(`\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${backup}`);
|
|
1681
|
+
}
|
|
1682
|
+
mkdirSync6(dirname3(userConfigPath), { recursive: true });
|
|
1683
|
+
writeFileSync6(userConfigPath, `
|
|
1684
|
+
${toml}`, { flag: "a" });
|
|
1685
|
+
console.log(`\u2705 \u5DF2\u5199\u5165 ${userConfigPath}\uFF0C\u91CD\u542F Codex \u751F\u6548`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1477
1688
|
async function resolveAdapter(projectRoot, specified) {
|
|
1478
1689
|
if (specified) {
|
|
1479
|
-
if (!
|
|
1690
|
+
if (!magicDirFor(specified)) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
|
|
1480
1691
|
console.log(`\u76EE\u6807 IDE: ${specified} (--adapter)`);
|
|
1481
1692
|
return specified;
|
|
1482
1693
|
}
|
|
@@ -1639,18 +1850,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
1639
1850
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
1640
1851
|
return;
|
|
1641
1852
|
}
|
|
1642
|
-
if (!
|
|
1853
|
+
if (!existsSync12(scriptsDir)) mkdirSync6(scriptsDir, { recursive: true });
|
|
1643
1854
|
writeFileSync6(scriptPath, content, "utf-8");
|
|
1644
1855
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
1645
1856
|
}
|
|
1646
1857
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
1647
1858
|
const pkgPath = resolve8(projectRoot, "package.json");
|
|
1648
|
-
if (!
|
|
1859
|
+
if (!existsSync12(pkgPath)) return;
|
|
1649
1860
|
if (dryRun) {
|
|
1650
1861
|
console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
|
|
1651
1862
|
return;
|
|
1652
1863
|
}
|
|
1653
|
-
const pkg = JSON.parse(
|
|
1864
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
1654
1865
|
if (!pkg.scripts) pkg.scripts = {};
|
|
1655
1866
|
if (!pkg.scripts["db:export"]) {
|
|
1656
1867
|
pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
|
|
@@ -1677,7 +1888,7 @@ async function resolveStack(options) {
|
|
|
1677
1888
|
async function prepare(options) {
|
|
1678
1889
|
const projectRoot = process.cwd();
|
|
1679
1890
|
const target = await resolveAdapter(projectRoot, options.adapter);
|
|
1680
|
-
const magicDir =
|
|
1891
|
+
const magicDir = magicDirFor(target);
|
|
1681
1892
|
const config = await loadConfig(projectRoot, options.config, { force: options.force });
|
|
1682
1893
|
config.projectRoot = projectRoot;
|
|
1683
1894
|
config.magicDir = magicDir;
|
|
@@ -1698,14 +1909,14 @@ function writeComposeEnv(ctx) {
|
|
|
1698
1909
|
if (!db.reuseExisting) {
|
|
1699
1910
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
1700
1911
|
const composePath = resolve8(projectRoot, composeName);
|
|
1701
|
-
if (!options.dryRun && (!
|
|
1912
|
+
if (!options.dryRun && (!existsSync12(composePath) || options.force)) {
|
|
1702
1913
|
writeFileSync6(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
1703
1914
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
1704
1915
|
}
|
|
1705
1916
|
}
|
|
1706
1917
|
const devEnvPath = resolve8(projectRoot, ".env.development");
|
|
1707
|
-
if (!options.dryRun &&
|
|
1708
|
-
const existing =
|
|
1918
|
+
if (!options.dryRun && existsSync12(devEnvPath)) {
|
|
1919
|
+
const existing = readFileSync10(devEnvPath, "utf-8");
|
|
1709
1920
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
1710
1921
|
writeFileSync6(devEnvPath, existing + `
|
|
1711
1922
|
DATABASE_USER=${db.user || "admin"}
|
|
@@ -1720,32 +1931,39 @@ PROJECT_NAME=${config.projectName || "add-project"}
|
|
|
1720
1931
|
async function renderAndWrite(ctx) {
|
|
1721
1932
|
const { projectRoot, options, magicDir, config, target } = ctx;
|
|
1722
1933
|
const dry = !!options.dryRun;
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
for (const [relPath, content] of coreFiles) {
|
|
1727
|
-
for (const t of [".add", magicDir]) {
|
|
1728
|
-
const targetPath = relPath.replace(/^\.add/, t);
|
|
1729
|
-
if (!allFiles.has(targetPath)) allFiles.set(targetPath, content);
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1934
|
+
const coreTargets = coreTargetsForAdapter(target, magicDir);
|
|
1935
|
+
const allFiles = renderCoreForTargets(config, dry, coreTargets);
|
|
1936
|
+
console.log(`Core \u6A21\u677F: ${allFiles.size} \u4E2A\u5E76\u5217\u76EE\u6807\u6587\u4EF6`);
|
|
1732
1937
|
const resolved = resolveAdapters2(target);
|
|
1733
1938
|
for (const adapter of resolved) {
|
|
1734
1939
|
const renderFn = ADAPTER_RENDERERS[adapter];
|
|
1735
1940
|
if (renderFn) {
|
|
1736
|
-
const
|
|
1941
|
+
const adapterMagicDir = magicDirFor(adapter);
|
|
1942
|
+
const adapterFiles = renderFn(
|
|
1943
|
+
{ ...config, magicDir: adapterMagicDir },
|
|
1944
|
+
projectRoot,
|
|
1945
|
+
dry,
|
|
1946
|
+
adapterMagicDir
|
|
1947
|
+
);
|
|
1737
1948
|
for (const [p, c] of adapterFiles) allFiles.set(p, c);
|
|
1738
1949
|
console.log(`${adapter} adapter: ${adapterFiles.size} \u6587\u4EF6`);
|
|
1739
1950
|
}
|
|
1740
1951
|
}
|
|
1741
|
-
if (
|
|
1742
|
-
const
|
|
1952
|
+
if (needsClaudeAgentHost(resolved)) {
|
|
1953
|
+
const claudeMagicDir = magicDirFor("claude");
|
|
1954
|
+
const claudeFiles = renderAdapter(
|
|
1955
|
+
{ ...config, magicDir: claudeMagicDir },
|
|
1956
|
+
projectRoot,
|
|
1957
|
+
dry,
|
|
1958
|
+
claudeMagicDir
|
|
1959
|
+
);
|
|
1743
1960
|
for (const [p, c] of claudeFiles) allFiles.set(p, c);
|
|
1744
1961
|
console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
|
|
1745
1962
|
}
|
|
1746
|
-
|
|
1963
|
+
const reviewTargets = target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
|
|
1964
|
+
for (const d of reviewTargets) {
|
|
1747
1965
|
const reviewsDir = resolve8(projectRoot, d, "reviews");
|
|
1748
|
-
if (!
|
|
1966
|
+
if (!existsSync12(reviewsDir)) {
|
|
1749
1967
|
if (dry) {
|
|
1750
1968
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
1751
1969
|
} else {
|
|
@@ -1757,7 +1975,7 @@ async function renderAndWrite(ctx) {
|
|
|
1757
1975
|
const hashMap = {};
|
|
1758
1976
|
let npmVer = "";
|
|
1759
1977
|
try {
|
|
1760
|
-
npmVer = JSON.parse(
|
|
1978
|
+
npmVer = JSON.parse(readFileSync10(resolve8(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
1761
1979
|
} catch {
|
|
1762
1980
|
}
|
|
1763
1981
|
for (const [rp, c] of allFiles) {
|
|
@@ -1796,7 +2014,7 @@ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
|
|
|
1796
2014
|
}
|
|
1797
2015
|
if (db.engine === "postgresql" && db.container === "manual") {
|
|
1798
2016
|
const dbScript = resolve8(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
1799
|
-
if (
|
|
2017
|
+
if (existsSync12(dbScript)) {
|
|
1800
2018
|
try {
|
|
1801
2019
|
const bashRun = runCommand("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
|
|
1802
2020
|
if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
|
|
@@ -1846,12 +2064,12 @@ function deployDocs(ctx) {
|
|
|
1846
2064
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
1847
2065
|
const srcDir = resolve8(groundingSrc, d);
|
|
1848
2066
|
const destDir = resolve8(docsBase, d);
|
|
1849
|
-
if (!
|
|
1850
|
-
if (!
|
|
1851
|
-
for (const f of
|
|
2067
|
+
if (!existsSync12(destDir)) mkdirSync6(destDir, { recursive: true });
|
|
2068
|
+
if (!existsSync12(srcDir)) continue;
|
|
2069
|
+
for (const f of readdirSync5(srcDir)) {
|
|
1852
2070
|
const src = resolve8(srcDir, f);
|
|
1853
2071
|
const dest = resolve8(destDir, f);
|
|
1854
|
-
if (
|
|
2072
|
+
if (existsSync12(dest)) continue;
|
|
1855
2073
|
try {
|
|
1856
2074
|
copyFileSync2(src, dest);
|
|
1857
2075
|
} catch {
|
|
@@ -1867,7 +2085,7 @@ function finalize(ctx, result, dbFail) {
|
|
|
1867
2085
|
return;
|
|
1868
2086
|
}
|
|
1869
2087
|
if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
|
|
1870
|
-
const pkg = JSON.parse(
|
|
2088
|
+
const pkg = JSON.parse(readFileSync10(resolve8(import.meta.dirname, "../package.json"), "utf-8"));
|
|
1871
2089
|
const peerNames = Object.keys(pkg.peerDependencies || {});
|
|
1872
2090
|
if (peerNames.length > 0) {
|
|
1873
2091
|
console.log(`
|
|
@@ -1895,8 +2113,8 @@ function finalize(ctx, result, dbFail) {
|
|
|
1895
2113
|
}
|
|
1896
2114
|
|
|
1897
2115
|
// src/cli/commands/sync.ts
|
|
1898
|
-
import { existsSync as
|
|
1899
|
-
import { resolve as resolve9, dirname as
|
|
2116
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
2117
|
+
import { resolve as resolve9, dirname as dirname4 } from "path";
|
|
1900
2118
|
import { createHash as createHash2 } from "crypto";
|
|
1901
2119
|
|
|
1902
2120
|
// src/lib/path-normalize.ts
|
|
@@ -1931,10 +2149,9 @@ var ADAPTER_RENDERERS2 = {
|
|
|
1931
2149
|
trae: renderAdapter4,
|
|
1932
2150
|
codex: renderAdapter5
|
|
1933
2151
|
};
|
|
1934
|
-
var MAGIC_DIR_MAP2 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
|
|
1935
2152
|
function resolveAdapter2(projectRoot, specified) {
|
|
1936
2153
|
if (specified) {
|
|
1937
|
-
if (!
|
|
2154
|
+
if (!magicDirFor(specified)) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
|
|
1938
2155
|
console.log(`\u76EE\u6807 IDE: ${specified} (--adapter)`);
|
|
1939
2156
|
return specified;
|
|
1940
2157
|
}
|
|
@@ -1952,9 +2169,25 @@ function isUserData(p) {
|
|
|
1952
2169
|
function hash8(c) {
|
|
1953
2170
|
return createHash2("sha256").update(c).digest("hex").slice(0, SYNC_CONFIG.HASH_HEX_LENGTH);
|
|
1954
2171
|
}
|
|
2172
|
+
function classifyPatchCandidate(currentContent, incomingContent, storedHash) {
|
|
2173
|
+
const currentHash = hash8(currentContent);
|
|
2174
|
+
if (currentHash === hash8(incomingContent)) return "same";
|
|
2175
|
+
if (storedHash && currentHash === storedHash) return "update";
|
|
2176
|
+
return "conflict";
|
|
2177
|
+
}
|
|
2178
|
+
function coreTargetsForAdapter2(target, magicDir = magicDirFor(target)) {
|
|
2179
|
+
return target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
|
|
2180
|
+
}
|
|
2181
|
+
function needsClaudeAgentHost2(adapters) {
|
|
2182
|
+
return adapters.includes("vscode") || adapters.includes("trae");
|
|
2183
|
+
}
|
|
2184
|
+
function resolvePatchConflictMode(yes = false, isTTY = Boolean(process.stdin.isTTY)) {
|
|
2185
|
+
if (yes) return "all";
|
|
2186
|
+
return isTTY ? "interactive" : "skip";
|
|
2187
|
+
}
|
|
1955
2188
|
function loadHashFile(root, magic) {
|
|
1956
2189
|
try {
|
|
1957
|
-
const raw = JSON.parse(
|
|
2190
|
+
const raw = JSON.parse(readFileSync11(resolve9(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
|
|
1958
2191
|
const normalized = {};
|
|
1959
2192
|
for (const [k, v] of Object.entries(raw)) normalized[normalizeRelPath(k)] = v;
|
|
1960
2193
|
return normalized;
|
|
@@ -1964,7 +2197,7 @@ function loadHashFile(root, magic) {
|
|
|
1964
2197
|
}
|
|
1965
2198
|
function loadVersionFile(root, magic) {
|
|
1966
2199
|
try {
|
|
1967
|
-
return
|
|
2200
|
+
return readFileSync11(resolve9(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
1968
2201
|
} catch {
|
|
1969
2202
|
return "";
|
|
1970
2203
|
}
|
|
@@ -1972,21 +2205,40 @@ function loadVersionFile(root, magic) {
|
|
|
1972
2205
|
function saveVersionFile(root, magic, version2) {
|
|
1973
2206
|
writeFileSync7(resolve9(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
|
|
1974
2207
|
}
|
|
2208
|
+
function loadTemplateVersion(root) {
|
|
2209
|
+
const candidates = [
|
|
2210
|
+
resolve9(root, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"),
|
|
2211
|
+
resolve9(import.meta.dirname, "../templates/.add-coder-src-hash.json"),
|
|
2212
|
+
resolve9(import.meta.dirname, "../../../templates/.add-coder-src-hash.json")
|
|
2213
|
+
];
|
|
2214
|
+
for (const candidate of candidates) {
|
|
2215
|
+
try {
|
|
2216
|
+
return JSON.parse(readFileSync11(candidate, "utf-8"))._version ?? "";
|
|
2217
|
+
} catch {
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
return "";
|
|
2221
|
+
}
|
|
1975
2222
|
function saveHashFile(root, magic, files) {
|
|
1976
2223
|
const m = {};
|
|
1977
2224
|
for (const [p, c] of files) m[p] = c;
|
|
1978
2225
|
writeFileSync7(resolve9(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
|
|
1979
2226
|
}
|
|
1980
|
-
function mergeFullHash(outHash, candidates, readDiskHash) {
|
|
2227
|
+
function mergeFullHash(outHash, candidates, readDiskHash, preserveKeys = /* @__PURE__ */ new Set()) {
|
|
1981
2228
|
const finalHash = /* @__PURE__ */ new Map();
|
|
1982
2229
|
for (const [k, v] of Object.entries(outHash)) finalHash.set(k, v);
|
|
1983
2230
|
for (const { relPath, absPath } of candidates) {
|
|
1984
2231
|
const key = normalizeRelPath(relPath);
|
|
2232
|
+
if (preserveKeys.has(key)) continue;
|
|
1985
2233
|
const h = readDiskHash(absPath);
|
|
1986
2234
|
if (h !== null) finalHash.set(key, h);
|
|
1987
2235
|
}
|
|
1988
2236
|
return finalHash;
|
|
1989
2237
|
}
|
|
2238
|
+
function pruneHashToCandidates(hashes, candidatePaths) {
|
|
2239
|
+
const allowed = new Set([...candidatePaths].map(normalizeRelPath));
|
|
2240
|
+
return new Map([...hashes].filter(([key]) => allowed.has(normalizeRelPath(key))));
|
|
2241
|
+
}
|
|
1990
2242
|
async function maybeModelDownload(options) {
|
|
1991
2243
|
let model;
|
|
1992
2244
|
try {
|
|
@@ -2010,30 +2262,35 @@ async function maybeModelDownload(options) {
|
|
|
2010
2262
|
async function syncCommand(options = {}) {
|
|
2011
2263
|
const projectRoot = process.cwd();
|
|
2012
2264
|
const target = resolveAdapter2(projectRoot, options.adapter);
|
|
2013
|
-
const magicDir =
|
|
2265
|
+
const magicDir = magicDirFor(target);
|
|
2014
2266
|
const config = await loadConfig(projectRoot);
|
|
2015
2267
|
config.projectRoot = projectRoot;
|
|
2016
2268
|
config.magicDir = magicDir;
|
|
2017
|
-
const
|
|
2018
|
-
const
|
|
2019
|
-
const allFiles = /* @__PURE__ */ new Map();
|
|
2020
|
-
for (const [relPath, content] of coreFiles) {
|
|
2021
|
-
for (const t of CORE_TARGETS) {
|
|
2022
|
-
const targetPath = relPath.replace(/^\.add/, t);
|
|
2023
|
-
if (!allFiles.has(targetPath)) allFiles.set(targetPath, content);
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2269
|
+
const coreTargets = coreTargetsForAdapter2(target, magicDir);
|
|
2270
|
+
const allFiles = renderCoreForTargets(config, false, coreTargets);
|
|
2026
2271
|
const resolved = resolveAdapters2(target);
|
|
2027
2272
|
for (const adapter of resolved) {
|
|
2028
2273
|
const renderFn = ADAPTER_RENDERERS2[adapter];
|
|
2029
2274
|
if (renderFn) {
|
|
2030
|
-
const
|
|
2275
|
+
const adapterMagicDir = magicDirFor(adapter);
|
|
2276
|
+
const adapterFiles = renderFn(
|
|
2277
|
+
{ ...config, magicDir: adapterMagicDir },
|
|
2278
|
+
projectRoot,
|
|
2279
|
+
false,
|
|
2280
|
+
adapterMagicDir
|
|
2281
|
+
);
|
|
2031
2282
|
for (const [p, c] of adapterFiles) allFiles.set(p, c);
|
|
2032
2283
|
console.log(`${adapter} adapter: ${adapterFiles.size} \u6587\u4EF6`);
|
|
2033
2284
|
}
|
|
2034
2285
|
}
|
|
2035
|
-
if (
|
|
2036
|
-
const
|
|
2286
|
+
if (needsClaudeAgentHost2(resolved)) {
|
|
2287
|
+
const claudeMagicDir = magicDirFor("claude");
|
|
2288
|
+
const claudeFiles = renderAdapter(
|
|
2289
|
+
{ ...config, magicDir: claudeMagicDir },
|
|
2290
|
+
projectRoot,
|
|
2291
|
+
false,
|
|
2292
|
+
claudeMagicDir
|
|
2293
|
+
);
|
|
2037
2294
|
for (const [p, c] of claudeFiles) allFiles.set(p, c);
|
|
2038
2295
|
console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
|
|
2039
2296
|
}
|
|
@@ -2048,12 +2305,7 @@ async function syncCommand(options = {}) {
|
|
|
2048
2305
|
candidates.set(p, c);
|
|
2049
2306
|
}
|
|
2050
2307
|
const outHash = loadHashFile(projectRoot, magicDir);
|
|
2051
|
-
const
|
|
2052
|
-
let npmVersion = "";
|
|
2053
|
-
try {
|
|
2054
|
-
npmVersion = JSON.parse(readFileSync10(srcHashPath, "utf-8"))._version ?? "";
|
|
2055
|
-
} catch {
|
|
2056
|
-
}
|
|
2308
|
+
const npmVersion = loadTemplateVersion(projectRoot);
|
|
2057
2309
|
const installedVersion = loadVersionFile(projectRoot, magicDir);
|
|
2058
2310
|
const isFirstPatch = !installedVersion;
|
|
2059
2311
|
const isUpgrade = installedVersion && npmVersion && installedVersion !== npmVersion;
|
|
@@ -2072,18 +2324,19 @@ async function syncCommand(options = {}) {
|
|
|
2072
2324
|
for (const [relPath, content] of candidates) {
|
|
2073
2325
|
const key = normalizeRelPath(relPath);
|
|
2074
2326
|
const absPath = resolve9(projectRoot, relPath);
|
|
2075
|
-
if (!
|
|
2327
|
+
if (!existsSync13(absPath)) {
|
|
2076
2328
|
missingFiles.set(key, content);
|
|
2077
2329
|
} else if (establishBaseline) {
|
|
2078
2330
|
missingFiles.set(key, content);
|
|
2079
2331
|
} else {
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2332
|
+
const classification = classifyPatchCandidate(
|
|
2333
|
+
readFileSync11(absPath, "utf-8"),
|
|
2334
|
+
content,
|
|
2335
|
+
outHash[key]
|
|
2336
|
+
);
|
|
2337
|
+
if (classification === "same") sameCount++;
|
|
2338
|
+
else if (classification === "update") missingFiles.set(key, content);
|
|
2339
|
+
else conflictFiles.set(key, content);
|
|
2087
2340
|
}
|
|
2088
2341
|
}
|
|
2089
2342
|
console.log(`patch: ${allFiles.size} \u2192 skip user ${skipped} | missing ${missingFiles.size} | conflict ${conflictFiles.size} | same ${sameCount}`);
|
|
@@ -2091,23 +2344,39 @@ async function syncCommand(options = {}) {
|
|
|
2091
2344
|
const r = await writeFiles2(projectRoot, missingFiles, { force: true, yes: true });
|
|
2092
2345
|
console.log(` missing: \u65B0\u5EFA ${r.created}`);
|
|
2093
2346
|
}
|
|
2347
|
+
const unresolvedConflictKeys = /* @__PURE__ */ new Set();
|
|
2094
2348
|
if (conflictFiles.size > 0) {
|
|
2095
|
-
const
|
|
2349
|
+
const conflictMode = resolvePatchConflictMode(options.yes);
|
|
2350
|
+
let sel = /* @__PURE__ */ new Map();
|
|
2351
|
+
if (conflictMode === "all") {
|
|
2352
|
+
sel = conflictFiles;
|
|
2353
|
+
} else if (conflictMode === "interactive") {
|
|
2354
|
+
sel = await selectFiles(projectRoot, conflictFiles);
|
|
2355
|
+
} else {
|
|
2356
|
+
console.warn(` conflict: \u975E\u4EA4\u4E92\u7EC8\u7AEF\u9ED8\u8BA4\u8DF3\u8FC7 ${conflictFiles.size} \u4E2A\u51B2\u7A81\u6587\u4EF6\uFF1B\u5982\u9700\u5168\u90E8\u8986\u76D6\u8BF7\u91CD\u8DD1\u5E76\u663E\u5F0F\u4F20\u5165 --yes`);
|
|
2357
|
+
}
|
|
2096
2358
|
if (sel.size > 0) {
|
|
2097
2359
|
const r = await writeFiles2(projectRoot, sel, { force: true, yes: true });
|
|
2098
2360
|
console.log(` conflict: \u8986\u76D6 ${r.overwritten}, \u8DF3\u8FC7 ${conflictFiles.size - sel.size}`);
|
|
2099
|
-
} else {
|
|
2361
|
+
} else if (conflictMode === "interactive") {
|
|
2100
2362
|
console.log(` conflict: \u7528\u6237\u53D6\u6D88\uFF0C${conflictFiles.size} \u4E2A\u6587\u4EF6\u672A\u5199\u5165`);
|
|
2101
2363
|
}
|
|
2364
|
+
for (const key of conflictFiles.keys()) {
|
|
2365
|
+
if (!sel.has(key)) unresolvedConflictKeys.add(key);
|
|
2366
|
+
}
|
|
2102
2367
|
}
|
|
2103
2368
|
if (missingFiles.size === 0 && conflictFiles.size === 0) {
|
|
2104
2369
|
console.log(SYNC_CONFIG.PROMPT_PATCH_DONE);
|
|
2105
2370
|
}
|
|
2106
|
-
|
|
2371
|
+
let finalHash = mergeFullHash(
|
|
2107
2372
|
outHash,
|
|
2108
2373
|
[...candidates].map(([relPath]) => ({ relPath, absPath: resolve9(projectRoot, relPath) })),
|
|
2109
|
-
(absPath) =>
|
|
2374
|
+
(absPath) => existsSync13(absPath) ? hash8(readFileSync11(absPath, "utf-8")) : null,
|
|
2375
|
+
unresolvedConflictKeys
|
|
2110
2376
|
);
|
|
2377
|
+
if (target === "codex") {
|
|
2378
|
+
finalHash = pruneHashToCandidates(finalHash, candidates.keys());
|
|
2379
|
+
}
|
|
2111
2380
|
saveHashFile(projectRoot, magicDir, finalHash);
|
|
2112
2381
|
saveVersionFile(projectRoot, magicDir, npmVersion);
|
|
2113
2382
|
await checkPrismaDiff(projectRoot, options);
|
|
@@ -2117,7 +2386,7 @@ async function syncCommand(options = {}) {
|
|
|
2117
2386
|
}
|
|
2118
2387
|
const missing = /* @__PURE__ */ new Map();
|
|
2119
2388
|
for (const [relPath, content] of allFiles) {
|
|
2120
|
-
if (!
|
|
2389
|
+
if (!existsSync13(resolve9(projectRoot, relPath))) {
|
|
2121
2390
|
missing.set(relPath, content);
|
|
2122
2391
|
}
|
|
2123
2392
|
}
|
|
@@ -2141,7 +2410,7 @@ async function syncCommand(options = {}) {
|
|
|
2141
2410
|
}
|
|
2142
2411
|
function printMigrateGuidance(targetPath, changed) {
|
|
2143
2412
|
const g = SYNC_PRISMA_CONFIG.POST_SYNC;
|
|
2144
|
-
const isManaged =
|
|
2413
|
+
const isManaged = existsSync13(resolve9(dirname4(targetPath), "migrations"));
|
|
2145
2414
|
const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
|
|
2146
2415
|
console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
|
|
2147
2416
|
for (const a of actions) {
|
|
@@ -2161,20 +2430,26 @@ function printMigrateGuidance(targetPath, changed) {
|
|
|
2161
2430
|
}
|
|
2162
2431
|
console.log(` \u26A0\uFE0F \u6240\u6709\u573A\u666F\u6536\u5C3E: ${g.FINAL}`);
|
|
2163
2432
|
}
|
|
2164
|
-
async function ensureAtlasCapability(projectRoot) {
|
|
2433
|
+
async function ensureAtlasCapability(projectRoot, yes = false) {
|
|
2165
2434
|
const bin = resolveAtlasBin(projectRoot);
|
|
2166
2435
|
if (bin) {
|
|
2167
2436
|
console.log(` \u2705 Atlas \u80FD\u529B\u5C31\u7EEA: ${bin}`);
|
|
2168
2437
|
return;
|
|
2169
2438
|
}
|
|
2439
|
+
if (process.platform === "win32") {
|
|
2440
|
+
console.warn(" \u26A0\uFE0F Atlas \u5728 Windows \u4E0D\u53EF\u7528\uFF08@ariga/atlas install.js \u4E0D\u652F\u6301 win32\uFF09");
|
|
2441
|
+
console.log(" \u964D\u7EA7\u8DEF\u5F84\uFF1A\u6570\u636E\u5E93\u540C\u6B65\u4F7F\u7528 prisma-diff\uFF08\u514D shadow\uFF09\uFF1B\u5982\u9700 Atlas \u8BF7\u5728 WSL \u73AF\u5883\u624B\u52A8\u5B89\u88C5");
|
|
2442
|
+
console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2170
2445
|
console.warn(" \u26A0\uFE0F Atlas \u4E0D\u53EF\u7528\u2014\u2014\u6570\u636E\u5E93\u540C\u6B65\uFF08Atlas diff/apply\uFF09\u80FD\u529B\u7F3A\u5931");
|
|
2171
|
-
const a = await ask(" \u662F\u5426\u81EA\u52A8\u5B89\u88C5 @ariga/atlas\uFF08npm \u4F9D\u8D56\uFF0C\u8D70 registry\uFF09\uFF1F[Y/n] ");
|
|
2446
|
+
const a = yes ? "y" : await ask(" \u662F\u5426\u81EA\u52A8\u5B89\u88C5 @ariga/atlas\uFF08npm \u4F9D\u8D56\uFF0C\u8D70 registry\uFF09\uFF1F[Y/n] ");
|
|
2172
2447
|
if (a === "n" || a === "no") {
|
|
2173
2448
|
console.log(" \u5DF2\u8DF3\u8FC7\u3002\u6570\u636E\u5E93\u540C\u6B65\u5C06\u964D\u7EA7 prisma-diff\uFF08\u514D shadow\uFF09\uFF1B\u53EF\u968F\u65F6\u8865\u88C5\u6062\u590D Atlas");
|
|
2174
2449
|
console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
|
|
2175
2450
|
return;
|
|
2176
2451
|
}
|
|
2177
|
-
const pm =
|
|
2452
|
+
const pm = existsSync13(resolve9(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
2178
2453
|
console.log(` \u5B89\u88C5 ${pm} add -D @ariga/atlas ...`);
|
|
2179
2454
|
const r = runCommand(pm, ["add", "-D", "@ariga/atlas"], { cwd: projectRoot, timeout: 18e4 });
|
|
2180
2455
|
if (r.status !== 0) {
|
|
@@ -2187,8 +2462,8 @@ async function ensureAtlasCapability(projectRoot) {
|
|
|
2187
2462
|
}
|
|
2188
2463
|
function checkHostAtlasSegment(projectRoot) {
|
|
2189
2464
|
const script = resolve9(projectRoot, "scripts", "db-ensure.sh");
|
|
2190
|
-
if (!
|
|
2191
|
-
const content =
|
|
2465
|
+
if (!existsSync13(script)) return;
|
|
2466
|
+
const content = readFileSync11(script, "utf-8");
|
|
2192
2467
|
if (content.includes("atlas_sync")) return;
|
|
2193
2468
|
console.warn(" \u26A0\uFE0F \u5BBF\u4E3B scripts/db-ensure.sh \u672A\u5305\u542B Atlas \u540C\u6B65\u6BB5\uFF08\u65E5\u5E38 db-ensure \u5C06\u7F3A\u5C11 ADD \u6CBB\u7406\u6A21\u578B\u540C\u6B65\uFF09");
|
|
2194
2469
|
console.warn(" \u804C\u8D23\u8FB9\u754C: add-coder \u53EA\u540C\u6B65 ADD \u6CBB\u7406\u6A21\u578B(7 \u8868)\uFF1B\u5BBF\u4E3B\u4E1A\u52A1\u8868 diff \u63A8\u8350 Atlas \u4F46\u4E0D\u5F3A\u6C42");
|
|
@@ -2202,13 +2477,13 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2202
2477
|
if (!options.patch) return;
|
|
2203
2478
|
const basePath = resolve9(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
|
|
2204
2479
|
const targetPath = resolve9(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
|
|
2205
|
-
if (!
|
|
2480
|
+
if (!existsSync13(basePath)) {
|
|
2206
2481
|
console.log(`
|
|
2207
2482
|
\u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
|
|
2208
2483
|
console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
|
|
2209
2484
|
return;
|
|
2210
2485
|
}
|
|
2211
|
-
await ensureAtlasCapability(projectRoot);
|
|
2486
|
+
await ensureAtlasCapability(projectRoot, options.yes);
|
|
2212
2487
|
checkHostAtlasSegment(projectRoot);
|
|
2213
2488
|
const result = diffPrisma(basePath, targetPath);
|
|
2214
2489
|
if (!result.hasDiff) {
|
|
@@ -2216,7 +2491,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2216
2491
|
\u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
|
|
2217
2492
|
return;
|
|
2218
2493
|
}
|
|
2219
|
-
const targetExists =
|
|
2494
|
+
const targetExists = existsSync13(targetPath);
|
|
2220
2495
|
let modifiedCount = 0;
|
|
2221
2496
|
console.log(`
|
|
2222
2497
|
\u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
|
|
@@ -2235,7 +2510,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2235
2510
|
}
|
|
2236
2511
|
},
|
|
2237
2512
|
async confirm() {
|
|
2238
|
-
const ans = await ask(`
|
|
2513
|
+
const ans = options.yes ? "a" : await ask(`
|
|
2239
2514
|
\u8F93\u5165\u7F16\u53F7\u9009\u62E9\u6027\u6CE8\u5165\uFF08\u5982 1,3\uFF09\uFF0Ca \u5168\u90E8\u6CE8\u5165\uFF0C\u56DE\u8F66\u8DF3\u8FC7: `);
|
|
2240
2515
|
if (ans === "a" || ans === "all") {
|
|
2241
2516
|
selected = [...result.missing];
|
|
@@ -2273,7 +2548,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2273
2548
|
}
|
|
2274
2549
|
},
|
|
2275
2550
|
async confirm() {
|
|
2276
|
-
const ans = await ask(` \u662F\u5426\u7528\u57FA\u51C6\u5B9A\u4E49\u8986\u76D6\u8FD9\u4E9B\u51B2\u7A81\u5B57\u6BB5\uFF1F(y/n): `);
|
|
2551
|
+
const ans = options.yes ? "y" : await ask(` \u662F\u5426\u7528\u57FA\u51C6\u5B9A\u4E49\u8986\u76D6\u8FD9\u4E9B\u51B2\u7A81\u5B57\u6BB5\uFF1F(y/n): `);
|
|
2277
2552
|
return ans === "y" || ans === "yes";
|
|
2278
2553
|
},
|
|
2279
2554
|
execute() {
|
|
@@ -2294,7 +2569,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2294
2569
|
}
|
|
2295
2570
|
},
|
|
2296
2571
|
async confirm() {
|
|
2297
|
-
const ans = await ask(` \u662F\u5426\u8865\u5145\u8FD9\u4E9B\u7F3A\u5931\u5B57\u6BB5\uFF1F(y/n): `);
|
|
2572
|
+
const ans = options.yes ? "y" : await ask(` \u662F\u5426\u8865\u5145\u8FD9\u4E9B\u7F3A\u5931\u5B57\u6BB5\uFF1F(y/n): `);
|
|
2298
2573
|
return ans === "y" || ans === "yes";
|
|
2299
2574
|
},
|
|
2300
2575
|
execute() {
|
|
@@ -2335,7 +2610,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2335
2610
|
} else {
|
|
2336
2611
|
console.log(`
|
|
2337
2612
|
\u76EE\u6807 schema \u6587\u4EF6\u4E0D\u5B58\u5728: ${result.targetPath}`);
|
|
2338
|
-
mkdirSync7(
|
|
2613
|
+
mkdirSync7(dirname4(targetPath), { recursive: true });
|
|
2339
2614
|
writeFileSync7(targetPath, "// add.prisma \u2014 ADD \u6CBB\u7406\u6A21\u578B\n\n", "utf-8");
|
|
2340
2615
|
const n = injectMissingModels(targetPath, result.missing);
|
|
2341
2616
|
console.log(` \u2705 \u5DF2\u521B\u5EFA\u5E76\u6CE8\u5165 ${n} \u4E2A\u6A21\u578B/\u679A\u4E3E`);
|
|
@@ -2367,7 +2642,7 @@ async function handleDiffAction(action, ctx) {
|
|
|
2367
2642
|
}
|
|
2368
2643
|
}
|
|
2369
2644
|
function injectMissingModels(targetPath, models) {
|
|
2370
|
-
let content =
|
|
2645
|
+
let content = readFileSync11(targetPath, "utf-8");
|
|
2371
2646
|
content = content.replace(/\n*$/, "\n");
|
|
2372
2647
|
content += `
|
|
2373
2648
|
// ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
|
|
@@ -2380,7 +2655,7 @@ function injectMissingModels(targetPath, models) {
|
|
|
2380
2655
|
return models.length;
|
|
2381
2656
|
}
|
|
2382
2657
|
function getBaseFieldLines(basePath, modelName) {
|
|
2383
|
-
const content =
|
|
2658
|
+
const content = readFileSync11(basePath, "utf-8");
|
|
2384
2659
|
const blocks = parseSchemaBlocks(content);
|
|
2385
2660
|
const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
2386
2661
|
if (!block) return {};
|
|
@@ -2403,7 +2678,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
2403
2678
|
console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u5728\u57FA\u51C6\u4E2D\u672A\u627E\u5230\u5B57\u6BB5\u5B9A\u4E49\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
|
|
2404
2679
|
return 0;
|
|
2405
2680
|
}
|
|
2406
|
-
const content =
|
|
2681
|
+
const content = readFileSync11(targetPath, "utf-8");
|
|
2407
2682
|
const lines = content.split("\n");
|
|
2408
2683
|
const blocks = parseSchemaBlocks(content);
|
|
2409
2684
|
const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
@@ -2450,7 +2725,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
2450
2725
|
function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
2451
2726
|
const baseFields = getBaseFieldLines(basePath, modelName);
|
|
2452
2727
|
if (Object.keys(baseFields).length === 0) return 0;
|
|
2453
|
-
let content =
|
|
2728
|
+
let content = readFileSync11(targetPath, "utf-8");
|
|
2454
2729
|
let count = 0;
|
|
2455
2730
|
for (const { fieldName } of conflicts) {
|
|
2456
2731
|
const baseLine = baseFields[fieldName];
|
|
@@ -2466,7 +2741,7 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
|
2466
2741
|
}
|
|
2467
2742
|
|
|
2468
2743
|
// src/cli/commands/status.ts
|
|
2469
|
-
import { existsSync as
|
|
2744
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2470
2745
|
import { resolve as resolve10 } from "path";
|
|
2471
2746
|
async function statusCommand() {
|
|
2472
2747
|
const projectRoot = process.cwd();
|
|
@@ -2476,7 +2751,7 @@ async function statusCommand() {
|
|
|
2476
2751
|
const missing = [];
|
|
2477
2752
|
const present = [];
|
|
2478
2753
|
for (const [relPath] of coreFiles) {
|
|
2479
|
-
if (
|
|
2754
|
+
if (existsSync14(resolve10(projectRoot, relPath))) {
|
|
2480
2755
|
present.push(relPath);
|
|
2481
2756
|
} else {
|
|
2482
2757
|
missing.push(relPath);
|
|
@@ -2494,32 +2769,30 @@ async function statusCommand() {
|
|
|
2494
2769
|
}
|
|
2495
2770
|
|
|
2496
2771
|
// src/cli/commands/stack.ts
|
|
2497
|
-
import { readdirSync as
|
|
2498
|
-
import { resolve as resolve11, join as
|
|
2772
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync8 } from "fs";
|
|
2773
|
+
import { resolve as resolve11, join as join9 } from "path";
|
|
2499
2774
|
import { createHash as createHash3 } from "crypto";
|
|
2500
|
-
var MAGIC_DIR_MAP3 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
|
|
2501
2775
|
var HASH_OUTPUT_FILE = ".add-coder-hash.json";
|
|
2502
2776
|
function hash82(c) {
|
|
2503
2777
|
return createHash3("sha256").update(c).digest("hex").slice(0, 8);
|
|
2504
2778
|
}
|
|
2505
2779
|
function resolveMagicDir(projectRoot, specified) {
|
|
2506
2780
|
if (specified) {
|
|
2507
|
-
|
|
2508
|
-
return MAGIC_DIR_MAP3[specified];
|
|
2781
|
+
return magicDirFor(specified);
|
|
2509
2782
|
}
|
|
2510
2783
|
const detected = detectIDE2(projectRoot);
|
|
2511
2784
|
const adapter = detected !== "auto" ? detected : "qoder";
|
|
2512
|
-
return
|
|
2785
|
+
return magicDirFor(adapter);
|
|
2513
2786
|
}
|
|
2514
2787
|
function listCustomProfiles(projectRoot, magicDir, registryNames) {
|
|
2515
2788
|
const dir = resolve11(projectRoot, magicDir, "rules", "profiles");
|
|
2516
|
-
if (!
|
|
2517
|
-
return
|
|
2789
|
+
if (!existsSync15(dir)) return [];
|
|
2790
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
|
|
2518
2791
|
}
|
|
2519
2792
|
function profileExists(projectRoot, magicDir, name) {
|
|
2520
2793
|
const registry = loadProfileRegistry();
|
|
2521
2794
|
if (registry.some((p) => p.name === name)) return true;
|
|
2522
|
-
return
|
|
2795
|
+
return existsSync15(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
|
|
2523
2796
|
}
|
|
2524
2797
|
function stackCommand(sub, name, options = {}) {
|
|
2525
2798
|
const projectRoot = process.cwd();
|
|
@@ -2557,11 +2830,11 @@ function stackCommand(sub, name, options = {}) {
|
|
|
2557
2830
|
return;
|
|
2558
2831
|
}
|
|
2559
2832
|
const profilePath = resolve11(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
|
|
2560
|
-
const stat =
|
|
2833
|
+
const stat = existsSync15(profilePath) ? readFileSync12(profilePath, "utf-8").length : 0;
|
|
2561
2834
|
console.log(`\u6280\u672F\u6808: ${current}`);
|
|
2562
|
-
console.log(`profile \u6587\u4EF6: ${profilePath}${
|
|
2835
|
+
console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync15(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
|
|
2563
2836
|
try {
|
|
2564
|
-
const raw = JSON.parse(
|
|
2837
|
+
const raw = JSON.parse(readFileSync12(resolve11(projectRoot, magicDir, "stack.json"), "utf-8"));
|
|
2565
2838
|
if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
|
|
2566
2839
|
} catch {
|
|
2567
2840
|
}
|
|
@@ -2587,8 +2860,8 @@ function buildConfig(projectRoot, magicDir, stack) {
|
|
|
2587
2860
|
};
|
|
2588
2861
|
try {
|
|
2589
2862
|
const pkgPath = resolve11(projectRoot, "package.json");
|
|
2590
|
-
if (
|
|
2591
|
-
const pkg = JSON.parse(
|
|
2863
|
+
if (existsSync15(pkgPath)) {
|
|
2864
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
2592
2865
|
if (pkg.name) config.projectName = pkg.name;
|
|
2593
2866
|
}
|
|
2594
2867
|
} catch {
|
|
@@ -2604,7 +2877,8 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2604
2877
|
}
|
|
2605
2878
|
const config = buildConfig(projectRoot, magicDir, name);
|
|
2606
2879
|
saveStack(projectRoot, magicDir, name);
|
|
2607
|
-
const
|
|
2880
|
+
const coreTargets = magicDir === magicDirFor("codex") ? [magicDir] : [ADD_DIR, magicDir];
|
|
2881
|
+
const coreFiles = renderCoreForTargets(config, false, coreTargets);
|
|
2608
2882
|
const stackRelated = /* @__PURE__ */ new Map();
|
|
2609
2883
|
for (const [relPath, content] of coreFiles) {
|
|
2610
2884
|
const rp = normalizeRelPath(relPath);
|
|
@@ -2614,18 +2888,16 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2614
2888
|
}
|
|
2615
2889
|
const hashMap = {};
|
|
2616
2890
|
try {
|
|
2617
|
-
Object.assign(hashMap, JSON.parse(
|
|
2891
|
+
Object.assign(hashMap, JSON.parse(readFileSync12(resolve11(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
|
|
2618
2892
|
} catch {
|
|
2619
2893
|
}
|
|
2620
2894
|
let written = 0;
|
|
2621
2895
|
for (const [relPath, content] of stackRelated) {
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
written++;
|
|
2628
|
-
}
|
|
2896
|
+
const targetPath = resolve11(projectRoot, relPath);
|
|
2897
|
+
mkdirSync8(join9(targetPath, ".."), { recursive: true });
|
|
2898
|
+
writeFileSync8(targetPath, content, "utf-8");
|
|
2899
|
+
hashMap[relPath] = hash82(content);
|
|
2900
|
+
written++;
|
|
2629
2901
|
}
|
|
2630
2902
|
const fail = (msg) => {
|
|
2631
2903
|
console.error(`\u2717 stack set \u5931\u8D25: ${msg}`);
|
|
@@ -2634,23 +2906,23 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2634
2906
|
const registry = loadProfileRegistry();
|
|
2635
2907
|
const isBuiltin = registry.some((p) => p.name === name);
|
|
2636
2908
|
const profilePathMagic = resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`);
|
|
2637
|
-
const profilePathAdd = resolve11(projectRoot,
|
|
2909
|
+
const profilePathAdd = resolve11(projectRoot, ADD_DIR, "rules", "profiles", `${name}-profile.md`);
|
|
2638
2910
|
const projectRulesPath = resolve11(projectRoot, magicDir, "rules", "project_rules.md");
|
|
2639
|
-
const projectRulesContent =
|
|
2911
|
+
const projectRulesContent = existsSync15(projectRulesPath) ? readFileSync12(projectRulesPath, "utf-8") : "";
|
|
2640
2912
|
if (written === 0) fail(`\u672A\u6E32\u67D3\u4EFB\u4F55 stack \u76F8\u5173\u6587\u4EF6\uFF08${name}\uFF09\u2014\u2014Windows \u8DEF\u5F84\u5339\u914D\u5931\u6548\u9057\u7559\u95EE\u9898`);
|
|
2641
|
-
if (isBuiltin && !
|
|
2642
|
-
if (!
|
|
2913
|
+
if (magicDir !== magicDirFor("codex") && isBuiltin && !existsSync15(profilePathAdd)) fail(`profile \u672A\u5199\u5165 ${ADD_DIR}: ${profilePathAdd}`);
|
|
2914
|
+
if (!existsSync15(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
|
|
2643
2915
|
if (!projectRulesContent.includes("**\u5F53\u524D\u6280\u672F\u6808**") || !projectRulesContent.includes(name)) fail(`project_rules.md \u672A\u5305\u542B ${name} \u5F15\u7528`);
|
|
2644
2916
|
writeFileSync8(resolve11(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
2645
2917
|
console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
|
|
2646
2918
|
console.log(` ${magicDir}/stack.json \u2192 ${name}`);
|
|
2647
|
-
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${
|
|
2919
|
+
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync15(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
|
|
2648
2920
|
console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
|
|
2649
2921
|
}
|
|
2650
2922
|
|
|
2651
2923
|
// src/cli/index.ts
|
|
2652
2924
|
var { version } = JSON.parse(
|
|
2653
|
-
|
|
2925
|
+
readFileSync13(new URL("../package.json", import.meta.url), "utf-8")
|
|
2654
2926
|
);
|
|
2655
2927
|
async function modelDownloadCommand(options) {
|
|
2656
2928
|
try {
|
|
@@ -2665,8 +2937,8 @@ async function modelDownloadCommand(options) {
|
|
|
2665
2937
|
}
|
|
2666
2938
|
var program = new Command();
|
|
2667
2939
|
program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
|
|
2668
|
-
program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").option("--skip-model", "\u8DF3\u8FC7 embedding \u6A21\u578B\u9884\u4E0B\u8F7D").action(initCommand);
|
|
2669
|
-
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").option("--model", "\u68C0\u6D4B\u5230\u7F3A\u5931\u65F6\u4E0B\u8F7D embedding \u6A21\u578B").action(syncCommand);
|
|
2940
|
+
program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").option("--skip-model", "\u8DF3\u8FC7 embedding \u6A21\u578B\u9884\u4E0B\u8F7D").option("--print-mcp-config", "Codex: stdout \u8F93\u51FA config.toml \u7247\u6BB5\uFF08\u4E0D\u5199\u76D8\uFF0C\u4E0D\u521D\u59CB\u5316\u9879\u76EE\uFF09").option("--write-user-config", "Codex: \u663E\u5F0F\u786E\u8BA4\u540E\u5199\u5165 ~/.codex/config.toml\uFF08\u5148\u5907\u4EFD\uFF09").action(initCommand);
|
|
2941
|
+
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").option("--model", "\u68C0\u6D4B\u5230\u7F3A\u5931\u65F6\u4E0B\u8F7D embedding \u6A21\u578B").option("--yes", "\u5168\u90E8\u786E\u8BA4\u8DF3\u8FC7\u8BE2\u95EE\uFF08RPT-06/#19\uFF09").action(syncCommand);
|
|
2670
2942
|
program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
|
|
2671
2943
|
program.command("stack").description("\u7BA1\u7406\u6280\u672F\u6808\u7EA6\u675F profile\uFF08list / set <name> / show / --clear\uFF09").argument("[sub]", "list | set | show").argument("[name]", "set <name>: profile \u540D\uFF08\u5185\u7F6E\u6216\u81EA\u5B9A\u4E49\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--clear", "\u6E05\u9664\u6280\u672F\u6808\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF09").action(
|
|
2672
2944
|
(sub, name, options) => stackCommand(sub, name, options)
|