cc4pm 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.claude-plugin/README.md +17 -0
  2. package/.claude-plugin/plugin.json +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +157 -0
  5. package/README.zh-CN.md +134 -0
  6. package/contexts/dev.md +20 -0
  7. package/contexts/research.md +26 -0
  8. package/contexts/review.md +22 -0
  9. package/examples/CLAUDE.md +100 -0
  10. package/examples/statusline.json +19 -0
  11. package/examples/user-CLAUDE.md +109 -0
  12. package/install.sh +17 -0
  13. package/manifests/install-components.json +173 -0
  14. package/manifests/install-modules.json +335 -0
  15. package/manifests/install-profiles.json +75 -0
  16. package/package.json +117 -0
  17. package/schemas/ecc-install-config.schema.json +58 -0
  18. package/schemas/hooks.schema.json +197 -0
  19. package/schemas/install-components.schema.json +56 -0
  20. package/schemas/install-modules.schema.json +105 -0
  21. package/schemas/install-profiles.schema.json +45 -0
  22. package/schemas/install-state.schema.json +210 -0
  23. package/schemas/package-manager.schema.json +23 -0
  24. package/schemas/plugin.schema.json +58 -0
  25. package/scripts/ci/catalog.js +83 -0
  26. package/scripts/ci/validate-agents.js +81 -0
  27. package/scripts/ci/validate-commands.js +135 -0
  28. package/scripts/ci/validate-hooks.js +239 -0
  29. package/scripts/ci/validate-install-manifests.js +211 -0
  30. package/scripts/ci/validate-no-personal-paths.js +63 -0
  31. package/scripts/ci/validate-rules.js +81 -0
  32. package/scripts/ci/validate-skills.js +54 -0
  33. package/scripts/claw.js +468 -0
  34. package/scripts/doctor.js +110 -0
  35. package/scripts/ecc.js +194 -0
  36. package/scripts/hooks/auto-tmux-dev.js +88 -0
  37. package/scripts/hooks/check-console-log.js +71 -0
  38. package/scripts/hooks/check-hook-enabled.js +12 -0
  39. package/scripts/hooks/cost-tracker.js +78 -0
  40. package/scripts/hooks/doc-file-warning.js +63 -0
  41. package/scripts/hooks/evaluate-session.js +100 -0
  42. package/scripts/hooks/insaits-security-monitor.py +269 -0
  43. package/scripts/hooks/insaits-security-wrapper.js +88 -0
  44. package/scripts/hooks/post-bash-build-complete.js +27 -0
  45. package/scripts/hooks/post-bash-pr-created.js +36 -0
  46. package/scripts/hooks/post-edit-console-warn.js +54 -0
  47. package/scripts/hooks/post-edit-format.js +109 -0
  48. package/scripts/hooks/post-edit-typecheck.js +96 -0
  49. package/scripts/hooks/pre-bash-dev-server-block.js +187 -0
  50. package/scripts/hooks/pre-bash-git-push-reminder.js +28 -0
  51. package/scripts/hooks/pre-bash-tmux-reminder.js +33 -0
  52. package/scripts/hooks/pre-compact.js +48 -0
  53. package/scripts/hooks/pre-write-doc-warn.js +9 -0
  54. package/scripts/hooks/quality-gate.js +168 -0
  55. package/scripts/hooks/run-with-flags-shell.sh +32 -0
  56. package/scripts/hooks/run-with-flags.js +120 -0
  57. package/scripts/hooks/session-end-marker.js +15 -0
  58. package/scripts/hooks/session-end.js +299 -0
  59. package/scripts/hooks/session-start.js +97 -0
  60. package/scripts/hooks/suggest-compact.js +80 -0
  61. package/scripts/install-apply.js +137 -0
  62. package/scripts/install-plan.js +254 -0
  63. package/scripts/lib/hook-flags.js +74 -0
  64. package/scripts/lib/install/apply.js +23 -0
  65. package/scripts/lib/install/config.js +82 -0
  66. package/scripts/lib/install/request.js +113 -0
  67. package/scripts/lib/install/runtime.js +42 -0
  68. package/scripts/lib/install-executor.js +605 -0
  69. package/scripts/lib/install-lifecycle.js +763 -0
  70. package/scripts/lib/install-manifests.js +305 -0
  71. package/scripts/lib/install-state.js +120 -0
  72. package/scripts/lib/install-targets/antigravity-project.js +9 -0
  73. package/scripts/lib/install-targets/claude-home.js +10 -0
  74. package/scripts/lib/install-targets/codex-home.js +10 -0
  75. package/scripts/lib/install-targets/cursor-project.js +10 -0
  76. package/scripts/lib/install-targets/helpers.js +89 -0
  77. package/scripts/lib/install-targets/opencode-home.js +10 -0
  78. package/scripts/lib/install-targets/registry.js +64 -0
  79. package/scripts/lib/orchestration-session.js +299 -0
  80. package/scripts/lib/package-manager.d.ts +119 -0
  81. package/scripts/lib/package-manager.js +431 -0
  82. package/scripts/lib/project-detect.js +428 -0
  83. package/scripts/lib/resolve-formatter.js +185 -0
  84. package/scripts/lib/session-adapters/canonical-session.js +138 -0
  85. package/scripts/lib/session-adapters/claude-history.js +149 -0
  86. package/scripts/lib/session-adapters/dmux-tmux.js +80 -0
  87. package/scripts/lib/session-adapters/registry.js +111 -0
  88. package/scripts/lib/session-aliases.d.ts +136 -0
  89. package/scripts/lib/session-aliases.js +481 -0
  90. package/scripts/lib/session-manager.d.ts +131 -0
  91. package/scripts/lib/session-manager.js +464 -0
  92. package/scripts/lib/shell-split.js +86 -0
  93. package/scripts/lib/skill-improvement/amendify.js +89 -0
  94. package/scripts/lib/skill-improvement/evaluate.js +59 -0
  95. package/scripts/lib/skill-improvement/health.js +118 -0
  96. package/scripts/lib/skill-improvement/observations.js +108 -0
  97. package/scripts/lib/tmux-worktree-orchestrator.js +491 -0
  98. package/scripts/lib/utils.d.ts +183 -0
  99. package/scripts/lib/utils.js +543 -0
  100. package/scripts/list-installed.js +90 -0
  101. package/scripts/orchestrate-codex-worker.sh +92 -0
  102. package/scripts/orchestrate-worktrees.js +108 -0
  103. package/scripts/orchestration-status.js +62 -0
  104. package/scripts/repair.js +97 -0
  105. package/scripts/session-inspect.js +150 -0
  106. package/scripts/setup-package-manager.js +204 -0
  107. package/scripts/skill-create-output.js +244 -0
  108. package/scripts/uninstall.js +96 -0
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Inspect selective-install profiles and module plans without mutating targets.
4
+ */
5
+
6
+ const {
7
+ listInstallComponents,
8
+ listInstallModules,
9
+ listInstallProfiles,
10
+ resolveInstallPlan,
11
+ } = require('./lib/install-manifests');
12
+ const { loadInstallConfig } = require('./lib/install/config');
13
+ const { normalizeInstallRequest } = require('./lib/install/request');
14
+
15
+ function showHelp() {
16
+ console.log(`
17
+ Inspect cc4pm selective-install manifests
18
+
19
+ Usage:
20
+ node scripts/install-plan.js --list-profiles
21
+ node scripts/install-plan.js --list-modules
22
+ node scripts/install-plan.js --list-components [--family <family>] [--target <target>] [--json]
23
+ node scripts/install-plan.js --profile <name> [--with <component>]... [--without <component>]... [--target <target>] [--json]
24
+ node scripts/install-plan.js --modules <id,id,...> [--with <component>]... [--without <component>]... [--target <target>] [--json]
25
+ node scripts/install-plan.js --config <path> [--json]
26
+
27
+ Options:
28
+ --list-profiles List available install profiles
29
+ --list-modules List install modules
30
+ --list-components List user-facing install components
31
+ --family <family> Filter listed components by family
32
+ --profile <name> Resolve an install profile
33
+ --modules <ids> Resolve explicit module IDs (comma-separated)
34
+ --with <component> Include a user-facing install component
35
+ --without <component>
36
+ Exclude a user-facing install component
37
+ --config <path> Load install intent from ecc-install.json
38
+ --target <target> Filter plan for a specific target
39
+ --json Emit machine-readable JSON
40
+ --help Show this help text
41
+ `);
42
+ }
43
+
44
+ function parseArgs(argv) {
45
+ const args = argv.slice(2);
46
+ const parsed = {
47
+ json: false,
48
+ help: false,
49
+ profileId: null,
50
+ moduleIds: [],
51
+ includeComponentIds: [],
52
+ excludeComponentIds: [],
53
+ configPath: null,
54
+ target: null,
55
+ family: null,
56
+ listProfiles: false,
57
+ listModules: false,
58
+ listComponents: false,
59
+ };
60
+
61
+ for (let index = 0; index < args.length; index += 1) {
62
+ const arg = args[index];
63
+ if (arg === '--help' || arg === '-h') {
64
+ parsed.help = true;
65
+ } else if (arg === '--json') {
66
+ parsed.json = true;
67
+ } else if (arg === '--list-profiles') {
68
+ parsed.listProfiles = true;
69
+ } else if (arg === '--list-modules') {
70
+ parsed.listModules = true;
71
+ } else if (arg === '--list-components') {
72
+ parsed.listComponents = true;
73
+ } else if (arg === '--family') {
74
+ parsed.family = args[index + 1] || null;
75
+ index += 1;
76
+ } else if (arg === '--profile') {
77
+ parsed.profileId = args[index + 1] || null;
78
+ index += 1;
79
+ } else if (arg === '--modules') {
80
+ const raw = args[index + 1] || '';
81
+ parsed.moduleIds = raw.split(',').map(value => value.trim()).filter(Boolean);
82
+ index += 1;
83
+ } else if (arg === '--with') {
84
+ const componentId = args[index + 1] || '';
85
+ if (componentId.trim()) {
86
+ parsed.includeComponentIds.push(componentId.trim());
87
+ }
88
+ index += 1;
89
+ } else if (arg === '--without') {
90
+ const componentId = args[index + 1] || '';
91
+ if (componentId.trim()) {
92
+ parsed.excludeComponentIds.push(componentId.trim());
93
+ }
94
+ index += 1;
95
+ } else if (arg === '--config') {
96
+ parsed.configPath = args[index + 1] || null;
97
+ index += 1;
98
+ } else if (arg === '--target') {
99
+ parsed.target = args[index + 1] || null;
100
+ index += 1;
101
+ } else {
102
+ throw new Error(`Unknown argument: ${arg}`);
103
+ }
104
+ }
105
+
106
+ return parsed;
107
+ }
108
+
109
+ function printProfiles(profiles) {
110
+ console.log('Install profiles:\n');
111
+ for (const profile of profiles) {
112
+ console.log(`- ${profile.id} (${profile.moduleCount} modules)`);
113
+ console.log(` ${profile.description}`);
114
+ }
115
+ }
116
+
117
+ function printModules(modules) {
118
+ console.log('Install modules:\n');
119
+ for (const module of modules) {
120
+ console.log(`- ${module.id} [${module.kind}]`);
121
+ console.log(
122
+ ` targets=${module.targets.join(', ')} default=${module.defaultInstall} cost=${module.cost} stability=${module.stability}`
123
+ );
124
+ console.log(` ${module.description}`);
125
+ }
126
+ }
127
+
128
+ function printComponents(components) {
129
+ console.log('Install components:\n');
130
+ for (const component of components) {
131
+ console.log(`- ${component.id} [${component.family}]`);
132
+ console.log(` targets=${component.targets.join(', ')} modules=${component.moduleIds.join(', ')}`);
133
+ console.log(` ${component.description}`);
134
+ }
135
+ }
136
+
137
+ function printPlan(plan) {
138
+ console.log('Install plan:\n');
139
+ console.log(
140
+ 'Note: target filtering and operation output currently reflect scaffold-level adapter planning, not a byte-for-byte mirror of legacy install.sh copy paths.\n'
141
+ );
142
+ console.log(`Profile: ${plan.profileId || '(custom modules)'}`);
143
+ console.log(`Target: ${plan.target || '(all targets)'}`);
144
+ console.log(`Included components: ${plan.includedComponentIds.join(', ') || '(none)'}`);
145
+ console.log(`Excluded components: ${plan.excludedComponentIds.join(', ') || '(none)'}`);
146
+ console.log(`Requested: ${plan.requestedModuleIds.join(', ')}`);
147
+ if (plan.targetAdapterId) {
148
+ console.log(`Adapter: ${plan.targetAdapterId}`);
149
+ console.log(`Target root: ${plan.targetRoot}`);
150
+ console.log(`Install-state: ${plan.installStatePath}`);
151
+ }
152
+ console.log('');
153
+ console.log(`Selected modules (${plan.selectedModuleIds.length}):`);
154
+ for (const module of plan.selectedModules) {
155
+ console.log(`- ${module.id} [${module.kind}]`);
156
+ }
157
+
158
+ if (plan.skippedModuleIds.length > 0) {
159
+ console.log('');
160
+ console.log(`Skipped for target ${plan.target} (${plan.skippedModuleIds.length}):`);
161
+ for (const module of plan.skippedModules) {
162
+ console.log(`- ${module.id} [${module.kind}]`);
163
+ }
164
+ }
165
+
166
+ if (plan.excludedModuleIds.length > 0) {
167
+ console.log('');
168
+ console.log(`Excluded by selection (${plan.excludedModuleIds.length}):`);
169
+ for (const module of plan.excludedModules) {
170
+ console.log(`- ${module.id} [${module.kind}]`);
171
+ }
172
+ }
173
+
174
+ if (plan.operations.length > 0) {
175
+ console.log('');
176
+ console.log(`Operation plan (${plan.operations.length}):`);
177
+ for (const operation of plan.operations) {
178
+ console.log(
179
+ `- ${operation.moduleId}: ${operation.sourceRelativePath} -> ${operation.destinationPath} [${operation.strategy}]`
180
+ );
181
+ }
182
+ }
183
+ }
184
+
185
+ function main() {
186
+ try {
187
+ const options = parseArgs(process.argv);
188
+
189
+ if (options.help || process.argv.length <= 2) {
190
+ showHelp();
191
+ process.exit(0);
192
+ }
193
+
194
+ if (options.listProfiles) {
195
+ const profiles = listInstallProfiles();
196
+ if (options.json) {
197
+ console.log(JSON.stringify({ profiles }, null, 2));
198
+ } else {
199
+ printProfiles(profiles);
200
+ }
201
+ return;
202
+ }
203
+
204
+ if (options.listModules) {
205
+ const modules = listInstallModules();
206
+ if (options.json) {
207
+ console.log(JSON.stringify({ modules }, null, 2));
208
+ } else {
209
+ printModules(modules);
210
+ }
211
+ return;
212
+ }
213
+
214
+ if (options.listComponents) {
215
+ const components = listInstallComponents({
216
+ family: options.family,
217
+ target: options.target,
218
+ });
219
+ if (options.json) {
220
+ console.log(JSON.stringify({ components }, null, 2));
221
+ } else {
222
+ printComponents(components);
223
+ }
224
+ return;
225
+ }
226
+
227
+ const config = options.configPath
228
+ ? loadInstallConfig(options.configPath, { cwd: process.cwd() })
229
+ : null;
230
+ const request = normalizeInstallRequest({
231
+ ...options,
232
+ languages: [],
233
+ config,
234
+ });
235
+ const plan = resolveInstallPlan({
236
+ profileId: request.profileId,
237
+ moduleIds: request.moduleIds,
238
+ includeComponentIds: request.includeComponentIds,
239
+ excludeComponentIds: request.excludeComponentIds,
240
+ target: request.target,
241
+ });
242
+
243
+ if (options.json) {
244
+ console.log(JSON.stringify(plan, null, 2));
245
+ } else {
246
+ printPlan(plan);
247
+ }
248
+ } catch (error) {
249
+ console.error(`Error: ${error.message}`);
250
+ process.exit(1);
251
+ }
252
+ }
253
+
254
+ main();
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared hook enable/disable controls.
4
+ *
5
+ * Controls:
6
+ * - ECC_HOOK_PROFILE=minimal|standard|strict (default: standard)
7
+ * - ECC_DISABLED_HOOKS=comma,separated,hook,ids
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const VALID_PROFILES = new Set(['minimal', 'standard', 'strict']);
13
+
14
+ function normalizeId(value) {
15
+ return String(value || '').trim().toLowerCase();
16
+ }
17
+
18
+ function getHookProfile() {
19
+ const raw = String(process.env.ECC_HOOK_PROFILE || 'standard').trim().toLowerCase();
20
+ return VALID_PROFILES.has(raw) ? raw : 'standard';
21
+ }
22
+
23
+ function getDisabledHookIds() {
24
+ const raw = String(process.env.ECC_DISABLED_HOOKS || '');
25
+ if (!raw.trim()) return new Set();
26
+
27
+ return new Set(
28
+ raw
29
+ .split(',')
30
+ .map(v => normalizeId(v))
31
+ .filter(Boolean)
32
+ );
33
+ }
34
+
35
+ function parseProfiles(rawProfiles, fallback = ['standard', 'strict']) {
36
+ if (!rawProfiles) return [...fallback];
37
+
38
+ if (Array.isArray(rawProfiles)) {
39
+ const parsed = rawProfiles
40
+ .map(v => String(v || '').trim().toLowerCase())
41
+ .filter(v => VALID_PROFILES.has(v));
42
+ return parsed.length > 0 ? parsed : [...fallback];
43
+ }
44
+
45
+ const parsed = String(rawProfiles)
46
+ .split(',')
47
+ .map(v => v.trim().toLowerCase())
48
+ .filter(v => VALID_PROFILES.has(v));
49
+
50
+ return parsed.length > 0 ? parsed : [...fallback];
51
+ }
52
+
53
+ function isHookEnabled(hookId, options = {}) {
54
+ const id = normalizeId(hookId);
55
+ if (!id) return true;
56
+
57
+ const disabled = getDisabledHookIds();
58
+ if (disabled.has(id)) {
59
+ return false;
60
+ }
61
+
62
+ const profile = getHookProfile();
63
+ const allowedProfiles = parseProfiles(options.profiles);
64
+ return allowedProfiles.includes(profile);
65
+ }
66
+
67
+ module.exports = {
68
+ VALID_PROFILES,
69
+ normalizeId,
70
+ getHookProfile,
71
+ getDisabledHookIds,
72
+ parseProfiles,
73
+ isHookEnabled,
74
+ };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+
5
+ const { writeInstallState } = require('../install-state');
6
+
7
+ function applyInstallPlan(plan) {
8
+ for (const operation of plan.operations) {
9
+ fs.mkdirSync(require('path').dirname(operation.destinationPath), { recursive: true });
10
+ fs.copyFileSync(operation.sourcePath, operation.destinationPath);
11
+ }
12
+
13
+ writeInstallState(plan.installStatePath, plan.statePreview);
14
+
15
+ return {
16
+ ...plan,
17
+ applied: true,
18
+ };
19
+ }
20
+
21
+ module.exports = {
22
+ applyInstallPlan,
23
+ };
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const Ajv = require('ajv');
6
+
7
+ const DEFAULT_INSTALL_CONFIG = 'ecc-install.json';
8
+ const CONFIG_SCHEMA_PATH = path.join(__dirname, '..', '..', '..', 'schemas', 'ecc-install-config.schema.json');
9
+
10
+ let cachedValidator = null;
11
+
12
+ function readJson(filePath, label) {
13
+ try {
14
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
15
+ } catch (error) {
16
+ throw new Error(`Invalid JSON in ${label}: ${error.message}`);
17
+ }
18
+ }
19
+
20
+ function getValidator() {
21
+ if (cachedValidator) {
22
+ return cachedValidator;
23
+ }
24
+
25
+ const schema = readJson(CONFIG_SCHEMA_PATH, 'ecc-install-config.schema.json');
26
+ const ajv = new Ajv({ allErrors: true });
27
+ cachedValidator = ajv.compile(schema);
28
+ return cachedValidator;
29
+ }
30
+
31
+ function dedupeStrings(values) {
32
+ return [...new Set((Array.isArray(values) ? values : []).map(value => String(value).trim()).filter(Boolean))];
33
+ }
34
+
35
+ function formatValidationErrors(errors = []) {
36
+ return errors.map(error => `${error.instancePath || '/'} ${error.message}`).join('; ');
37
+ }
38
+
39
+ function resolveInstallConfigPath(configPath, options = {}) {
40
+ if (!configPath) {
41
+ throw new Error('An install config path is required');
42
+ }
43
+
44
+ const cwd = options.cwd || process.cwd();
45
+ return path.isAbsolute(configPath)
46
+ ? configPath
47
+ : path.resolve(cwd, configPath);
48
+ }
49
+
50
+ function loadInstallConfig(configPath, options = {}) {
51
+ const resolvedPath = resolveInstallConfigPath(configPath, options);
52
+
53
+ if (!fs.existsSync(resolvedPath)) {
54
+ throw new Error(`Install config not found: ${resolvedPath}`);
55
+ }
56
+
57
+ const raw = readJson(resolvedPath, path.basename(resolvedPath));
58
+ const validator = getValidator();
59
+
60
+ if (!validator(raw)) {
61
+ throw new Error(
62
+ `Invalid install config ${resolvedPath}: ${formatValidationErrors(validator.errors)}`
63
+ );
64
+ }
65
+
66
+ return {
67
+ path: resolvedPath,
68
+ version: raw.version,
69
+ target: raw.target || null,
70
+ profileId: raw.profile || null,
71
+ moduleIds: dedupeStrings(raw.modules),
72
+ includeComponentIds: dedupeStrings(raw.include),
73
+ excludeComponentIds: dedupeStrings(raw.exclude),
74
+ options: raw.options && typeof raw.options === 'object' ? { ...raw.options } : {},
75
+ };
76
+ }
77
+
78
+ module.exports = {
79
+ DEFAULT_INSTALL_CONFIG,
80
+ loadInstallConfig,
81
+ resolveInstallConfigPath,
82
+ };
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ const LEGACY_INSTALL_TARGETS = ['claude', 'cursor', 'antigravity'];
4
+
5
+ function dedupeStrings(values) {
6
+ return [...new Set((Array.isArray(values) ? values : []).map(value => String(value).trim()).filter(Boolean))];
7
+ }
8
+
9
+ function parseInstallArgs(argv) {
10
+ const args = argv.slice(2);
11
+ const parsed = {
12
+ target: null,
13
+ dryRun: false,
14
+ json: false,
15
+ help: false,
16
+ configPath: null,
17
+ profileId: null,
18
+ moduleIds: [],
19
+ includeComponentIds: [],
20
+ excludeComponentIds: [],
21
+ languages: [],
22
+ };
23
+
24
+ for (let index = 0; index < args.length; index += 1) {
25
+ const arg = args[index];
26
+
27
+ if (arg === '--target') {
28
+ parsed.target = args[index + 1] || null;
29
+ index += 1;
30
+ } else if (arg === '--config') {
31
+ parsed.configPath = args[index + 1] || null;
32
+ index += 1;
33
+ } else if (arg === '--profile') {
34
+ parsed.profileId = args[index + 1] || null;
35
+ index += 1;
36
+ } else if (arg === '--modules') {
37
+ const raw = args[index + 1] || '';
38
+ parsed.moduleIds = raw.split(',').map(value => value.trim()).filter(Boolean);
39
+ index += 1;
40
+ } else if (arg === '--with') {
41
+ const componentId = args[index + 1] || '';
42
+ if (componentId.trim()) {
43
+ parsed.includeComponentIds.push(componentId.trim());
44
+ }
45
+ index += 1;
46
+ } else if (arg === '--without') {
47
+ const componentId = args[index + 1] || '';
48
+ if (componentId.trim()) {
49
+ parsed.excludeComponentIds.push(componentId.trim());
50
+ }
51
+ index += 1;
52
+ } else if (arg === '--dry-run') {
53
+ parsed.dryRun = true;
54
+ } else if (arg === '--json') {
55
+ parsed.json = true;
56
+ } else if (arg === '--help' || arg === '-h') {
57
+ parsed.help = true;
58
+ } else if (arg.startsWith('--')) {
59
+ throw new Error(`Unknown argument: ${arg}`);
60
+ } else {
61
+ parsed.languages.push(arg);
62
+ }
63
+ }
64
+
65
+ return parsed;
66
+ }
67
+
68
+ function normalizeInstallRequest(options = {}) {
69
+ const config = options.config && typeof options.config === 'object'
70
+ ? options.config
71
+ : null;
72
+ const profileId = options.profileId || config?.profileId || null;
73
+ const moduleIds = dedupeStrings([...(config?.moduleIds || []), ...(options.moduleIds || [])]);
74
+ const includeComponentIds = dedupeStrings([
75
+ ...(config?.includeComponentIds || []),
76
+ ...(options.includeComponentIds || []),
77
+ ]);
78
+ const excludeComponentIds = dedupeStrings([
79
+ ...(config?.excludeComponentIds || []),
80
+ ...(options.excludeComponentIds || []),
81
+ ]);
82
+ const languages = Array.isArray(options.languages) ? [...options.languages] : [];
83
+ const target = options.target || config?.target || 'claude';
84
+ const hasManifestBaseSelection = Boolean(profileId) || moduleIds.length > 0 || includeComponentIds.length > 0;
85
+ const usingManifestMode = hasManifestBaseSelection || excludeComponentIds.length > 0;
86
+
87
+ if (usingManifestMode && languages.length > 0) {
88
+ throw new Error(
89
+ 'Legacy language arguments cannot be combined with --profile, --modules, --with, --without, or manifest config selections'
90
+ );
91
+ }
92
+
93
+ if (!options.help && !hasManifestBaseSelection && languages.length === 0) {
94
+ throw new Error('No install profile, module IDs, included components, or legacy languages were provided');
95
+ }
96
+
97
+ return {
98
+ mode: usingManifestMode ? 'manifest' : 'legacy',
99
+ target,
100
+ profileId,
101
+ moduleIds,
102
+ includeComponentIds,
103
+ excludeComponentIds,
104
+ languages,
105
+ configPath: config?.path || options.configPath || null,
106
+ };
107
+ }
108
+
109
+ module.exports = {
110
+ LEGACY_INSTALL_TARGETS,
111
+ normalizeInstallRequest,
112
+ parseInstallArgs,
113
+ };
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ createLegacyInstallPlan,
5
+ createManifestInstallPlan,
6
+ } = require('../install-executor');
7
+
8
+ function createInstallPlanFromRequest(request, options = {}) {
9
+ if (!request || typeof request !== 'object') {
10
+ throw new Error('A normalized install request is required');
11
+ }
12
+
13
+ if (request.mode === 'manifest') {
14
+ return createManifestInstallPlan({
15
+ target: request.target,
16
+ profileId: request.profileId,
17
+ moduleIds: request.moduleIds,
18
+ includeComponentIds: request.includeComponentIds,
19
+ excludeComponentIds: request.excludeComponentIds,
20
+ projectRoot: options.projectRoot,
21
+ homeDir: options.homeDir,
22
+ sourceRoot: options.sourceRoot,
23
+ });
24
+ }
25
+
26
+ if (request.mode === 'legacy') {
27
+ return createLegacyInstallPlan({
28
+ target: request.target,
29
+ languages: request.languages,
30
+ projectRoot: options.projectRoot,
31
+ homeDir: options.homeDir,
32
+ claudeRulesDir: options.claudeRulesDir,
33
+ sourceRoot: options.sourceRoot,
34
+ });
35
+ }
36
+
37
+ throw new Error(`Unsupported install request mode: ${request.mode}`);
38
+ }
39
+
40
+ module.exports = {
41
+ createInstallPlanFromRequest,
42
+ };