@dusky-bluehour/agent-service 0.6.6 → 0.6.8
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/README.md +255 -36
- package/catalog/tool-catalog.ko.json +92 -57
- package/claude-code/README.md +75 -30
- package/claude-code/commands/native/cmd-dev-be-api.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-hook-separate.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-ui-componentize.md +51 -0
- package/claude-code/commands/native/cmd-dev-perf-optimize.md +51 -0
- package/claude-code/commands/native/cmd-dev-sequential-autorun.md +51 -0
- package/claude-code/commands/native/cmd-doc-handoff.md +52 -0
- package/claude-code/commands/native/cmd-improve-techdebt.md +51 -0
- package/claude-code/commands/native/cmd-incident-triage.md +51 -0
- package/claude-code/commands/native/cmd-ops-ci-cd-gate.md +51 -0
- package/claude-code/commands/native/cmd-ops-deploy.md +51 -0
- package/claude-code/commands/native/cmd-ops-monitoring.md +51 -0
- package/claude-code/commands/native/cmd-plan-arch-decision.md +51 -0
- package/claude-code/commands/native/cmd-plan-implementation-bootstrap.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-details.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-master.md +52 -0
- package/claude-code/commands/native/cmd-plan-req-lock.md +52 -0
- package/claude-code/commands/native/cmd-review-code.md +51 -0
- package/claude-code/commands/native/cmd-sec-dependency-audit.md +51 -0
- package/claude-code/commands/native/cmd-sec-threat-model.md +51 -0
- package/claude-code/commands/native/cmd-test-unit-integration.md +51 -0
- package/claude-code/instructions/CLAUDE.template.md +42 -0
- package/claude-code/settings/settings.json +183 -0
- package/claude-code/settings/settings.local.json +10 -0
- package/codex/README.md +58 -24
- package/codex/instructions/AGENTS.permissions.generated.md +121 -0
- package/codex/instructions/AGENTS.template.md +18 -3
- package/codex/settings/runtime-policy.json +188 -0
- package/codex/skills/cmd-dev-be-api/SKILL.md +43 -0
- package/codex/skills/cmd-dev-be-api/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-hook-separate/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-hook-separate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-perf-optimize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-perf-optimize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-sequential-autorun/SKILL.md +43 -0
- package/codex/skills/cmd-dev-sequential-autorun/agents/openai.yaml +4 -0
- package/codex/skills/cmd-doc-handoff/SKILL.md +43 -0
- package/codex/skills/cmd-doc-handoff/agents/openai.yaml +4 -0
- package/codex/skills/cmd-improve-techdebt/SKILL.md +43 -0
- package/codex/skills/cmd-improve-techdebt/agents/openai.yaml +4 -0
- package/codex/skills/cmd-incident-triage/SKILL.md +43 -0
- package/codex/skills/cmd-incident-triage/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-ci-cd-gate/SKILL.md +43 -0
- package/codex/skills/cmd-ops-ci-cd-gate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-deploy/SKILL.md +43 -0
- package/codex/skills/cmd-ops-deploy/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-monitoring/SKILL.md +43 -0
- package/codex/skills/cmd-ops-monitoring/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-arch-decision/SKILL.md +43 -0
- package/codex/skills/cmd-plan-arch-decision/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/SKILL.md +43 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-details/SKILL.md +43 -0
- package/codex/skills/cmd-plan-prd-details/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-master/SKILL.md +44 -0
- package/codex/skills/cmd-plan-prd-master/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-req-lock/SKILL.md +44 -0
- package/codex/skills/cmd-plan-req-lock/agents/openai.yaml +4 -0
- package/codex/skills/cmd-review-code/SKILL.md +43 -0
- package/codex/skills/cmd-review-code/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-dependency-audit/SKILL.md +43 -0
- package/codex/skills/cmd-sec-dependency-audit/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-threat-model/SKILL.md +43 -0
- package/codex/skills/cmd-sec-threat-model/agents/openai.yaml +4 -0
- package/codex/skills/cmd-test-unit-integration/SKILL.md +43 -0
- package/codex/skills/cmd-test-unit-integration/agents/openai.yaml +4 -0
- package/common/settings/security-policy.json +221 -0
- package/common/skills/skill-catalog.json +368 -136
- package/common/workflows/workflow-catalog.json +89 -1238
- package/gemini/README.md +104 -0
- package/gemini/commands/definitions/cmd-dev-be-api.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-hook-separate.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-ui-componentize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-perf-optimize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-sequential-autorun.toml +35 -0
- package/gemini/commands/definitions/cmd-doc-handoff.toml +36 -0
- package/gemini/commands/definitions/cmd-improve-techdebt.toml +35 -0
- package/gemini/commands/definitions/cmd-incident-triage.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-ci-cd-gate.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-deploy.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-monitoring.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-arch-decision.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-implementation-bootstrap.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-details.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-master.toml +36 -0
- package/gemini/commands/definitions/cmd-plan-req-lock.toml +36 -0
- package/gemini/commands/definitions/cmd-review-code.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-dependency-audit.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-threat-model.toml +35 -0
- package/gemini/commands/definitions/cmd-test-unit-integration.toml +35 -0
- package/gemini/gemini-extension.json +6 -0
- package/gemini/instructions/GEMINI.template.md +34 -0
- package/gemini/settings/editor-policy.json +193 -0
- package/{antigravity → gemini}/skills/change-safety-review/SKILL.md +8 -6
- package/{antigravity → gemini}/skills/code-review-and-improvement/SKILL.md +8 -3
- package/gemini/skills/frontend-repetition-pack/SKILL.md +44 -0
- package/gemini/skills/incident-response/SKILL.md +44 -0
- package/{antigravity → gemini}/skills/prd-to-production-pipeline/SKILL.md +13 -4
- package/{antigravity → gemini}/skills/release-and-operations/SKILL.md +11 -3
- package/gemini/skills/security-hardening/SKILL.md +43 -0
- package/gemini/skills/service-lifecycle-orchestration/SKILL.md +46 -0
- package/{antigravity → gemini}/workflows/workflow-catalog.json +1 -1
- package/package.json +4 -4
- package/scripts/generate-from-common.mjs +537 -23
- package/scripts/init.mjs +310 -41
- package/scripts/validate.mjs +264 -32
- package/antigravity/README.md +0 -43
- package/antigravity/skills/frontend-repetition-pack/SKILL.md +0 -35
- package/antigravity/skills/incident-response/SKILL.md +0 -35
- package/antigravity/skills/security-hardening/SKILL.md +0 -35
- package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +0 -36
- package/claude-code/workflows/workflow-catalog.json +0 -688
- package/codex/workflows/workflow-catalog.json +0 -450
- /package/{antigravity/agents → common/gemini}/agent-catalog.json +0 -0
- /package/{antigravity/artifacts → common/gemini}/artifact-catalog.json +0 -0
- /package/{common/antigravity → gemini/agents}/agent-catalog.json +0 -0
- /package/{common/antigravity → gemini/artifacts}/artifact-catalog.json +0 -0
- /package/{antigravity → gemini}/commands/command-catalog.json +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +0 -0
package/scripts/init.mjs
CHANGED
|
@@ -16,6 +16,21 @@ const CLI_NAME = 'tri-agent-manager';
|
|
|
16
16
|
const STATE_DIR_NAME = '.tri-agent-manager';
|
|
17
17
|
const LEGACY_STATE_DIR_NAME = '.tri-agent-os';
|
|
18
18
|
const GUIDE_FILE_NAME = 'USAGE.ko.md';
|
|
19
|
+
const PROJECT_RULE_MODE_VALUES = new Set(['always', 'if-instructions', 'if-present', 'never']);
|
|
20
|
+
const PROJECT_RULE_BOOTSTRAP = {
|
|
21
|
+
'claude-code': {
|
|
22
|
+
sourceRelativePath: path.join('claude-code', 'instructions', 'CLAUDE.template.md'),
|
|
23
|
+
destinationRelativePath: 'CLAUDE.md'
|
|
24
|
+
},
|
|
25
|
+
codex: {
|
|
26
|
+
sourceRelativePath: path.join('codex', 'instructions', 'AGENTS.template.md'),
|
|
27
|
+
destinationRelativePath: 'AGENTS.md'
|
|
28
|
+
},
|
|
29
|
+
gemini: {
|
|
30
|
+
sourceRelativePath: path.join('gemini', 'instructions', 'GEMINI.template.md'),
|
|
31
|
+
destinationRelativePath: 'GEMINI.md'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
19
34
|
|
|
20
35
|
const HELP_TEXT = [
|
|
21
36
|
`${CLI_NAME} 사용법`,
|
|
@@ -24,8 +39,8 @@ const HELP_TEXT = [
|
|
|
24
39
|
` ${CLI_NAME} list`,
|
|
25
40
|
` ${CLI_NAME} setup (install + interactive 별칭)`,
|
|
26
41
|
` ${CLI_NAME} wizard (install + interactive 별칭)`,
|
|
27
|
-
` ${CLI_NAME} install [--preset <id>] [--tool <ids>] [--components <ids>] [--target <path>] [--force] [--dry-run] [--yes]`,
|
|
28
|
-
` ${CLI_NAME} update [--preset <id>] [--tool <ids>] [--components <ids>] [--target <path>] [--dry-run] [--yes]`,
|
|
42
|
+
` ${CLI_NAME} install [--preset <id>] [--tool <ids>] [--components <ids>] [--target <path>] [--install-root <map>] [--project-rules <mode>] [--force] [--dry-run] [--yes]`,
|
|
43
|
+
` ${CLI_NAME} update [--preset <id>] [--tool <ids>] [--components <ids>] [--target <path>] [--install-root <map>] [--project-rules <mode>] [--dry-run] [--yes]`,
|
|
29
44
|
` ${CLI_NAME} init (install의 별칭)`,
|
|
30
45
|
'',
|
|
31
46
|
'옵션:',
|
|
@@ -33,7 +48,8 @@ const HELP_TEXT = [
|
|
|
33
48
|
' --tool 도구 선택 (예: codex,claude-code) / all',
|
|
34
49
|
' --components 구성요소 선택 (예: skills,workflows,commands) / all',
|
|
35
50
|
' --target 설치 경로 (기본: .)',
|
|
36
|
-
' --install-root 도구별 설치 루트 오버라이드 (예: codex=.codex,
|
|
51
|
+
' --install-root 도구별 설치 루트 오버라이드 (예: codex=.codex,gemini=.gemini)',
|
|
52
|
+
' --project-rules 프로젝트 규칙 파일 정책 (always|if-instructions|if-present|never)',
|
|
37
53
|
' --force install 시 기존 파일 덮어쓰기',
|
|
38
54
|
' --dry-run 복사하지 않고 작업 계획만 출력',
|
|
39
55
|
' --yes 확인 프롬프트 생략',
|
|
@@ -41,11 +57,10 @@ const HELP_TEXT = [
|
|
|
41
57
|
' --non-interactive 비대화형 모드 강제',
|
|
42
58
|
' (중복 선택 입력은 자동으로 1회로 정리됨)',
|
|
43
59
|
'',
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
' pnpm dlx --package=@dusky-bluehour/agent-service@latest tri-agent-manager list'
|
|
60
|
+
'npx 예시:',
|
|
61
|
+
' npx --yes --package @dusky-bluehour/agent-service tri-agent-manager --interactive',
|
|
62
|
+
' npx --yes --package @dusky-bluehour/agent-service tri-agent-manager update --interactive',
|
|
63
|
+
' npx --yes --package @dusky-bluehour/agent-service tri-agent-manager list'
|
|
49
64
|
].join('\n');
|
|
50
65
|
|
|
51
66
|
function parseArgs(argv) {
|
|
@@ -73,6 +88,7 @@ function parseArgs(argv) {
|
|
|
73
88
|
componentFlag: getFlag('--components'),
|
|
74
89
|
targetFlag: getFlag('--target') ?? '.',
|
|
75
90
|
installRootFlag: getFlag('--install-root'),
|
|
91
|
+
projectRulesFlag: getFlag('--project-rules'),
|
|
76
92
|
force: hasFlag('--force'),
|
|
77
93
|
dryRun: hasFlag('--dry-run'),
|
|
78
94
|
yes: hasFlag('--yes'),
|
|
@@ -207,6 +223,15 @@ function getComponentInstallPath(component) {
|
|
|
207
223
|
return installPath || component.path;
|
|
208
224
|
}
|
|
209
225
|
|
|
226
|
+
function getComponentDescription(toolId, component) {
|
|
227
|
+
const base = component?.description ?? '';
|
|
228
|
+
if (component?.id !== 'instructions' || !PROJECT_RULE_BOOTSTRAP[toolId]) {
|
|
229
|
+
return base;
|
|
230
|
+
}
|
|
231
|
+
const suffix = '규칙 파일 정책: always | if-instructions | if-present | never';
|
|
232
|
+
return base ? `${base} (${suffix})` : suffix;
|
|
233
|
+
}
|
|
234
|
+
|
|
210
235
|
function normalizeInstallRoot(rawRoot, toolId) {
|
|
211
236
|
const trimmed = String(rawRoot ?? '').trim();
|
|
212
237
|
if (!trimmed) {
|
|
@@ -227,6 +252,57 @@ function normalizeInstallRoot(rawRoot, toolId) {
|
|
|
227
252
|
return normalized.replace(/\/+$/, '');
|
|
228
253
|
}
|
|
229
254
|
|
|
255
|
+
function normalizeProjectRulesMode(rawMode) {
|
|
256
|
+
const mode = String(rawMode ?? '')
|
|
257
|
+
.trim()
|
|
258
|
+
.toLowerCase();
|
|
259
|
+
|
|
260
|
+
if (!mode) {
|
|
261
|
+
return 'always';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const alias = {
|
|
265
|
+
on: 'always',
|
|
266
|
+
all: 'always',
|
|
267
|
+
instructions: 'if-instructions',
|
|
268
|
+
selected: 'if-instructions',
|
|
269
|
+
present: 'if-present',
|
|
270
|
+
existing: 'if-present',
|
|
271
|
+
off: 'never',
|
|
272
|
+
none: 'never'
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const normalized = alias[mode] ?? mode;
|
|
276
|
+
if (!PROJECT_RULE_MODE_VALUES.has(normalized)) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`--project-rules 값이 올바르지 않습니다: ${rawMode} (always|if-instructions|if-present|never)`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return normalized;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function describeProjectRulesMode(mode) {
|
|
286
|
+
switch (mode) {
|
|
287
|
+
case 'always':
|
|
288
|
+
return '항상 생성/업데이트';
|
|
289
|
+
case 'if-instructions':
|
|
290
|
+
return '`instructions` 구성요소를 선택한 경우에만 생성/업데이트';
|
|
291
|
+
case 'if-present':
|
|
292
|
+
return '대상 규칙 파일이 이미 존재할 때만 업데이트';
|
|
293
|
+
case 'never':
|
|
294
|
+
return '생성/업데이트하지 않음';
|
|
295
|
+
default:
|
|
296
|
+
return mode;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function resolveProjectRuleDestination(toolId, installRoot) {
|
|
301
|
+
const meta = PROJECT_RULE_BOOTSTRAP[toolId];
|
|
302
|
+
if (!meta) return null;
|
|
303
|
+
return meta.destinationRelativePath.replace('{installRoot}', installRoot || '');
|
|
304
|
+
}
|
|
305
|
+
|
|
230
306
|
function parseInstallRootFlag(installRootFlag, catalog) {
|
|
231
307
|
if (!installRootFlag) {
|
|
232
308
|
return {};
|
|
@@ -240,7 +316,7 @@ function parseInstallRootFlag(installRootFlag, catalog) {
|
|
|
240
316
|
const idx = token.indexOf('=');
|
|
241
317
|
if (idx <= 0 || idx === token.length - 1) {
|
|
242
318
|
throw new Error(
|
|
243
|
-
`--install-root 형식이 올바르지 않습니다: ${token} (예: codex=.codex,
|
|
319
|
+
`--install-root 형식이 올바르지 않습니다: ${token} (예: codex=.codex,gemini=.gemini)`
|
|
244
320
|
);
|
|
245
321
|
}
|
|
246
322
|
|
|
@@ -399,11 +475,7 @@ async function loadToolWorkflowSummary(tool, selection) {
|
|
|
399
475
|
function getToolExecutionGuide(tool, installRoot) {
|
|
400
476
|
if (tool.id === 'codex') {
|
|
401
477
|
return [
|
|
402
|
-
|
|
403
|
-
installRoot,
|
|
404
|
-
'instructions',
|
|
405
|
-
'AGENTS.template.md'
|
|
406
|
-
)}\`를 기반으로 팀 규칙을 확정합니다.`,
|
|
478
|
+
'프로젝트 루트의 `AGENTS.md`(자동 생성됨)를 열고 팀 규칙으로 보완합니다.',
|
|
407
479
|
`\`${path.join(
|
|
408
480
|
installRoot,
|
|
409
481
|
'workflows',
|
|
@@ -415,6 +487,7 @@ function getToolExecutionGuide(tool, installRoot) {
|
|
|
415
487
|
|
|
416
488
|
if (tool.id === 'claude-code') {
|
|
417
489
|
return [
|
|
490
|
+
'프로젝트 루트의 `CLAUDE.md`(자동 생성됨)를 열고 팀 컨텍스트를 채웁니다.',
|
|
418
491
|
`\`${path.join(
|
|
419
492
|
installRoot,
|
|
420
493
|
'workflows',
|
|
@@ -428,8 +501,9 @@ function getToolExecutionGuide(tool, installRoot) {
|
|
|
428
501
|
];
|
|
429
502
|
}
|
|
430
503
|
|
|
431
|
-
if (tool.id === '
|
|
504
|
+
if (tool.id === 'gemini') {
|
|
432
505
|
return [
|
|
506
|
+
'`GEMINI.md`(자동 생성됨)를 열고 운영 규칙을 확정합니다.',
|
|
433
507
|
`\`${path.join(
|
|
434
508
|
installRoot,
|
|
435
509
|
'workflows',
|
|
@@ -454,6 +528,7 @@ function getToolExecutionGuide(tool, installRoot) {
|
|
|
454
528
|
function getToolInteractionTips(tool, installRoot) {
|
|
455
529
|
if (tool.id === 'codex') {
|
|
456
530
|
return [
|
|
531
|
+
'프로젝트 루트의 `AGENTS.md`를 팀 규칙 단일 진입점으로 유지하세요.',
|
|
457
532
|
'Codex 대화 입력창에서 `$`를 누르면 사용 가능한 스킬 목록을 바로 열 수 있습니다.',
|
|
458
533
|
`프로젝트 루트에서 스킬 파일을 직접 확인하려면 \`ls ${path.join(
|
|
459
534
|
installRoot,
|
|
@@ -469,6 +544,7 @@ function getToolInteractionTips(tool, installRoot) {
|
|
|
469
544
|
|
|
470
545
|
if (tool.id === 'claude-code') {
|
|
471
546
|
return [
|
|
547
|
+
'프로젝트 루트 `CLAUDE.md`를 최신 상태로 유지하면 세션마다 동일한 기준으로 동작합니다.',
|
|
472
548
|
`서브에이전트 이름은 \`${path.join(installRoot, 'agents')}\` 경로의 파일명 기준입니다.`,
|
|
473
549
|
`프로젝트 루트에서 \`ls ${path.join(
|
|
474
550
|
installRoot,
|
|
@@ -478,8 +554,9 @@ function getToolInteractionTips(tool, installRoot) {
|
|
|
478
554
|
];
|
|
479
555
|
}
|
|
480
556
|
|
|
481
|
-
if (tool.id === '
|
|
557
|
+
if (tool.id === 'gemini') {
|
|
482
558
|
return [
|
|
559
|
+
'`GEMINI.md`를 Manager 단계의 승인 규칙 문서로 사용하세요.',
|
|
483
560
|
`워크플로우 선택은 \`${path.join(
|
|
484
561
|
installRoot,
|
|
485
562
|
'workflows',
|
|
@@ -520,6 +597,11 @@ async function writeUsageGuide({ catalog, selection, targetDir, mode }) {
|
|
|
520
597
|
lines.push(`- 생성 시각: ${new Date().toISOString()}`);
|
|
521
598
|
lines.push(`- 모드: ${mode}`);
|
|
522
599
|
lines.push(`- 프리셋: ${selection.presetId ?? '수동/없음'}`);
|
|
600
|
+
lines.push(
|
|
601
|
+
`- 프로젝트 규칙 파일 정책: ${selection.projectRulesMode} (${describeProjectRulesMode(
|
|
602
|
+
selection.projectRulesMode
|
|
603
|
+
)})`
|
|
604
|
+
);
|
|
523
605
|
lines.push(`- 대상 경로: ${targetDir}`);
|
|
524
606
|
lines.push('');
|
|
525
607
|
lines.push('이 문서는 현재 설치 선택값 기준으로 생성되었습니다.');
|
|
@@ -538,6 +620,10 @@ async function writeUsageGuide({ catalog, selection, targetDir, mode }) {
|
|
|
538
620
|
lines.push(`## ${tool.title} (${tool.id})`);
|
|
539
621
|
lines.push('');
|
|
540
622
|
lines.push(`- 설치 루트: \`${path.join(targetDir, installRoot)}\``);
|
|
623
|
+
const projectRuleDestination = resolveProjectRuleDestination(tool.id, installRoot);
|
|
624
|
+
if (projectRuleDestination) {
|
|
625
|
+
lines.push(`- 프로젝트 규칙 파일: \`${path.join(targetDir, projectRuleDestination)}\``);
|
|
626
|
+
}
|
|
541
627
|
if (tool.install_root_basis) {
|
|
542
628
|
lines.push(`- 경로 기준: ${tool.install_root_basis}`);
|
|
543
629
|
}
|
|
@@ -650,11 +736,11 @@ function resolveComponentsFromFlag(componentFlag, tool, mode) {
|
|
|
650
736
|
}
|
|
651
737
|
|
|
652
738
|
function clearTuiScreen() {
|
|
653
|
-
output.write('\
|
|
739
|
+
output.write('\x1B[2J\x1B[H');
|
|
654
740
|
}
|
|
655
741
|
|
|
656
742
|
function formatTuiLine(prefix, label, selected = false) {
|
|
657
|
-
const marker = selected ? '
|
|
743
|
+
const marker = selected ? '\x1B[32m[x]\x1B[0m' : '[ ]';
|
|
658
744
|
return `${prefix} ${marker} ${label}`;
|
|
659
745
|
}
|
|
660
746
|
|
|
@@ -700,7 +786,7 @@ async function runSingleSelectMenu({ title, help, options, defaultIndex = 0 }) {
|
|
|
700
786
|
while (true) {
|
|
701
787
|
renderTuiHeader(title, help);
|
|
702
788
|
options.forEach((option, index) => {
|
|
703
|
-
const pointer = index === cursor ? '
|
|
789
|
+
const pointer = index === cursor ? '◉' : '○';
|
|
704
790
|
output.write(`${pointer} ${option.label}\n`);
|
|
705
791
|
if (option.description) {
|
|
706
792
|
output.write(` ${option.description}\n`);
|
|
@@ -739,8 +825,8 @@ async function runMultiSelectMenu({
|
|
|
739
825
|
const selected = new Set(preselectedIds);
|
|
740
826
|
const optionRows = [
|
|
741
827
|
...choices.map((choice) => ({ kind: 'choice', ...choice })),
|
|
742
|
-
{ kind: 'action', action: 'toggle-all', label: '
|
|
743
|
-
{ kind: 'action', action: 'done', label: '
|
|
828
|
+
{ kind: 'action', action: 'toggle-all', label: '[*] 전체 선택/해제' },
|
|
829
|
+
{ kind: 'action', action: 'done', label: '[>] 선택 완료' }
|
|
744
830
|
];
|
|
745
831
|
let cursor = 0;
|
|
746
832
|
let notice = '';
|
|
@@ -754,7 +840,7 @@ async function runMultiSelectMenu({
|
|
|
754
840
|
output.write('\n');
|
|
755
841
|
|
|
756
842
|
optionRows.forEach((row, index) => {
|
|
757
|
-
const pointer = index === cursor ? '
|
|
843
|
+
const pointer = index === cursor ? '◉' : '○';
|
|
758
844
|
if (row.kind === 'choice') {
|
|
759
845
|
output.write(`${formatTuiLine(pointer, row.label, selected.has(row.id))}\n`);
|
|
760
846
|
if (row.description) {
|
|
@@ -823,7 +909,8 @@ async function promptInteractiveTui({
|
|
|
823
909
|
mode,
|
|
824
910
|
defaultTarget,
|
|
825
911
|
presetFlag,
|
|
826
|
-
installRootOverridesFromFlag
|
|
912
|
+
installRootOverridesFromFlag,
|
|
913
|
+
projectRulesModeFromFlag
|
|
827
914
|
}) {
|
|
828
915
|
readline.emitKeypressEvents(input);
|
|
829
916
|
const wasRawMode = Boolean(input.isRaw);
|
|
@@ -856,6 +943,9 @@ async function promptInteractiveTui({
|
|
|
856
943
|
|
|
857
944
|
const targetDir = path.resolve(process.cwd(), targetInput);
|
|
858
945
|
const state = await readState(targetDir);
|
|
946
|
+
const defaultProjectRulesMode = normalizeProjectRulesMode(
|
|
947
|
+
projectRulesModeFromFlag ?? (mode === 'update' ? state?.project_rules_mode : 'always')
|
|
948
|
+
);
|
|
859
949
|
const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
|
|
860
950
|
|
|
861
951
|
const defaultPresetId =
|
|
@@ -938,7 +1028,7 @@ async function promptInteractiveTui({
|
|
|
938
1028
|
choices: tool.components.map((component) => ({
|
|
939
1029
|
id: component.id,
|
|
940
1030
|
label: `${component.title} (${component.id})`,
|
|
941
|
-
description: component
|
|
1031
|
+
description: getComponentDescription(tool.id, component)
|
|
942
1032
|
})),
|
|
943
1033
|
preselectedIds: defaultComponents,
|
|
944
1034
|
minSelected: 1
|
|
@@ -953,12 +1043,47 @@ async function promptInteractiveTui({
|
|
|
953
1043
|
cliOverrides: installRootOverridesFromFlag
|
|
954
1044
|
});
|
|
955
1045
|
|
|
1046
|
+
let projectRulesMode = defaultProjectRulesMode;
|
|
1047
|
+
if (!projectRulesModeFromFlag) {
|
|
1048
|
+
const modeOption = await runSingleSelectMenu({
|
|
1049
|
+
title: '🧭 프로젝트 규칙 파일 정책',
|
|
1050
|
+
help: '조작: 위/아래/좌/우 이동, Enter 선택, q 종료',
|
|
1051
|
+
options: [
|
|
1052
|
+
{
|
|
1053
|
+
id: 'always',
|
|
1054
|
+
label: '🟢 always',
|
|
1055
|
+
description: describeProjectRulesMode('always')
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
id: 'if-instructions',
|
|
1059
|
+
label: '🟡 if-instructions',
|
|
1060
|
+
description: describeProjectRulesMode('if-instructions')
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
id: 'if-present',
|
|
1064
|
+
label: '🔵 if-present',
|
|
1065
|
+
description: describeProjectRulesMode('if-present')
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
id: 'never',
|
|
1069
|
+
label: '⚪ never',
|
|
1070
|
+
description: describeProjectRulesMode('never')
|
|
1071
|
+
}
|
|
1072
|
+
],
|
|
1073
|
+
defaultIndex: ['always', 'if-instructions', 'if-present', 'never'].indexOf(
|
|
1074
|
+
defaultProjectRulesMode
|
|
1075
|
+
)
|
|
1076
|
+
});
|
|
1077
|
+
projectRulesMode = normalizeProjectRulesMode(modeOption.id);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
956
1080
|
return {
|
|
957
1081
|
targetDir,
|
|
958
1082
|
selectedToolIds,
|
|
959
1083
|
componentSelection,
|
|
960
1084
|
presetId: selectedPreset?.id ?? null,
|
|
961
|
-
installRootOverrides
|
|
1085
|
+
installRootOverrides,
|
|
1086
|
+
projectRulesMode
|
|
962
1087
|
};
|
|
963
1088
|
} finally {
|
|
964
1089
|
setRawMode(wasRawMode);
|
|
@@ -971,7 +1096,8 @@ async function promptInteractiveText({
|
|
|
971
1096
|
mode,
|
|
972
1097
|
defaultTarget,
|
|
973
1098
|
presetFlag,
|
|
974
|
-
installRootOverridesFromFlag
|
|
1099
|
+
installRootOverridesFromFlag,
|
|
1100
|
+
projectRulesModeFromFlag
|
|
975
1101
|
}) {
|
|
976
1102
|
const rl = createInterface({ input, output });
|
|
977
1103
|
|
|
@@ -1006,6 +1132,9 @@ async function promptInteractiveText({
|
|
|
1006
1132
|
}
|
|
1007
1133
|
const targetDir = path.resolve(process.cwd(), targetInput);
|
|
1008
1134
|
const state = await readState(targetDir);
|
|
1135
|
+
const defaultProjectRulesMode = normalizeProjectRulesMode(
|
|
1136
|
+
projectRulesModeFromFlag ?? (mode === 'update' ? state?.project_rules_mode : 'always')
|
|
1137
|
+
);
|
|
1009
1138
|
|
|
1010
1139
|
const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
|
|
1011
1140
|
let selectedPreset = findPreset(catalog, presetFlag);
|
|
@@ -1059,7 +1188,7 @@ async function promptInteractiveText({
|
|
|
1059
1188
|
console.log(`📦 [${tool.title}] 구성요소 목록`);
|
|
1060
1189
|
tool.components.forEach((component, index) => {
|
|
1061
1190
|
console.log(`${index + 1}. ${component.title} (${component.id})`);
|
|
1062
|
-
console.log(` - ${component
|
|
1191
|
+
console.log(` - ${getComponentDescription(tool.id, component)}`);
|
|
1063
1192
|
});
|
|
1064
1193
|
|
|
1065
1194
|
let defaultComponents = tool.components.map((c) => c.id);
|
|
@@ -1094,12 +1223,32 @@ async function promptInteractiveText({
|
|
|
1094
1223
|
cliOverrides: installRootOverridesFromFlag
|
|
1095
1224
|
});
|
|
1096
1225
|
|
|
1226
|
+
let projectRulesMode = defaultProjectRulesMode;
|
|
1227
|
+
if (!projectRulesModeFromFlag) {
|
|
1228
|
+
console.log('');
|
|
1229
|
+
console.log('🧭 프로젝트 규칙 파일 정책');
|
|
1230
|
+
console.log(`1. always - ${describeProjectRulesMode('always')}`);
|
|
1231
|
+
console.log(`2. if-instructions - ${describeProjectRulesMode('if-instructions')}`);
|
|
1232
|
+
console.log(`3. if-present - ${describeProjectRulesMode('if-present')}`);
|
|
1233
|
+
console.log(`4. never - ${describeProjectRulesMode('never')}`);
|
|
1234
|
+
const modeAnswer = await ask('정책을 선택하세요. (번호/ID)', defaultProjectRulesMode);
|
|
1235
|
+
const normalizedModeAnswer = modeAnswer.trim().toLowerCase();
|
|
1236
|
+
const modeByNumber = {
|
|
1237
|
+
'1': 'always',
|
|
1238
|
+
'2': 'if-instructions',
|
|
1239
|
+
'3': 'if-present',
|
|
1240
|
+
'4': 'never'
|
|
1241
|
+
};
|
|
1242
|
+
projectRulesMode = normalizeProjectRulesMode(modeByNumber[normalizedModeAnswer] ?? modeAnswer);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1097
1245
|
return {
|
|
1098
1246
|
targetDir,
|
|
1099
1247
|
selectedToolIds,
|
|
1100
1248
|
componentSelection,
|
|
1101
1249
|
presetId: selectedPreset?.id ?? null,
|
|
1102
|
-
installRootOverrides
|
|
1250
|
+
installRootOverrides,
|
|
1251
|
+
projectRulesMode
|
|
1103
1252
|
};
|
|
1104
1253
|
} finally {
|
|
1105
1254
|
rl.close();
|
|
@@ -1120,7 +1269,8 @@ function buildSelectionFromFlags({
|
|
|
1120
1269
|
toolFlag,
|
|
1121
1270
|
componentFlag,
|
|
1122
1271
|
targetFlag,
|
|
1123
|
-
installRootOverridesFromFlag
|
|
1272
|
+
installRootOverridesFromFlag,
|
|
1273
|
+
projectRulesModeFromFlag
|
|
1124
1274
|
}) {
|
|
1125
1275
|
const preset = findPreset(catalog, presetFlag);
|
|
1126
1276
|
const presetToolIds = resolveToolsFromPreset(preset, catalog);
|
|
@@ -1163,7 +1313,8 @@ function buildSelectionFromFlags({
|
|
|
1163
1313
|
selectedToolIds,
|
|
1164
1314
|
componentSelection,
|
|
1165
1315
|
presetId: preset?.id ?? null,
|
|
1166
|
-
installRootOverrides
|
|
1316
|
+
installRootOverrides,
|
|
1317
|
+
projectRulesMode: normalizeProjectRulesMode(projectRulesModeFromFlag ?? 'always')
|
|
1167
1318
|
};
|
|
1168
1319
|
}
|
|
1169
1320
|
|
|
@@ -1189,6 +1340,54 @@ async function copyEntry(srcPath, destPath, { overwrite, dryRun }) {
|
|
|
1189
1340
|
return { status: destExists ? 'overwritten' : 'copied' };
|
|
1190
1341
|
}
|
|
1191
1342
|
|
|
1343
|
+
async function copyProjectRuleBootstrap({
|
|
1344
|
+
toolId,
|
|
1345
|
+
targetDir,
|
|
1346
|
+
installRoot,
|
|
1347
|
+
overwrite,
|
|
1348
|
+
dryRun,
|
|
1349
|
+
selectedComponentIds,
|
|
1350
|
+
projectRulesMode
|
|
1351
|
+
}) {
|
|
1352
|
+
const ruleMeta = PROJECT_RULE_BOOTSTRAP[toolId];
|
|
1353
|
+
if (!ruleMeta) {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
const normalizedMode = normalizeProjectRulesMode(projectRulesMode ?? 'always');
|
|
1358
|
+
if (normalizedMode === 'never') {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const componentIds = new Set(selectedComponentIds ?? []);
|
|
1363
|
+
if (normalizedMode === 'if-instructions' && !componentIds.has('instructions')) {
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const srcPath = path.join(rootDir, ruleMeta.sourceRelativePath);
|
|
1368
|
+
if (!(await exists(srcPath))) {
|
|
1369
|
+
throw new Error(`[${toolId}] 프로젝트 규칙 템플릿 누락: ${ruleMeta.sourceRelativePath}`);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const destinationRelativePath = ruleMeta.destinationRelativePath.replace(
|
|
1373
|
+
'{installRoot}',
|
|
1374
|
+
installRoot || ''
|
|
1375
|
+
);
|
|
1376
|
+
const dstPath = path.join(targetDir, destinationRelativePath);
|
|
1377
|
+
|
|
1378
|
+
if (normalizedMode === 'if-present' && !(await exists(dstPath))) {
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
const result = await copyEntry(srcPath, dstPath, { overwrite, dryRun });
|
|
1383
|
+
return {
|
|
1384
|
+
toolId,
|
|
1385
|
+
source: ruleMeta.sourceRelativePath,
|
|
1386
|
+
destination: destinationRelativePath,
|
|
1387
|
+
status: result.status
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1192
1391
|
function statusLabel(status) {
|
|
1193
1392
|
switch (status) {
|
|
1194
1393
|
case 'copied':
|
|
@@ -1209,17 +1408,17 @@ function statusLabel(status) {
|
|
|
1209
1408
|
function statusIcon(status) {
|
|
1210
1409
|
switch (status) {
|
|
1211
1410
|
case 'copied':
|
|
1212
|
-
return '
|
|
1411
|
+
return '🟢';
|
|
1213
1412
|
case 'overwritten':
|
|
1214
|
-
return '
|
|
1413
|
+
return '🔵';
|
|
1215
1414
|
case 'skipped':
|
|
1216
|
-
return '
|
|
1415
|
+
return '⚪';
|
|
1217
1416
|
case 'would-copy':
|
|
1218
|
-
return '
|
|
1417
|
+
return '🟡';
|
|
1219
1418
|
case 'would-overwrite':
|
|
1220
|
-
return '
|
|
1419
|
+
return '🟣';
|
|
1221
1420
|
default:
|
|
1222
|
-
return '
|
|
1421
|
+
return '○';
|
|
1223
1422
|
}
|
|
1224
1423
|
}
|
|
1225
1424
|
|
|
@@ -1241,8 +1440,19 @@ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
|
|
|
1241
1440
|
console.log(`실행 타입 : ${dryRun ? 'dry-run (실제 파일 변경 없음)' : '실행 (파일 변경 반영)'}`);
|
|
1242
1441
|
console.log(`프리셋 : ${selection.presetId ?? '수동/없음'}`);
|
|
1243
1442
|
console.log(`대상 경로 : ${targetDir}`);
|
|
1443
|
+
console.log(
|
|
1444
|
+
`규칙 파일 : ${selection.projectRulesMode} (${describeProjectRulesMode(
|
|
1445
|
+
selection.projectRulesMode
|
|
1446
|
+
)})`
|
|
1447
|
+
);
|
|
1244
1448
|
console.log(`선택 도구 : ${selection.selectedToolIds.length}개`);
|
|
1245
1449
|
|
|
1450
|
+
let totalComponents = 0;
|
|
1451
|
+
for (const toolId of selection.selectedToolIds) {
|
|
1452
|
+
totalComponents += (selection.componentSelection[toolId] ?? []).length;
|
|
1453
|
+
}
|
|
1454
|
+
console.log(`총 구성요소 : ${totalComponents}개`);
|
|
1455
|
+
|
|
1246
1456
|
selection.selectedToolIds.forEach((toolId, index) => {
|
|
1247
1457
|
const tool = findTool(catalog, toolId);
|
|
1248
1458
|
if (!tool) {
|
|
@@ -1272,6 +1482,20 @@ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
|
|
|
1272
1482
|
}
|
|
1273
1483
|
}
|
|
1274
1484
|
|
|
1485
|
+
const projectRuleDestination = resolveProjectRuleDestination(tool.id, installRoot);
|
|
1486
|
+
if (projectRuleDestination) {
|
|
1487
|
+
let applyHint = '적용됨';
|
|
1488
|
+
if (selection.projectRulesMode === 'if-instructions' && !componentIds.includes('instructions')) {
|
|
1489
|
+
applyHint = '`instructions` 미선택으로 이번 실행에서는 생성/업데이트 안 함';
|
|
1490
|
+
} else if (selection.projectRulesMode === 'if-present') {
|
|
1491
|
+
applyHint = '대상 파일이 이미 있을 때만 업데이트';
|
|
1492
|
+
} else if (selection.projectRulesMode === 'never') {
|
|
1493
|
+
applyHint = '생성/업데이트 안 함';
|
|
1494
|
+
}
|
|
1495
|
+
console.log(` 규칙 파일 : ${projectRuleDestination}`);
|
|
1496
|
+
console.log(` 적용 정책 : ${applyHint}`);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1275
1499
|
if (tool.install_root_basis) {
|
|
1276
1500
|
console.log(` 경로 기준 : ${tool.install_root_basis}`);
|
|
1277
1501
|
}
|
|
@@ -1357,6 +1581,7 @@ async function writeState({ catalog, targetDir, packageData, mode, selection })
|
|
|
1357
1581
|
package_version: packageData.version,
|
|
1358
1582
|
updated_at: new Date().toISOString(),
|
|
1359
1583
|
preset_id: selection.presetId ?? null,
|
|
1584
|
+
project_rules_mode: normalizeProjectRulesMode(selection.projectRulesMode ?? 'always'),
|
|
1360
1585
|
install_roots: installRoots,
|
|
1361
1586
|
tools
|
|
1362
1587
|
};
|
|
@@ -1381,6 +1606,22 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
|
|
|
1381
1606
|
|
|
1382
1607
|
const results = [];
|
|
1383
1608
|
|
|
1609
|
+
let totalSteps = 0;
|
|
1610
|
+
for (const toolId of selection.selectedToolIds) {
|
|
1611
|
+
const componentIds = selection.componentSelection[toolId] ?? [];
|
|
1612
|
+
totalSteps += 1 + componentIds.length + 1;
|
|
1613
|
+
}
|
|
1614
|
+
let currentStep = 0;
|
|
1615
|
+
const logStep = (label) => {
|
|
1616
|
+
currentStep += 1;
|
|
1617
|
+
const progress = `[${String(currentStep).padStart(String(totalSteps).length, ' ')}/${totalSteps}]`;
|
|
1618
|
+
if (output.isTTY) {
|
|
1619
|
+
output.write(`\r\x1B[K${progress} ${label}`);
|
|
1620
|
+
} else {
|
|
1621
|
+
console.log(`${progress} ${label}`);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1384
1625
|
for (const toolId of selection.selectedToolIds) {
|
|
1385
1626
|
const tool = findTool(catalog, toolId);
|
|
1386
1627
|
if (!tool) {
|
|
@@ -1391,6 +1632,7 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
|
|
|
1391
1632
|
const toolDestRoot = path.join(targetDir, installRoot);
|
|
1392
1633
|
await fs.mkdir(toolDestRoot, { recursive: true });
|
|
1393
1634
|
|
|
1635
|
+
logStep(`${tool.title} README 복사 중...`);
|
|
1394
1636
|
const readmeSrc = path.join(rootDir, tool.root, tool.readme);
|
|
1395
1637
|
const readmeDst = path.join(toolDestRoot, `README.tri-agent-manager.${tool.id}.md`);
|
|
1396
1638
|
const readmeResult = await copyEntry(readmeSrc, readmeDst, { overwrite, dryRun });
|
|
@@ -1408,6 +1650,7 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
|
|
|
1408
1650
|
throw new Error(`[${tool.id}] 알 수 없는 구성요소: ${componentId}`);
|
|
1409
1651
|
}
|
|
1410
1652
|
|
|
1653
|
+
logStep(`${tool.title} ${component.title} 복사 중...`);
|
|
1411
1654
|
const src = path.join(rootDir, tool.root, component.path);
|
|
1412
1655
|
const componentInstallPath = getComponentInstallPath(component);
|
|
1413
1656
|
const dst = path.join(targetDir, installRoot, componentInstallPath);
|
|
@@ -1419,8 +1662,25 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
|
|
|
1419
1662
|
status: result.status
|
|
1420
1663
|
});
|
|
1421
1664
|
}
|
|
1665
|
+
|
|
1666
|
+
logStep(`${tool.title} 프로젝트 규칙 처리 중...`);
|
|
1667
|
+
const ruleResult = await copyProjectRuleBootstrap({
|
|
1668
|
+
toolId,
|
|
1669
|
+
targetDir,
|
|
1670
|
+
installRoot,
|
|
1671
|
+
overwrite,
|
|
1672
|
+
dryRun,
|
|
1673
|
+
selectedComponentIds: componentIds,
|
|
1674
|
+
projectRulesMode: selection.projectRulesMode
|
|
1675
|
+
});
|
|
1676
|
+
if (ruleResult) {
|
|
1677
|
+
results.push(ruleResult);
|
|
1678
|
+
}
|
|
1422
1679
|
}
|
|
1423
1680
|
|
|
1681
|
+
if (output.isTTY) {
|
|
1682
|
+
output.write('\r\x1B[K');
|
|
1683
|
+
}
|
|
1424
1684
|
console.log('');
|
|
1425
1685
|
console.log('━━━━━━━━ 적용 결과 ━━━━━━━━');
|
|
1426
1686
|
const summaryCounter = {
|
|
@@ -1505,8 +1765,9 @@ function printList(catalog) {
|
|
|
1505
1765
|
getComponentInstallPath(component)
|
|
1506
1766
|
)}`
|
|
1507
1767
|
);
|
|
1508
|
-
|
|
1509
|
-
|
|
1768
|
+
const description = getComponentDescription(tool.id, component);
|
|
1769
|
+
if (description) {
|
|
1770
|
+
console.log(` ${description}`);
|
|
1510
1771
|
}
|
|
1511
1772
|
});
|
|
1512
1773
|
});
|
|
@@ -1530,7 +1791,7 @@ function printList(catalog) {
|
|
|
1530
1791
|
}
|
|
1531
1792
|
|
|
1532
1793
|
console.log('━━━━━━━━ 권장 흐름 ━━━━━━━━');
|
|
1533
|
-
console.log(`1)
|
|
1794
|
+
console.log(`1) npx --yes --package @dusky-bluehour/agent-service ${CLI_NAME} --interactive`);
|
|
1534
1795
|
console.log('2) 설치 후 .tri-agent-manager/state.json 기준으로 update');
|
|
1535
1796
|
console.log('3) update 시 필요한 도구만 부분 갱신');
|
|
1536
1797
|
}
|
|
@@ -1573,6 +1834,9 @@ async function main() {
|
|
|
1573
1834
|
}
|
|
1574
1835
|
|
|
1575
1836
|
const installRootOverridesFromFlag = parseInstallRootFlag(options.installRootFlag, catalog);
|
|
1837
|
+
const projectRulesModeFromFlag = options.projectRulesFlag
|
|
1838
|
+
? normalizeProjectRulesMode(options.projectRulesFlag)
|
|
1839
|
+
: null;
|
|
1576
1840
|
|
|
1577
1841
|
const shouldUseInteractive =
|
|
1578
1842
|
!options.nonInteractive &&
|
|
@@ -1584,7 +1848,8 @@ async function main() {
|
|
|
1584
1848
|
mode: command,
|
|
1585
1849
|
defaultTarget: options.targetFlag,
|
|
1586
1850
|
presetFlag: options.presetFlag,
|
|
1587
|
-
installRootOverridesFromFlag
|
|
1851
|
+
installRootOverridesFromFlag,
|
|
1852
|
+
projectRulesModeFromFlag
|
|
1588
1853
|
})
|
|
1589
1854
|
: buildSelectionFromFlags({
|
|
1590
1855
|
catalog,
|
|
@@ -1593,12 +1858,16 @@ async function main() {
|
|
|
1593
1858
|
toolFlag: options.toolFlag,
|
|
1594
1859
|
componentFlag: options.componentFlag,
|
|
1595
1860
|
targetFlag: options.targetFlag,
|
|
1596
|
-
installRootOverridesFromFlag
|
|
1861
|
+
installRootOverridesFromFlag,
|
|
1862
|
+
projectRulesModeFromFlag
|
|
1597
1863
|
});
|
|
1598
1864
|
|
|
1599
1865
|
const stateForTarget = await readState(rawSelection.targetDir);
|
|
1600
1866
|
const selection = {
|
|
1601
1867
|
...rawSelection,
|
|
1868
|
+
projectRulesMode: normalizeProjectRulesMode(
|
|
1869
|
+
projectRulesModeFromFlag ?? stateForTarget?.project_rules_mode ?? rawSelection.projectRulesMode
|
|
1870
|
+
),
|
|
1602
1871
|
installRootOverrides: buildInstallRootOverrides({
|
|
1603
1872
|
catalog,
|
|
1604
1873
|
selectedToolIds: rawSelection.selectedToolIds,
|