@dusky-bluehour/agent-service 0.6.5 → 0.6.7
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 +36 -25
- package/antigravity/README.md +14 -1
- package/antigravity/agents/agent-catalog.json +5 -5
- package/antigravity/commands/definitions/cmd-dev-be-api.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-fe-hook-separate.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-fe-ui-componentize.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-perf-optimize.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-sequential-autorun.md +43 -0
- package/antigravity/commands/definitions/cmd-doc-handoff.md +44 -0
- package/antigravity/commands/definitions/cmd-improve-techdebt.md +43 -0
- package/antigravity/commands/definitions/cmd-incident-triage.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-ci-cd-gate.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-deploy.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-monitoring.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-arch-decision.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-implementation-bootstrap.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-prd-details.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-prd-master.md +44 -0
- package/antigravity/commands/definitions/cmd-plan-req-lock.md +44 -0
- package/antigravity/commands/definitions/cmd-review-code.md +43 -0
- package/antigravity/commands/definitions/cmd-sec-dependency-audit.md +43 -0
- package/antigravity/commands/definitions/cmd-sec-threat-model.md +43 -0
- package/antigravity/commands/definitions/cmd-test-unit-integration.md +43 -0
- package/antigravity/instructions/WORKSPACE-RULES.template.md +34 -0
- package/antigravity/settings/editor-policy.json +193 -0
- package/antigravity/skills/change-safety-review/SKILL.md +40 -0
- package/antigravity/skills/code-review-and-improvement/SKILL.md +20 -0
- package/antigravity/skills/frontend-repetition-pack/SKILL.md +20 -0
- package/antigravity/skills/incident-response/SKILL.md +20 -0
- package/antigravity/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/antigravity/skills/release-and-operations/SKILL.md +20 -0
- package/antigravity/skills/security-hardening/SKILL.md +21 -1
- package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/antigravity/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +38 -0
- package/antigravity/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +41 -0
- package/antigravity/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +76 -0
- package/antigravity/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +40 -0
- package/antigravity/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +67 -0
- package/antigravity/workflows/workflow-catalog.json +5 -5
- package/catalog/tool-catalog.ko.json +78 -17
- package/claude-code/README.md +31 -1
- package/claude-code/agent-teams/team-catalog.json +7 -7
- 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/claude-code/skills/change-safety-review/SKILL.md +40 -0
- package/claude-code/skills/code-review-and-improvement/SKILL.md +21 -1
- package/claude-code/skills/frontend-repetition-pack/SKILL.md +21 -1
- package/claude-code/skills/incident-response/SKILL.md +21 -1
- package/claude-code/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/claude-code/skills/release-and-operations/SKILL.md +21 -1
- package/claude-code/skills/security-hardening/SKILL.md +21 -1
- package/claude-code/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/claude-code/workflows/workflow-catalog.json +8 -8
- package/codex/README.md +18 -3
- package/codex/automations/automation-recipes.toml +4 -4
- package/codex/instructions/AGENTS.permissions.generated.md +121 -0
- package/codex/instructions/AGENTS.template.md +24 -8
- package/codex/settings/runtime-policy.json +188 -0
- package/codex/skills/change-safety-review/SKILL.md +40 -0
- package/codex/skills/change-safety-review/agents/openai.yaml +4 -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/codex/skills/code-review-and-improvement/SKILL.md +21 -1
- package/codex/skills/frontend-repetition-pack/SKILL.md +20 -0
- package/codex/skills/incident-response/SKILL.md +21 -1
- package/codex/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/codex/skills/release-and-operations/SKILL.md +20 -0
- package/codex/skills/security-hardening/SKILL.md +21 -1
- package/codex/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/codex/workflows/workflow-catalog.json +6 -6
- package/common/antigravity/agent-catalog.json +72 -0
- package/common/antigravity/artifact-catalog.json +184 -0
- package/common/claude/subagent-catalog.json +419 -0
- package/common/claude/team-catalog.json +69 -0
- package/common/commands/command-catalog.json +942 -0
- package/common/settings/security-policy.json +221 -0
- package/common/skills/skill-catalog.json +566 -0
- package/common/workflows/workflow-catalog.json +1550 -0
- package/package.json +6 -2
- package/scripts/generate-from-common.mjs +872 -0
- package/scripts/init.mjs +295 -36
- package/scripts/validate.mjs +451 -10
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
|
+
antigravity: {
|
|
30
|
+
sourceRelativePath: path.join('antigravity', 'instructions', 'WORKSPACE-RULES.template.md'),
|
|
31
|
+
destinationRelativePath: '{installRoot}/rules/workspace-core-rules.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
|
'옵션:',
|
|
@@ -34,6 +49,7 @@ const HELP_TEXT = [
|
|
|
34
49
|
' --components 구성요소 선택 (예: skills,workflows,commands) / all',
|
|
35
50
|
' --target 설치 경로 (기본: .)',
|
|
36
51
|
' --install-root 도구별 설치 루트 오버라이드 (예: codex=.codex,antigravity=.agent)',
|
|
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 {};
|
|
@@ -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',
|
|
@@ -430,11 +503,21 @@ function getToolExecutionGuide(tool, installRoot) {
|
|
|
430
503
|
|
|
431
504
|
if (tool.id === 'antigravity') {
|
|
432
505
|
return [
|
|
506
|
+
`\`${path.join(
|
|
507
|
+
installRoot,
|
|
508
|
+
'rules',
|
|
509
|
+
'workspace-core-rules.md'
|
|
510
|
+
)}\`(자동 생성됨)를 열고 운영 규칙을 확정합니다.`,
|
|
433
511
|
`\`${path.join(
|
|
434
512
|
installRoot,
|
|
435
513
|
'workflows',
|
|
436
514
|
'workflow-catalog.json'
|
|
437
515
|
)}\`에서 workflow ID를 선택합니다.`,
|
|
516
|
+
`선택한 workflow ID와 같은 이름의 정의 파일을 \`${path.join(
|
|
517
|
+
installRoot,
|
|
518
|
+
'workflows',
|
|
519
|
+
'definitions'
|
|
520
|
+
)}\`에서 확인합니다.`,
|
|
438
521
|
`각 stage의 \`input_artifact\`를 \`${path.join(
|
|
439
522
|
installRoot,
|
|
440
523
|
'artifacts'
|
|
@@ -449,6 +532,7 @@ function getToolExecutionGuide(tool, installRoot) {
|
|
|
449
532
|
function getToolInteractionTips(tool, installRoot) {
|
|
450
533
|
if (tool.id === 'codex') {
|
|
451
534
|
return [
|
|
535
|
+
'프로젝트 루트의 `AGENTS.md`를 팀 규칙 단일 진입점으로 유지하세요.',
|
|
452
536
|
'Codex 대화 입력창에서 `$`를 누르면 사용 가능한 스킬 목록을 바로 열 수 있습니다.',
|
|
453
537
|
`프로젝트 루트에서 스킬 파일을 직접 확인하려면 \`ls ${path.join(
|
|
454
538
|
installRoot,
|
|
@@ -464,6 +548,7 @@ function getToolInteractionTips(tool, installRoot) {
|
|
|
464
548
|
|
|
465
549
|
if (tool.id === 'claude-code') {
|
|
466
550
|
return [
|
|
551
|
+
'프로젝트 루트 `CLAUDE.md`를 최신 상태로 유지하면 세션마다 동일한 기준으로 동작합니다.',
|
|
467
552
|
`서브에이전트 이름은 \`${path.join(installRoot, 'agents')}\` 경로의 파일명 기준입니다.`,
|
|
468
553
|
`프로젝트 루트에서 \`ls ${path.join(
|
|
469
554
|
installRoot,
|
|
@@ -475,11 +560,21 @@ function getToolInteractionTips(tool, installRoot) {
|
|
|
475
560
|
|
|
476
561
|
if (tool.id === 'antigravity') {
|
|
477
562
|
return [
|
|
563
|
+
`\`${path.join(
|
|
564
|
+
installRoot,
|
|
565
|
+
'rules',
|
|
566
|
+
'workspace-core-rules.md'
|
|
567
|
+
)}\`를 Manager 단계의 승인 규칙 문서로 사용하세요.`,
|
|
478
568
|
`워크플로우 선택은 \`${path.join(
|
|
479
569
|
installRoot,
|
|
480
570
|
'workflows',
|
|
481
571
|
'workflow-catalog.json'
|
|
482
572
|
)}\` 에서 WF ID를 먼저 고르는 방식으로 진행하세요.`,
|
|
573
|
+
`실행용 정의 파일은 \`${path.join(
|
|
574
|
+
installRoot,
|
|
575
|
+
'workflows',
|
|
576
|
+
'definitions'
|
|
577
|
+
)}\` 의 \`*.workflow.yaml\`을 사용하세요.`,
|
|
483
578
|
`에이전트 역할은 \`${path.join(installRoot, 'agents', 'agent-catalog.json')}\` 에서 확인하세요.`,
|
|
484
579
|
`입출력 아티팩트 스키마는 \`${path.join(
|
|
485
580
|
installRoot,
|
|
@@ -510,6 +605,11 @@ async function writeUsageGuide({ catalog, selection, targetDir, mode }) {
|
|
|
510
605
|
lines.push(`- 생성 시각: ${new Date().toISOString()}`);
|
|
511
606
|
lines.push(`- 모드: ${mode}`);
|
|
512
607
|
lines.push(`- 프리셋: ${selection.presetId ?? '수동/없음'}`);
|
|
608
|
+
lines.push(
|
|
609
|
+
`- 프로젝트 규칙 파일 정책: ${selection.projectRulesMode} (${describeProjectRulesMode(
|
|
610
|
+
selection.projectRulesMode
|
|
611
|
+
)})`
|
|
612
|
+
);
|
|
513
613
|
lines.push(`- 대상 경로: ${targetDir}`);
|
|
514
614
|
lines.push('');
|
|
515
615
|
lines.push('이 문서는 현재 설치 선택값 기준으로 생성되었습니다.');
|
|
@@ -528,6 +628,10 @@ async function writeUsageGuide({ catalog, selection, targetDir, mode }) {
|
|
|
528
628
|
lines.push(`## ${tool.title} (${tool.id})`);
|
|
529
629
|
lines.push('');
|
|
530
630
|
lines.push(`- 설치 루트: \`${path.join(targetDir, installRoot)}\``);
|
|
631
|
+
const projectRuleDestination = resolveProjectRuleDestination(tool.id, installRoot);
|
|
632
|
+
if (projectRuleDestination) {
|
|
633
|
+
lines.push(`- 프로젝트 규칙 파일: \`${path.join(targetDir, projectRuleDestination)}\``);
|
|
634
|
+
}
|
|
531
635
|
if (tool.install_root_basis) {
|
|
532
636
|
lines.push(`- 경로 기준: ${tool.install_root_basis}`);
|
|
533
637
|
}
|
|
@@ -644,7 +748,7 @@ function clearTuiScreen() {
|
|
|
644
748
|
}
|
|
645
749
|
|
|
646
750
|
function formatTuiLine(prefix, label, selected = false) {
|
|
647
|
-
const marker = selected ? '
|
|
751
|
+
const marker = selected ? '🟢' : '⚪';
|
|
648
752
|
return `${prefix} ${marker} ${label}`;
|
|
649
753
|
}
|
|
650
754
|
|
|
@@ -690,7 +794,7 @@ async function runSingleSelectMenu({ title, help, options, defaultIndex = 0 }) {
|
|
|
690
794
|
while (true) {
|
|
691
795
|
renderTuiHeader(title, help);
|
|
692
796
|
options.forEach((option, index) => {
|
|
693
|
-
const pointer = index === cursor ? '
|
|
797
|
+
const pointer = index === cursor ? '◉' : '○';
|
|
694
798
|
output.write(`${pointer} ${option.label}\n`);
|
|
695
799
|
if (option.description) {
|
|
696
800
|
output.write(` ${option.description}\n`);
|
|
@@ -729,8 +833,8 @@ async function runMultiSelectMenu({
|
|
|
729
833
|
const selected = new Set(preselectedIds);
|
|
730
834
|
const optionRows = [
|
|
731
835
|
...choices.map((choice) => ({ kind: 'choice', ...choice })),
|
|
732
|
-
{ kind: 'action', action: 'toggle-all', label: '
|
|
733
|
-
{ kind: 'action', action: 'done', label: '
|
|
836
|
+
{ kind: 'action', action: 'toggle-all', label: '🟣 전체 선택/해제' },
|
|
837
|
+
{ kind: 'action', action: 'done', label: '🔵 선택 완료' }
|
|
734
838
|
];
|
|
735
839
|
let cursor = 0;
|
|
736
840
|
let notice = '';
|
|
@@ -744,7 +848,7 @@ async function runMultiSelectMenu({
|
|
|
744
848
|
output.write('\n');
|
|
745
849
|
|
|
746
850
|
optionRows.forEach((row, index) => {
|
|
747
|
-
const pointer = index === cursor ? '
|
|
851
|
+
const pointer = index === cursor ? '◉' : '○';
|
|
748
852
|
if (row.kind === 'choice') {
|
|
749
853
|
output.write(`${formatTuiLine(pointer, row.label, selected.has(row.id))}\n`);
|
|
750
854
|
if (row.description) {
|
|
@@ -813,7 +917,8 @@ async function promptInteractiveTui({
|
|
|
813
917
|
mode,
|
|
814
918
|
defaultTarget,
|
|
815
919
|
presetFlag,
|
|
816
|
-
installRootOverridesFromFlag
|
|
920
|
+
installRootOverridesFromFlag,
|
|
921
|
+
projectRulesModeFromFlag
|
|
817
922
|
}) {
|
|
818
923
|
readline.emitKeypressEvents(input);
|
|
819
924
|
const wasRawMode = Boolean(input.isRaw);
|
|
@@ -846,6 +951,9 @@ async function promptInteractiveTui({
|
|
|
846
951
|
|
|
847
952
|
const targetDir = path.resolve(process.cwd(), targetInput);
|
|
848
953
|
const state = await readState(targetDir);
|
|
954
|
+
const defaultProjectRulesMode = normalizeProjectRulesMode(
|
|
955
|
+
projectRulesModeFromFlag ?? (mode === 'update' ? state?.project_rules_mode : 'always')
|
|
956
|
+
);
|
|
849
957
|
const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
|
|
850
958
|
|
|
851
959
|
const defaultPresetId =
|
|
@@ -928,7 +1036,7 @@ async function promptInteractiveTui({
|
|
|
928
1036
|
choices: tool.components.map((component) => ({
|
|
929
1037
|
id: component.id,
|
|
930
1038
|
label: `${component.title} (${component.id})`,
|
|
931
|
-
description: component
|
|
1039
|
+
description: getComponentDescription(tool.id, component)
|
|
932
1040
|
})),
|
|
933
1041
|
preselectedIds: defaultComponents,
|
|
934
1042
|
minSelected: 1
|
|
@@ -943,12 +1051,47 @@ async function promptInteractiveTui({
|
|
|
943
1051
|
cliOverrides: installRootOverridesFromFlag
|
|
944
1052
|
});
|
|
945
1053
|
|
|
1054
|
+
let projectRulesMode = defaultProjectRulesMode;
|
|
1055
|
+
if (!projectRulesModeFromFlag) {
|
|
1056
|
+
const modeOption = await runSingleSelectMenu({
|
|
1057
|
+
title: '🧭 프로젝트 규칙 파일 정책',
|
|
1058
|
+
help: '조작: 위/아래/좌/우 이동, Enter 선택, q 종료',
|
|
1059
|
+
options: [
|
|
1060
|
+
{
|
|
1061
|
+
id: 'always',
|
|
1062
|
+
label: '🟢 always',
|
|
1063
|
+
description: describeProjectRulesMode('always')
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
id: 'if-instructions',
|
|
1067
|
+
label: '🟡 if-instructions',
|
|
1068
|
+
description: describeProjectRulesMode('if-instructions')
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
id: 'if-present',
|
|
1072
|
+
label: '🔵 if-present',
|
|
1073
|
+
description: describeProjectRulesMode('if-present')
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
id: 'never',
|
|
1077
|
+
label: '⚪ never',
|
|
1078
|
+
description: describeProjectRulesMode('never')
|
|
1079
|
+
}
|
|
1080
|
+
],
|
|
1081
|
+
defaultIndex: ['always', 'if-instructions', 'if-present', 'never'].indexOf(
|
|
1082
|
+
defaultProjectRulesMode
|
|
1083
|
+
)
|
|
1084
|
+
});
|
|
1085
|
+
projectRulesMode = normalizeProjectRulesMode(modeOption.id);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
946
1088
|
return {
|
|
947
1089
|
targetDir,
|
|
948
1090
|
selectedToolIds,
|
|
949
1091
|
componentSelection,
|
|
950
1092
|
presetId: selectedPreset?.id ?? null,
|
|
951
|
-
installRootOverrides
|
|
1093
|
+
installRootOverrides,
|
|
1094
|
+
projectRulesMode
|
|
952
1095
|
};
|
|
953
1096
|
} finally {
|
|
954
1097
|
setRawMode(wasRawMode);
|
|
@@ -961,7 +1104,8 @@ async function promptInteractiveText({
|
|
|
961
1104
|
mode,
|
|
962
1105
|
defaultTarget,
|
|
963
1106
|
presetFlag,
|
|
964
|
-
installRootOverridesFromFlag
|
|
1107
|
+
installRootOverridesFromFlag,
|
|
1108
|
+
projectRulesModeFromFlag
|
|
965
1109
|
}) {
|
|
966
1110
|
const rl = createInterface({ input, output });
|
|
967
1111
|
|
|
@@ -996,6 +1140,9 @@ async function promptInteractiveText({
|
|
|
996
1140
|
}
|
|
997
1141
|
const targetDir = path.resolve(process.cwd(), targetInput);
|
|
998
1142
|
const state = await readState(targetDir);
|
|
1143
|
+
const defaultProjectRulesMode = normalizeProjectRulesMode(
|
|
1144
|
+
projectRulesModeFromFlag ?? (mode === 'update' ? state?.project_rules_mode : 'always')
|
|
1145
|
+
);
|
|
999
1146
|
|
|
1000
1147
|
const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
|
|
1001
1148
|
let selectedPreset = findPreset(catalog, presetFlag);
|
|
@@ -1049,7 +1196,7 @@ async function promptInteractiveText({
|
|
|
1049
1196
|
console.log(`📦 [${tool.title}] 구성요소 목록`);
|
|
1050
1197
|
tool.components.forEach((component, index) => {
|
|
1051
1198
|
console.log(`${index + 1}. ${component.title} (${component.id})`);
|
|
1052
|
-
console.log(` - ${component
|
|
1199
|
+
console.log(` - ${getComponentDescription(tool.id, component)}`);
|
|
1053
1200
|
});
|
|
1054
1201
|
|
|
1055
1202
|
let defaultComponents = tool.components.map((c) => c.id);
|
|
@@ -1084,12 +1231,32 @@ async function promptInteractiveText({
|
|
|
1084
1231
|
cliOverrides: installRootOverridesFromFlag
|
|
1085
1232
|
});
|
|
1086
1233
|
|
|
1234
|
+
let projectRulesMode = defaultProjectRulesMode;
|
|
1235
|
+
if (!projectRulesModeFromFlag) {
|
|
1236
|
+
console.log('');
|
|
1237
|
+
console.log('🧭 프로젝트 규칙 파일 정책');
|
|
1238
|
+
console.log(`1. always - ${describeProjectRulesMode('always')}`);
|
|
1239
|
+
console.log(`2. if-instructions - ${describeProjectRulesMode('if-instructions')}`);
|
|
1240
|
+
console.log(`3. if-present - ${describeProjectRulesMode('if-present')}`);
|
|
1241
|
+
console.log(`4. never - ${describeProjectRulesMode('never')}`);
|
|
1242
|
+
const modeAnswer = await ask('정책을 선택하세요. (번호/ID)', defaultProjectRulesMode);
|
|
1243
|
+
const normalizedModeAnswer = modeAnswer.trim().toLowerCase();
|
|
1244
|
+
const modeByNumber = {
|
|
1245
|
+
'1': 'always',
|
|
1246
|
+
'2': 'if-instructions',
|
|
1247
|
+
'3': 'if-present',
|
|
1248
|
+
'4': 'never'
|
|
1249
|
+
};
|
|
1250
|
+
projectRulesMode = normalizeProjectRulesMode(modeByNumber[normalizedModeAnswer] ?? modeAnswer);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1087
1253
|
return {
|
|
1088
1254
|
targetDir,
|
|
1089
1255
|
selectedToolIds,
|
|
1090
1256
|
componentSelection,
|
|
1091
1257
|
presetId: selectedPreset?.id ?? null,
|
|
1092
|
-
installRootOverrides
|
|
1258
|
+
installRootOverrides,
|
|
1259
|
+
projectRulesMode
|
|
1093
1260
|
};
|
|
1094
1261
|
} finally {
|
|
1095
1262
|
rl.close();
|
|
@@ -1110,7 +1277,8 @@ function buildSelectionFromFlags({
|
|
|
1110
1277
|
toolFlag,
|
|
1111
1278
|
componentFlag,
|
|
1112
1279
|
targetFlag,
|
|
1113
|
-
installRootOverridesFromFlag
|
|
1280
|
+
installRootOverridesFromFlag,
|
|
1281
|
+
projectRulesModeFromFlag
|
|
1114
1282
|
}) {
|
|
1115
1283
|
const preset = findPreset(catalog, presetFlag);
|
|
1116
1284
|
const presetToolIds = resolveToolsFromPreset(preset, catalog);
|
|
@@ -1153,7 +1321,8 @@ function buildSelectionFromFlags({
|
|
|
1153
1321
|
selectedToolIds,
|
|
1154
1322
|
componentSelection,
|
|
1155
1323
|
presetId: preset?.id ?? null,
|
|
1156
|
-
installRootOverrides
|
|
1324
|
+
installRootOverrides,
|
|
1325
|
+
projectRulesMode: normalizeProjectRulesMode(projectRulesModeFromFlag ?? 'always')
|
|
1157
1326
|
};
|
|
1158
1327
|
}
|
|
1159
1328
|
|
|
@@ -1179,6 +1348,54 @@ async function copyEntry(srcPath, destPath, { overwrite, dryRun }) {
|
|
|
1179
1348
|
return { status: destExists ? 'overwritten' : 'copied' };
|
|
1180
1349
|
}
|
|
1181
1350
|
|
|
1351
|
+
async function copyProjectRuleBootstrap({
|
|
1352
|
+
toolId,
|
|
1353
|
+
targetDir,
|
|
1354
|
+
installRoot,
|
|
1355
|
+
overwrite,
|
|
1356
|
+
dryRun,
|
|
1357
|
+
selectedComponentIds,
|
|
1358
|
+
projectRulesMode
|
|
1359
|
+
}) {
|
|
1360
|
+
const ruleMeta = PROJECT_RULE_BOOTSTRAP[toolId];
|
|
1361
|
+
if (!ruleMeta) {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
const normalizedMode = normalizeProjectRulesMode(projectRulesMode ?? 'always');
|
|
1366
|
+
if (normalizedMode === 'never') {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const componentIds = new Set(selectedComponentIds ?? []);
|
|
1371
|
+
if (normalizedMode === 'if-instructions' && !componentIds.has('instructions')) {
|
|
1372
|
+
return null;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
const srcPath = path.join(rootDir, ruleMeta.sourceRelativePath);
|
|
1376
|
+
if (!(await exists(srcPath))) {
|
|
1377
|
+
throw new Error(`[${toolId}] 프로젝트 규칙 템플릿 누락: ${ruleMeta.sourceRelativePath}`);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const destinationRelativePath = ruleMeta.destinationRelativePath.replace(
|
|
1381
|
+
'{installRoot}',
|
|
1382
|
+
installRoot || ''
|
|
1383
|
+
);
|
|
1384
|
+
const dstPath = path.join(targetDir, destinationRelativePath);
|
|
1385
|
+
|
|
1386
|
+
if (normalizedMode === 'if-present' && !(await exists(dstPath))) {
|
|
1387
|
+
return null;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const result = await copyEntry(srcPath, dstPath, { overwrite, dryRun });
|
|
1391
|
+
return {
|
|
1392
|
+
toolId,
|
|
1393
|
+
source: ruleMeta.sourceRelativePath,
|
|
1394
|
+
destination: destinationRelativePath,
|
|
1395
|
+
status: result.status
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1182
1399
|
function statusLabel(status) {
|
|
1183
1400
|
switch (status) {
|
|
1184
1401
|
case 'copied':
|
|
@@ -1199,17 +1416,17 @@ function statusLabel(status) {
|
|
|
1199
1416
|
function statusIcon(status) {
|
|
1200
1417
|
switch (status) {
|
|
1201
1418
|
case 'copied':
|
|
1202
|
-
return '
|
|
1419
|
+
return '🟢';
|
|
1203
1420
|
case 'overwritten':
|
|
1204
|
-
return '
|
|
1421
|
+
return '🔵';
|
|
1205
1422
|
case 'skipped':
|
|
1206
|
-
return '
|
|
1423
|
+
return '⚪';
|
|
1207
1424
|
case 'would-copy':
|
|
1208
|
-
return '
|
|
1425
|
+
return '🟡';
|
|
1209
1426
|
case 'would-overwrite':
|
|
1210
|
-
return '
|
|
1427
|
+
return '🟣';
|
|
1211
1428
|
default:
|
|
1212
|
-
return '
|
|
1429
|
+
return '○';
|
|
1213
1430
|
}
|
|
1214
1431
|
}
|
|
1215
1432
|
|
|
@@ -1231,6 +1448,11 @@ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
|
|
|
1231
1448
|
console.log(`실행 타입 : ${dryRun ? 'dry-run (실제 파일 변경 없음)' : '실행 (파일 변경 반영)'}`);
|
|
1232
1449
|
console.log(`프리셋 : ${selection.presetId ?? '수동/없음'}`);
|
|
1233
1450
|
console.log(`대상 경로 : ${targetDir}`);
|
|
1451
|
+
console.log(
|
|
1452
|
+
`규칙 파일 : ${selection.projectRulesMode} (${describeProjectRulesMode(
|
|
1453
|
+
selection.projectRulesMode
|
|
1454
|
+
)})`
|
|
1455
|
+
);
|
|
1234
1456
|
console.log(`선택 도구 : ${selection.selectedToolIds.length}개`);
|
|
1235
1457
|
|
|
1236
1458
|
selection.selectedToolIds.forEach((toolId, index) => {
|
|
@@ -1262,6 +1484,20 @@ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
|
|
|
1262
1484
|
}
|
|
1263
1485
|
}
|
|
1264
1486
|
|
|
1487
|
+
const projectRuleDestination = resolveProjectRuleDestination(tool.id, installRoot);
|
|
1488
|
+
if (projectRuleDestination) {
|
|
1489
|
+
let applyHint = '적용됨';
|
|
1490
|
+
if (selection.projectRulesMode === 'if-instructions' && !componentIds.includes('instructions')) {
|
|
1491
|
+
applyHint = '`instructions` 미선택으로 이번 실행에서는 생성/업데이트 안 함';
|
|
1492
|
+
} else if (selection.projectRulesMode === 'if-present') {
|
|
1493
|
+
applyHint = '대상 파일이 이미 있을 때만 업데이트';
|
|
1494
|
+
} else if (selection.projectRulesMode === 'never') {
|
|
1495
|
+
applyHint = '생성/업데이트 안 함';
|
|
1496
|
+
}
|
|
1497
|
+
console.log(` 규칙 파일 : ${projectRuleDestination}`);
|
|
1498
|
+
console.log(` 적용 정책 : ${applyHint}`);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1265
1501
|
if (tool.install_root_basis) {
|
|
1266
1502
|
console.log(` 경로 기준 : ${tool.install_root_basis}`);
|
|
1267
1503
|
}
|
|
@@ -1347,6 +1583,7 @@ async function writeState({ catalog, targetDir, packageData, mode, selection })
|
|
|
1347
1583
|
package_version: packageData.version,
|
|
1348
1584
|
updated_at: new Date().toISOString(),
|
|
1349
1585
|
preset_id: selection.presetId ?? null,
|
|
1586
|
+
project_rules_mode: normalizeProjectRulesMode(selection.projectRulesMode ?? 'always'),
|
|
1350
1587
|
install_roots: installRoots,
|
|
1351
1588
|
tools
|
|
1352
1589
|
};
|
|
@@ -1409,6 +1646,19 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
|
|
|
1409
1646
|
status: result.status
|
|
1410
1647
|
});
|
|
1411
1648
|
}
|
|
1649
|
+
|
|
1650
|
+
const ruleResult = await copyProjectRuleBootstrap({
|
|
1651
|
+
toolId,
|
|
1652
|
+
targetDir,
|
|
1653
|
+
installRoot,
|
|
1654
|
+
overwrite,
|
|
1655
|
+
dryRun,
|
|
1656
|
+
selectedComponentIds: componentIds,
|
|
1657
|
+
projectRulesMode: selection.projectRulesMode
|
|
1658
|
+
});
|
|
1659
|
+
if (ruleResult) {
|
|
1660
|
+
results.push(ruleResult);
|
|
1661
|
+
}
|
|
1412
1662
|
}
|
|
1413
1663
|
|
|
1414
1664
|
console.log('');
|
|
@@ -1495,8 +1745,9 @@ function printList(catalog) {
|
|
|
1495
1745
|
getComponentInstallPath(component)
|
|
1496
1746
|
)}`
|
|
1497
1747
|
);
|
|
1498
|
-
|
|
1499
|
-
|
|
1748
|
+
const description = getComponentDescription(tool.id, component);
|
|
1749
|
+
if (description) {
|
|
1750
|
+
console.log(` ${description}`);
|
|
1500
1751
|
}
|
|
1501
1752
|
});
|
|
1502
1753
|
});
|
|
@@ -1520,7 +1771,7 @@ function printList(catalog) {
|
|
|
1520
1771
|
}
|
|
1521
1772
|
|
|
1522
1773
|
console.log('━━━━━━━━ 권장 흐름 ━━━━━━━━');
|
|
1523
|
-
console.log(`1)
|
|
1774
|
+
console.log(`1) npx --yes --package @dusky-bluehour/agent-service ${CLI_NAME} --interactive`);
|
|
1524
1775
|
console.log('2) 설치 후 .tri-agent-manager/state.json 기준으로 update');
|
|
1525
1776
|
console.log('3) update 시 필요한 도구만 부분 갱신');
|
|
1526
1777
|
}
|
|
@@ -1563,6 +1814,9 @@ async function main() {
|
|
|
1563
1814
|
}
|
|
1564
1815
|
|
|
1565
1816
|
const installRootOverridesFromFlag = parseInstallRootFlag(options.installRootFlag, catalog);
|
|
1817
|
+
const projectRulesModeFromFlag = options.projectRulesFlag
|
|
1818
|
+
? normalizeProjectRulesMode(options.projectRulesFlag)
|
|
1819
|
+
: null;
|
|
1566
1820
|
|
|
1567
1821
|
const shouldUseInteractive =
|
|
1568
1822
|
!options.nonInteractive &&
|
|
@@ -1574,7 +1828,8 @@ async function main() {
|
|
|
1574
1828
|
mode: command,
|
|
1575
1829
|
defaultTarget: options.targetFlag,
|
|
1576
1830
|
presetFlag: options.presetFlag,
|
|
1577
|
-
installRootOverridesFromFlag
|
|
1831
|
+
installRootOverridesFromFlag,
|
|
1832
|
+
projectRulesModeFromFlag
|
|
1578
1833
|
})
|
|
1579
1834
|
: buildSelectionFromFlags({
|
|
1580
1835
|
catalog,
|
|
@@ -1583,12 +1838,16 @@ async function main() {
|
|
|
1583
1838
|
toolFlag: options.toolFlag,
|
|
1584
1839
|
componentFlag: options.componentFlag,
|
|
1585
1840
|
targetFlag: options.targetFlag,
|
|
1586
|
-
installRootOverridesFromFlag
|
|
1841
|
+
installRootOverridesFromFlag,
|
|
1842
|
+
projectRulesModeFromFlag
|
|
1587
1843
|
});
|
|
1588
1844
|
|
|
1589
1845
|
const stateForTarget = await readState(rawSelection.targetDir);
|
|
1590
1846
|
const selection = {
|
|
1591
1847
|
...rawSelection,
|
|
1848
|
+
projectRulesMode: normalizeProjectRulesMode(
|
|
1849
|
+
projectRulesModeFromFlag ?? stateForTarget?.project_rules_mode ?? rawSelection.projectRulesMode
|
|
1850
|
+
),
|
|
1592
1851
|
installRootOverrides: buildInstallRootOverrides({
|
|
1593
1852
|
catalog,
|
|
1594
1853
|
selectedToolIds: rawSelection.selectedToolIds,
|