@nerviq/cli 1.17.3 → 1.19.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 +4 -4
- package/bin/cli.js +61 -274
- package/package.json +60 -60
- 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 +158 -158
- 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 +16 -16
- 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/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/context.js +320 -320
- package/src/continuous-ops.js +681 -681
- package/src/copilot/activity.js +309 -309
- package/src/copilot/config-parser.js +280 -226
- package/src/copilot/context.js +218 -197
- 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/copilot/techniques.js +219 -78
- 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 -0
- package/src/fix-prompts.js +122 -122
- package/src/formatters/sarif.js +115 -115
- package/src/freshness.js +74 -74
- package/src/gemini/config-parser.js +275 -275
- package/src/gemini/context.js +221 -221
- 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/gemini/techniques.js +14 -14
- 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/context.js +249 -249
- 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/windsurf/techniques.js +17 -17
- package/src/workspace.js +375 -375
package/src/codex/patch.js
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex Patch Intelligence — CP-09
|
|
3
|
-
*
|
|
4
|
-
* Safe patching of existing Codex files using managed blocks.
|
|
5
|
-
* Supports AGENTS.md (HTML comment blocks) and config.toml (TOML comment blocks).
|
|
6
|
-
*
|
|
7
|
-
* Managed blocks are sections that nerviq controls.
|
|
8
|
-
* Hand-authored content outside managed blocks is preserved.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const { writeRollbackArtifact, writeActivityArtifact } = require('../activity');
|
|
14
|
-
|
|
15
|
-
// Managed block markers
|
|
16
|
-
const MANAGED_START_MD = '<!-- nerviq:managed:start -->';
|
|
17
|
-
const MANAGED_END_MD = '<!-- nerviq:managed:end -->';
|
|
18
|
-
const MANAGED_START_TOML = '# <!-- nerviq:managed:start -->';
|
|
19
|
-
const MANAGED_END_TOML = '# <!-- nerviq:managed:end -->';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Extract managed blocks from a file.
|
|
23
|
-
* Returns { before, managed, after } where managed is the content between markers.
|
|
24
|
-
*/
|
|
25
|
-
function extractManagedBlock(content, startMarker, endMarker) {
|
|
26
|
-
const startIdx = content.indexOf(startMarker);
|
|
27
|
-
const endIdx = content.indexOf(endMarker);
|
|
28
|
-
|
|
29
|
-
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
30
|
-
return { before: content, managed: null, after: '' };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
before: content.substring(0, startIdx),
|
|
35
|
-
managed: content.substring(startIdx + startMarker.length, endIdx).trim(),
|
|
36
|
-
after: content.substring(endIdx + endMarker.length),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Replace or insert a managed block in a file.
|
|
42
|
-
* If the file already has managed markers, replace the content between them.
|
|
43
|
-
* If not, append the managed block at the end.
|
|
44
|
-
*/
|
|
45
|
-
function upsertManagedBlock(content, newManaged, startMarker, endMarker) {
|
|
46
|
-
const { before, managed, after } = extractManagedBlock(content, startMarker, endMarker);
|
|
47
|
-
|
|
48
|
-
if (managed !== null) {
|
|
49
|
-
// Replace existing managed block
|
|
50
|
-
return `${before}${startMarker}\n${newManaged}\n${endMarker}${after}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Append new managed block
|
|
54
|
-
const separator = content.endsWith('\n') ? '\n' : '\n\n';
|
|
55
|
-
return `${content}${separator}${startMarker}\n${newManaged}\n${endMarker}\n`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Patch AGENTS.md with managed sections.
|
|
60
|
-
* Preserves all hand-authored content.
|
|
61
|
-
*/
|
|
62
|
-
function patchAgentsMd(existingContent, managedSections) {
|
|
63
|
-
const newManaged = Object.entries(managedSections)
|
|
64
|
-
.map(([section, content]) => `## ${section}\n${content}`)
|
|
65
|
-
.join('\n\n');
|
|
66
|
-
|
|
67
|
-
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_MD, MANAGED_END_MD);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Patch config.toml by safely adding new sections.
|
|
72
|
-
* Never weakens existing sandbox or approval posture.
|
|
73
|
-
* Only adds new [section] blocks that don't already exist.
|
|
74
|
-
*/
|
|
75
|
-
function patchConfigToml(existingContent, newSections) {
|
|
76
|
-
const existingSections = new Set();
|
|
77
|
-
const sectionPattern = /^\[([^\]]+)\]/gm;
|
|
78
|
-
let match;
|
|
79
|
-
while ((match = sectionPattern.exec(existingContent)) !== null) {
|
|
80
|
-
existingSections.add(match[1]);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const additions = [];
|
|
84
|
-
for (const [section, content] of Object.entries(newSections)) {
|
|
85
|
-
if (!existingSections.has(section)) {
|
|
86
|
-
additions.push(`[${section}]\n${content}`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (additions.length === 0) return existingContent;
|
|
91
|
-
|
|
92
|
-
const newManaged = additions.join('\n\n');
|
|
93
|
-
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_TOML, MANAGED_END_TOML);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Detect if a repo has both Claude and Codex surfaces (mixed-agent repo).
|
|
98
|
-
*/
|
|
99
|
-
function detectMixedAgentRepo(dir) {
|
|
100
|
-
const hasClaude = fs.existsSync(path.join(dir, 'CLAUDE.md')) ||
|
|
101
|
-
fs.existsSync(path.join(dir, '.claude'));
|
|
102
|
-
const hasCodex = fs.existsSync(path.join(dir, 'AGENTS.md')) ||
|
|
103
|
-
fs.existsSync(path.join(dir, '.codex'));
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
isMixed: hasClaude && hasCodex,
|
|
107
|
-
hasClaude,
|
|
108
|
-
hasCodex,
|
|
109
|
-
guidance: hasClaude && hasCodex
|
|
110
|
-
? 'This is a mixed-agent repo. Keep Claude instructions in CLAUDE.md and Codex instructions in AGENTS.md. Do not merge them.'
|
|
111
|
-
: null,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Generate a diff preview for a patch operation.
|
|
117
|
-
*/
|
|
118
|
-
function generatePatchPreview(originalContent, patchedContent, filePath) {
|
|
119
|
-
const origLines = originalContent.split('\n');
|
|
120
|
-
const patchLines = patchedContent.split('\n');
|
|
121
|
-
|
|
122
|
-
const lines = [`--- ${filePath} (original)`, `+++ ${filePath} (patched)`];
|
|
123
|
-
|
|
124
|
-
// Simple line-by-line diff showing only changed sections
|
|
125
|
-
let inChange = false;
|
|
126
|
-
for (let i = 0; i < Math.max(origLines.length, patchLines.length); i++) {
|
|
127
|
-
const orig = origLines[i] || '';
|
|
128
|
-
const patched = patchLines[i] || '';
|
|
129
|
-
if (orig !== patched) {
|
|
130
|
-
if (!inChange) {
|
|
131
|
-
lines.push(`@@ line ${i + 1} @@`);
|
|
132
|
-
inChange = true;
|
|
133
|
-
}
|
|
134
|
-
if (i < origLines.length) lines.push(`-${orig}`);
|
|
135
|
-
if (i < patchLines.length) lines.push(`+${patched}`);
|
|
136
|
-
} else {
|
|
137
|
-
inChange = false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return lines.join('\n');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Apply a patch to a file with backup and rollback support.
|
|
146
|
-
*/
|
|
147
|
-
function applyPatch(dir, filePath, patchFn, options = {}) {
|
|
148
|
-
const fullPath = path.join(dir, filePath);
|
|
149
|
-
const dryRun = options.dryRun === true;
|
|
150
|
-
|
|
151
|
-
if (!fs.existsSync(fullPath)) {
|
|
152
|
-
return { success: false, reason: `${filePath} does not exist`, preview: null };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const original = fs.readFileSync(fullPath, 'utf8');
|
|
156
|
-
const patched = patchFn(original);
|
|
157
|
-
|
|
158
|
-
if (patched === original) {
|
|
159
|
-
return { success: true, reason: 'no changes needed', preview: null, unchanged: true };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const preview = generatePatchPreview(original, patched, filePath);
|
|
163
|
-
|
|
164
|
-
if (dryRun) {
|
|
165
|
-
return { success: true, reason: 'dry run', preview, unchanged: false };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Backup + write
|
|
169
|
-
const backupPath = fullPath + '.nerviq-backup';
|
|
170
|
-
fs.writeFileSync(backupPath, original, 'utf8');
|
|
171
|
-
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
172
|
-
|
|
173
|
-
// Rollback artifact
|
|
174
|
-
const rollback = writeRollbackArtifact(dir, {
|
|
175
|
-
sourcePlan: 'codex-patch',
|
|
176
|
-
patchedFiles: [filePath],
|
|
177
|
-
backupFiles: [{ original: filePath, backup: path.relative(dir, backupPath) }],
|
|
178
|
-
rollbackInstructions: [`Restore ${filePath} from ${path.relative(dir, backupPath)}`],
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const activity = writeActivityArtifact(dir, 'codex-patch', {
|
|
182
|
-
platform: 'codex',
|
|
183
|
-
patchedFiles: [filePath],
|
|
184
|
-
rollbackArtifact: rollback.relativePath,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
success: true,
|
|
189
|
-
reason: 'patched',
|
|
190
|
-
preview,
|
|
191
|
-
unchanged: false,
|
|
192
|
-
rollbackArtifact: rollback.relativePath,
|
|
193
|
-
activityArtifact: activity.relativePath,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
module.exports = {
|
|
198
|
-
MANAGED_START_MD,
|
|
199
|
-
MANAGED_END_MD,
|
|
200
|
-
MANAGED_START_TOML,
|
|
201
|
-
MANAGED_END_TOML,
|
|
202
|
-
extractManagedBlock,
|
|
203
|
-
upsertManagedBlock,
|
|
204
|
-
patchAgentsMd,
|
|
205
|
-
patchConfigToml,
|
|
206
|
-
detectMixedAgentRepo,
|
|
207
|
-
generatePatchPreview,
|
|
208
|
-
applyPatch,
|
|
209
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Codex Patch Intelligence — CP-09
|
|
3
|
+
*
|
|
4
|
+
* Safe patching of existing Codex files using managed blocks.
|
|
5
|
+
* Supports AGENTS.md (HTML comment blocks) and config.toml (TOML comment blocks).
|
|
6
|
+
*
|
|
7
|
+
* Managed blocks are sections that nerviq controls.
|
|
8
|
+
* Hand-authored content outside managed blocks is preserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { writeRollbackArtifact, writeActivityArtifact } = require('../activity');
|
|
14
|
+
|
|
15
|
+
// Managed block markers
|
|
16
|
+
const MANAGED_START_MD = '<!-- nerviq:managed:start -->';
|
|
17
|
+
const MANAGED_END_MD = '<!-- nerviq:managed:end -->';
|
|
18
|
+
const MANAGED_START_TOML = '# <!-- nerviq:managed:start -->';
|
|
19
|
+
const MANAGED_END_TOML = '# <!-- nerviq:managed:end -->';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extract managed blocks from a file.
|
|
23
|
+
* Returns { before, managed, after } where managed is the content between markers.
|
|
24
|
+
*/
|
|
25
|
+
function extractManagedBlock(content, startMarker, endMarker) {
|
|
26
|
+
const startIdx = content.indexOf(startMarker);
|
|
27
|
+
const endIdx = content.indexOf(endMarker);
|
|
28
|
+
|
|
29
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
30
|
+
return { before: content, managed: null, after: '' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
before: content.substring(0, startIdx),
|
|
35
|
+
managed: content.substring(startIdx + startMarker.length, endIdx).trim(),
|
|
36
|
+
after: content.substring(endIdx + endMarker.length),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Replace or insert a managed block in a file.
|
|
42
|
+
* If the file already has managed markers, replace the content between them.
|
|
43
|
+
* If not, append the managed block at the end.
|
|
44
|
+
*/
|
|
45
|
+
function upsertManagedBlock(content, newManaged, startMarker, endMarker) {
|
|
46
|
+
const { before, managed, after } = extractManagedBlock(content, startMarker, endMarker);
|
|
47
|
+
|
|
48
|
+
if (managed !== null) {
|
|
49
|
+
// Replace existing managed block
|
|
50
|
+
return `${before}${startMarker}\n${newManaged}\n${endMarker}${after}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Append new managed block
|
|
54
|
+
const separator = content.endsWith('\n') ? '\n' : '\n\n';
|
|
55
|
+
return `${content}${separator}${startMarker}\n${newManaged}\n${endMarker}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Patch AGENTS.md with managed sections.
|
|
60
|
+
* Preserves all hand-authored content.
|
|
61
|
+
*/
|
|
62
|
+
function patchAgentsMd(existingContent, managedSections) {
|
|
63
|
+
const newManaged = Object.entries(managedSections)
|
|
64
|
+
.map(([section, content]) => `## ${section}\n${content}`)
|
|
65
|
+
.join('\n\n');
|
|
66
|
+
|
|
67
|
+
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_MD, MANAGED_END_MD);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Patch config.toml by safely adding new sections.
|
|
72
|
+
* Never weakens existing sandbox or approval posture.
|
|
73
|
+
* Only adds new [section] blocks that don't already exist.
|
|
74
|
+
*/
|
|
75
|
+
function patchConfigToml(existingContent, newSections) {
|
|
76
|
+
const existingSections = new Set();
|
|
77
|
+
const sectionPattern = /^\[([^\]]+)\]/gm;
|
|
78
|
+
let match;
|
|
79
|
+
while ((match = sectionPattern.exec(existingContent)) !== null) {
|
|
80
|
+
existingSections.add(match[1]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const additions = [];
|
|
84
|
+
for (const [section, content] of Object.entries(newSections)) {
|
|
85
|
+
if (!existingSections.has(section)) {
|
|
86
|
+
additions.push(`[${section}]\n${content}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (additions.length === 0) return existingContent;
|
|
91
|
+
|
|
92
|
+
const newManaged = additions.join('\n\n');
|
|
93
|
+
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_TOML, MANAGED_END_TOML);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Detect if a repo has both Claude and Codex surfaces (mixed-agent repo).
|
|
98
|
+
*/
|
|
99
|
+
function detectMixedAgentRepo(dir) {
|
|
100
|
+
const hasClaude = fs.existsSync(path.join(dir, 'CLAUDE.md')) ||
|
|
101
|
+
fs.existsSync(path.join(dir, '.claude'));
|
|
102
|
+
const hasCodex = fs.existsSync(path.join(dir, 'AGENTS.md')) ||
|
|
103
|
+
fs.existsSync(path.join(dir, '.codex'));
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
isMixed: hasClaude && hasCodex,
|
|
107
|
+
hasClaude,
|
|
108
|
+
hasCodex,
|
|
109
|
+
guidance: hasClaude && hasCodex
|
|
110
|
+
? 'This is a mixed-agent repo. Keep Claude instructions in CLAUDE.md and Codex instructions in AGENTS.md. Do not merge them.'
|
|
111
|
+
: null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generate a diff preview for a patch operation.
|
|
117
|
+
*/
|
|
118
|
+
function generatePatchPreview(originalContent, patchedContent, filePath) {
|
|
119
|
+
const origLines = originalContent.split('\n');
|
|
120
|
+
const patchLines = patchedContent.split('\n');
|
|
121
|
+
|
|
122
|
+
const lines = [`--- ${filePath} (original)`, `+++ ${filePath} (patched)`];
|
|
123
|
+
|
|
124
|
+
// Simple line-by-line diff showing only changed sections
|
|
125
|
+
let inChange = false;
|
|
126
|
+
for (let i = 0; i < Math.max(origLines.length, patchLines.length); i++) {
|
|
127
|
+
const orig = origLines[i] || '';
|
|
128
|
+
const patched = patchLines[i] || '';
|
|
129
|
+
if (orig !== patched) {
|
|
130
|
+
if (!inChange) {
|
|
131
|
+
lines.push(`@@ line ${i + 1} @@`);
|
|
132
|
+
inChange = true;
|
|
133
|
+
}
|
|
134
|
+
if (i < origLines.length) lines.push(`-${orig}`);
|
|
135
|
+
if (i < patchLines.length) lines.push(`+${patched}`);
|
|
136
|
+
} else {
|
|
137
|
+
inChange = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Apply a patch to a file with backup and rollback support.
|
|
146
|
+
*/
|
|
147
|
+
function applyPatch(dir, filePath, patchFn, options = {}) {
|
|
148
|
+
const fullPath = path.join(dir, filePath);
|
|
149
|
+
const dryRun = options.dryRun === true;
|
|
150
|
+
|
|
151
|
+
if (!fs.existsSync(fullPath)) {
|
|
152
|
+
return { success: false, reason: `${filePath} does not exist`, preview: null };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const original = fs.readFileSync(fullPath, 'utf8');
|
|
156
|
+
const patched = patchFn(original);
|
|
157
|
+
|
|
158
|
+
if (patched === original) {
|
|
159
|
+
return { success: true, reason: 'no changes needed', preview: null, unchanged: true };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const preview = generatePatchPreview(original, patched, filePath);
|
|
163
|
+
|
|
164
|
+
if (dryRun) {
|
|
165
|
+
return { success: true, reason: 'dry run', preview, unchanged: false };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Backup + write
|
|
169
|
+
const backupPath = fullPath + '.nerviq-backup';
|
|
170
|
+
fs.writeFileSync(backupPath, original, 'utf8');
|
|
171
|
+
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
172
|
+
|
|
173
|
+
// Rollback artifact
|
|
174
|
+
const rollback = writeRollbackArtifact(dir, {
|
|
175
|
+
sourcePlan: 'codex-patch',
|
|
176
|
+
patchedFiles: [filePath],
|
|
177
|
+
backupFiles: [{ original: filePath, backup: path.relative(dir, backupPath) }],
|
|
178
|
+
rollbackInstructions: [`Restore ${filePath} from ${path.relative(dir, backupPath)}`],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const activity = writeActivityArtifact(dir, 'codex-patch', {
|
|
182
|
+
platform: 'codex',
|
|
183
|
+
patchedFiles: [filePath],
|
|
184
|
+
rollbackArtifact: rollback.relativePath,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
reason: 'patched',
|
|
190
|
+
preview,
|
|
191
|
+
unchanged: false,
|
|
192
|
+
rollbackArtifact: rollback.relativePath,
|
|
193
|
+
activityArtifact: activity.relativePath,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
MANAGED_START_MD,
|
|
199
|
+
MANAGED_END_MD,
|
|
200
|
+
MANAGED_START_TOML,
|
|
201
|
+
MANAGED_END_TOML,
|
|
202
|
+
extractManagedBlock,
|
|
203
|
+
upsertManagedBlock,
|
|
204
|
+
patchAgentsMd,
|
|
205
|
+
patchConfigToml,
|
|
206
|
+
detectMixedAgentRepo,
|
|
207
|
+
generatePatchPreview,
|
|
208
|
+
applyPatch,
|
|
209
|
+
};
|