@nerviq/cli 1.20.1 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -23
- package/README.md +20 -2
- package/bin/cli.js +3 -3
- package/package.json +1 -1
- package/src/activity.js +1039 -1039
- package/src/adoption-advisor.js +299 -299
- package/src/aider/config-parser.js +166 -166
- package/src/aider/context.js +4 -1
- package/src/aider/deep-review.js +316 -316
- package/src/aider/domain-packs.js +303 -303
- package/src/aider/freshness.js +93 -93
- package/src/aider/governance.js +253 -253
- package/src/aider/interactive.js +334 -334
- package/src/aider/mcp-packs.js +329 -329
- package/src/aider/patch.js +214 -214
- package/src/aider/plans.js +186 -186
- package/src/aider/premium.js +360 -360
- package/src/aider/setup.js +404 -404
- package/src/aider/techniques.js +312 -67
- package/src/analyze.js +951 -951
- package/src/anti-patterns.js +485 -485
- package/src/audit/instruction-files.js +180 -180
- package/src/audit/recommendations.js +577 -577
- package/src/audit.js +20 -0
- package/src/auto-suggest.js +154 -154
- package/src/badge.js +13 -13
- package/src/behavioral-drift.js +801 -801
- package/src/benchmark.js +67 -67
- package/src/catalog.js +103 -103
- package/src/certification.js +128 -128
- package/src/codex/config-parser.js +183 -183
- package/src/codex/context.js +223 -223
- package/src/codex/deep-review.js +493 -493
- package/src/codex/domain-packs.js +394 -394
- package/src/codex/freshness.js +84 -84
- package/src/codex/governance.js +192 -192
- package/src/codex/interactive.js +618 -618
- package/src/codex/mcp-packs.js +914 -914
- package/src/codex/patch.js +209 -209
- package/src/codex/plans.js +251 -251
- package/src/codex/premium.js +614 -614
- package/src/codex/setup.js +591 -591
- package/src/continuous-ops.js +681 -681
- package/src/copilot/activity.js +309 -309
- package/src/copilot/deep-review.js +346 -346
- package/src/copilot/domain-packs.js +372 -372
- package/src/copilot/freshness.js +57 -57
- package/src/copilot/governance.js +222 -222
- package/src/copilot/interactive.js +406 -406
- package/src/copilot/mcp-packs.js +826 -826
- package/src/copilot/plans.js +253 -253
- package/src/copilot/premium.js +451 -451
- package/src/copilot/setup.js +488 -488
- package/src/cost-tracking.js +61 -61
- package/src/cursor/activity.js +301 -301
- package/src/cursor/config-parser.js +265 -265
- package/src/cursor/context.js +256 -256
- package/src/cursor/deep-review.js +334 -334
- package/src/cursor/domain-packs.js +368 -368
- package/src/cursor/freshness.js +65 -65
- package/src/cursor/governance.js +229 -229
- package/src/cursor/interactive.js +391 -391
- package/src/cursor/mcp-packs.js +828 -828
- package/src/cursor/plans.js +254 -254
- package/src/cursor/premium.js +469 -469
- package/src/cursor/setup.js +488 -488
- package/src/dashboard.js +493 -493
- package/src/deep-review.js +428 -428
- package/src/deprecation.js +98 -98
- package/src/diff-only.js +280 -280
- package/src/doctor.js +119 -119
- package/src/domain-pack-expansion.js +1033 -1033
- package/src/domain-packs.js +387 -387
- package/src/feedback.js +178 -178
- package/src/fix-engine.js +783 -783
- package/src/fix-prompts.js +122 -122
- package/src/formatters/csv.js +69 -0
- package/src/formatters/junit.js +99 -0
- package/src/formatters/markdown.js +118 -0
- package/src/formatters/sarif.js +115 -115
- package/src/freshness.js +74 -74
- package/src/gemini/config-parser.js +275 -275
- package/src/gemini/deep-review.js +559 -559
- package/src/gemini/domain-packs.js +393 -393
- package/src/gemini/freshness.js +66 -66
- package/src/gemini/governance.js +201 -201
- package/src/gemini/interactive.js +860 -860
- package/src/gemini/mcp-packs.js +915 -915
- package/src/gemini/plans.js +269 -269
- package/src/gemini/premium.js +760 -760
- package/src/gemini/setup.js +692 -692
- package/src/governance.js +72 -72
- package/src/harmony/add.js +68 -68
- package/src/harmony/advisor.js +333 -333
- package/src/harmony/canon.js +565 -565
- package/src/harmony/cli.js +591 -591
- package/src/harmony/drift.js +401 -401
- package/src/harmony/governance.js +313 -313
- package/src/harmony/memory.js +239 -239
- package/src/harmony/sync.js +475 -475
- package/src/harmony/watch.js +370 -370
- package/src/hook-validation.js +342 -342
- package/src/index.js +271 -271
- package/src/init.js +184 -184
- package/src/instruction-surfaces.js +185 -185
- package/src/integrations.js +144 -144
- package/src/interactive.js +118 -118
- package/src/locales/en.json +1 -1
- package/src/locales/es.json +1 -1
- package/src/mcp-packs.js +830 -830
- package/src/mcp-server.js +726 -726
- package/src/mcp-validation.js +337 -337
- package/src/nerviq-sync.json +7 -7
- package/src/opencode/config-parser.js +109 -109
- package/src/opencode/context.js +247 -247
- package/src/opencode/deep-review.js +313 -313
- package/src/opencode/domain-packs.js +262 -262
- package/src/opencode/freshness.js +66 -66
- package/src/opencode/governance.js +159 -159
- package/src/opencode/interactive.js +392 -392
- package/src/opencode/mcp-packs.js +705 -705
- package/src/opencode/patch.js +184 -184
- package/src/opencode/plans.js +231 -231
- package/src/opencode/premium.js +413 -413
- package/src/opencode/setup.js +449 -449
- package/src/opencode/techniques.js +27 -27
- package/src/operating-profile.js +574 -574
- package/src/org.js +152 -152
- package/src/permission-rules.js +218 -218
- package/src/plans.js +839 -839
- package/src/platform-change-manifest.js +86 -86
- package/src/plugins.js +110 -110
- package/src/policy-layers.js +210 -210
- package/src/profiles.js +124 -124
- package/src/prompt-injection.js +74 -74
- package/src/public-api.js +173 -173
- package/src/recommendation-rules.js +84 -84
- package/src/repo-archetype.js +386 -386
- package/src/secret-patterns.js +39 -39
- package/src/server.js +527 -527
- package/src/setup/analysis.js +607 -607
- package/src/setup/runtime.js +172 -172
- package/src/setup.js +677 -677
- package/src/shared/capabilities.js +194 -194
- package/src/source-urls.js +132 -132
- package/src/stack-checks.js +565 -565
- package/src/supplemental-checks.js +13 -13
- package/src/synergy/adaptive.js +261 -261
- package/src/synergy/compensation.js +137 -137
- package/src/synergy/evidence.js +193 -193
- package/src/synergy/learning.js +199 -199
- package/src/synergy/patterns.js +227 -227
- package/src/synergy/ranking.js +83 -83
- package/src/synergy/report.js +165 -165
- package/src/synergy/routing.js +146 -146
- package/src/techniques/api.js +407 -407
- package/src/techniques/automation.js +316 -316
- package/src/techniques/compliance.js +257 -257
- package/src/techniques/hygiene.js +294 -294
- package/src/techniques/instructions.js +243 -243
- package/src/techniques/observability.js +226 -226
- package/src/techniques/optimization.js +142 -142
- package/src/techniques/quality.js +318 -318
- package/src/techniques/security.js +237 -237
- package/src/techniques/shared.js +443 -443
- package/src/techniques/stacks.js +2294 -2294
- package/src/techniques/tools.js +106 -106
- package/src/techniques/workflow.js +413 -413
- package/src/techniques.js +81 -81
- package/src/terminology.js +73 -73
- package/src/token-estimate.js +35 -35
- package/src/usage-patterns.js +99 -99
- package/src/verification-metadata.js +145 -145
- package/src/watch.js +247 -247
- package/src/windsurf/activity.js +302 -302
- package/src/windsurf/config-parser.js +267 -267
- package/src/windsurf/deep-review.js +337 -337
- package/src/windsurf/domain-packs.js +370 -370
- package/src/windsurf/freshness.js +36 -36
- package/src/windsurf/governance.js +231 -231
- package/src/windsurf/interactive.js +388 -388
- package/src/windsurf/mcp-packs.js +792 -792
- package/src/windsurf/plans.js +247 -247
- package/src/windsurf/premium.js +468 -468
- package/src/windsurf/setup.js +471 -471
- package/src/workspace.js +375 -375
package/src/governance.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { DOMAIN_PACKS } = require('./domain-packs');
|
|
2
|
-
const { MCP_PACKS, mergeMcpServers, normalizeMcpPackKeys } = require('./mcp-packs');
|
|
3
|
-
const { getCodexGovernanceSummary } = require('./codex/governance');
|
|
4
|
-
const { formatTerminologyLines } = require('./terminology');
|
|
1
|
+
const { DOMAIN_PACKS } = require('./domain-packs');
|
|
2
|
+
const { MCP_PACKS, mergeMcpServers, normalizeMcpPackKeys } = require('./mcp-packs');
|
|
3
|
+
const { getCodexGovernanceSummary } = require('./codex/governance');
|
|
4
|
+
const { formatTerminologyLines } = require('./terminology');
|
|
5
5
|
|
|
6
6
|
const PERMISSION_PROFILES = [
|
|
7
7
|
{
|
|
@@ -104,19 +104,19 @@ const HOOK_REGISTRY = [
|
|
|
104
104
|
dryRunExample: 'Edit a catalog file and verify duplicate check runs without blocking.',
|
|
105
105
|
rollbackPath: 'Remove the PostToolUse hook entry from settings.',
|
|
106
106
|
},
|
|
107
|
-
{
|
|
108
|
-
key: 'injection-defense',
|
|
109
|
-
file: '.claude/hooks/injection-defense.js',
|
|
110
|
-
triggerPoint: 'PostToolUse',
|
|
111
|
-
matcher: 'WebFetch|WebSearch|Read|Grep|Glob|mcp__.*',
|
|
112
|
-
purpose: 'Scans external content flows for common prompt injection patterns and logs suspicious findings.',
|
|
113
|
-
filesTouched: ['.claude/logs/prompt-injection-alerts.log'],
|
|
114
|
-
sideEffects: ['Appends an alert line when suspicious external content is detected.'],
|
|
115
|
-
risk: 'low',
|
|
116
|
-
riskLevel: 'low',
|
|
117
|
-
dryRunExample: 'Run a WebFetch or MCP-backed tool call and verify suspicious content is logged for review.',
|
|
118
|
-
rollbackPath: 'Remove the PostToolUse hook entry from settings.',
|
|
119
|
-
},
|
|
107
|
+
{
|
|
108
|
+
key: 'injection-defense',
|
|
109
|
+
file: '.claude/hooks/injection-defense.js',
|
|
110
|
+
triggerPoint: 'PostToolUse',
|
|
111
|
+
matcher: 'WebFetch|WebSearch|Read|Grep|Glob|mcp__.*',
|
|
112
|
+
purpose: 'Scans external content flows for common prompt injection patterns and logs suspicious findings.',
|
|
113
|
+
filesTouched: ['.claude/logs/prompt-injection-alerts.log'],
|
|
114
|
+
sideEffects: ['Appends an alert line when suspicious external content is detected.'],
|
|
115
|
+
risk: 'low',
|
|
116
|
+
riskLevel: 'low',
|
|
117
|
+
dryRunExample: 'Run a WebFetch or MCP-backed tool call and verify suspicious content is logged for review.',
|
|
118
|
+
rollbackPath: 'Remove the PostToolUse hook entry from settings.',
|
|
119
|
+
},
|
|
120
120
|
{
|
|
121
121
|
key: 'trust-drift-check',
|
|
122
122
|
file: '.claude/hooks/trust-drift-check.sh',
|
|
@@ -299,24 +299,24 @@ function buildHookConfig(hookFiles, profileKey) {
|
|
|
299
299
|
return {};
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
// Detect hook runtime: .js files use node, .sh files use bash
|
|
303
|
-
const hookCommand = (file) => {
|
|
304
|
-
if (file.endsWith('.js')) return `node .claude/hooks/${file}`;
|
|
305
|
-
return `bash .claude/hooks/${file}`;
|
|
306
|
-
};
|
|
307
|
-
const isSecrets = (f) => f === 'protect-secrets.sh' || f === 'protect-secrets.js';
|
|
308
|
-
const isSession = (f) => f === 'session-start.sh' || f === 'session-start.js';
|
|
309
|
-
const isInjection = (f) => f === 'injection-defense.sh' || f === 'injection-defense.js';
|
|
310
|
-
|
|
311
|
-
const hookConfig = {
|
|
312
|
-
PostToolUse: [{
|
|
313
|
-
matcher: 'Write|Edit',
|
|
314
|
-
hooks: uniqueFiles
|
|
315
|
-
.filter(file => !isSecrets(file) && !isSession(file) && !isInjection(file))
|
|
316
|
-
.map(file => ({
|
|
317
|
-
type: 'command',
|
|
318
|
-
command: hookCommand(file),
|
|
319
|
-
timeout: 10,
|
|
302
|
+
// Detect hook runtime: .js files use node, .sh files use bash
|
|
303
|
+
const hookCommand = (file) => {
|
|
304
|
+
if (file.endsWith('.js')) return `node .claude/hooks/${file}`;
|
|
305
|
+
return `bash .claude/hooks/${file}`;
|
|
306
|
+
};
|
|
307
|
+
const isSecrets = (f) => f === 'protect-secrets.sh' || f === 'protect-secrets.js';
|
|
308
|
+
const isSession = (f) => f === 'session-start.sh' || f === 'session-start.js';
|
|
309
|
+
const isInjection = (f) => f === 'injection-defense.sh' || f === 'injection-defense.js';
|
|
310
|
+
|
|
311
|
+
const hookConfig = {
|
|
312
|
+
PostToolUse: [{
|
|
313
|
+
matcher: 'Write|Edit',
|
|
314
|
+
hooks: uniqueFiles
|
|
315
|
+
.filter(file => !isSecrets(file) && !isSession(file) && !isInjection(file))
|
|
316
|
+
.map(file => ({
|
|
317
|
+
type: 'command',
|
|
318
|
+
command: hookCommand(file),
|
|
319
|
+
timeout: 10,
|
|
320
320
|
})),
|
|
321
321
|
}],
|
|
322
322
|
};
|
|
@@ -334,29 +334,29 @@ function buildHookConfig(hookFiles, profileKey) {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
const sessionFile = uniqueFiles.find(isSession);
|
|
337
|
-
if (sessionFile) {
|
|
338
|
-
hookConfig.SessionStart = [{
|
|
339
|
-
matcher: '*',
|
|
340
|
-
hooks: [{
|
|
341
|
-
type: 'command',
|
|
337
|
+
if (sessionFile) {
|
|
338
|
+
hookConfig.SessionStart = [{
|
|
339
|
+
matcher: '*',
|
|
340
|
+
hooks: [{
|
|
341
|
+
type: 'command',
|
|
342
342
|
command: hookCommand(sessionFile),
|
|
343
343
|
timeout: 5,
|
|
344
|
-
}],
|
|
345
|
-
}];
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const injectionFile = uniqueFiles.find(isInjection);
|
|
349
|
-
if (injectionFile) {
|
|
350
|
-
hookConfig.PostToolUse = hookConfig.PostToolUse || [];
|
|
351
|
-
hookConfig.PostToolUse.push({
|
|
352
|
-
matcher: 'WebFetch|WebSearch|Read|Grep|Glob|mcp__.*',
|
|
353
|
-
hooks: [{
|
|
354
|
-
type: 'command',
|
|
355
|
-
command: hookCommand(injectionFile),
|
|
356
|
-
timeout: 5,
|
|
357
|
-
}],
|
|
358
|
-
});
|
|
359
|
-
}
|
|
344
|
+
}],
|
|
345
|
+
}];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const injectionFile = uniqueFiles.find(isInjection);
|
|
349
|
+
if (injectionFile) {
|
|
350
|
+
hookConfig.PostToolUse = hookConfig.PostToolUse || [];
|
|
351
|
+
hookConfig.PostToolUse.push({
|
|
352
|
+
matcher: 'WebFetch|WebSearch|Read|Grep|Glob|mcp__.*',
|
|
353
|
+
hooks: [{
|
|
354
|
+
type: 'command',
|
|
355
|
+
command: hookCommand(injectionFile),
|
|
356
|
+
timeout: 5,
|
|
357
|
+
}],
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
360
|
|
|
361
361
|
if ((hookConfig.PostToolUse[0].hooks || []).length === 0) {
|
|
362
362
|
delete hookConfig.PostToolUse;
|
|
@@ -421,15 +421,15 @@ function printGovernanceSummary(summary, options = {}) {
|
|
|
421
421
|
console.log('');
|
|
422
422
|
console.log(` nerviq ${summary.platformLabel.toLowerCase()} governance`);
|
|
423
423
|
console.log(' ═══════════════════════════════════════');
|
|
424
|
-
console.log(` Safe defaults, hook transparency, and pilot guidance for ${summary.platformLabel}.`);
|
|
425
|
-
console.log('');
|
|
426
|
-
|
|
427
|
-
for (const line of formatTerminologyLines(['governance', 'hooks', 'denyRules', 'mcp'])) {
|
|
428
|
-
console.log(line);
|
|
429
|
-
}
|
|
430
|
-
console.log('');
|
|
431
|
-
|
|
432
|
-
console.log(' Permission Profiles');
|
|
424
|
+
console.log(` Safe defaults, hook transparency, and pilot guidance for ${summary.platformLabel}.`);
|
|
425
|
+
console.log('');
|
|
426
|
+
|
|
427
|
+
for (const line of formatTerminologyLines(['governance', 'hooks', 'denyRules', 'mcp'])) {
|
|
428
|
+
console.log(line);
|
|
429
|
+
}
|
|
430
|
+
console.log('');
|
|
431
|
+
|
|
432
|
+
console.log(' Permission Profiles');
|
|
433
433
|
for (const profile of summary.permissionProfiles) {
|
|
434
434
|
console.log(` - ${profile.label} [${profile.risk}]`);
|
|
435
435
|
console.log(` ${profile.useWhen}`);
|
|
@@ -590,13 +590,13 @@ function renderGovernanceMarkdown(summary) {
|
|
|
590
590
|
return lines.join('\n');
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
-
module.exports = {
|
|
594
|
-
PERMISSION_PROFILES,
|
|
595
|
-
HOOK_REGISTRY,
|
|
596
|
-
POLICY_PACKS,
|
|
597
|
-
getPermissionProfile,
|
|
598
|
-
isWritableProfile,
|
|
599
|
-
ensureWritableProfile,
|
|
593
|
+
module.exports = {
|
|
594
|
+
PERMISSION_PROFILES,
|
|
595
|
+
HOOK_REGISTRY,
|
|
596
|
+
POLICY_PACKS,
|
|
597
|
+
getPermissionProfile,
|
|
598
|
+
isWritableProfile,
|
|
599
|
+
ensureWritableProfile,
|
|
600
600
|
buildSettingsForProfile,
|
|
601
601
|
getGovernanceSummary,
|
|
602
602
|
printGovernanceSummary,
|
package/src/harmony/add.js
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Platform Addition Wizard
|
|
3
|
-
* Helps users add a new platform config to their project.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { detectActivePlatforms, PLATFORM_SIGNATURES } = require('./canon');
|
|
9
|
-
const { applyHarmonySync } = require('./sync');
|
|
10
|
-
|
|
11
|
-
const PLATFORM_BOOTSTRAPS = {
|
|
12
|
-
claude: { files: [{ path: 'CLAUDE.md', content: '# Project Instructions\n\nAdd your Claude Code instructions here.\n' }] },
|
|
13
|
-
codex: { files: [{ path: 'AGENTS.md', content: '# Agents Instructions\n\nAdd your Codex instructions here.\n' }] },
|
|
14
|
-
gemini: { files: [{ path: 'GEMINI.md', content: '# Gemini Instructions\n\nAdd your Gemini CLI instructions here.\n' }] },
|
|
15
|
-
copilot: { files: [{ path: '.github/copilot-instructions.md', content: '# Copilot Instructions\n\nAdd your GitHub Copilot instructions here.\n' }] },
|
|
16
|
-
cursor: { files: [{ path: '.cursorrules', content: '# Cursor Rules\n\nAdd your Cursor rules here.\n' }] },
|
|
17
|
-
windsurf: { files: [{ path: '.windsurfrules', content: '# Windsurf Rules\n\nAdd your Windsurf rules here.\n' }] },
|
|
18
|
-
aider: { files: [{ path: '.aider.conf.yml', content: '# Aider Configuration\n# See: https://aider.chat/docs/config/aider_conf.html\n' }] },
|
|
19
|
-
opencode: { files: [{ path: 'opencode.json', content: '{\n "instructions": "Add your OpenCode instructions here."\n}\n' }] },
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function addPlatform(dir, platformKey) {
|
|
23
|
-
// Validate platform
|
|
24
|
-
if (!PLATFORM_SIGNATURES[platformKey]) {
|
|
25
|
-
return { success: false, error: `Unknown platform: ${platformKey}. Available: ${Object.keys(PLATFORM_SIGNATURES).join(', ')}` };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check if already active
|
|
29
|
-
const active = detectActivePlatforms(dir);
|
|
30
|
-
const alreadyActive = active.find(p => p.platform === platformKey);
|
|
31
|
-
if (alreadyActive) {
|
|
32
|
-
return { success: false, error: `${platformKey} is already active in this project.` };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const beforeCount = active.length;
|
|
36
|
-
const bootstrap = PLATFORM_BOOTSTRAPS[platformKey];
|
|
37
|
-
const created = [];
|
|
38
|
-
|
|
39
|
-
// Create bootstrap files
|
|
40
|
-
for (const file of bootstrap.files) {
|
|
41
|
-
const fullPath = path.join(dir, file.path);
|
|
42
|
-
if (fs.existsSync(fullPath)) continue;
|
|
43
|
-
const dirName = path.dirname(fullPath);
|
|
44
|
-
fs.mkdirSync(dirName, { recursive: true });
|
|
45
|
-
fs.writeFileSync(fullPath, file.content, 'utf8');
|
|
46
|
-
created.push(file.path);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Run harmony sync to populate managed blocks
|
|
50
|
-
let syncResult = null;
|
|
51
|
-
try {
|
|
52
|
-
syncResult = applyHarmonySync(dir);
|
|
53
|
-
} catch { /* sync is optional */ }
|
|
54
|
-
|
|
55
|
-
const afterActive = detectActivePlatforms(dir);
|
|
56
|
-
const afterCount = afterActive.length;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
success: true,
|
|
60
|
-
platform: platformKey,
|
|
61
|
-
created,
|
|
62
|
-
beforeCount,
|
|
63
|
-
afterCount,
|
|
64
|
-
syncApplied: syncResult ? syncResult.applied.length : 0,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
module.exports = { addPlatform, PLATFORM_BOOTSTRAPS };
|
|
1
|
+
/**
|
|
2
|
+
* Platform Addition Wizard
|
|
3
|
+
* Helps users add a new platform config to their project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { detectActivePlatforms, PLATFORM_SIGNATURES } = require('./canon');
|
|
9
|
+
const { applyHarmonySync } = require('./sync');
|
|
10
|
+
|
|
11
|
+
const PLATFORM_BOOTSTRAPS = {
|
|
12
|
+
claude: { files: [{ path: 'CLAUDE.md', content: '# Project Instructions\n\nAdd your Claude Code instructions here.\n' }] },
|
|
13
|
+
codex: { files: [{ path: 'AGENTS.md', content: '# Agents Instructions\n\nAdd your Codex instructions here.\n' }] },
|
|
14
|
+
gemini: { files: [{ path: 'GEMINI.md', content: '# Gemini Instructions\n\nAdd your Gemini CLI instructions here.\n' }] },
|
|
15
|
+
copilot: { files: [{ path: '.github/copilot-instructions.md', content: '# Copilot Instructions\n\nAdd your GitHub Copilot instructions here.\n' }] },
|
|
16
|
+
cursor: { files: [{ path: '.cursorrules', content: '# Cursor Rules\n\nAdd your Cursor rules here.\n' }] },
|
|
17
|
+
windsurf: { files: [{ path: '.windsurfrules', content: '# Windsurf Rules\n\nAdd your Windsurf rules here.\n' }] },
|
|
18
|
+
aider: { files: [{ path: '.aider.conf.yml', content: '# Aider Configuration\n# See: https://aider.chat/docs/config/aider_conf.html\n' }] },
|
|
19
|
+
opencode: { files: [{ path: 'opencode.json', content: '{\n "instructions": "Add your OpenCode instructions here."\n}\n' }] },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function addPlatform(dir, platformKey) {
|
|
23
|
+
// Validate platform
|
|
24
|
+
if (!PLATFORM_SIGNATURES[platformKey]) {
|
|
25
|
+
return { success: false, error: `Unknown platform: ${platformKey}. Available: ${Object.keys(PLATFORM_SIGNATURES).join(', ')}` };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if already active
|
|
29
|
+
const active = detectActivePlatforms(dir);
|
|
30
|
+
const alreadyActive = active.find(p => p.platform === platformKey);
|
|
31
|
+
if (alreadyActive) {
|
|
32
|
+
return { success: false, error: `${platformKey} is already active in this project.` };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const beforeCount = active.length;
|
|
36
|
+
const bootstrap = PLATFORM_BOOTSTRAPS[platformKey];
|
|
37
|
+
const created = [];
|
|
38
|
+
|
|
39
|
+
// Create bootstrap files
|
|
40
|
+
for (const file of bootstrap.files) {
|
|
41
|
+
const fullPath = path.join(dir, file.path);
|
|
42
|
+
if (fs.existsSync(fullPath)) continue;
|
|
43
|
+
const dirName = path.dirname(fullPath);
|
|
44
|
+
fs.mkdirSync(dirName, { recursive: true });
|
|
45
|
+
fs.writeFileSync(fullPath, file.content, 'utf8');
|
|
46
|
+
created.push(file.path);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Run harmony sync to populate managed blocks
|
|
50
|
+
let syncResult = null;
|
|
51
|
+
try {
|
|
52
|
+
syncResult = applyHarmonySync(dir);
|
|
53
|
+
} catch { /* sync is optional */ }
|
|
54
|
+
|
|
55
|
+
const afterActive = detectActivePlatforms(dir);
|
|
56
|
+
const afterCount = afterActive.length;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
platform: platformKey,
|
|
61
|
+
created,
|
|
62
|
+
beforeCount,
|
|
63
|
+
afterCount,
|
|
64
|
+
syncApplied: syncResult ? syncResult.applied.length : 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { addPlatform, PLATFORM_BOOTSTRAPS };
|