skillcaddy 0.27.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 (99) hide show
  1. package/CHANGELOG.md +497 -0
  2. package/CONTEXT.md +149 -0
  3. package/LICENSE +21 -0
  4. package/README.md +499 -0
  5. package/README_CN.md +482 -0
  6. package/collection-metadata/github/mattpocock.json +16 -0
  7. package/lib/canonicalJson.js +11 -0
  8. package/lib/claudeStore.js +135 -0
  9. package/lib/collectionEnablement.js +115 -0
  10. package/lib/collectionSetup.js +199 -0
  11. package/lib/enablePlan.js +116 -0
  12. package/lib/enablementScope.js +8 -0
  13. package/lib/hermesStore.js +21 -0
  14. package/lib/libraryImage.js +1785 -0
  15. package/lib/managerInstall.js +109 -0
  16. package/lib/managerSourceWorkflow.js +234 -0
  17. package/lib/projectActions.js +24 -0
  18. package/lib/projectPath.js +51 -0
  19. package/lib/roots.js +99 -0
  20. package/lib/skillLinkStore.js +68 -0
  21. package/lib/skillMetadata.js +191 -0
  22. package/lib/skillStore.js +541 -0
  23. package/lib/sourceAcquisition.js +475 -0
  24. package/lib/sourceAcquisitionAdapter.js +47 -0
  25. package/lib/sourceAcquisitionError.js +18 -0
  26. package/lib/sourceAcquisitionPolicy.js +139 -0
  27. package/lib/sourceArchive.js +363 -0
  28. package/lib/sourceArchiveWorkspace.js +36 -0
  29. package/lib/sourceGit.js +95 -0
  30. package/lib/sourceGitAcquisitionAdapter.js +145 -0
  31. package/lib/sourceGitBatchUpdate.js +168 -0
  32. package/lib/sourceGitRepair.js +227 -0
  33. package/lib/sourceGitStaging.js +139 -0
  34. package/lib/sourceGitUpgradeAdapter.js +583 -0
  35. package/lib/sourceGitUrl.js +170 -0
  36. package/lib/sourceHttp.js +302 -0
  37. package/lib/sourceLocal.js +200 -0
  38. package/lib/sourceLocalAcquisitionAdapter.js +107 -0
  39. package/lib/sourceManager.js +242 -0
  40. package/lib/sourceMigration.js +172 -0
  41. package/lib/sourceMigrationIssue.js +6 -0
  42. package/lib/sourcePath.js +6 -0
  43. package/lib/sourcePolicy.js +53 -0
  44. package/lib/sourceRecord.js +295 -0
  45. package/lib/sourceRegistry.js +155 -0
  46. package/lib/sourceRemoteArchiveAcquisitionAdapter.js +147 -0
  47. package/lib/sourceRemoteFileAcquisitionAdapter.js +95 -0
  48. package/lib/sourceRemoteFilePolicy.js +62 -0
  49. package/lib/sourceRemoteFileWorkspace.js +55 -0
  50. package/lib/sourceSkillDiscovery.js +117 -0
  51. package/lib/sourceTree.js +185 -0
  52. package/lib/sourceUpgrade.js +596 -0
  53. package/lib/sourceUpgradeAdapter.js +41 -0
  54. package/lib/sourceUpgradePolicy.js +170 -0
  55. package/lib/sourceUpgradeTransaction.js +202 -0
  56. package/lib/sourceValidation.js +120 -0
  57. package/lib/sourceWorkspace.js +37 -0
  58. package/lib/specGate.js +235 -0
  59. package/lib/tuiActions.js +169 -0
  60. package/lib/tuiInstall.js +133 -0
  61. package/lib/tuiLayout.js +94 -0
  62. package/lib/version.js +17 -0
  63. package/lib/webManager.js +370 -0
  64. package/package.json +74 -0
  65. package/public/agentsUi.js +138 -0
  66. package/public/app.js +1431 -0
  67. package/public/claude.css +2 -0
  68. package/public/claudeUi.js +119 -0
  69. package/public/emptyState.js +12 -0
  70. package/public/enabled.css +303 -0
  71. package/public/favicon.svg +5 -0
  72. package/public/index.html +199 -0
  73. package/public/skillcaddy_CN.png +0 -0
  74. package/public/skillcaddy_EN.png +0 -0
  75. package/public/styles.css +1611 -0
  76. package/scripts/cli.js +647 -0
  77. package/scripts/cron-sync-update-git.sh +128 -0
  78. package/scripts/pull-github.sh +22 -0
  79. package/scripts/source.js +638 -0
  80. package/scripts/tui.js +1058 -0
  81. package/server.js +196 -0
  82. package/skills/skillcaddy-manager/SKILL.md +93 -0
  83. package/skills/skillcaddy-manager/agents/openai.yaml +7 -0
  84. package/skills/skillcaddy-manager/references/METADATA.md +75 -0
  85. package/skills/skillcaddy-manager/references/OPERATIONS.md +315 -0
  86. package/skills/skillcaddy-manager/references/RECOMMENDATION_GUIDE.md +51 -0
  87. package/skills/skillcaddy-manager/references/featured-skills.json +564 -0
  88. package/skills/skillcaddy-manager/references/skill-platforms.json +48 -0
  89. package/skills/skillcaddy-manager/scripts/check-conflicts.cjs +117 -0
  90. package/skills/skillcaddy-manager/scripts/check-global-skills.cjs +169 -0
  91. package/skills/skillcaddy-manager/scripts/translate-skill-notes.cjs +297 -0
  92. package/skills/skillcaddy-manager/scripts/version-manager.cjs +146 -0
  93. package/skills/skillcaddy-manager/scripts/view-recommendations.cjs +279 -0
  94. package/skills/skillcaddy-manager/skillcaddy.json +9 -0
  95. package/skills/spec-pipeline/spec-direction-gate/SKILL.md +119 -0
  96. package/skills/spec-pipeline/spec-direction-gate/check-approval.py +110 -0
  97. package/skills/spec-pipeline/spec-draft-intake/SKILL.md +116 -0
  98. package/skills/spec-pipeline/spec-role-contract/SKILL.md +113 -0
  99. package/skills/spec-pipeline/spec-role-contract/check-roles.py +108 -0
package/server.js ADDED
@@ -0,0 +1,196 @@
1
+ import http from 'node:http';
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import {
6
+ disableSkill,
7
+ ensureSourceFolders,
8
+ getState
9
+ } from './lib/skillStore.js';
10
+ import { syncClaudeSkills, unlinkClaudeSkill, unlinkClaudeSkills } from './lib/claudeStore.js';
11
+ import { executeCollectionEnablement } from './lib/collectionEnablement.js';
12
+ import { resolveRoots } from './lib/roots.js';
13
+ import { readVersion } from './lib/version.js';
14
+ import { enableProjectSkill } from './lib/projectActions.js';
15
+ import { buildCollectionEnablePlan } from './lib/enablePlan.js';
16
+ import { updateSkillMetadata } from './lib/skillMetadata.js';
17
+ import { normalizeEnablementScope } from './lib/enablementScope.js';
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+ // 代码根 = server.js 所在目录;原件库根由 SKILLCADDY_ROOT 指定,或按代码根推导。
21
+ const roots = resolveRoots({ codeRoot: __dirname });
22
+ const rootDir = roots.libraryRoot;
23
+ const publicDir = path.join(roots.codeRoot, 'public');
24
+ const port = Number(process.env.PORT || 4173);
25
+
26
+ await ensureSourceFolders(roots);
27
+ const appInfo = await readVersion(roots);
28
+
29
+ const mimeTypes = {
30
+ '.html': 'text/html; charset=utf-8',
31
+ '.css': 'text/css; charset=utf-8',
32
+ '.svg': 'image/svg+xml',
33
+ '.js': 'text/javascript; charset=utf-8',
34
+ '.json': 'application/json; charset=utf-8'
35
+ };
36
+
37
+ const server = http.createServer(async (req, res) => {
38
+ try {
39
+ const url = new URL(req.url || '/', `http://${req.headers.host}`);
40
+
41
+ if (url.pathname.startsWith('/api/')) {
42
+ await handleApi(req, res, url);
43
+ return;
44
+ }
45
+
46
+ await serveStatic(res, url.pathname);
47
+ } catch (error) {
48
+ sendJson(res, 500, { error: error.message || 'Internal server error' });
49
+ }
50
+ });
51
+
52
+ server.listen(port, '127.0.0.1', () => {
53
+ console.log(`Skillcaddy: http://127.0.0.1:${port}`);
54
+ });
55
+
56
+ async function handleApi(req, res, url) {
57
+ if (req.method === 'GET' && url.pathname === '/api/version') {
58
+ sendJson(res, 200, appInfo);
59
+ return;
60
+ }
61
+
62
+ if (req.method === 'GET' && url.pathname === '/api/state') {
63
+ const projectPath = url.searchParams.get('projectPath') || rootDir;
64
+ sendJson(res, 200, await getState(roots, projectPath));
65
+ return;
66
+ }
67
+
68
+ if (req.method === 'POST' && url.pathname === '/api/enable') {
69
+ const body = await readJson(req);
70
+ const scope = requireApiScope(body);
71
+ requireProjectForScope(body, scope);
72
+ sendJson(res, 200, await enableProjectSkill(roots, {
73
+ scope,
74
+ ...(scope === 'project' ? { projectPath: body.projectPath } : {}),
75
+ skillPath: body.skillPath,
76
+ alias: body.alias
77
+ }));
78
+ return;
79
+ }
80
+
81
+ if (req.method === 'POST' && url.pathname === '/api/enable-plan') {
82
+ const body = await readJson(req);
83
+ const scope = requireApiScope(body);
84
+ requireProjectForScope(body, scope);
85
+ const state = await getState(roots, body.projectPath || rootDir);
86
+ sendJson(res, 200, buildCollectionEnablePlan(
87
+ state,
88
+ Array.isArray(body.skillIds) ? body.skillIds : [],
89
+ { scope }
90
+ ));
91
+ return;
92
+ }
93
+
94
+ if (req.method === 'POST' && url.pathname === '/api/enable-collection') {
95
+ const body = await readJson(req);
96
+ const scope = requireApiScope(body);
97
+ requireProjectForScope(body, scope);
98
+ sendJson(res, 200, await executeCollectionEnablement(roots, {
99
+ scope,
100
+ ...(body.projectPath ? { projectPath: body.projectPath } : {}),
101
+ skillIds: body.skillIds
102
+ }));
103
+ return;
104
+ }
105
+
106
+ if (req.method === 'POST' && url.pathname === '/api/skill-metadata') {
107
+ const body = await readJson(req);
108
+ sendJson(res, 200, await updateSkillMetadata(roots, body));
109
+ return;
110
+ }
111
+
112
+ if (req.method === 'POST' && url.pathname === '/api/disable') {
113
+ const body = await readJson(req);
114
+ const scope = requireApiScope(body);
115
+ requireProjectForScope(body, scope);
116
+ sendJson(res, 200, await disableSkill({
117
+ scope,
118
+ roots,
119
+ ...(scope === 'project' ? { projectPath: body.projectPath } : {}),
120
+ alias: body.alias
121
+ }));
122
+ return;
123
+ }
124
+
125
+ if (req.method === 'POST' && url.pathname === '/api/sync-claude') {
126
+ const body = await readJson(req);
127
+ sendJson(res, 200, await syncClaudeSkills(body.projectPath));
128
+ return;
129
+ }
130
+
131
+ if (req.method === 'POST' && url.pathname === '/api/unlink-claude') {
132
+ const body = await readJson(req);
133
+ sendJson(res, 200, await unlinkClaudeSkills(body.projectPath));
134
+ return;
135
+ }
136
+
137
+ if (req.method === 'POST' && url.pathname === '/api/unlink-claude-skill') {
138
+ const body = await readJson(req);
139
+ sendJson(res, 200, await unlinkClaudeSkill(body));
140
+ return;
141
+ }
142
+
143
+ sendJson(res, 404, { error: 'Not found' });
144
+ }
145
+
146
+ async function serveStatic(res, pathname) {
147
+ const requestPath = pathname === '/' ? '/index.html' : pathname;
148
+ const normalized = path.normalize(requestPath).replace(/^(\.\.[/\\])+/, '');
149
+ const filePath = path.join(publicDir, normalized);
150
+
151
+ if (!filePath.startsWith(publicDir)) {
152
+ sendJson(res, 403, { error: 'Forbidden' });
153
+ return;
154
+ }
155
+
156
+ try {
157
+ const file = await readFile(filePath);
158
+ res.writeHead(200, {
159
+ 'Content-Type': mimeTypes[path.extname(filePath)] || 'application/octet-stream'
160
+ });
161
+ res.end(file);
162
+ } catch {
163
+ sendJson(res, 404, { error: 'Not found' });
164
+ }
165
+ }
166
+
167
+ async function readJson(req) {
168
+ const chunks = [];
169
+ for await (const chunk of req) {
170
+ chunks.push(chunk);
171
+ }
172
+
173
+ if (chunks.length === 0) {
174
+ return {};
175
+ }
176
+
177
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
178
+ }
179
+
180
+ function sendJson(res, status, payload) {
181
+ res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
182
+ res.end(JSON.stringify(payload));
183
+ }
184
+
185
+ function requireApiScope(body) {
186
+ if (!body || !Object.hasOwn(body, 'scope')) {
187
+ throw new Error('scope 必须显式指定为 project、global 或 hermes');
188
+ }
189
+ return normalizeEnablementScope(body.scope);
190
+ }
191
+
192
+ function requireProjectForScope(body, scope) {
193
+ if (scope === 'project' && (!body.projectPath || typeof body.projectPath !== 'string')) {
194
+ throw new Error('project scope 需要 projectPath');
195
+ }
196
+ }
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: skillcaddy-manager
3
+ description: Manage Skillcaddy source acquisition, source updates, migrations, project skill links, and the Web/TUI CLI. Use for discovery, add/update requests, enable/disable, audits, maintenance, bootstrap, or Web/TUI access.
4
+ ---
5
+
6
+ # Skillcaddy Manager
7
+
8
+ Treat Skillcaddy as a central skill library with explicit project/global/Hermes symlink activation. Keep one execution loop across every branch: **state -> route -> preflight -> act -> verify**.
9
+
10
+ ## Core Loop
11
+
12
+ 1. Resolve the Skillcaddy repository and target project. Default the project to the current working directory only when the user did not provide one.
13
+ - Complete when both absolute paths are known.
14
+ 2. For recommendations, audits, or mutations, read `GET /api/state?projectPath=<encoded-path>`. For source acquisition, updates, or migration, also inspect the source registry with `npm run source -- list` and `npm run source -- inspect <source-id>`. If the server is unavailable, inspect the equivalent paths listed under **Core Model**.
15
+ - Complete when repository, source registry, project, global, Hermes, Claude, metadata, and `advice` state relevant to the request are known.
16
+ 3. Route to the required branch and load only its reference; load multiple references only when the request combines branches:
17
+ - Discovery, source acquisition/update/migration, enable/disable, audit, health, Git update, bootstrap, Web/TUI: [OPERATIONS.md](references/OPERATIONS.md)
18
+ - Recommendations: [RECOMMENDATION_GUIDE.md](references/RECOMMENDATION_GUIDE.md)
19
+ - Notes, tags, `autoEnable`, migration, batch Chinese notes: [METADATA.md](references/METADATA.md)
20
+ - Complete when the selected branch, target identities, and completion criterion are explicit.
21
+ 4. For a mutation, apply the **Mutation Gate** before acting. Prefer the existing API, TUI, or repository commands over ad hoc filesystem changes.
22
+ - Complete when the preflight accounts for every affected item and all blocking ownership or alias issues are resolved.
23
+ 5. Execute the narrowest requested operation, rescan state, and report the observed result.
24
+ - Complete when the post-operation state proves every requested item succeeded, remained unchanged, was skipped, or failed with a stated reason.
25
+
26
+ ## Core Model
27
+
28
+ | Scope | Location | Role |
29
+ |---|---|---|
30
+ | Bundled | `skills/<name>/` | Skills shipped by this repository |
31
+ | Central | `official/`, `github/`, `personal/`, `archived/` | Source libraries |
32
+ | Source registry | `.skillcaddy/sources/` | Sidecar identity, provenance, integrity, install path, and discovered skill paths |
33
+ | Project | `<project>/.agents/skills/` | Codex-compatible activation symlinks |
34
+ | Project Claude | `<project>/.claude/skills/` | Claude compatibility symlinks |
35
+ | Global Agents | `~/.agents/skills/` | Shared user-level links managed by Skillcaddy |
36
+ | Global Claude | `~/.claude/skills/` | Read-only external Agent directory; Skillcaddy does not write it |
37
+ | Hermes | `~/.hermes/skills/` | Explicit independent scope for eligible `official`/`github`/`personal` sources |
38
+ | Collection setup | `collection-metadata/<source>/<collection>.json` | Tracked, read-only setup contract and readiness checks |
39
+
40
+ Preserve these invariants:
41
+
42
+ - Enable and disable operations are explicit about `project` or `global` scope; source skill directories remain intact.
43
+ - Project enablement writes `<project>/.agents/skills/` and best-effort syncs project Claude links. Global enablement writes only `~/.agents/skills/` and does not require a project.
44
+ - Project and global aliases may coexist; project state is the higher-priority context when an Agent applies its normal precedence rules.
45
+ - Never take over an occupied ordinary entry, foreign symlink, or broken link. Same-target enablement is an unchanged result.
46
+ - Global disable may remove only a symlink whose target is provably inside the current Skillcaddy root's allowed source folders; foreign links and ordinary entries are read-only.
47
+ - Source acquisition changes only the central library. It never implies enablement, setup, or execution of acquired code.
48
+ - `add` is for a new source identity. Only a matching source-registry record authorizes `update`; a destination collision never does.
49
+ - An explicit combined request runs acquisition first, rescans state, resolves one requested acquired skill, and enables only that selection.
50
+ - Unknown setup readiness produces no reminder or gate. Declared setup reminders remain non-blocking; publisher runtime preflight owns proprietary credentials and setup.
51
+ - `archived/` skills require an explicit user request.
52
+ - GitHub-backed source directories remain free of Skillcaddy metadata; write catalog metadata to the sidecar store.
53
+ - Collection activation and project readiness are separate: setup may be missing after links are enabled.
54
+ - Interactive setup is never executed silently; obtain confirmation and let the declared setup skill own project edits.
55
+ - Repository behavior in `lib/skillStore.js`, `lib/projectActions.js`, and `lib/claudeStore.js` is authoritative when documentation and implementation differ.
56
+ - The fixed default Web manager URL is `http://127.0.0.1:4173`.
57
+ - The clone-backed `skillcaddy` entry keeps no-argument TUI compatibility and also supports `start`, `stop`, `restart`, `-u` for registered Git source updates, and read-only `-a` analysis.
58
+ - Global bootstrap is explicit: run `npm run install:cli` and `npm run check:cli` from the clone; the installed command provides both CLI and TUI, while `install:tui` remains a compatibility alias.
59
+ - Source update and repair CLIs require an explicit `--project`; global links are always scanned in addition to that project context.
60
+ - If a registered Git checkout was manually advanced with `git pull`, use `npm run source -- repair <source-id> --project <project-dir>` to explicitly adopt the clean fast-forward state into the sidecar registry; never edit the registry directly.
61
+
62
+ ## Mutation Gate
63
+
64
+ Before changing state, present a compact preflight containing:
65
+
66
+ | Action | Skill | Source/collection | Alias | Target path | Advice/result |
67
+ |---|---|---|---|---|---|
68
+
69
+ Account for skills that will be changed, left unchanged, skipped, or rejected. Include relevant `/api/state.advice` and metadata writes.
70
+
71
+ Require confirmation before proceeding when:
72
+
73
+ - one request matches multiple skills and the user has not selected one;
74
+ - a project or global alias points to a different target;
75
+ - a project or global entry is not a symlink or is externally managed;
76
+ - a GitHub source has uncommitted changes before update;
77
+ - a breaking source replacement affects a known project or global link;
78
+ - a source identity is ambiguous or a destination collision is not backed by a matching registry record;
79
+ - the request reaches into `archived/` without naming the archived target;
80
+ - the request would delete a source skill rather than a project link;
81
+ - an advice item exposes a destructive or ownership ambiguity.
82
+ - an affected collection reports missing or partial interactive setup.
83
+
84
+ Surface informational duplicate-name and global-shadowing advice, but do not block solely on it. Use the full skill ID and a confirmed alias to resolve duplicates.
85
+
86
+ ## Reporting
87
+
88
+ For summaries, distinguish repository availability, project enablement, global presence, Claude compatibility, and unmanaged or broken state. Prefer:
89
+
90
+ | Action | Skill | Source | Alias | Result | Notes |
91
+ |---|---|---|---|---|---|
92
+
93
+ End with the verification performed: rescan/API state, validation command, tests, or server URL.
@@ -0,0 +1,7 @@
1
+ interface:
2
+ display_name: "Skillcaddy Manager"
3
+ short_description: "Manage project, global, and Hermes skills."
4
+ default_prompt: "Use $skillcaddy-manager to audit project/global/Hermes skills, review capability overlaps, and enable or disable skills safely."
5
+
6
+ policy:
7
+ allow_implicit_invocation: true
@@ -0,0 +1,75 @@
1
+ # Skillcaddy Metadata
2
+
3
+ Use this reference for notes, tags, `autoEnable`, legacy migration, and batch Chinese note generation.
4
+
5
+ ## Storage Contract
6
+
7
+ `SKILL.md` is the agent-facing execution contract. Skillcaddy catalog metadata is human-facing and lives at:
8
+
9
+ ```text
10
+ .skillcaddy/metadata/<source>/<relative-skill-path>/skillcaddy.json
11
+ ```
12
+
13
+ Since v0.15.0, runtime scans ignore legacy `<skill-dir>/skillcaddy.json`. The migration command explicitly discovers those files, remains through v0.15.x, and is removed in v0.16.0. New writes use the API or sidecar path, especially for GitHub-backed sources.
14
+
15
+ Supported shape:
16
+
17
+ ```json
18
+ {
19
+ "note": "简短、面向用户的用途说明。",
20
+ "tags": ["Developer Tools", "Productivity"],
21
+ "autoEnable": true
22
+ }
23
+ ```
24
+
25
+ Apply these rules:
26
+
27
+ - Describe what the skill helps with; keep trigger logic in `SKILL.md`.
28
+ - Use 1-4 broad, reusable product-style tags.
29
+ - Preserve existing tags and `autoEnable` unless the request changes them or they are demonstrably wrong.
30
+ - Set `autoEnable: false` for deprecated, risky, heavyweight, or highly situational skills that should be skipped by collection-level enablement.
31
+ - Keep external GitHub clones clean; never add catalog metadata inside them.
32
+
33
+ ## Write Metadata
34
+
35
+ Prefer the API:
36
+
37
+ ```bash
38
+ curl -sS -X POST http://127.0.0.1:4173/api/skill-metadata \
39
+ -H 'Content-Type: application/json' \
40
+ -d '{"skillPath":"/path/to/skill","note":"简短说明","tags":["Developer Tools"],"autoEnable":true}'
41
+ ```
42
+
43
+ Run the mutation gate from `SKILL.md`, then rescan `/api/state`. Complete when the response and rescan show the intended sidecar values and unrelated fields are unchanged.
44
+
45
+ ## Migrate Legacy Metadata
46
+
47
+ Preview first, then obtain explicit confirmation before applying:
48
+
49
+ ```bash
50
+ npm run migrate:metadata
51
+ npm run migrate:metadata -- --yes
52
+ ```
53
+
54
+ Migration writes equivalent sidecar metadata and retains the legacy file for rollback. Complete when a rescan uses the sidecar and no source metadata was removed.
55
+
56
+ ## Batch Chinese Notes
57
+
58
+ Run commands from the Skillcaddy repository root. The script performs filesystem and API operations; the agent supplies the Chinese notes.
59
+
60
+ ```bash
61
+ # 1. Produce a manifest bound to this library root.
62
+ node skills/skillcaddy-manager/scripts/translate-skill-notes.cjs list --project=. > /tmp/skill-note-manifest.json
63
+
64
+ # 2. Fill each manifest entry's note; retain schemaVersion and libraryRoot.
65
+
66
+ # 3. Preflight without writes.
67
+ node skills/skillcaddy-manager/scripts/translate-skill-notes.cjs apply /tmp/skill-note-manifest.json --project=.
68
+
69
+ # 4. Apply only after reviewing the preflight.
70
+ node skills/skillcaddy-manager/scripts/translate-skill-notes.cjs apply /tmp/skill-note-manifest.json --project=. --yes
71
+ ```
72
+
73
+ Generate one short Chinese sentence per note. Leave `tags` and `autoEnable` unset to preserve their current values. Existing notes remain protected; use `--force-rewrite` only after explicit confirmation. The manifest's `schemaVersion` and `libraryRoot` must remain unchanged.
74
+
75
+ If the API is unavailable, start `npm start`. Complete when preflight validates every entry before the first write, the applied count matches the intended manifest entries, and `/api/state` returns the new sidecar notes.