@rch4com/agent-setup 1.1.0 → 1.4.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 (56) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +197 -55
  3. package/install.mjs +152 -101
  4. package/lib/args.mjs +276 -311
  5. package/lib/bootstrap/adapter.mjs +96 -95
  6. package/lib/bootstrap/apply.mjs +380 -379
  7. package/lib/bootstrap/flow.mjs +73 -70
  8. package/lib/bootstrap/record.mjs +154 -142
  9. package/lib/bootstrap/templates.mjs +148 -148
  10. package/lib/bootstrap/text.mjs +19 -19
  11. package/lib/catalog.mjs +213 -145
  12. package/lib/claude-plugins.mjs +64 -64
  13. package/lib/clis.mjs +102 -102
  14. package/lib/context.mjs +66 -65
  15. package/lib/deps.mjs +5 -7
  16. package/lib/design-md/cache/awesome-design-md/LICENSE.md +38 -0
  17. package/lib/design-md/catalog.json +454 -454
  18. package/lib/design-md/catalog.mjs +266 -268
  19. package/lib/design-md/flow.mjs +181 -166
  20. package/lib/design-md/open.mjs +51 -50
  21. package/lib/design-md/providers/awesome-design-md.mjs +124 -114
  22. package/lib/design-md/providers/index.mjs +9 -9
  23. package/lib/design-md/scan.mjs +14 -10
  24. package/lib/engine.mjs +48 -44
  25. package/lib/gitignore.mjs +18 -18
  26. package/lib/i18n/catalog/en.mjs +369 -0
  27. package/lib/i18n/catalog/ko.mjs +359 -0
  28. package/lib/i18n/detect.mjs +39 -0
  29. package/lib/i18n/index.mjs +93 -0
  30. package/lib/items/mcp.codebase-memory.mjs +6 -6
  31. package/lib/items/mcp.graphify.mjs +8 -0
  32. package/lib/items/mcp.headroom.mjs +8 -0
  33. package/lib/items/mcp.notion.mjs +6 -6
  34. package/lib/items/mcp.supabase.mjs +6 -6
  35. package/lib/items/mcp.vercel.mjs +6 -6
  36. package/lib/items/plugin.bkit.mjs +7 -7
  37. package/lib/items/plugin.ecc.mjs +11 -0
  38. package/lib/items/plugin.impeccable.mjs +14 -0
  39. package/lib/items/plugin.mattpocock-skills.mjs +2 -2
  40. package/lib/items/plugin.ponytail.mjs +104 -0
  41. package/lib/items/plugin.superpowers.mjs +7 -7
  42. package/lib/items/plugin.understand-anything.mjs +10 -0
  43. package/lib/items/skill.caveman.mjs +7 -0
  44. package/lib/items/skill.gsd.mjs +25 -24
  45. package/lib/items/skill.gstack.mjs +40 -39
  46. package/lib/items/skill.karpathy.mjs +7 -0
  47. package/lib/items/skill.taste.mjs +9 -0
  48. package/lib/jsonfile.mjs +40 -40
  49. package/lib/status.mjs +93 -92
  50. package/lib/tomlfile.mjs +60 -60
  51. package/lib/tui/render.mjs +41 -63
  52. package/lib/tui/rows.mjs +278 -206
  53. package/lib/tui/run.mjs +64 -28
  54. package/lib/update.mjs +85 -85
  55. package/lib/width.mjs +57 -0
  56. package/package.json +36 -36
@@ -0,0 +1,369 @@
1
+ // 기준 카탈로그 — 모든 키의 원본이자 폴백이다.
2
+ // ko.mjs는 이 키 집합을 그대로 따라야 한다. 키·자리·타입이 어긋나는 순간
3
+ // i18n.test.mjs가 실패한다.
4
+ //
5
+ // 값은 문자열 또는 문자열 배열(여러 줄)만 쓴다. 함수를 두면 카탈로그가
6
+ // 데이터가 아니게 되어 완전성 검사를 할 수 없다.
7
+ export default {
8
+ // 언어 이름은 어떤 화면에서 보든 자기 언어로 쓴다 — 두 카탈로그에서 값이 같다.
9
+ 'locale.en': 'English',
10
+ 'locale.ko': '한국어',
11
+
12
+ 'error.notGitRepo': 'Run this inside a Git repository.',
13
+ 'error.pathOutsideRepo': 'Cannot write outside the repository: {path}',
14
+ 'error.pathUnresolvable': 'Cannot resolve path: {path} ({code})',
15
+ 'error.pathEscapesViaLink': 'This in-repo path escapes through an external link: {path} -> {real}',
16
+
17
+ 'usage.bootstrap': [
18
+ 'Usage: npx @rch4com/agent-setup bootstrap [--skill-mode auto|link|copy] [--dry-run]',
19
+ ' node install.mjs bootstrap [options] # when the installer lives in the repo',
20
+ '',
21
+ 'Options:',
22
+ ' --skill-mode auto|link|copy How skills are wired up. (default: auto)',
23
+ ' auto: try a symlink first, fall back to copying.',
24
+ ' link: symlink only. Exit with an error if it fails.',
25
+ ' copy: always make copies.',
26
+ ' --adopt Create nothing. Record only the existing files that',
27
+ ' already match this version of the templates.',
28
+ ' --lang en|ko Display language for this run.',
29
+ ' --dry-run Change nothing; print what would happen.',
30
+ ' -h, --help Print this help and exit.',
31
+ '',
32
+ 'The launchers work too:',
33
+ ' ./setup-agents.sh [options]',
34
+ ' pwsh -File ./setup-agents.ps1 [options]',
35
+ ' Pass --tui (or -Tui) to install dependencies and open the interactive screen,',
36
+ ' where bootstrap, agents, and design.md live in one searchable list.',
37
+ ],
38
+ 'usage.root': [
39
+ 'Usage: npx @rch4com/agent-setup [options]',
40
+ ' npx @rch4com/agent-setup bootstrap [options]',
41
+ ' npx @rch4com/agent-setup update [options]',
42
+ ' npx @rch4com/agent-setup status [options]',
43
+ ' npx @rch4com/agent-setup design [options]',
44
+ '',
45
+ 'When the installer lives in the repo, use node install.mjs in place of',
46
+ 'npx @rch4com/agent-setup.',
47
+ '',
48
+ 'With no options this opens the interactive screen, where bootstrap, agents,',
49
+ 'and design.md live in one searchable list.',
50
+ '',
51
+ 'Options:',
52
+ ' --list Print the current install state and exit.',
53
+ ' --set <list> Converge on a comma-separated set of items.',
54
+ ' Use --set "" to remove everything.',
55
+ ' --skill-mode auto|link|copy How the bootstrap launched from the interactive',
56
+ ' screen wires up skills. (default: auto)',
57
+ ' --design-dir <path> Add a design.md source. Accepts <source-id>=<path>',
58
+ ' and may be repeated.',
59
+ ' --lang en|ko Display language for this run.',
60
+ ' --dry-run Change nothing; print what would happen.',
61
+ ' -h, --help Print this help and exit.',
62
+ '',
63
+ '--list and --set both choose an action, so they cannot be combined.',
64
+ '',
65
+ 'Subcommand help: npx @rch4com/agent-setup bootstrap --help',
66
+ ' npx @rch4com/agent-setup update --help',
67
+ ' npx @rch4com/agent-setup status --help',
68
+ ' npx @rch4com/agent-setup design --help',
69
+ ],
70
+ 'usage.design': [
71
+ 'Usage: npx @rch4com/agent-setup design [options]',
72
+ ' node install.mjs design [options] # when the installer lives in the repo',
73
+ '',
74
+ 'With no options this opens the interactive screen, where the DESIGN.MD tab lives.',
75
+ '',
76
+ 'Options:',
77
+ ' --list Print the catalog and install state.',
78
+ ' --set <list> Converge on a comma-separated set of items.',
79
+ ' A token is <name> or <provider>/<name>.',
80
+ ' Use --set "" to remove everything.',
81
+ ' --preview <list> Open previews.',
82
+ ' --sync=installed Refetch installed copies from their sources.',
83
+ ' --sync=catalog Rebuild the available list and categories from sources.',
84
+ ' --sync=stale Compare installed copies against their sources by hash.',
85
+ ' --design-dir <path> Add a design.md source. Accepts <source-id>=<path>',
86
+ ' and may be repeated.',
87
+ ' --lang en|ko Display language for this run.',
88
+ ' --dry-run Change nothing; print what would happen.',
89
+ ' -h, --help Print this help and exit.',
90
+ '',
91
+ '--list, --set, --preview, and --sync each choose an action, so only one',
92
+ 'may be given at a time.',
93
+ ],
94
+ 'usage.update': [
95
+ 'Usage: npx @rch4com/agent-setup update [options]',
96
+ '',
97
+ 'Compares hashes against the install record and refreshes only the managed',
98
+ 'files that are still exactly as we wrote them. Files you edited are left',
99
+ 'alone and reported as drift.',
100
+ '',
101
+ 'Options:',
102
+ ' --force Overwrite drifted files too. The working tree must be clean',
103
+ ' (git is the only way back).',
104
+ ' --lang en|ko Display language for this run.',
105
+ ' --dry-run Change nothing; print what would happen.',
106
+ ' -h, --help Print this help and exit.',
107
+ ],
108
+ 'usage.status': [
109
+ 'Usage: npx @rch4com/agent-setup status [options]',
110
+ '',
111
+ 'Shows intent (the install record), reality (a scan of the repository), and',
112
+ 'the running tool version side by side. Changes nothing.',
113
+ '',
114
+ 'Options:',
115
+ ' --json Print machine-readable output (for CI checks).',
116
+ ' --lang en|ko Display language for this run.',
117
+ ' -h, --help Print this help and exit.',
118
+ ],
119
+
120
+ 'error.unknownArg': 'Unknown argument: {token}\n\n{usage}',
121
+ 'error.flagTakesNoValue': '{name} takes no value: {token}\n\n{usage}',
122
+ 'error.singleAction': 'Cannot be combined: {given} — give only one action flag at a time.\n\n{usage}',
123
+ 'error.badSkillMode': '--skill-mode must be one of {list}: {value}\n\n{usage}',
124
+ 'error.skillModeNeedsValue': '--skill-mode needs a value ({list}).\n\n{usage}',
125
+ 'error.badLang': '--lang must be one of {list}: {value}\n\n{usage}',
126
+ 'error.langNeedsValue': '--lang needs a value ({list}).\n\n{usage}',
127
+ 'error.needsValue': '{name} needs a value.',
128
+ 'error.setNeedsValue': '--set needs an item list. Use --set "" to remove everything.',
129
+ 'error.badSync': 'Use --sync=installed|catalog|stale.',
130
+
131
+ 'status.installed': 'Installed',
132
+ 'status.partial': 'Partial',
133
+ 'status.absent': 'Not installed',
134
+
135
+ 'change.install': 'Install',
136
+ 'change.complete': 'Finish',
137
+ 'change.uninstall': 'Remove',
138
+
139
+ 'apply.noChanges': 'Nothing to change.',
140
+ 'apply.finalState': 'Final state:',
141
+ 'apply.seeGitDiff': 'Use git diff to review the config changes.',
142
+ 'error.unknownItem': 'Unknown item: {id}',
143
+
144
+ 'error.depsMissing': [
145
+ 'This feature needs dependencies. Run one of:',
146
+ ' ./setup-agents.sh --tui',
147
+ ' npm install --prefix agent-installer',
148
+ ],
149
+
150
+ 'log.record.write': 'install record written: {path}',
151
+ 'error.recordUnreadable': 'Cannot read {path} ({code})',
152
+ 'error.recordNotJson': 'Cannot read {path} — not JSON ({message})',
153
+ 'error.recordVersion': '{path} has format version {found}. This tool uses {expected} — upgrade the tool or recreate the record.',
154
+
155
+ 'log.repoRoot': 'repository root: {path}',
156
+ 'log.noGlobalWrites': 'Global config paths are never read or modified.',
157
+ 'log.dir.create': 'create directory: {path}',
158
+ 'log.file.keep': 'keep existing file: {path}',
159
+ 'log.file.create': 'create file: {path}',
160
+ 'log.file.update': 'update file: {path}',
161
+ 'log.file.userEdited': 'edited by you — left alone: {path}',
162
+ 'log.block.keep': 'managed block present: {path}',
163
+ 'log.block.add': 'add managed block: {path}',
164
+ 'log.block.update': 'update managed block: {path}',
165
+ 'log.block.missing': 'no managed block — left alone: {path}',
166
+ 'log.block.userEdited': 'block edited by you — left alone: {path}',
167
+ 'log.json.keep': 'config key present: {path} — {key}',
168
+ 'log.json.add': 'add config key: {path} — {key}',
169
+ 'log.ignore.keep': '.gitignore entries present: {entries}',
170
+ 'log.ignore.add': 'add .gitignore entries: {entries}',
171
+ 'log.warn.unreadable': 'warning: cannot read {path}, skipping ({code})',
172
+ 'log.warn.noRootObject': 'warning: no root object in {path}, skipping',
173
+ 'log.warn.ignoreShadowed': 'warning: "{parent}" in .gitignore excludes the whole directory, so the "{entry}" negation has no effect. Change it to "{parent}/*" to include it again.',
174
+ 'log.skill.linkOk': '{tool} skill link present: {path}',
175
+ 'log.skill.linkCreate': '{tool} skill link created: {path} -> {target}',
176
+ 'log.skill.copyCreate': '{tool} skill copy created: {path}',
177
+ 'log.skill.copySync': '{tool} skill copy synced: {path}',
178
+ 'log.skill.plan': '{tool} skill adapter to be created: {path} ({mode})',
179
+ 'log.skill.warnForeignLink': 'warning: {path} is a link pointing elsewhere. Leaving it alone.',
180
+ 'log.skill.warnUnmanaged': 'warning: {path} already exists and is not managed by agent-kit. Leaving it alone.',
181
+ 'log.skill.linkFellBack': 'warning: {tool} link creation failed; falling back to copying.',
182
+
183
+ 'msg.readFailed': 'read failed',
184
+ 'msg.noRootObject': 'no root object',
185
+ 'msg.foreignLink': 'link pointing elsewhere',
186
+ 'msg.unmanagedExisting': 'existing entry not under management',
187
+ 'msg.noManagedBlock': 'no managed block',
188
+ 'msg.linkFailed': 'link creation failed: {message}',
189
+
190
+ 'bootstrap.failures': '{count} failed:',
191
+ 'bootstrap.done': 'done.',
192
+ 'bootstrap.sharedGuide': 'shared guide: AGENTS.md',
193
+ 'bootstrap.sharedSkills': 'shared skills: .agents/skills/',
194
+ 'bootstrap.tools': 'tools covered: {list}',
195
+ 'bootstrap.repoOnly': 'Every per-tool config was created inside this repository only.',
196
+ 'bootstrap.noOverwrite': 'No existing config file was overwritten.',
197
+ 'bootstrap.record': 'install record: {path}',
198
+
199
+ 'error.badSkillModeRuntime': '--skill-mode must be one of {list}: {value}',
200
+
201
+ // status·update 화면 문구. status.row.*는 표 정렬용 공백을 포함한다 — tool과
202
+ // items의 값 시작 열이 같아야 한다(status.test.mjs의 열 정렬 테스트가 검증).
203
+ 'status.noRecord': 'No install record.',
204
+ 'status.noRecord.hint1': ' To bring this repository under the record, run bootstrap --adopt.',
205
+ 'status.noRecord.hint2': ' It creates nothing and records only files that match the templates.',
206
+ 'status.row.tool': 'tool {version}',
207
+ 'status.version.pinned': '{pinned} pinned · running {running}',
208
+ 'status.version.latest': '{pinned} pinned · running {running} · latest {latest}',
209
+ 'status.hint.update': ' → update can move the pinned version',
210
+ 'status.row.files': 'managed {current} of {total} current · {pending} pending · {drift} edited by you',
211
+ 'status.hint.pending': ' → update',
212
+ 'status.hint.drift': ' → update leaves files you edited alone',
213
+ 'status.row.items': 'items installed {list}',
214
+ 'status.row.recordOnly': ' record only {list}',
215
+ 'status.row.repoOnly': ' repo only {list}',
216
+ 'status.none': '(none)',
217
+ 'update.versionMove': 'pinned {pinned} → running {running}',
218
+ 'update.summary': '{updated} updated · {created} created · {drift} drifted',
219
+ 'update.driftHeader': 'drift (left alone)',
220
+ 'update.driftHint': 'run update --force to take the latest templates (the working tree must be clean)',
221
+ 'error.forceNeedsCleanTree': '--force needs a clean working tree. git is the only way back, so overwriting uncommitted changes cannot be undone.',
222
+ 'error.noRecordForUpdate': '{path} does not exist. To bring this repository under the record, run bootstrap --adopt first — it creates nothing and only writes the record.',
223
+
224
+ // 항목 카탈로그 — note·unsupported 사유·detect()의 detail은 만들어지는 시점에
225
+ // 로케일을 모른다. catalog.mjs·lib/items/*가 여기 키를 담아 두고, 표시하는
226
+ // 쪽이 렌더 시점에 t나 toText로 푼다.
227
+ 'item.unsupported.claudePlugin': 'Claude Code plugin only',
228
+ 'item.unsupported.claudeSkill': 'Claude Code skill install',
229
+ 'item.unsupported.ponytailUser': 'upstream installs it per user (codex/copilot plugin, gemini extension)',
230
+ 'item.unsupported.ponytailRules': 'no plugin mechanism — copy the ruleset into AGENTS.md by hand',
231
+ 'item.mcp.partial': 'registered: {present} / missing: {missing}',
232
+ 'item.plugin.partial': 'wired: {present} / missing: {missing}',
233
+ 'item.plugin.deferred': 'recorded in config — it downloads on the next Claude Code run',
234
+ 'item.scanFailed': 'detection failed: {message}',
235
+ 'log.mcp.add': ' [dry-run] register {name} in the {cli} config',
236
+ 'log.mcp.remove': ' [dry-run] remove {name} from the {cli} config',
237
+ 'log.plugin.add': ' [dry-run] add plugin {name} to the {cli} config',
238
+ 'log.plugin.remove': ' [dry-run] remove plugin {name} from the {cli} config',
239
+
240
+ 'item.mcp.notion.note': 'auth: OAuth on first use in each CLI',
241
+ 'item.mcp.vercel.note': 'auth: OAuth on first use (only approved clients can connect)',
242
+ 'item.mcp.supabase.note': 'auth: OAuth dynamic registration. Add ?project_ref=<id> to the URL to pin a project',
243
+ 'item.mcp.codebase-memory.note': 'needs the codebase-memory-mcp binary on PATH. Install: https://github.com/DeusData/codebase-memory-mcp (install.sh / install.ps1)',
244
+ 'item.mcp.graphify.note': 'needs the graphify-mcp binary on PATH. Install: uv tool install "graphifyy[mcp]". Reads graphify-out/graph.json — build it first with /graphify .',
245
+ 'item.mcp.headroom.note': 'needs the headroom binary on PATH. Install: uv tool install --python 3.13 "headroom-ai[proxy,mcp]". The proxy (headroom wrap) is a way of launching, not repo config',
246
+ 'item.plugin.superpowers.note': 'official marketplace plugin',
247
+ 'item.plugin.mattpocock-skills.note': '22 engineering and productivity skills (tdd, code-review, research, …)',
248
+ 'item.plugin.ponytail.note': 'lean-code ruleset. Wires the Claude Code plugin and the OpenCode plugin entry, both repo-scoped',
249
+ 'item.plugin.ecc.note': 'skills, instincts, memory and security for agent harnesses. Installed as a plugin — its skills/ holds 281 entries, too many to copy into the shared skills directory',
250
+ 'item.plugin.impeccable.note': 'design language for AI harnesses. Plugin route on purpose: npx impeccable install replaces .claude/skills and breaks the shared .agents/skills link',
251
+ 'item.plugin.understand-anything.note': 'turns the codebase into an explorable knowledge graph. The script for other CLIs clones into ~/.understand-anything (user scope), so only the plugin is wired',
252
+ 'item.skill.caveman.note': 'cuts output tokens by answering in caveman-speak; code and errors stay byte-exact. Toggle with /caveman',
253
+ 'item.skill.taste.note': 'anti-slop frontend skill for landing pages, portfolios and redesigns (skill name: design-taste-frontend)',
254
+ 'item.skill.karpathy.note': 'behavioral guidelines that curb overbuilding and vague success criteria',
255
+ 'item.skill.gstack.note': 'repo-local clone + setup (needs bash; Git Bash on Windows). Runtime state (~/.gstack) may be created globally.',
256
+ 'item.skill.gsd.note': 'npx @opengsd/gsd-core, installed per project',
257
+ 'error.gstackClone': 'gstack clone failed: {output}',
258
+ 'error.gstackSetup': 'gstack setup failed: {output}',
259
+ 'error.gsdInstall': 'GSD install failed: {output}',
260
+ 'error.gsdUninstall': 'GSD uninstall failed: {output}',
261
+ 'error.ponytailOpencodePlugin': '{file}: the plugin key is not an array. Leaving it alone — add "@dietrichgebert/ponytail" by hand.',
262
+ 'error.registrySkillInstall': 'skill registry install failed ({skill}): {output}',
263
+ 'error.itemFieldMissing': '{file}: {field} missing',
264
+ 'error.itemReasonMissing': '{id}: needs a reason for unsupported CLI \'{cli}\' (unsupported.{cli})',
265
+ 'error.shellQuote': 'The argument contains a double quote that cannot be passed to the shell: {value}',
266
+
267
+ // TUI 탭 이름. rows.mjs의 SECTION_ORDER id(action·plugin·mcp·skill·design)를
268
+ // 그대로 따른다 — 탭 순서·검색 스코프는 id로 갈리고, 여기서는 표시만 맡는다.
269
+ 'section.action': 'ACTION',
270
+ 'section.plugin': 'PLUGIN',
271
+ 'section.mcp': 'MCP',
272
+ 'section.skill': 'SKILL',
273
+ 'section.design': 'DESIGN.MD',
274
+
275
+ // TUI 목록·검토 화면 문구. item.*는 agentHint가, action.*.label/hint는
276
+ // buildActions가 쓴다. action.*.label과 section.*는 LABEL_WIDTH(24) 안에
277
+ // 들어야 한다 — i18n.test.mjs가 검사한다.
278
+ 'tui.counts': 'selected {picked} / {total}',
279
+ 'tui.search.prefix': 'Search › ',
280
+ 'tui.search.placeholder': 'type to search · ↓ for the list',
281
+ 'tui.empty.filtered': ' Nothing matches in this tab. Press Tab to try another.',
282
+ 'tui.empty.none': ' Nothing here.',
283
+ 'tui.hint.search': 'type to search (spaces included) ↓ to list Tab switch tab Esc clear',
284
+ 'tui.hint.list': 'Space select ↑↓ move (↑ at top = search) Tab switch tab Enter run/submit Ctrl+A all Ctrl+O preview',
285
+ 'tui.review.title': 'Review — {count} change(s)',
286
+ 'tui.review.more': ' …and {count} more',
287
+ 'tui.review.hint': 'Enter apply Esc cancel',
288
+ 'tui.notItemRow': 'Press Enter to run this row.',
289
+ 'tui.noPreview': 'This item has no preview.',
290
+ 'tui.opened': 'Opened: {target}',
291
+ 'tui.noChanges': 'Nothing to change.',
292
+ 'tui.submitCancelled': 'Submission cancelled.',
293
+ 'tui.toggledAll': 'Selected every visible item in the {tab} tab.',
294
+ 'tui.toggledNone': 'Cleared every visible item in the {tab} tab.',
295
+ 'tui.nonInteractive': 'The interactive screen only opens in a terminal. --list and --set work too.',
296
+ 'tui.applyHeader': '{count} change(s) to apply{suffix}:',
297
+ 'tui.pressAnyKey': 'Press any key to continue…',
298
+ 'tui.confirmSuffix': ' [y/N] ',
299
+
300
+ // 언어 행(action.language) 전용. 저장 성공·dry-run·실패·강제 로케일 네 갈래를 덮는다.
301
+ 'tui.lang.saved': 'Saved to {path}',
302
+ 'tui.lang.dryRun': 'dry-run — applied to this session only',
303
+ 'tui.lang.saveFailed': 'Could not save the language: {message}',
304
+ 'tui.lang.overridden': '--lang / AGENT_SETUP_LANG still wins on the next run.',
305
+
306
+ 'item.location.user': 'installs to: user global',
307
+ // 커버리지는 전부 지원할 때도 찍는다 — 표시가 없는 것과 "10/10"은 뜻이 다르다.
308
+ 'item.cliCoverage': 'CLI {covered}/{total}',
309
+ 'item.unsupportedList': 'not wired for {count}: {groups}',
310
+ 'item.unsupportedGroup': '{clis} — {why}',
311
+
312
+ // 상주 언어 행 — 작업 탭 맨 위. hint는 현재 로케일의 자기 이름을 담는다.
313
+ 'action.language.label': 'Language',
314
+ 'action.language.hint': '{current} · Enter to change',
315
+ 'action.bootstrap.label': 'Run bootstrap',
316
+ 'action.bootstrap.hint': 'guides · skills · per-tool config · {present} of {total} files exist',
317
+ 'action.sync.installed.label': 'Update installed',
318
+ 'action.sync.installed.hint': 'refetch installed design.md from their sources',
319
+ 'action.sync.catalog.label': 'Refresh catalog',
320
+ 'action.sync.catalog.hint': 'rebuild the design.md list and categories from sources',
321
+ 'action.sync.stale.label': 'Check stale',
322
+ 'action.sync.stale.hint': 'compare installed copies against their sources by hash',
323
+
324
+ // design.md 서브시스템. category.*는 우리가 만든 catch-all id(__other·__local)를
325
+ // 화면에 낼 때만 쓴다 — 공급자가 준 카테고리는 이미 영어 데이터라 번역하지 않는다.
326
+ 'category.other': 'Other',
327
+ 'category.local': 'In-house',
328
+ // 항목 탭 안쪽의 성격 그룹. rows.mjs의 GROUP_ORDER가 표시 순서를 정한다.
329
+ 'category.token': 'Token savings',
330
+ 'category.context': 'Code understanding',
331
+ 'category.style': 'Design taste',
332
+ 'category.flow': 'Way of working',
333
+ 'category.service': 'External services',
334
+ 'design.localSuffix': '{id} (local)',
335
+ 'design.localTag': 'local',
336
+ 'design.unknownItem': ' unknown item: {token}',
337
+ 'design.ambiguous': ' ambiguous name \'{token}\' — pick a provider: {options}',
338
+ 'design.provider.count': ' {label}: {count}',
339
+ 'design.provider.failed': ' {label}: failed — {message}',
340
+ 'design.catalog.empty': 'Nothing fetched; leaving the catalog alone.',
341
+ 'design.catalog.planned': ' [dry-run] would refresh to {total} entries',
342
+ 'design.catalog.updated': 'catalog refreshed: {total} entries',
343
+ 'design.none': 'No design.md is installed.',
344
+ 'design.updated': ' ✔ updated {label}',
345
+ 'design.updateFailed': ' ✖ update {label} — {message}',
346
+ 'design.sourceCheckFailed': ' ? {label}: cannot reach the source',
347
+ 'design.allCurrent': 'All installed copies are current.',
348
+ 'design.staleList': '{count} stale: {names}',
349
+ 'design.staleConfirm': 'Update {count} now?',
350
+ 'design.unknownSync': 'Unknown sync action: {action}',
351
+ 'design.write': ' [dry-run] write design-md/{provider}/{name}/DESIGN.md',
352
+ 'design.remove': ' [dry-run] remove design-md/{provider}/{name}',
353
+ 'design.nameClash': ' design.md names collide; using \'{picked}\': {path}',
354
+ 'design.sourceIdClash': ' design.md source ids collide; using \'{picked}\': {path}',
355
+ 'design.noDesignFile': ' no DESIGN.md in this design.md source: {path}',
356
+ 'design.pathMissing': ' cannot find the design.md path: {path}',
357
+ 'design.noPreviewUrl': ' {label}: no preview URL.',
358
+ 'design.openFailed': ' {label}: could not open — open it yourself: {target}',
359
+ 'design.notOpenable': 'not an openable target',
360
+
361
+ 'error.catalogUnreadable': 'Cannot read the design.md catalog: {path}\n{message}\nRebuild it with `design --sync=catalog`.',
362
+ 'error.responseTooLarge': 'The response exceeded the {limit} byte cap: {url}',
363
+ 'error.badDesignId': 'Invalid design.md identifier: {provider}/{name}',
364
+ // resolveTokens(--set/--preview 토큰 해석)의 예외 전용 — design.ambiguous와
365
+ // 같은 상황이지만 로그 줄이 아니라 최상위 예외라 들여쓰기가 없다.
366
+ 'error.ambiguousDesignId': 'ambiguous name \'{token}\' — pick a provider: {options}',
367
+ 'error.designDownload': '{provider}/{name}: DESIGN.md download failed',
368
+ 'error.readmeFetch': 'README fetch failed: HTTP {status}',
369
+ }