create-dwy 0.19.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/src/cli-help.js +31 -2
  4. package/src/sync-all.js +168 -577
  5. package/src/sync-codex.js +42 -28
  6. package/src/sync-cursor.js +0 -25
  7. package/src/sync-global-skills.js +2 -2
  8. package/src/sync-opencode.js +7 -33
  9. package/src/sync.js +59 -226
  10. package/src/tab-multiselect.js +15 -53
  11. package/src/utils.js +0 -1
  12. package/templates/ai-tools/AGENTS.md +223 -0
  13. package/templates/ai-tools/CHANGELOG.md +195 -0
  14. package/templates/ai-tools/VERSION +1 -1
  15. package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +5 -4
  16. package/templates/ai-tools/rules/Android/dwy-android-core.md +147 -13
  17. package/templates/ai-tools/rules/Android/dwy-android-layering.md +142 -0
  18. package/templates/ai-tools/rules/Android/dwy-android-mvp.md +55 -0
  19. package/templates/ai-tools/rules/Android/dwy-android-support-only.md +2 -0
  20. package/templates/ai-tools/rules/Docker/dwy-docker.md +23 -1
  21. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +2 -0
  22. package/templates/ai-tools/rules/Flutter/dwy-flutter-testing.md +1 -1
  23. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +12 -8
  24. package/templates/ai-tools/rules/Python/dwy-python-backend.md +4 -43
  25. package/templates/ai-tools/rules/Python/dwy-python-layering.md +169 -0
  26. package/templates/ai-tools/rules/Vue/dwy-vue-core.md +11 -49
  27. package/templates/ai-tools/rules/Vue/dwy-vue-layering.md +153 -0
  28. package/templates/ai-tools/rules/iOS/dwy-apple-layering.md +133 -0
  29. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +2 -0
  30. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-code-craft.md +83 -0
  31. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +27 -66
  32. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +8 -219
  33. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +27 -123
  34. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prod-branch.md +31 -0
  35. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-postgres.md +17 -10
  36. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-redis.md +6 -3
  37. package/templates/ai-tools/skills/Docker/dwy-deploy-first/SKILL.md +14 -14
  38. package/templates/ai-tools/skills/Docker/dwy-deploy-first/references/chapter-01-mirrors.md +2 -2
  39. package/templates/ai-tools/skills/Docker/dwy-docker/SKILL.md +21 -13
  40. package/templates/ai-tools/skills/Docker/dwy-docker/references/ask-templates.md +13 -12
  41. package/templates/ai-tools/skills/Docker/dwy-docker/references/templates.md +3 -1
  42. package/templates/ai-tools/skills/Docker/dwy-docker/references/version-rules.md +10 -6
  43. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/query_dockerhub.py +21 -6
  44. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/scan_images.sh +2 -2
  45. package/templates/ai-tools/skills/Docker/dwy-mirror-source/SKILL.md +44 -30
  46. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/dockerfile-snippets.md +2 -0
  47. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/mirror-providers.md +8 -8
  48. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/apply_mirrors.py +14 -1
  49. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/check_mirrors.py +4 -0
  50. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/SKILL.md +8 -6
  51. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/python-sdk.md +3 -3
  52. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/stream-engine.md +2 -2
  53. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/anti-patterns.md +3 -1
  54. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/change-checklist.md +4 -4
  55. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/lethal-violations.md +5 -5
  56. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/perf-baselines.md +5 -5
  57. package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +2 -2
  58. package/templates/ai-tools/skills/Python/dwy-eapi/references/tasks-integration-guide.md +4 -4
  59. package/templates/ai-tools/skills/Python/dwy-eapi/v0/breaking.md +3 -0
  60. package/templates/ai-tools/skills/Python/dwy-eapi/v0/email-canonical.md +28 -3
  61. package/templates/ai-tools/skills/Python/dwy-eapi/v0/index.md +2 -2
  62. package/templates/ai-tools/skills/Python/dwy-eapi/v0/providers-email.md +12 -2
  63. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-design-guide.md +1 -1
  64. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-integration-guide.md +12 -0
  65. package/templates/ai-tools/skills/Vue/dwy-eui/shared/config-theme.md +2 -0
  66. package/templates/ai-tools/skills/Vue/dwy-eui/shared/form.md +10 -0
  67. package/templates/ai-tools/skills/Vue/dwy-eui/v2/index.md +5 -0
  68. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +11 -14
  69. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +1 -1
  70. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +1 -1
  71. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +1 -1
  72. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +32 -15
  73. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +3 -1
  74. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/release-workflow-template.yml +8 -5
  75. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +1 -1
  76. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +1 -1
  77. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/aso.md +2 -0
  78. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +1 -1
  79. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +4 -3
  80. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +1 -1
  81. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  82. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +45 -27
  83. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  84. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +1 -1
  85. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +1 -1
  86. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +1 -1
  87. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +1 -1
  88. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +8 -0
  89. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +7 -7
  90. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +63 -59
  91. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_base.sh +10 -1
  92. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_capacity.sh +21 -23
  93. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_db.sh +9 -1
  94. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +20 -24
  95. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_logs.sh +3 -3
  96. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +4 -2
  97. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +1 -1
  98. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +1 -0
  99. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +7 -5
  100. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +5 -3
  101. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +140 -0
  102. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-git-commit/SKILL.md +113 -0
  103. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-shared/SKILL.md +17 -14
  104. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-whiteboard-prototype/SKILL.md +1 -1
  105. package/templates/project/fullstack-monorepo/{CLAUDE.md.hbs → AGENTS.md.hbs} +1 -1
  106. package/src/sync-packs.js +0 -223
  107. package/src/sync-scene.js +0 -178
  108. package/templates/ai-tools/CLAUDE.md +0 -210
  109. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +0 -115
  110. /package/templates/project/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  111. /package/templates/project/fullstack-monorepo/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  112. /package/templates/project/fullstack-monorepo/frontend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  113. /package/templates/project/web/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
package/src/sync-codex.js CHANGED
@@ -4,9 +4,7 @@ import { chalk } from './utils.js'
4
4
  import {
5
5
  extractDescription,
6
6
  loadHookManifests,
7
- readBaselineDoc,
8
7
  scanSkills,
9
- scanRules,
10
8
  scanHooks,
11
9
  resolveSourceDir,
12
10
  copyFilter,
@@ -112,12 +110,37 @@ function upsertBaselineBlock(content, baselineBody) {
112
110
  return base ? `${block}\n\n${base}` : `${block}\n`
113
111
  }
114
112
 
115
- export async function syncProjectAgentsMd(sourceDir, projectDir, selectedRules, {
113
+ /** 更新 CLI 全局基线,仅替换 dwy 托管块,并清除旧版写入的项目 Rules 托管块。 */
114
+ export async function syncGlobalBaselineDoc(baseline, dest) {
115
+ if (!baseline.trim()) return 0
116
+ const current = await fs.pathExists(dest) ? await fs.readFile(dest, 'utf-8') : ''
117
+ const previous = current.trim() === baseline.trim() ? '' : current
118
+ const next = upsertManagedBlock(upsertBaselineBlock(previous, baseline), '')
119
+ if (next === current) return 0
120
+ await fs.ensureDir(path.dirname(dest))
121
+ await fs.writeFile(dest, next)
122
+ logAction(`global baseline → ${dest}`)
123
+ return 1
124
+ }
125
+
126
+ /** 迁移旧入口中的基线;无托管标记时只移除与当前模板完全一致的文件。 */
127
+ export async function removeLegacyBaseline(baseline, dest) {
128
+ if (!await fs.pathExists(dest)) return
129
+ if (!baseline.trim()) return
130
+ const current = await fs.readFile(dest, 'utf-8')
131
+ const next = current.trim() === baseline.trim() ? '' : upsertBaselineBlock(current, '')
132
+ if (next === current) return
133
+ if (next.trim()) await fs.writeFile(dest, next)
134
+ else await fs.remove(dest)
135
+ logAction(`legacy baseline → ${dest}`, 'red', '×')
136
+ }
137
+
138
+ /** 三种 CLI 共用项目根 AGENTS.md;只写所选 Rules,保留托管块之外的项目内容。 */
139
+ export async function syncProjectAgentsMd(projectDir, selectedRules, {
116
140
  preservedRuleNames = new Set(),
117
141
  } = {}) {
118
142
  const dest = path.join(projectDir, 'AGENTS.md')
119
143
  const current = await fs.pathExists(dest) ? await fs.readFile(dest, 'utf-8') : ''
120
- const baselineBody = await readBaselineDoc(sourceDir)
121
144
  const selectedRuleNames = new Set(selectedRules.map(rule => rule.name))
122
145
  const existingSections = parseManagedRuleSections(current)
123
146
  const sections = []
@@ -129,14 +152,11 @@ export async function syncProjectAgentsMd(sourceDir, projectDir, selectedRules,
129
152
  }
130
153
  const blockBody = sections.join('\n\n---\n\n')
131
154
 
132
- const withBaseline = upsertBaselineBlock(current, baselineBody)
133
- const next = upsertManagedBlock(withBaseline, blockBody)
155
+ const next = upsertManagedBlock(upsertBaselineBlock(current, ''), blockBody)
134
156
  if (next === current) return 0
157
+ await fs.ensureDir(path.dirname(dest))
135
158
  await fs.writeFile(dest, next)
136
- const parts = []
137
- if (baselineBody.trim()) parts.push('baseline')
138
- parts.push(blockBody.trim() ? `${selectedRules.length} rules` : 'rules removed')
139
- logAction(`AGENTS.md — ${parts.join(', ')}`)
159
+ logAction(`AGENTS.md — ${blockBody.trim() ? `${selectedRules.length} rules` : 'rules removed'}`)
140
160
  return 1
141
161
  }
142
162
 
@@ -174,7 +194,7 @@ async function copySkillsFlat(skills, proj) {
174
194
  for (const s of skills) {
175
195
  const dest = path.join(root, s.name)
176
196
  await fs.ensureDir(path.dirname(dest))
177
- await fs.copy(s.sourcePath, dest, { overwrite: true, filter: copyFilter })
197
+ await fs.copy(s.sourcePath, dest, { overwrite: true, filter: copyFilter(s.sourcePath) })
178
198
  logAction(`.agents/skills/${s.name}`)
179
199
  }
180
200
  return skills.length
@@ -212,7 +232,7 @@ async function copyCodexHooks(hooks, proj) {
212
232
  continue
213
233
  }
214
234
  const dest = path.join(dir, h.name)
215
- await fs.copy(h.sourcePath, dest, { overwrite: true, filter: copyFilter })
235
+ await fs.copy(h.sourcePath, dest, { overwrite: true, filter: copyFilter(h.sourcePath) })
216
236
  if (!await fs.pathExists(dest)) {
217
237
  logAction(`.codex/hooks/${h.name}`, 'yellow', '!')
218
238
  continue
@@ -223,7 +243,7 @@ async function copyCodexHooks(hooks, proj) {
223
243
  return hooks.length
224
244
  }
225
245
 
226
- function buildCodexHooksJson(manifests, selectedHookNames) {
246
+ function buildCodexHooksJson(manifests, selectedHookNames, global) {
227
247
  const hooksJson = { hooks: {} }
228
248
  const grouped = new Map()
229
249
 
@@ -237,7 +257,9 @@ function buildCodexHooksJson(manifests, selectedHookNames) {
237
257
  const hooks = grouped.get(key) || []
238
258
  hooks.push({
239
259
  type: 'command',
240
- command: `bash \"$(git rev-parse --show-toplevel)\"/.codex/hooks/${manifest.name}`,
260
+ command: global
261
+ ? `bash "$HOME/.codex/hooks/${manifest.name}"`
262
+ : `bash "$(git rev-parse --show-toplevel)/.codex/hooks/${manifest.name}"`,
241
263
  ...(manifest.timeout ? { timeout: manifest.timeout } : {}),
242
264
  })
243
265
  grouped.set(key, hooks)
@@ -280,10 +302,10 @@ function mergeCodexHooks(existingHooks, generatedHooks, managedHookNames) {
280
302
  return Object.keys(merged).length > 0 ? merged : undefined
281
303
  }
282
304
 
283
- async function syncCodexHooksJson(sourceDir, proj, selectedHookNames) {
305
+ async function syncCodexHooksJson(sourceDir, proj, selectedHookNames, global) {
284
306
  const dest = path.join(proj, '.codex', 'hooks.json')
285
307
  const manifests = await loadHookManifests(sourceDir)
286
- const generated = buildCodexHooksJson(manifests, selectedHookNames)
308
+ const generated = buildCodexHooksJson(manifests, selectedHookNames, global)
287
309
  const managedHookNames = new Set(
288
310
  manifests
289
311
  .filter(manifest => !manifest.platforms || manifest.platforms.includes('codex'))
@@ -317,12 +339,13 @@ async function syncCodexHooksJson(sourceDir, proj, selectedHookNames) {
317
339
  return 1
318
340
  }
319
341
 
342
+ /** 同步 Codex 配置;global 模式将规则写入用户 .codex/AGENTS.md。 */
320
343
  export async function syncCodex({
321
344
  sourceDir: sourceDirOverride,
322
345
  projectDir,
346
+ global = false,
323
347
  selected,
324
348
  staleRemovals = {},
325
- preserveMissingRules = new Set(),
326
349
  } = {}) {
327
350
  const sourceDir = sourceDirOverride || await resolveSourceDir()
328
351
  if (!await fs.pathExists(sourceDir)) {
@@ -335,22 +358,17 @@ export async function syncCodex({
335
358
 
336
359
  const scans = {
337
360
  skills: await scanSkills(sourceDir),
338
- rules: await scanRules(sourceDir),
339
361
  hooks: await scanHooks(sourceDir),
340
362
  }
341
- console.log(chalk.yellow(`Found ${scans.skills.length} skills, ${scans.rules.length} rules, ${scans.hooks.length} hooks\n`))
363
+ console.log(chalk.yellow(`Found ${scans.skills.length} skills, ${scans.hooks.length} hooks\n`))
342
364
 
343
- const agentsMdPath = path.join(proj, 'AGENTS.md')
344
- const agentsMdContent = await fs.pathExists(agentsMdPath) ? await fs.readFile(agentsMdPath, 'utf-8') : ''
345
365
  const existing = {
346
366
  skills: await scanExistingCodexSkills(proj),
347
- rules: parseManagedRuleNames(agentsMdContent),
348
367
  hooks: await scanExistingCodexHooks(proj),
349
368
  }
350
369
  const selectedNames = key => new Set((selected[key] || []).map(item => item.name))
351
370
  const syncedSelection = {
352
371
  skills: scans.skills.filter(item => selectedNames('skills').has(item.name)),
353
- rules: scans.rules.filter(item => selectedNames('rules').has(item.name)),
354
372
  hooks: scans.hooks.filter(item => selectedNames('hooks').has(item.name)),
355
373
  }
356
374
  const selectedHookNames = new Set(syncedSelection.hooks.map(h => h.name))
@@ -361,11 +379,8 @@ export async function syncCodex({
361
379
  let removedCount = 0
362
380
 
363
381
  syncedCount += await copySkillsFlat(syncedSelection.skills, proj)
364
- syncedCount += await syncProjectAgentsMd(sourceDir, proj, syncedSelection.rules, {
365
- preservedRuleNames: preserveMissingRules,
366
- })
367
382
  syncedCount += await copyCodexHooks(syncedSelection.hooks, proj)
368
- syncedCount += await syncCodexHooksJson(sourceDir, proj, selectedHookNames)
383
+ syncedCount += await syncCodexHooksJson(sourceDir, proj, selectedHookNames, global)
369
384
 
370
385
  const skillTemplateNames = new Set([
371
386
  ...scans.skills.map(s => s.name),
@@ -379,7 +394,6 @@ export async function syncCodex({
379
394
  removedCount += await removeUnselectedFlat(['.codex', 'hooks'], existing.hooks, selectedHookNames, hookTemplateNames, proj)
380
395
 
381
396
  console.log(chalk.blue(`\nDone! ${syncedCount} synced${removedCount > 0 ? `, ${removedCount} removed` : ''}.`))
382
- console.log(chalk.gray(` Rules → ${agentsMdPath}`))
383
397
  console.log(chalk.gray(` Skills → ${path.join(proj, '.agents', 'skills')}`))
384
398
  console.log(chalk.gray(` Hooks → ${path.join(proj, '.codex')}`))
385
399
  }
@@ -4,7 +4,6 @@ import { chalk } from './utils.js'
4
4
  import {
5
5
  extractDescription,
6
6
  logAction,
7
- readBaselineDoc,
8
7
  scanRules,
9
8
  resolveSourceDir,
10
9
  } from './sync.js'
@@ -79,28 +78,6 @@ async function syncCursorRules(selectedRules, projectDir) {
79
78
  return syncedCount
80
79
  }
81
80
 
82
- async function syncCursorBaselineRule(sourceDir, projectDir) {
83
- const baseline = await readBaselineDoc(sourceDir)
84
- if (!baseline) return 0
85
-
86
- const rulesDir = path.join(projectDir, '.cursor', 'rules')
87
- const destPath = path.join(rulesDir, BASELINE_RULE_NAME)
88
- const content = [
89
- '---',
90
- `description: ${JSON.stringify('[dwy] Global baseline')}`,
91
- 'alwaysApply: true',
92
- '---',
93
- '',
94
- baseline.trim(),
95
- '',
96
- ].join('\n')
97
-
98
- await fs.ensureDir(rulesDir)
99
- await fs.writeFile(destPath, content)
100
- logAction(`.cursor/rules/${BASELINE_RULE_NAME}`)
101
- return 1
102
- }
103
-
104
81
  async function removeUnselectedCursorRules(existingNames, selectedNames, managedNames, projectDir) {
105
82
  const rulesDir = path.join(projectDir, '.cursor', 'rules')
106
83
  let removedCount = 0
@@ -159,9 +136,7 @@ export async function syncCursor({
159
136
  let syncedCount = 0
160
137
  let removedCount = 0
161
138
 
162
- syncedCount += await syncCursorBaselineRule(sourceDir, proj)
163
139
  syncedCount += await syncCursorRules(syncedSelection, proj)
164
- nextRuleNames.add(BASELINE_RULE_NAME)
165
140
  removedCount += await removeUnselectedCursorRules(existingRules, nextRuleNames, managedRuleNames, proj)
166
141
 
167
142
  console.log(chalk.blue(`\nDone! ${syncedCount} synced${removedCount > 0 ? `, ${removedCount} removed` : ''}.`))
@@ -131,7 +131,7 @@ export async function copySkillsToGlobalDirs({ skills, destIds, homeDir = os.hom
131
131
  for (const skill of skills) {
132
132
  const dest = path.join(destRoot, skill.name)
133
133
  await fs.ensureDir(path.dirname(dest))
134
- await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter })
134
+ await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter(skill.sourcePath) })
135
135
  logAction(`${DEST_LABELS[destId] || destId}/${skill.name}`)
136
136
  count++
137
137
  }
@@ -157,7 +157,7 @@ export async function copySkillsToProjectPlatforms({ projectDir, platforms, skil
157
157
  for (const skill of skills) {
158
158
  const dest = path.join(destRoot, skill.name)
159
159
  await fs.ensureDir(path.dirname(dest))
160
- await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter })
160
+ await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter(skill.sourcePath) })
161
161
  logAction(`${path.relative(projectDir, destRoot)}/${skill.name}`)
162
162
  count++
163
163
  }
@@ -5,12 +5,9 @@ import {
5
5
  copyFilter,
6
6
  logAction,
7
7
  resolveSourceDir,
8
- scanCommands,
9
8
  scanExisting,
10
- scanRules,
11
9
  scanSkills,
12
10
  } from './sync.js'
13
- import { syncProjectAgentsMd } from './sync-codex.js'
14
11
 
15
12
  async function copyItemsTo(baseDir, typeDir, items) {
16
13
  const targetDir = path.join(baseDir, typeDir)
@@ -19,7 +16,7 @@ async function copyItemsTo(baseDir, typeDir, items) {
19
16
  for (const item of items) {
20
17
  const dest = path.join(targetDir, item.name)
21
18
  await fs.ensureDir(path.dirname(dest))
22
- await fs.copy(item.sourcePath, dest, { overwrite: true, filter: copyFilter })
19
+ await fs.copy(item.sourcePath, dest, { overwrite: true, filter: copyFilter(item.sourcePath) })
23
20
  logAction(`.opencode/${typeDir}/${item.name}`)
24
21
  count++
25
22
  }
@@ -27,8 +24,8 @@ async function copyItemsTo(baseDir, typeDir, items) {
27
24
  return count
28
25
  }
29
26
 
30
- async function removeUnselected(typeDir, existingNames, selectedNames, templateNames, projectDir) {
31
- const targetDir = path.join(projectDir, '.opencode', typeDir)
27
+ async function removeUnselected(typeDir, existingNames, selectedNames, templateNames, baseDir) {
28
+ const targetDir = path.join(baseDir, typeDir)
32
29
  let count = 0
33
30
 
34
31
  for (const name of existingNames) {
@@ -42,12 +39,13 @@ async function removeUnselected(typeDir, existingNames, selectedNames, templateN
42
39
  return count
43
40
  }
44
41
 
42
+ /** 同步 OpenCode 技能;基线和项目 Rules 由总同步入口分别处理。 */
45
43
  export async function syncOpenCode({
46
44
  sourceDir: sourceDirOverride,
47
45
  projectDir,
46
+ global = false,
48
47
  selected,
49
48
  staleRemovals = {},
50
- preserveMissingRules = new Set(),
51
49
  } = {}) {
52
50
  const sourceDir = sourceDirOverride || await resolveSourceDir()
53
51
  if (!await fs.pathExists(sourceDir)) {
@@ -55,39 +53,28 @@ export async function syncOpenCode({
55
53
  }
56
54
 
57
55
  const proj = projectDir || process.cwd()
58
- const openCodeDir = path.join(proj, '.opencode')
56
+ const openCodeDir = global ? path.join(proj, '.config', 'opencode') : path.join(proj, '.opencode')
59
57
  await fs.ensureDir(openCodeDir)
60
58
 
61
59
  console.log(chalk.blue('\nSyncing OpenCode configuration...\n'))
62
60
 
63
61
  const scans = {
64
62
  skills: await scanSkills(sourceDir),
65
- rules: await scanRules(sourceDir),
66
- commands: await scanCommands(sourceDir),
67
63
  }
68
64
 
69
65
  const selectedNames = key => new Set((selected[key] || []).map(item => item.name))
70
66
  const syncedSelection = {
71
67
  skills: scans.skills.filter(item => selectedNames('skills').has(item.name)),
72
- rules: scans.rules.filter(item => selectedNames('rules').has(item.name)),
73
- commands: scans.commands.filter(item => selectedNames('commands').has(item.name)),
74
68
  }
75
69
 
76
70
  const existing = {
77
71
  skills: await scanExisting(openCodeDir, 'skills'),
78
- commands: await scanExisting(openCodeDir, 'commands'),
79
72
  }
80
73
 
81
74
  let syncedCount = 0
82
75
  let removedCount = 0
83
76
 
84
- const agentsPath = path.join(proj, 'AGENTS.md')
85
- syncedCount += await syncProjectAgentsMd(sourceDir, proj, syncedSelection.rules, {
86
- preservedRuleNames: preserveMissingRules,
87
- })
88
-
89
77
  syncedCount += await copyItemsTo(openCodeDir, 'skills', syncedSelection.skills)
90
- syncedCount += await copyItemsTo(openCodeDir, 'commands', syncedSelection.commands)
91
78
 
92
79
  removedCount += await removeUnselected(
93
80
  'skills',
@@ -97,22 +84,9 @@ export async function syncOpenCode({
97
84
  ...scans.skills.map(item => item.name),
98
85
  ...(staleRemovals.skills || new Set()),
99
86
  ]),
100
- proj,
87
+ openCodeDir,
101
88
  )
102
- removedCount += await removeUnselected(
103
- 'commands',
104
- existing.commands,
105
- new Set(syncedSelection.commands.map(item => item.name)),
106
- new Set([
107
- ...scans.commands.map(item => item.name),
108
- ...(staleRemovals.commands || new Set()),
109
- ]),
110
- proj,
111
- )
112
-
113
89
  console.log(chalk.blue(`\nDone! ${syncedCount} synced${removedCount > 0 ? `, ${removedCount} removed` : ''}.`))
114
- console.log(chalk.gray(` Rules → ${agentsPath}`))
115
90
  console.log(chalk.gray(` Skills → ${path.join(openCodeDir, 'skills')}`))
116
- console.log(chalk.gray(` Commands → ${path.join(openCodeDir, 'commands')}`))
117
91
  console.log(chalk.gray(' Hooks → 当前适配未支持'))
118
92
  }