@nerviq/cli 1.2.6 → 1.3.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/bin/cli.js CHANGED
@@ -24,7 +24,7 @@ const COMMAND_ALIASES = {
24
24
  gov: 'governance',
25
25
  outcome: 'feedback',
26
26
  };
27
- const KNOWN_COMMANDS = ['audit', 'org', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'catalog', 'certify', 'serve', 'harmony-audit', 'harmony-sync', 'harmony-drift', 'harmony-advise', 'harmony-watch', 'harmony-governance', 'synergy-report', 'help', 'version'];
27
+ const KNOWN_COMMANDS = ['audit', 'org', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'catalog', 'certify', 'serve', 'harmony-audit', 'harmony-sync', 'harmony-drift', 'harmony-advise', 'harmony-watch', 'harmony-governance', 'harmony-add', 'synergy-report', 'help', 'version'];
28
28
 
29
29
  function levenshtein(a, b) {
30
30
  const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
@@ -315,6 +315,7 @@ const HELP = `
315
315
  nerviq harmony-sync Preview cross-platform sync (dry run)
316
316
  nerviq harmony-sync --fix Apply cross-platform sync (write files)
317
317
  nerviq harmony-sync --json JSON output for CI/automation
318
+ nerviq harmony-add <platform> Add a new platform to the project
318
319
  nerviq synergy-report Multi-agent amplification opportunities
319
320
  nerviq convert --from X --to Y Convert configs between platforms
320
321
  nerviq migrate --platform X Platform version migration helper
@@ -412,6 +413,7 @@ async function main() {
412
413
  snapshot: flags.includes('--snapshot'),
413
414
  feedback: flags.includes('--feedback'),
414
415
  fix: flags.includes('--fix'),
416
+ autoSync: flags.includes('--auto-sync'),
415
417
  dryRun: flags.includes('--dry-run'),
416
418
  threshold: parsed.threshold !== null ? Number(parsed.threshold) : null,
417
419
  out: parsed.out,
@@ -505,7 +507,7 @@ async function main() {
505
507
  'history', 'compare', 'trend', 'feedback', 'catalog', 'certify', 'serve', 'help', 'version',
506
508
  // Harmony + Synergy (cross-platform)
507
509
  'harmony-audit', 'harmony-sync', 'harmony-drift', 'harmony-advise',
508
- 'harmony-watch', 'harmony-governance', 'synergy-report',
510
+ 'harmony-watch', 'harmony-governance', 'harmony-add', 'synergy-report',
509
511
  ]);
510
512
 
511
513
  if (options.platform === 'codex') {
@@ -962,6 +964,29 @@ async function main() {
962
964
  const { runHarmonyGovernance } = require('../src/harmony/cli');
963
965
  await runHarmonyGovernance(options);
964
966
  process.exit(0);
967
+ } else if (normalizedCommand === 'harmony-add') {
968
+ const { addPlatform } = require('../src/harmony/add');
969
+ const platformArg = parsed.extraArgs[0];
970
+ if (!platformArg) {
971
+ console.log('\n Usage: nerviq harmony-add <platform>');
972
+ console.log(' Available: claude, codex, gemini, copilot, cursor, windsurf, aider, opencode\n');
973
+ process.exit(1);
974
+ }
975
+ const dir = options.dir || process.cwd();
976
+ const result = addPlatform(dir, platformArg.toLowerCase());
977
+ if (options.json) {
978
+ console.log(JSON.stringify(result, null, 2));
979
+ } else if (result.success) {
980
+ console.log(`\n \x1b[32m\u2713\x1b[0m Added ${result.platform} to project`);
981
+ result.created.forEach(f => console.log(` Created: ${f}`));
982
+ console.log(` Platforms: ${result.beforeCount} \u2192 ${result.afterCount}`);
983
+ if (result.syncApplied > 0) console.log(` Harmony sync: ${result.syncApplied} file(s) updated`);
984
+ console.log('');
985
+ } else {
986
+ console.log(`\n \x1b[31m\u2717\x1b[0m ${result.error}\n`);
987
+ process.exit(1);
988
+ }
989
+ process.exit(0);
965
990
  } else if (normalizedCommand === 'synergy-report') {
966
991
  // Placeholder — synergy report is referenced but may not be implemented yet
967
992
  console.log('\n Synergy report: coming soon.\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "1.2.6",
3
+ "version": "1.3.0",
4
4
  "description": "The intelligent nervous system for AI coding agents — 2,306 checks across 8 platforms and 10 languages. Audit, align, and amplify.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +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 };
@@ -300,6 +300,7 @@ async function runHarmonyWatch(options) {
300
300
 
301
301
  await startHarmonyWatch({
302
302
  dir,
303
+ autoSync: !!options.autoSync,
303
304
  debounceMs: options.debounce || 800,
304
305
  onDriftDetected: (platform, details) => {
305
306
  console.log(c(` DRIFT ALERT: ${platform} score dropped by ${Math.abs(details.delta)}`, 'red'));
@@ -36,6 +36,13 @@ const PLATFORM_WATCH_FILES = [
36
36
  '.github/copilot-review-instructions.md',
37
37
  // Cursor
38
38
  '.cursorrules',
39
+ // Windsurf
40
+ '.windsurfrules',
41
+ // Aider
42
+ '.aider.conf.yml',
43
+ '.aiderignore',
44
+ // OpenCode
45
+ 'opencode.json',
39
46
  // Shared
40
47
  '.gitignore',
41
48
  'package.json',
@@ -53,6 +60,9 @@ const PLATFORM_WATCH_DIRS = [
53
60
  '.github',
54
61
  '.cursor',
55
62
  '.cursor/rules',
63
+ '.windsurf',
64
+ '.windsurf/rules',
65
+ '.opencode',
56
66
  ];
57
67
 
58
68
  // ─── fs.watch helpers (mirror pattern from src/watch.js) ──────────────────────
@@ -174,6 +184,9 @@ function identifyPlatform(filePath) {
174
184
  if (normalized.includes('.gemini') || normalized.includes('gemini.md')) return 'gemini';
175
185
  if (normalized.includes('copilot') || normalized.includes('.github')) return 'copilot';
176
186
  if (normalized.includes('.cursor') || normalized.includes('cursorrules')) return 'cursor';
187
+ if (normalized.includes('.windsurf') || normalized.includes('windsurfrules')) return 'windsurf';
188
+ if (normalized.includes('.aider') || normalized.includes('aiderignore')) return 'aider';
189
+ if (normalized.includes('.opencode') || normalized.includes('opencode.json')) return 'opencode';
177
190
  return 'unknown';
178
191
  }
179
192
 
@@ -187,6 +200,7 @@ function identifyPlatform(filePath) {
187
200
  * @param {Function} [options.onDriftDetected] - Callback when drift increases: (platform, details) => void
188
201
  * @param {Function} [options.onPlatformChange] - Callback on any platform config change: (platform, file) => void
189
202
  * @param {Function} [options.runAudit] - Optional audit function to re-run on changes
203
+ * @param {boolean} [options.autoSync=false] - Auto-apply harmony sync when drift is detected
190
204
  * @param {number} [options.debounceMs=800] - Debounce interval in ms
191
205
  */
192
206
  async function startHarmonyWatch(options) {
@@ -195,6 +209,7 @@ async function startHarmonyWatch(options) {
195
209
  onDriftDetected,
196
210
  onPlatformChange,
197
211
  runAudit,
212
+ autoSync = false,
198
213
  debounceMs = 800,
199
214
  } = options;
200
215
 
@@ -204,7 +219,10 @@ async function startHarmonyWatch(options) {
204
219
  console.log(c(' nerviq harmony watch', 'bold'));
205
220
  console.log(c(' ═══════════════════════════════════════', 'dim'));
206
221
  console.log(c(` Watching: ${dir}`, 'dim'));
207
- console.log(c(` Platforms: Claude, Codex, Gemini, Copilot, Cursor`, 'dim'));
222
+ console.log(c(` Platforms: Claude, Codex, Gemini, Copilot, Cursor, Windsurf, Aider, OpenCode`, 'dim'));
223
+ if (autoSync) {
224
+ console.log(c(` Auto-sync: ON — drift will be auto-corrected`, 'green'));
225
+ }
208
226
  console.log(c(` Mode: ${recursiveSupported ? 'native recursive' : 'expanded directory fallback'}`, 'dim'));
209
227
  console.log(c(' Press Ctrl+C to stop', 'dim'));
210
228
  console.log('');
@@ -293,6 +311,22 @@ async function startHarmonyWatch(options) {
293
311
  }
294
312
  }
295
313
 
314
+ // Auto-sync on drift
315
+ if (delta < 0 && autoSync) {
316
+ try {
317
+ const { applyHarmonySync } = require('./sync');
318
+ const syncResult = applyHarmonySync(dir);
319
+ if (syncResult.applied.length > 0) {
320
+ console.log(c(` Auto-sync: applied ${syncResult.applied.length} fix(es)`, 'green'));
321
+ for (const item of syncResult.applied) {
322
+ console.log(c(` ✓ ${item.action} ${item.path}`, 'dim'));
323
+ }
324
+ }
325
+ } catch (_e) {
326
+ console.log(c(` Auto-sync failed: ${_e.message}`, 'yellow'));
327
+ }
328
+ }
329
+
296
330
  lastScores[p] = newScore;
297
331
  }
298
332
  }