@nerviq/cli 1.20.1 → 1.21.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 +2 -2
- 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/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/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/aider/patch.js
CHANGED
|
@@ -1,214 +1,214 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Aider Patch Intelligence — managed blocks for YAML config
|
|
3
|
-
*
|
|
4
|
-
* Safe patching of existing Aider files using managed blocks.
|
|
5
|
-
* Supports .aider.conf.yml (YAML comment blocks) and CONVENTIONS.md (HTML 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_YAML = '# <!-- nerviq:managed:start -->';
|
|
19
|
-
const MANAGED_END_YAML = '# <!-- nerviq:managed:end -->';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Extract managed blocks from a file.
|
|
23
|
-
*/
|
|
24
|
-
function extractManagedBlock(content, startMarker, endMarker) {
|
|
25
|
-
const startIdx = content.indexOf(startMarker);
|
|
26
|
-
const endIdx = content.indexOf(endMarker);
|
|
27
|
-
|
|
28
|
-
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
29
|
-
return { before: content, managed: null, after: '' };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
before: content.substring(0, startIdx),
|
|
34
|
-
managed: content.substring(startIdx + startMarker.length, endIdx).trim(),
|
|
35
|
-
after: content.substring(endIdx + endMarker.length),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Replace or insert a managed block in a file.
|
|
41
|
-
*/
|
|
42
|
-
function upsertManagedBlock(content, newManaged, startMarker, endMarker) {
|
|
43
|
-
const { before, managed, after } = extractManagedBlock(content, startMarker, endMarker);
|
|
44
|
-
|
|
45
|
-
if (managed !== null) {
|
|
46
|
-
return `${before}${startMarker}\n${newManaged}\n${endMarker}${after}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const separator = content.endsWith('\n') ? '\n' : '\n\n';
|
|
50
|
-
return `${content}${separator}${startMarker}\n${newManaged}\n${endMarker}\n`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Patch CONVENTIONS.md with managed sections.
|
|
55
|
-
*/
|
|
56
|
-
function patchConventionsMd(existingContent, managedSections) {
|
|
57
|
-
const newManaged = Object.entries(managedSections)
|
|
58
|
-
.map(([section, content]) => `## ${section}\n${content}`)
|
|
59
|
-
.join('\n\n');
|
|
60
|
-
|
|
61
|
-
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_MD, MANAGED_END_MD);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Patch .aider.conf.yml by safely adding new keys.
|
|
66
|
-
* Never weakens existing safety settings (auto-commits, etc.).
|
|
67
|
-
* Only adds keys that don't already exist.
|
|
68
|
-
*/
|
|
69
|
-
function patchAiderConfYml(existingContent, newKeys) {
|
|
70
|
-
const existingKeys = new Set();
|
|
71
|
-
const lines = existingContent.split(/\r?\n/);
|
|
72
|
-
|
|
73
|
-
for (const line of lines) {
|
|
74
|
-
const trimmed = line.trim();
|
|
75
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
76
|
-
const colonIdx = trimmed.indexOf(':');
|
|
77
|
-
if (colonIdx > 0) {
|
|
78
|
-
existingKeys.add(trimmed.slice(0, colonIdx).trim());
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const additions = [];
|
|
83
|
-
for (const [key, value] of Object.entries(newKeys)) {
|
|
84
|
-
if (existingKeys.has(key)) continue;
|
|
85
|
-
|
|
86
|
-
// Safety: never weaken these settings
|
|
87
|
-
if (key === 'auto-commits' && value === false) continue;
|
|
88
|
-
if (key === 'yes-always' && value === true) continue;
|
|
89
|
-
|
|
90
|
-
if (typeof value === 'string') {
|
|
91
|
-
additions.push(`${key}: "${value}"`);
|
|
92
|
-
} else if (typeof value === 'boolean' || typeof value === 'number') {
|
|
93
|
-
additions.push(`${key}: ${value}`);
|
|
94
|
-
} else if (Array.isArray(value)) {
|
|
95
|
-
additions.push(`${key}:`);
|
|
96
|
-
for (const item of value) {
|
|
97
|
-
additions.push(` - ${item}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (additions.length === 0) return existingContent;
|
|
103
|
-
|
|
104
|
-
const newContent = additions.join('\n');
|
|
105
|
-
return upsertManagedBlock(existingContent, newContent, MANAGED_START_YAML, MANAGED_END_YAML);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Detect mixed-platform repo (e.g., has both .aider.conf.yml and CLAUDE.md).
|
|
110
|
-
*/
|
|
111
|
-
function detectMixedAiderRepo(ctx) {
|
|
112
|
-
const platforms = [];
|
|
113
|
-
|
|
114
|
-
if (ctx.fileContent('.aider.conf.yml')) platforms.push('aider');
|
|
115
|
-
if (ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md')) platforms.push('claude');
|
|
116
|
-
if (ctx.fileContent('AGENTS.md') || ctx.fileContent('.codex')) platforms.push('codex');
|
|
117
|
-
if (ctx.fileContent('.cursor/rules') || ctx.fileContent('.cursorrules')) platforms.push('cursor');
|
|
118
|
-
if (ctx.fileContent('.github/copilot-instructions.md')) platforms.push('copilot');
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
isMultiPlatform: platforms.length > 1,
|
|
122
|
-
platforms,
|
|
123
|
-
primaryPlatform: platforms[0] || null,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Generate a patch preview without writing.
|
|
129
|
-
*/
|
|
130
|
-
function generatePatchPreview(ctx, patches = {}) {
|
|
131
|
-
const previews = [];
|
|
132
|
-
|
|
133
|
-
if (patches.confYml) {
|
|
134
|
-
const existing = ctx.configContent ? (ctx.configContent() || '') : '';
|
|
135
|
-
const patched = existing
|
|
136
|
-
? patchAiderConfYml(existing, patches.confYml)
|
|
137
|
-
: Object.entries(patches.confYml).map(([k, v]) => `${k}: ${v}`).join('\n');
|
|
138
|
-
|
|
139
|
-
previews.push({
|
|
140
|
-
file: '.aider.conf.yml',
|
|
141
|
-
exists: Boolean(existing),
|
|
142
|
-
unchanged: patched === existing,
|
|
143
|
-
preview: patched,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (patches.conventions) {
|
|
148
|
-
const existing = ctx.fileContent('CONVENTIONS.md') || '';
|
|
149
|
-
const patched = existing
|
|
150
|
-
? patchConventionsMd(existing, patches.conventions)
|
|
151
|
-
: Object.entries(patches.conventions).map(([s, c]) => `## ${s}\n${c}`).join('\n\n');
|
|
152
|
-
|
|
153
|
-
previews.push({
|
|
154
|
-
file: 'CONVENTIONS.md',
|
|
155
|
-
exists: Boolean(existing),
|
|
156
|
-
unchanged: patched === existing,
|
|
157
|
-
preview: patched,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return previews;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Apply patches to disk with rollback support.
|
|
166
|
-
*/
|
|
167
|
-
function applyPatch(dir, filePath, newContent) {
|
|
168
|
-
const fullPath = path.join(dir, filePath);
|
|
169
|
-
const existed = fs.existsSync(fullPath);
|
|
170
|
-
const originalContent = existed ? fs.readFileSync(fullPath, 'utf8') : null;
|
|
171
|
-
|
|
172
|
-
if (existed && originalContent === newContent) {
|
|
173
|
-
return { success: true, reason: 'unchanged', unchanged: true };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const dirName = path.dirname(fullPath);
|
|
177
|
-
if (!fs.existsSync(dirName)) {
|
|
178
|
-
fs.mkdirSync(dirName, { recursive: true });
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
fs.writeFileSync(fullPath, newContent, 'utf8');
|
|
182
|
-
|
|
183
|
-
const rollback = writeRollbackArtifact(dir, 'aider-patch', [filePath], {
|
|
184
|
-
[filePath]: originalContent,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const activity = writeActivityArtifact(dir, 'aider-patch', {
|
|
188
|
-
platform: 'aider',
|
|
189
|
-
patchedFiles: [filePath],
|
|
190
|
-
rollbackArtifact: rollback.relativePath,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
success: true,
|
|
195
|
-
reason: 'patched',
|
|
196
|
-
unchanged: false,
|
|
197
|
-
rollbackArtifact: rollback.relativePath,
|
|
198
|
-
activityArtifact: activity.relativePath,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
module.exports = {
|
|
203
|
-
MANAGED_START_MD,
|
|
204
|
-
MANAGED_END_MD,
|
|
205
|
-
MANAGED_START_YAML,
|
|
206
|
-
MANAGED_END_YAML,
|
|
207
|
-
extractManagedBlock,
|
|
208
|
-
upsertManagedBlock,
|
|
209
|
-
patchConventionsMd,
|
|
210
|
-
patchAiderConfYml,
|
|
211
|
-
detectMixedAiderRepo,
|
|
212
|
-
generatePatchPreview,
|
|
213
|
-
applyPatch,
|
|
214
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Aider Patch Intelligence — managed blocks for YAML config
|
|
3
|
+
*
|
|
4
|
+
* Safe patching of existing Aider files using managed blocks.
|
|
5
|
+
* Supports .aider.conf.yml (YAML comment blocks) and CONVENTIONS.md (HTML 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_YAML = '# <!-- nerviq:managed:start -->';
|
|
19
|
+
const MANAGED_END_YAML = '# <!-- nerviq:managed:end -->';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extract managed blocks from a file.
|
|
23
|
+
*/
|
|
24
|
+
function extractManagedBlock(content, startMarker, endMarker) {
|
|
25
|
+
const startIdx = content.indexOf(startMarker);
|
|
26
|
+
const endIdx = content.indexOf(endMarker);
|
|
27
|
+
|
|
28
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
29
|
+
return { before: content, managed: null, after: '' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
before: content.substring(0, startIdx),
|
|
34
|
+
managed: content.substring(startIdx + startMarker.length, endIdx).trim(),
|
|
35
|
+
after: content.substring(endIdx + endMarker.length),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Replace or insert a managed block in a file.
|
|
41
|
+
*/
|
|
42
|
+
function upsertManagedBlock(content, newManaged, startMarker, endMarker) {
|
|
43
|
+
const { before, managed, after } = extractManagedBlock(content, startMarker, endMarker);
|
|
44
|
+
|
|
45
|
+
if (managed !== null) {
|
|
46
|
+
return `${before}${startMarker}\n${newManaged}\n${endMarker}${after}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const separator = content.endsWith('\n') ? '\n' : '\n\n';
|
|
50
|
+
return `${content}${separator}${startMarker}\n${newManaged}\n${endMarker}\n`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Patch CONVENTIONS.md with managed sections.
|
|
55
|
+
*/
|
|
56
|
+
function patchConventionsMd(existingContent, managedSections) {
|
|
57
|
+
const newManaged = Object.entries(managedSections)
|
|
58
|
+
.map(([section, content]) => `## ${section}\n${content}`)
|
|
59
|
+
.join('\n\n');
|
|
60
|
+
|
|
61
|
+
return upsertManagedBlock(existingContent, newManaged, MANAGED_START_MD, MANAGED_END_MD);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Patch .aider.conf.yml by safely adding new keys.
|
|
66
|
+
* Never weakens existing safety settings (auto-commits, etc.).
|
|
67
|
+
* Only adds keys that don't already exist.
|
|
68
|
+
*/
|
|
69
|
+
function patchAiderConfYml(existingContent, newKeys) {
|
|
70
|
+
const existingKeys = new Set();
|
|
71
|
+
const lines = existingContent.split(/\r?\n/);
|
|
72
|
+
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
const trimmed = line.trim();
|
|
75
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
76
|
+
const colonIdx = trimmed.indexOf(':');
|
|
77
|
+
if (colonIdx > 0) {
|
|
78
|
+
existingKeys.add(trimmed.slice(0, colonIdx).trim());
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const additions = [];
|
|
83
|
+
for (const [key, value] of Object.entries(newKeys)) {
|
|
84
|
+
if (existingKeys.has(key)) continue;
|
|
85
|
+
|
|
86
|
+
// Safety: never weaken these settings
|
|
87
|
+
if (key === 'auto-commits' && value === false) continue;
|
|
88
|
+
if (key === 'yes-always' && value === true) continue;
|
|
89
|
+
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
additions.push(`${key}: "${value}"`);
|
|
92
|
+
} else if (typeof value === 'boolean' || typeof value === 'number') {
|
|
93
|
+
additions.push(`${key}: ${value}`);
|
|
94
|
+
} else if (Array.isArray(value)) {
|
|
95
|
+
additions.push(`${key}:`);
|
|
96
|
+
for (const item of value) {
|
|
97
|
+
additions.push(` - ${item}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (additions.length === 0) return existingContent;
|
|
103
|
+
|
|
104
|
+
const newContent = additions.join('\n');
|
|
105
|
+
return upsertManagedBlock(existingContent, newContent, MANAGED_START_YAML, MANAGED_END_YAML);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Detect mixed-platform repo (e.g., has both .aider.conf.yml and CLAUDE.md).
|
|
110
|
+
*/
|
|
111
|
+
function detectMixedAiderRepo(ctx) {
|
|
112
|
+
const platforms = [];
|
|
113
|
+
|
|
114
|
+
if (ctx.fileContent('.aider.conf.yml')) platforms.push('aider');
|
|
115
|
+
if (ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md')) platforms.push('claude');
|
|
116
|
+
if (ctx.fileContent('AGENTS.md') || ctx.fileContent('.codex')) platforms.push('codex');
|
|
117
|
+
if (ctx.fileContent('.cursor/rules') || ctx.fileContent('.cursorrules')) platforms.push('cursor');
|
|
118
|
+
if (ctx.fileContent('.github/copilot-instructions.md')) platforms.push('copilot');
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
isMultiPlatform: platforms.length > 1,
|
|
122
|
+
platforms,
|
|
123
|
+
primaryPlatform: platforms[0] || null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate a patch preview without writing.
|
|
129
|
+
*/
|
|
130
|
+
function generatePatchPreview(ctx, patches = {}) {
|
|
131
|
+
const previews = [];
|
|
132
|
+
|
|
133
|
+
if (patches.confYml) {
|
|
134
|
+
const existing = ctx.configContent ? (ctx.configContent() || '') : '';
|
|
135
|
+
const patched = existing
|
|
136
|
+
? patchAiderConfYml(existing, patches.confYml)
|
|
137
|
+
: Object.entries(patches.confYml).map(([k, v]) => `${k}: ${v}`).join('\n');
|
|
138
|
+
|
|
139
|
+
previews.push({
|
|
140
|
+
file: '.aider.conf.yml',
|
|
141
|
+
exists: Boolean(existing),
|
|
142
|
+
unchanged: patched === existing,
|
|
143
|
+
preview: patched,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (patches.conventions) {
|
|
148
|
+
const existing = ctx.fileContent('CONVENTIONS.md') || '';
|
|
149
|
+
const patched = existing
|
|
150
|
+
? patchConventionsMd(existing, patches.conventions)
|
|
151
|
+
: Object.entries(patches.conventions).map(([s, c]) => `## ${s}\n${c}`).join('\n\n');
|
|
152
|
+
|
|
153
|
+
previews.push({
|
|
154
|
+
file: 'CONVENTIONS.md',
|
|
155
|
+
exists: Boolean(existing),
|
|
156
|
+
unchanged: patched === existing,
|
|
157
|
+
preview: patched,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return previews;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Apply patches to disk with rollback support.
|
|
166
|
+
*/
|
|
167
|
+
function applyPatch(dir, filePath, newContent) {
|
|
168
|
+
const fullPath = path.join(dir, filePath);
|
|
169
|
+
const existed = fs.existsSync(fullPath);
|
|
170
|
+
const originalContent = existed ? fs.readFileSync(fullPath, 'utf8') : null;
|
|
171
|
+
|
|
172
|
+
if (existed && originalContent === newContent) {
|
|
173
|
+
return { success: true, reason: 'unchanged', unchanged: true };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const dirName = path.dirname(fullPath);
|
|
177
|
+
if (!fs.existsSync(dirName)) {
|
|
178
|
+
fs.mkdirSync(dirName, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fs.writeFileSync(fullPath, newContent, 'utf8');
|
|
182
|
+
|
|
183
|
+
const rollback = writeRollbackArtifact(dir, 'aider-patch', [filePath], {
|
|
184
|
+
[filePath]: originalContent,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const activity = writeActivityArtifact(dir, 'aider-patch', {
|
|
188
|
+
platform: 'aider',
|
|
189
|
+
patchedFiles: [filePath],
|
|
190
|
+
rollbackArtifact: rollback.relativePath,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
reason: 'patched',
|
|
196
|
+
unchanged: false,
|
|
197
|
+
rollbackArtifact: rollback.relativePath,
|
|
198
|
+
activityArtifact: activity.relativePath,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
MANAGED_START_MD,
|
|
204
|
+
MANAGED_END_MD,
|
|
205
|
+
MANAGED_START_YAML,
|
|
206
|
+
MANAGED_END_YAML,
|
|
207
|
+
extractManagedBlock,
|
|
208
|
+
upsertManagedBlock,
|
|
209
|
+
patchConventionsMd,
|
|
210
|
+
patchAiderConfYml,
|
|
211
|
+
detectMixedAiderRepo,
|
|
212
|
+
generatePatchPreview,
|
|
213
|
+
applyPatch,
|
|
214
|
+
};
|