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.
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/cli-help.js +31 -2
- package/src/sync-all.js +168 -577
- package/src/sync-codex.js +42 -28
- package/src/sync-cursor.js +0 -25
- package/src/sync-global-skills.js +2 -2
- package/src/sync-opencode.js +7 -33
- package/src/sync.js +59 -226
- package/src/tab-multiselect.js +15 -53
- package/src/utils.js +0 -1
- package/templates/ai-tools/AGENTS.md +223 -0
- package/templates/ai-tools/CHANGELOG.md +195 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +5 -4
- package/templates/ai-tools/rules/Android/dwy-android-core.md +147 -13
- package/templates/ai-tools/rules/Android/dwy-android-layering.md +142 -0
- package/templates/ai-tools/rules/Android/dwy-android-mvp.md +55 -0
- package/templates/ai-tools/rules/Android/dwy-android-support-only.md +2 -0
- package/templates/ai-tools/rules/Docker/dwy-docker.md +23 -1
- package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +2 -0
- package/templates/ai-tools/rules/Flutter/dwy-flutter-testing.md +1 -1
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +12 -8
- package/templates/ai-tools/rules/Python/dwy-python-backend.md +4 -43
- package/templates/ai-tools/rules/Python/dwy-python-layering.md +169 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +11 -49
- package/templates/ai-tools/rules/Vue/dwy-vue-layering.md +153 -0
- package/templates/ai-tools/rules/iOS/dwy-apple-layering.md +133 -0
- package/templates/ai-tools/rules/iOS/dwy-swift-core.md +2 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-code-craft.md +83 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +27 -66
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +8 -219
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +27 -123
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prod-branch.md +31 -0
- package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-postgres.md +17 -10
- package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-redis.md +6 -3
- package/templates/ai-tools/skills/Docker/dwy-deploy-first/SKILL.md +14 -14
- package/templates/ai-tools/skills/Docker/dwy-deploy-first/references/chapter-01-mirrors.md +2 -2
- package/templates/ai-tools/skills/Docker/dwy-docker/SKILL.md +21 -13
- package/templates/ai-tools/skills/Docker/dwy-docker/references/ask-templates.md +13 -12
- package/templates/ai-tools/skills/Docker/dwy-docker/references/templates.md +3 -1
- package/templates/ai-tools/skills/Docker/dwy-docker/references/version-rules.md +10 -6
- package/templates/ai-tools/skills/Docker/dwy-docker/scripts/query_dockerhub.py +21 -6
- package/templates/ai-tools/skills/Docker/dwy-docker/scripts/scan_images.sh +2 -2
- package/templates/ai-tools/skills/Docker/dwy-mirror-source/SKILL.md +44 -30
- package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/dockerfile-snippets.md +2 -0
- package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/mirror-providers.md +8 -8
- package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/apply_mirrors.py +14 -1
- package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/check_mirrors.py +4 -0
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/SKILL.md +8 -6
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/python-sdk.md +3 -3
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/stream-engine.md +2 -2
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/anti-patterns.md +3 -1
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/change-checklist.md +4 -4
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/lethal-violations.md +5 -5
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/perf-baselines.md +5 -5
- package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +2 -2
- package/templates/ai-tools/skills/Python/dwy-eapi/references/tasks-integration-guide.md +4 -4
- package/templates/ai-tools/skills/Python/dwy-eapi/v0/breaking.md +3 -0
- package/templates/ai-tools/skills/Python/dwy-eapi/v0/email-canonical.md +28 -3
- package/templates/ai-tools/skills/Python/dwy-eapi/v0/index.md +2 -2
- package/templates/ai-tools/skills/Python/dwy-eapi/v0/providers-email.md +12 -2
- package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-design-guide.md +1 -1
- package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-integration-guide.md +12 -0
- package/templates/ai-tools/skills/Vue/dwy-eui/shared/config-theme.md +2 -0
- package/templates/ai-tools/skills/Vue/dwy-eui/shared/form.md +10 -0
- package/templates/ai-tools/skills/Vue/dwy-eui/v2/index.md +5 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +11 -14
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +1 -1
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +4 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +1 -1
- 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
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +45 -27
- 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
- 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
- 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
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +8 -0
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +7 -7
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +63 -59
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_base.sh +10 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_capacity.sh +21 -23
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_db.sh +9 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +20 -24
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_logs.sh +3 -3
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +4 -2
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +1 -1
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +1 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +7 -5
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +5 -3
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +140 -0
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-git-commit/SKILL.md +113 -0
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-shared/SKILL.md +17 -14
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-whiteboard-prototype/SKILL.md +1 -1
- package/templates/project/fullstack-monorepo/{CLAUDE.md.hbs → AGENTS.md.hbs} +1 -1
- package/src/sync-packs.js +0 -223
- package/src/sync-scene.js +0 -178
- package/templates/ai-tools/CLAUDE.md +0 -210
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +0 -115
- /package/templates/project/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
- /package/templates/project/fullstack-monorepo/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
- /package/templates/project/fullstack-monorepo/frontend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
- /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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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
|
}
|
package/src/sync-cursor.js
CHANGED
|
@@ -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
|
}
|
package/src/sync-opencode.js
CHANGED
|
@@ -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,
|
|
31
|
-
const targetDir = path.join(
|
|
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
|
-
|
|
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
|
}
|