@rch4com/agent-setup 1.16.0 → 1.17.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/install.mjs +4 -3
- package/lib/i18n/catalog/en.mjs +9 -2
- package/lib/i18n/catalog/ko.mjs +9 -2
- package/lib/items/global.ponytail.mjs +1 -1
- package/lib/items/global.superpowers.mjs +1 -1
- package/lib/items/plugin.mattpocock-skills.mjs +1 -1
- package/lib/items/plugin.superpowers.mjs +1 -1
- package/lib/labels.mjs +9 -0
- package/lib/tui/progress.mjs +3 -2
- package/lib/tui/render.mjs +50 -11
- package/lib/tui/rows.mjs +13 -4
- package/lib/tui/run.mjs +5 -1
- package/package.json +1 -1
package/install.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { withDeps } from './lib/deps.mjs'
|
|
|
6
6
|
import { LocalizedError, createT, isLocaleForced, resolveLocale, toText } from './lib/i18n/index.mjs'
|
|
7
7
|
import { detectLocale } from './lib/i18n/detect.mjs'
|
|
8
8
|
import { labelWidth, pad } from './lib/width.mjs'
|
|
9
|
+
import { scopedLabel } from './lib/labels.mjs'
|
|
9
10
|
import {
|
|
10
11
|
bootstrapUsage, designUsage, rootUsage, statusUsage, updateUsage,
|
|
11
12
|
parseBootstrapArgs, parseDesignArgs, parseRootArgs, parseStatusArgs, parseUpdateArgs, preScanLang,
|
|
@@ -60,7 +61,7 @@ async function runClassic(root, { dryRun, listOnly, setArg, t }) {
|
|
|
60
61
|
if (listOnly) {
|
|
61
62
|
for (const s of states) {
|
|
62
63
|
const detail = toText(t, s.detail)
|
|
63
|
-
console.log(`${pad(t(`status.${s.status}`), statusWidth)} ${s.item.id} — ${s.item
|
|
64
|
+
console.log(`${pad(t(`status.${s.status}`), statusWidth)} ${s.item.id} — ${scopedLabel(s.item, t)}${detail ? ` (${detail})` : ''}`)
|
|
64
65
|
}
|
|
65
66
|
return
|
|
66
67
|
}
|
|
@@ -97,12 +98,12 @@ async function runClassic(root, { dryRun, listOnly, setArg, t }) {
|
|
|
97
98
|
},
|
|
98
99
|
})
|
|
99
100
|
for (const r of results) {
|
|
100
|
-
console.log(`${r.ok ? '✔' : '✖'} ${t(`change.${r.action}`)} ${r.item
|
|
101
|
+
console.log(`${r.ok ? '✔' : '✖'} ${t(`change.${r.action}`)} ${scopedLabel(r.item, t)}${r.message ? ` — ${toText(t, r.message)}` : ''}`)
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
const after = await scan(root, items)
|
|
104
105
|
console.log(`\n${t('apply.finalState')}`)
|
|
105
|
-
for (const s of after) console.log(` ${pad(t(`status.${s.status}`), statusWidth)} ${s.item
|
|
106
|
+
for (const s of after) console.log(` ${pad(t(`status.${s.status}`), statusWidth)} ${scopedLabel(s.item, t)}`)
|
|
106
107
|
console.log(`\n${t('apply.seeGitDiff')}`)
|
|
107
108
|
if (results.some((r) => !r.ok)) process.exitCode = 1
|
|
108
109
|
}
|
package/lib/i18n/catalog/en.mjs
CHANGED
|
@@ -234,10 +234,10 @@ export default {
|
|
|
234
234
|
'item.unsupported.mattpocockRegistry': 'upstream also documents npx skills add (the shared .agents/skills), but this item wires the Claude Code plugin only',
|
|
235
235
|
'item.unsupported.upstreamNone': 'no official install path upstream',
|
|
236
236
|
'item.unsupported.superpowersSeparate': 'upstream supports it via a separate per-harness install — this item wires the Claude Code plugin only',
|
|
237
|
-
'item.unsupported.superpowersGlobalClaude': 'covered by the
|
|
237
|
+
'item.unsupported.superpowersGlobalClaude': 'covered by the repo-scope item superpowers (repo)',
|
|
238
238
|
'item.unsupported.superpowersGlobalGrok': 'upstream 6.3.0 ships grok plugin install superpowers@xai-official, but the detect/remove paths are unverified — not wired yet',
|
|
239
239
|
'item.unsupported.superpowersGlobalKimi': 'upstream installs only via the interactive /plugins command — no headless path',
|
|
240
|
-
'item.unsupported.globalProjectItem': 'covered by the
|
|
240
|
+
'item.unsupported.globalProjectItem': 'covered by the repo-scope item {item}',
|
|
241
241
|
'item.unsupported.ponytailGlobalGrok': 'upstream ships grok plugin install DietrichGebert/ponytail --trust, but the detect/remove paths are unverified — not wired yet',
|
|
242
242
|
'item.unsupported.bkitPort': 'upstream ships separate ports (bkit-codex / bkit-gemini) installed apart from this plugin; the Gemini port is still a Gemini CLI extension and has had no update since the 2026-06-18 sunset',
|
|
243
243
|
'item.unsupported.impeccableJunction': 'upstream supports it, but npx impeccable install breaks the shared .agents/skills link, so only the plugin route is wired',
|
|
@@ -331,11 +331,13 @@ export default {
|
|
|
331
331
|
'tui.review.title': 'Review — {count} change(s)',
|
|
332
332
|
'tui.review.more': ' …and {count} more',
|
|
333
333
|
'tui.review.hint': 'Enter apply Esc cancel Ctrl+Q quit',
|
|
334
|
+
'tui.review.globalWarning': '{count} machine-global change(s) — settings outside this repo (~/.codex etc.) will change',
|
|
334
335
|
'tui.notItemRow': 'Press Enter to run this row.',
|
|
335
336
|
'tui.noPreview': 'This item has no preview.',
|
|
336
337
|
'tui.opened': 'Opened: {target}',
|
|
337
338
|
'tui.noChanges': 'Nothing to change.',
|
|
338
339
|
'tui.submitCancelled': 'Submission cancelled.',
|
|
340
|
+
'tui.selectedGlobal': 'Machine-global item — applies to this whole computer',
|
|
339
341
|
'tui.toggledAll': 'Selected every visible item in the {tab} tab.',
|
|
340
342
|
'tui.toggledNone': 'Cleared every visible item in the {tab} tab.',
|
|
341
343
|
// 배타 묶음에서 형제가 함께 꺼졌을 때. 조용히 뒤집히면 오조작으로 읽힌다.
|
|
@@ -373,6 +375,8 @@ export default {
|
|
|
373
375
|
'tui.filter.empty': ' Nothing in this tab is wired for {cli}.',
|
|
374
376
|
|
|
375
377
|
'item.location.user': 'installs to: user global',
|
|
378
|
+
'label.scope.project': '(repo)',
|
|
379
|
+
'label.scope.user': '(global)',
|
|
376
380
|
'item.target': 'file: {path}',
|
|
377
381
|
// 커버리지는 전부 지원할 때도 찍는다 — 표시가 없는 것과 "10/10"은 뜻이 다르다.
|
|
378
382
|
'item.cliCoverage': 'CLI {covered}/{total}',
|
|
@@ -413,6 +417,9 @@ export default {
|
|
|
413
417
|
// 대상 파일이 하나뿐인 배타 묶음이라 라디오 표시와 짝이 된다.
|
|
414
418
|
'category.commit': 'Commit template · .gitmessage.txt · pick one',
|
|
415
419
|
'category.service': 'External services',
|
|
420
|
+
// PLUGIN 탭 전용 범위 그룹 — 검토 화면(renderReview)의 소제목도 같은 키를 쓴다.
|
|
421
|
+
'category.scope-project': 'Repo scope — this repository only',
|
|
422
|
+
'category.scope-user': 'Machine global — applies to this whole computer',
|
|
416
423
|
'design.localSuffix': '{id} (local)',
|
|
417
424
|
'design.localTag': 'local',
|
|
418
425
|
'design.unknownItem': ' unknown item: {token}',
|
package/lib/i18n/catalog/ko.mjs
CHANGED
|
@@ -227,10 +227,10 @@ export default {
|
|
|
227
227
|
'item.unsupported.mattpocockRegistry': '상류가 npx skills add(공유 .agents/skills)도 안내하지만 이 항목은 Claude Code 플러그인만 배선합니다',
|
|
228
228
|
'item.unsupported.upstreamNone': '상류에 공식 설치 경로가 없습니다',
|
|
229
229
|
'item.unsupported.superpowersSeparate': '상류가 하니스별 별도 설치로 지원합니다 — 이 항목은 Claude Code 플러그인만 배선합니다',
|
|
230
|
-
'item.unsupported.superpowersGlobalClaude': '
|
|
230
|
+
'item.unsupported.superpowersGlobalClaude': '저장소 범위 항목 superpowers (저장소)가 담당합니다',
|
|
231
231
|
'item.unsupported.superpowersGlobalGrok': '상류 6.3.0에 grok plugin install superpowers@xai-official 명령이 있지만 감지·제거 경로를 실측하지 못해 아직 배선하지 않습니다',
|
|
232
232
|
'item.unsupported.superpowersGlobalKimi': '상류 설치가 대화형 /plugins 명령뿐이라 헤드리스 경로가 없습니다',
|
|
233
|
-
'item.unsupported.globalProjectItem': '
|
|
233
|
+
'item.unsupported.globalProjectItem': '저장소 범위 항목 {item}이 담당합니다',
|
|
234
234
|
'item.unsupported.ponytailGlobalGrok': '상류 명령(grok plugin install DietrichGebert/ponytail --trust)이 있지만 감지·제거 경로 미실측이라 배선하지 않습니다',
|
|
235
235
|
'item.unsupported.bkitPort': '상류가 별도 배포판(bkit-codex·bkit-gemini)으로 지원합니다 — 이 플러그인과 별개 설치입니다. Gemini판은 Gemini CLI 확장 그대로이고 2026-06-18 종료 이후 갱신이 없습니다',
|
|
236
236
|
'item.unsupported.impeccableJunction': '상류는 지원하지만 npx impeccable install이 공유 .agents/skills 연결을 끊어 플러그인 경로만 배선합니다',
|
|
@@ -321,11 +321,13 @@ export default {
|
|
|
321
321
|
'tui.review.title': '제출 검토 — 변경 {count}건',
|
|
322
322
|
'tui.review.more': ' …외 {count}건',
|
|
323
323
|
'tui.review.hint': 'Enter 적용 Esc 취소 Ctrl+Q 종료',
|
|
324
|
+
'tui.review.globalWarning': '머신 전역 변경 {count}건 — 이 저장소 밖 설정(~/.codex 등)이 바뀝니다',
|
|
324
325
|
'tui.notItemRow': '이 행은 Enter로 실행합니다.',
|
|
325
326
|
'tui.noPreview': '이 항목은 미리보기를 제공하지 않습니다.',
|
|
326
327
|
'tui.opened': '열었습니다: {target}',
|
|
327
328
|
'tui.noChanges': '변경할 항목이 없습니다.',
|
|
328
329
|
'tui.submitCancelled': '제출을 취소했습니다.',
|
|
330
|
+
'tui.selectedGlobal': '머신 전역 항목 — 이 컴퓨터 전체에 적용됩니다',
|
|
329
331
|
'tui.toggledAll': '{tab} 탭의 보이는 항목을 모두 선택했습니다.',
|
|
330
332
|
'tui.toggledNone': '{tab} 탭의 보이는 항목을 모두 해제했습니다.',
|
|
331
333
|
// 배타 묶음에서 형제가 함께 꺼졌을 때. en.mjs 쪽 주석 참고.
|
|
@@ -360,6 +362,8 @@ export default {
|
|
|
360
362
|
'tui.filter.empty': ' 이 탭에는 {cli}에 배선되는 항목이 없습니다.',
|
|
361
363
|
|
|
362
364
|
'item.location.user': '설치 위치: 사용자 글로벌',
|
|
365
|
+
'label.scope.project': '(저장소)',
|
|
366
|
+
'label.scope.user': '(전역)',
|
|
363
367
|
'item.target': '대상 파일: {path}',
|
|
364
368
|
// en.mjs 쪽 주석 참고.
|
|
365
369
|
'item.cliCoverage': 'CLI {covered}/{total}',
|
|
@@ -398,6 +402,9 @@ export default {
|
|
|
398
402
|
// 헤더 하나가 무엇을 고르는 자리이고 규칙이 무엇인지 함께 말한다. en.mjs 쪽 주석 참고.
|
|
399
403
|
'category.commit': '커밋 메시지 템플릿 · .gitmessage.txt · 하나만 고릅니다',
|
|
400
404
|
'category.service': '외부 서비스',
|
|
405
|
+
// PLUGIN 탭 전용 범위 그룹 — 검토 화면(renderReview)의 소제목도 같은 키를 쓴다.
|
|
406
|
+
'category.scope-project': '저장소 범위 — 이 저장소에만 적용',
|
|
407
|
+
'category.scope-user': '머신 전역 — 이 컴퓨터 전체에 적용',
|
|
401
408
|
'design.localSuffix': '{id} (로컬)',
|
|
402
409
|
'design.localTag': '로컬',
|
|
403
410
|
'design.unknownItem': ' 알 수 없는 항목: {token}',
|
|
@@ -28,7 +28,7 @@ import { msg } from '../i18n/index.mjs'
|
|
|
28
28
|
const SUPPORTS = ['codex', 'gemini', 'copilot']
|
|
29
29
|
|
|
30
30
|
const CONFIG = {
|
|
31
|
-
id: 'global.ponytail', label: 'Ponytail
|
|
31
|
+
id: 'global.ponytail', label: 'Ponytail', group: '__token',
|
|
32
32
|
note: 'item.global.ponytail.note',
|
|
33
33
|
pluginName: 'ponytail',
|
|
34
34
|
marketplaceRepo: 'DietrichGebert/ponytail',
|
|
@@ -26,7 +26,7 @@ import { msg } from '../i18n/index.mjs'
|
|
|
26
26
|
const SUPPORTS = ['codex', 'gemini', 'opencode', 'copilot']
|
|
27
27
|
|
|
28
28
|
const CONFIG = {
|
|
29
|
-
id: 'global.superpowers', label: 'superpowers
|
|
29
|
+
id: 'global.superpowers', label: 'superpowers', group: '__flow',
|
|
30
30
|
note: 'item.global.superpowers.note',
|
|
31
31
|
pluginName: 'superpowers',
|
|
32
32
|
marketplaceRepo: 'obra/superpowers-marketplace',
|
|
@@ -7,7 +7,7 @@ import { msg } from '../i18n/index.mjs'
|
|
|
7
7
|
// 아니라 "우리가 플러그인 쪽을 골랐다"이고, 사유가 그렇게 말해야 레지스트리
|
|
8
8
|
// 항목으로 옮길 수 있다는 것도 화면에서 읽힌다.
|
|
9
9
|
export default definePlugin({
|
|
10
|
-
id: 'plugin.mattpocock-skills', label: 'Matt Pocock
|
|
10
|
+
id: 'plugin.mattpocock-skills', label: 'Matt Pocock', group: '__flow',
|
|
11
11
|
exclusive: 'mattpocock',
|
|
12
12
|
installId: 'mattpocock-skills@mattpocock',
|
|
13
13
|
detectIds: ['mattpocock-skills@mattpocock'],
|
|
@@ -13,7 +13,7 @@ import { msg } from '../i18n/index.mjs'
|
|
|
13
13
|
const SEPARATE = ['codex', 'gemini', 'opencode', 'kimi', 'copilot', 'grok']
|
|
14
14
|
|
|
15
15
|
export default definePlugin({
|
|
16
|
-
id: 'plugin.superpowers', label: 'superpowers
|
|
16
|
+
id: 'plugin.superpowers', label: 'superpowers', group: '__flow',
|
|
17
17
|
exclusive: 'superpowers',
|
|
18
18
|
installId: 'superpowers@claude-plugins-official',
|
|
19
19
|
detectIds: ['superpowers@claude-plugins-official', 'superpowers@superpowers-marketplace'],
|
package/lib/labels.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 플러그인 항목 라벨에 설치 범위 접미사를 붙인다. 목록 밖(검토·진행·결과
|
|
2
|
+
// 화면)에는 범위 그룹 헤더가 없어, 같은 이름의 두 판(superpowers 저장소/전역)을
|
|
3
|
+
// 이 접미사만이 가른다. import가 없는 잎 모듈이어야 한다 — install.mjs가
|
|
4
|
+
// 정적으로 당기므로, 의존성이 들어오면 npm install 없이 도는 부트스트랩
|
|
5
|
+
// (bootstrap.isolation.test.mjs)이 깨진다.
|
|
6
|
+
export function scopedLabel(item, t) {
|
|
7
|
+
if (item?.category !== 'plugin') return item?.label ?? ''
|
|
8
|
+
return `${item.label} ${t(`label.scope.${item.scope ?? 'project'}`)}`
|
|
9
|
+
}
|
package/lib/tui/progress.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// 실제 시간에 묶여 "14초 경과"를 검증할 방법이 없다.
|
|
5
5
|
import { createT } from '../i18n/index.mjs'
|
|
6
6
|
import { cut, labelWidth, pad, width } from '../width.mjs'
|
|
7
|
+
import { scopedLabel } from '../labels.mjs'
|
|
7
8
|
|
|
8
9
|
const ESC = String.fromCharCode(27)
|
|
9
10
|
const DIM = `${ESC}[2m`
|
|
@@ -66,7 +67,7 @@ function entryLines(entry, w, actionWidth, now, t) {
|
|
|
66
67
|
tail = ` ${t('progress.skipped')}`
|
|
67
68
|
}
|
|
68
69
|
const mark = MARK[entry.state] ?? ' '
|
|
69
|
-
const out = [cut(`${mark} ${pad(t(`change.${entry.action}`), actionWidth)} ${entry.item
|
|
70
|
+
const out = [cut(`${mark} ${pad(t(`change.${entry.action}`), actionWidth)} ${scopedLabel(entry.item, t)}${tail}`, w)]
|
|
70
71
|
// 명령은 실행 중일 때만. 끝난 뒤에도 남기면 화면이 명령 목록이 된다.
|
|
71
72
|
if (entry.state === 'running' && entry.command) out.push(cut(` ${entry.command}`, w))
|
|
72
73
|
return out
|
|
@@ -150,7 +151,7 @@ export function plainLine(event, t = createT('en')) {
|
|
|
150
151
|
index: event.index + 1,
|
|
151
152
|
total: event.total,
|
|
152
153
|
action: t(`change.${event.action}`),
|
|
153
|
-
label: event.item
|
|
154
|
+
label: scopedLabel(event.item, t),
|
|
154
155
|
})
|
|
155
156
|
}
|
|
156
157
|
if (event.phase === 'done') {
|
package/lib/tui/render.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { createT } from '../i18n/index.mjs'
|
|
|
6
6
|
import { categoryLabel } from '../design-md/flow.mjs'
|
|
7
7
|
import { cut, pad, width } from '../width.mjs'
|
|
8
8
|
import { detailLines } from './detail.mjs'
|
|
9
|
+
import { scopedLabel } from '../labels.mjs'
|
|
9
10
|
|
|
10
11
|
// 폭 계산은 화면 전용이 아니다 — 비대화형 목록도 같은 열 맞춤이 필요해
|
|
11
12
|
// lib/width.mjs에 있다. 여기서 다시 내보내는 것은 호출부(테스트 포함)가
|
|
@@ -244,23 +245,61 @@ export function renderReview(changes, opts = {}) {
|
|
|
244
245
|
const title = `${t('tui.review.title', { count: changes.length })}${dryRun ? ' (dry-run)' : ''}`
|
|
245
246
|
const lines = [color ? `${BOLD}${cut(title, w)}${RESET}` : cut(title, w), '']
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// 적용 직전 마지막 화면이다. 일부 CLI에만 들어가는 항목은 여기서도 그 사실을
|
|
252
|
-
// 밝힌다 — 목록에서 지나쳤더라도 되돌릴 수 있는 마지막 지점이다.
|
|
253
|
-
// 전부 지원하는 항목은 조용히 둔다: 경고가 흔해지면 아무도 읽지 않는다.
|
|
248
|
+
// 적용 직전 마지막 화면이다. 일부 CLI에만 들어가는 항목은 여기서도 그 사실을
|
|
249
|
+
// 밝힌다 — 목록에서 지나쳤더라도 되돌릴 수 있는 마지막 지점이다.
|
|
250
|
+
// 전부 지원하는 항목은 조용히 둔다: 경고가 흔해지면 아무도 읽지 않는다.
|
|
251
|
+
const changeLine = (c) => {
|
|
254
252
|
const partial = c.item.supports && c.item.supports.length < CLI_IDS.length
|
|
255
253
|
const cov = partial ? ` · ${t('item.cliCoverage', { covered: c.item.supports.length, total: CLI_IDS.length })}` : ''
|
|
256
|
-
|
|
254
|
+
return cut(` ${CHANGE_MARK[c.action] ?? '?'} ${pad(t(`change.${c.action}`), 10)} ${scopedLabel(c.item, t)}${cov}`, w)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 전역 변경이 있을 때만 범위로 가른다 — 전부 저장소 범위인 흔한 경우에
|
|
258
|
+
// 소제목은 잡음이다. 가를 때는 저장소 묶음이 먼저다(목록 화면과 같은 순서).
|
|
259
|
+
const globals = changes.filter((c) => c.item.scope === 'user')
|
|
260
|
+
const split = globals.length > 0
|
|
261
|
+
const groups = split
|
|
262
|
+
? [
|
|
263
|
+
{ header: t('category.scope-project'), list: changes.filter((c) => c.item.scope !== 'user') },
|
|
264
|
+
{ header: t('category.scope-user'), list: globals },
|
|
265
|
+
].filter((g) => g.list.length > 0)
|
|
266
|
+
: [{ header: null, list: changes }]
|
|
267
|
+
|
|
268
|
+
// 소제목·경고 줄이 목록 지면을 갉아먹는다 — 그만큼 떼어 두지 않으면
|
|
269
|
+
// "…외 N건" 판정이 어긋나 화면이 넘친다. 지면이 소제목까지 감당 못 하면
|
|
270
|
+
// 소제목을 버리고 경고만 남긴다 — 경고가 범위 사실을 말하는 최후의 줄이고,
|
|
271
|
+
// 화면이 넘치면 alt 화면 전체가 밀린다.
|
|
272
|
+
const body = reviewBodyHeight(height)
|
|
273
|
+
let overhead = split ? groups.length + 1 : 0
|
|
274
|
+
let showHeaders = split
|
|
275
|
+
if (split && body - 1 - overhead < 1) {
|
|
276
|
+
showHeaders = false
|
|
277
|
+
overhead = 1
|
|
278
|
+
}
|
|
279
|
+
const room = Math.max(1, body - 1 - overhead)
|
|
280
|
+
|
|
281
|
+
let quota = room
|
|
282
|
+
for (const g of groups) {
|
|
283
|
+
// 소제목 지면조차 없으면 위에서 이미 강등돼(showHeaders=false) 이
|
|
284
|
+
// 지점에 오지 않는다 — 여기 온다는 건 소제목 지면은 남았다는 뜻이고,
|
|
285
|
+
// 그때는 목록 지면(quota)이 다해도 소제목만은 낸다: 전역 묶음이
|
|
286
|
+
// 통째로 잘리더라도 그 평면이 있다는 사실은 화면에 남아야 한다
|
|
287
|
+
// (경고 줄이 건수를 마저 말한다).
|
|
288
|
+
if (showHeaders && g.header !== null) lines.push(paint(DIM, cut(` ${g.header}`, w)))
|
|
289
|
+
for (const c of g.list) {
|
|
290
|
+
if (quota <= 0) break
|
|
291
|
+
lines.push(changeLine(c))
|
|
292
|
+
quota--
|
|
293
|
+
}
|
|
257
294
|
}
|
|
258
|
-
|
|
259
|
-
|
|
295
|
+
const shown = room - quota
|
|
296
|
+
if (changes.length > shown) {
|
|
297
|
+
lines.push(paint(DIM, cut(t('tui.review.more', { count: changes.length - shown }), w)))
|
|
260
298
|
} else {
|
|
261
299
|
lines.push('')
|
|
262
300
|
}
|
|
263
|
-
|
|
301
|
+
if (split) lines.push(paint(DIM, cut(t('tui.review.globalWarning', { count: globals.length }), w)))
|
|
302
|
+
while (lines.length < 2 + body) lines.push('')
|
|
264
303
|
|
|
265
304
|
lines.push('')
|
|
266
305
|
lines.push(paint(DIM, cut(t('tui.review.hint'), w)))
|
package/lib/tui/rows.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import { loadCatalog, buildItems, netFetch, CATALOG_PATH } from '../design-md/ca
|
|
|
12
12
|
import { discoverSources, extraDirsFromEnv } from '../design-md/scan.mjs'
|
|
13
13
|
import { refreshCatalog, updateInstalled, findStale, categoryLabel } from '../design-md/flow.mjs'
|
|
14
14
|
import { unsupportedGroups } from './detail.mjs'
|
|
15
|
+
import { scopedLabel } from '../labels.mjs'
|
|
15
16
|
|
|
16
17
|
// 섹션은 탭 이름이자 정렬 키이자 state.tabs의 원소다. 표시 문자열을 그대로
|
|
17
18
|
// 쓰면 번역하는 순간 정렬이 깨진다 — id를 두고 render.mjs가 표시할 때만
|
|
@@ -29,7 +30,7 @@ export const SECTION_ORDER = [ACTION_SECTION, 'plugin', 'mcp', 'skill', 'config'
|
|
|
29
30
|
// __commit은 CONFIG 탭에만 나오지만 규칙은 같다 — 헤더가 "무엇을 고르는
|
|
30
31
|
// 자리인가"를 말한다. 커밋 템플릿은 대상 파일이 하나뿐이라 헤더에 그 파일
|
|
31
32
|
// 이름과 "하나만"이라는 규칙까지 실어, 라디오 표시와 함께 읽히게 한다.
|
|
32
|
-
export const GROUP_ORDER = ['__token', '__context', '__style', '__flow', '__commit', '__service']
|
|
33
|
+
export const GROUP_ORDER = ['__scope-project', '__scope-user', '__token', '__context', '__style', '__flow', '__commit', '__service']
|
|
33
34
|
|
|
34
35
|
function groupRank(group) {
|
|
35
36
|
const i = GROUP_ORDER.indexOf(group)
|
|
@@ -109,7 +110,10 @@ export function designHint(state, multiProvider = false, t) {
|
|
|
109
110
|
export function agentShortHint(item, state, t = createT('en')) {
|
|
110
111
|
const parts = []
|
|
111
112
|
if (state.status !== 'absent') parts.push(t(`status.${state.status}`))
|
|
112
|
-
|
|
113
|
+
// plugin은 수 대신 이름을 나열한다 — 어느 CLI에 설치되는가가 이 탭에서
|
|
114
|
+
// 고르는 기준이고, 가장 긴 나열(codex·gemini·opencode·copilot)도 자리에 든다.
|
|
115
|
+
if (item.category === 'plugin' && item.supports?.length) parts.push(item.supports.join('·'))
|
|
116
|
+
else if (item.supports) parts.push(t('item.cliCoverage', { covered: item.supports.length, total: CLI_IDS.length }))
|
|
113
117
|
return parts.join(' · ')
|
|
114
118
|
}
|
|
115
119
|
|
|
@@ -235,8 +239,13 @@ export function buildRows({ actions = [], agentStates = [], designStates = [], m
|
|
|
235
239
|
itemRow({
|
|
236
240
|
id: s.item.id,
|
|
237
241
|
section: s.item.category,
|
|
238
|
-
|
|
239
|
-
|
|
242
|
+
// plugin 탭은 성격이 아니라 설치 범위로 가른다 — 같은 상류의 두 판
|
|
243
|
+
// (저장소/전역)이 무엇을 건드리는지가 이 탭에서 고르는 기준이다.
|
|
244
|
+
// 표시 계층의 오버라이드다: 항목의 group 필드는 성격의 진실로 남는다.
|
|
245
|
+
group: s.item.category === 'plugin'
|
|
246
|
+
? (s.item.scope === 'user' ? '__scope-user' : '__scope-project')
|
|
247
|
+
: s.item.group ?? null,
|
|
248
|
+
label: scopedLabel(s.item, t),
|
|
240
249
|
hint: agentShortHint(s.item, s, t),
|
|
241
250
|
fullHint: agentHint(s.item, s, t),
|
|
242
251
|
statusDetail: toText(t, s.detail) ?? null,
|
package/lib/tui/run.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from './state.mjs'
|
|
15
15
|
import { render, renderReview, bodyHeight } from './render.mjs'
|
|
16
16
|
import { createProgress, applyEvent, progressLines } from './progress.mjs'
|
|
17
|
+
import { scopedLabel } from '../labels.mjs'
|
|
17
18
|
|
|
18
19
|
const ESC = String.fromCharCode(27)
|
|
19
20
|
const HIDE_CURSOR = `${ESC}[?25l`
|
|
@@ -183,6 +184,9 @@ export async function runTui(root, opts = {}) {
|
|
|
183
184
|
// 화면 밖에 있을 때는 아예 무슨 일이 일어났는지 볼 수조차 없다.
|
|
184
185
|
const dropped = state.rows.filter((r) => r.id !== row.id && before.has(r.id) && !state.selected.has(r.id))
|
|
185
186
|
if (dropped.length > 0) return t('tui.exclusiveSwitched', { kept: row.label, dropped: dropped.map((r) => r.label).join(', ') })
|
|
187
|
+
// 전역 항목을 막 켰다면 알린다 — 이 체크 하나가 저장소 밖을 건드린다.
|
|
188
|
+
// 배타 전환 메시지가 우선한다: 상태줄은 한 줄뿐이고 선택이 뒤집힌 쪽이 급하다.
|
|
189
|
+
if (row.item?.scope === 'user' && state.selected.has(row.id) && !before.has(row.id)) return t('tui.selectedGlobal')
|
|
186
190
|
return ''
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -373,7 +377,7 @@ export async function runTui(root, opts = {}) {
|
|
|
373
377
|
log('')
|
|
374
378
|
for (const r of notable) {
|
|
375
379
|
const message = toText(t, r.message)
|
|
376
|
-
log(` ${r.ok ? '✔' : '✖'} ${t(`change.${r.action}`)} ${r.item
|
|
380
|
+
log(` ${r.ok ? '✔' : '✖'} ${t(`change.${r.action}`)} ${scopedLabel(r.item, t)}${message ? ` — ${message}` : ''}`)
|
|
377
381
|
}
|
|
378
382
|
}
|
|
379
383
|
log(`\n${t('apply.seeGitDiff')}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rch4com/agent-setup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "Repository-scoped bootstrap and optional-item installer for using multiple coding-agent CLIs in one repository | 여러 코딩 에이전트 CLI를 한 저장소에서 함께 쓰기 위한 저장소 범위 부트스트랩과 선택 항목 설치기",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents-md",
|