codex-overleaf-link 1.1.1
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 +21 -0
- package/README.md +457 -0
- package/bin/codex-overleaf-link.mjs +223 -0
- package/extension/src/shared/agentTranscript.js +1175 -0
- package/extension/src/shared/auditRecords.js +568 -0
- package/extension/src/shared/compatibility.js +372 -0
- package/extension/src/shared/compileAdapter.js +176 -0
- package/extension/src/shared/governanceRules.js +252 -0
- package/extension/src/shared/i18n.js +565 -0
- package/extension/src/shared/models.js +106 -0
- package/extension/src/shared/otText.js +505 -0
- package/extension/src/shared/projectFiles.js +180 -0
- package/extension/src/shared/reviewing.js +99 -0
- package/extension/src/shared/sensitiveScan.js +116 -0
- package/extension/src/shared/sessionState.js +1084 -0
- package/extension/src/shared/staleGuard.js +150 -0
- package/extension/src/shared/storageDb.js +986 -0
- package/extension/src/shared/storageKeys.js +29 -0
- package/extension/src/shared/storageMigration.js +168 -0
- package/extension/src/shared/summary.js +248 -0
- package/extension/src/shared/undoOperations.js +369 -0
- package/native-host/src/codexArgs.js +43 -0
- package/native-host/src/codexHome.js +538 -0
- package/native-host/src/codexModels.js +247 -0
- package/native-host/src/codexPrompt.js +192 -0
- package/native-host/src/codexPromptAssembly.js +411 -0
- package/native-host/src/codexSessionRunner.js +1247 -0
- package/native-host/src/commandApproval.js +914 -0
- package/native-host/src/debugLog.js +78 -0
- package/native-host/src/diffEngine.js +247 -0
- package/native-host/src/index.js +132 -0
- package/native-host/src/launcher.js +81 -0
- package/native-host/src/localSkills.js +476 -0
- package/native-host/src/manifest.js +226 -0
- package/native-host/src/mirrorSensitiveScan.js +119 -0
- package/native-host/src/mirrorWorkspace.js +1019 -0
- package/native-host/src/nativeDoctor.js +826 -0
- package/native-host/src/nativeEnvironment.js +315 -0
- package/native-host/src/nativeHostPlatform.js +112 -0
- package/native-host/src/nativeMessaging.js +60 -0
- package/native-host/src/nativeQuotas.js +294 -0
- package/native-host/src/nativeResponseBudget.js +194 -0
- package/native-host/src/runtimeInstaller.js +357 -0
- package/native-host/src/taskRunner.js +3 -0
- package/native-host/src/taskRunnerRuntime.js +1083 -0
- package/native-host/src/textPatch.js +287 -0
- package/package.json +40 -0
- package/scripts/codex-json-agent.mjs +269 -0
- package/scripts/install-native-host.mjs +255 -0
- package/scripts/npm-package-files-v1.1.1.txt +52 -0
- package/scripts/uninstall-native-host.mjs +298 -0
- package/scripts/verify-npm-package.mjs +296 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { FALLBACK_MODELS } = require('../../extension/src/shared/models');
|
|
6
|
+
const {
|
|
7
|
+
getPluginCodexHome,
|
|
8
|
+
getUserCodexHome
|
|
9
|
+
} = require('./codexHome');
|
|
10
|
+
|
|
11
|
+
const DEFAULT_REASONING_EFFORTS = Object.freeze(['low', 'medium', 'high', 'xhigh']);
|
|
12
|
+
const DEFAULT_SPEED_TIERS = Object.freeze(['standard']);
|
|
13
|
+
|
|
14
|
+
function resolveCodexModels(params = {}, env = process.env) {
|
|
15
|
+
const cacheResult = resolveModelsFromCodexCache(params, env);
|
|
16
|
+
if (cacheResult) {
|
|
17
|
+
return cacheResult;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
models: buildFallbackModels(),
|
|
22
|
+
source: 'fallback',
|
|
23
|
+
fetchedAt: new Date().toISOString()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveModelsFromCodexCache(params, env) {
|
|
28
|
+
for (const cachePath of getModelsCacheCandidates(params, env)) {
|
|
29
|
+
const parsed = readModelsCache(cachePath);
|
|
30
|
+
if (!parsed) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const models = normalizeCacheModels(parsed.value);
|
|
34
|
+
if (!models.length) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
models,
|
|
39
|
+
source: 'codex-cache',
|
|
40
|
+
fetchedAt: getString(parsed.value?.fetched_at) || getString(parsed.value?.fetchedAt) || new Date().toISOString(),
|
|
41
|
+
clientVersion: getString(parsed.value?.client_version) || getString(parsed.value?.clientVersion) || ''
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getModelsCacheCandidates(params = {}, env = process.env) {
|
|
48
|
+
const candidates = [];
|
|
49
|
+
const addHome = home => {
|
|
50
|
+
if (!home || typeof home !== 'string') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
candidates.push(path.join(path.resolve(home), 'models_cache.json'));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
void params;
|
|
57
|
+
addHome(env.CODEX_HOME);
|
|
58
|
+
addHome(env.CODEX_OVERLEAF_USER_CODEX_HOME);
|
|
59
|
+
addHome(getUserCodexHome({ ...env, CODEX_HOME: '' }));
|
|
60
|
+
addHome(env.CODEX_OVERLEAF_CODEX_HOME);
|
|
61
|
+
addHome(getPluginCodexHome(env));
|
|
62
|
+
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
return candidates.filter(cachePath => {
|
|
65
|
+
const normalized = path.resolve(cachePath);
|
|
66
|
+
if (seen.has(normalized)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
seen.add(normalized);
|
|
70
|
+
return true;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readModelsCache(cachePath) {
|
|
75
|
+
try {
|
|
76
|
+
const stat = fs.statSync(cachePath);
|
|
77
|
+
if (!stat.isFile()) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
cachePath,
|
|
82
|
+
value: JSON.parse(fs.readFileSync(cachePath, 'utf8'))
|
|
83
|
+
};
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeCacheModels(cache) {
|
|
90
|
+
const rawModels = getRawModels(cache);
|
|
91
|
+
if (!rawModels.length) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const models = [];
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
for (const rawModel of rawModels) {
|
|
98
|
+
const model = normalizeCacheModel(rawModel);
|
|
99
|
+
if (!model || seen.has(model.id)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
seen.add(model.id);
|
|
103
|
+
models.push(model);
|
|
104
|
+
}
|
|
105
|
+
return models;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getRawModels(cache) {
|
|
109
|
+
if (Array.isArray(cache)) {
|
|
110
|
+
return cache;
|
|
111
|
+
}
|
|
112
|
+
if (!cache || typeof cache !== 'object') {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
if (Array.isArray(cache.models)) {
|
|
116
|
+
return cache.models;
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(cache.data)) {
|
|
119
|
+
return cache.data;
|
|
120
|
+
}
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeCacheModel(rawModel) {
|
|
125
|
+
if (typeof rawModel === 'string') {
|
|
126
|
+
const id = rawModel.trim();
|
|
127
|
+
return id ? buildModelEntry({ id, label: id }) : null;
|
|
128
|
+
}
|
|
129
|
+
if (!rawModel || typeof rawModel !== 'object' || rawModel.visibility === 'hide') {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const id = getString(rawModel.slug)
|
|
134
|
+
|| getString(rawModel.id)
|
|
135
|
+
|| getString(rawModel.model)
|
|
136
|
+
|| getString(rawModel.name);
|
|
137
|
+
if (!id) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const label = getString(rawModel.display_name)
|
|
142
|
+
|| getString(rawModel.displayName)
|
|
143
|
+
|| getString(rawModel.label)
|
|
144
|
+
|| getString(rawModel.name)
|
|
145
|
+
|| id;
|
|
146
|
+
const reasoningEfforts = normalizeReasoningEfforts(
|
|
147
|
+
rawModel.supported_reasoning_levels
|
|
148
|
+
|| rawModel.supportedReasoningLevels
|
|
149
|
+
|| rawModel.reasoning_efforts
|
|
150
|
+
|| rawModel.reasoningEfforts
|
|
151
|
+
);
|
|
152
|
+
const defaultReasoningEffort = getString(rawModel.default_reasoning_level)
|
|
153
|
+
|| getString(rawModel.defaultReasoningLevel)
|
|
154
|
+
|| getString(rawModel.default_reasoning_effort)
|
|
155
|
+
|| getString(rawModel.defaultReasoningEffort);
|
|
156
|
+
const speedTiers = normalizeSpeedTiers(
|
|
157
|
+
rawModel.additional_speed_tiers
|
|
158
|
+
|| rawModel.additionalSpeedTiers
|
|
159
|
+
|| rawModel.speed_tiers
|
|
160
|
+
|| rawModel.speedTiers
|
|
161
|
+
);
|
|
162
|
+
const defaultSpeedTier = getString(rawModel.default_speed_tier)
|
|
163
|
+
|| getString(rawModel.defaultSpeedTier);
|
|
164
|
+
|
|
165
|
+
return buildModelEntry({
|
|
166
|
+
id,
|
|
167
|
+
label,
|
|
168
|
+
reasoningEfforts,
|
|
169
|
+
defaultReasoningEffort,
|
|
170
|
+
speedTiers,
|
|
171
|
+
defaultSpeedTier
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildModelEntry({
|
|
176
|
+
id,
|
|
177
|
+
label,
|
|
178
|
+
reasoningEfforts = DEFAULT_REASONING_EFFORTS,
|
|
179
|
+
defaultReasoningEffort = 'medium',
|
|
180
|
+
speedTiers = DEFAULT_SPEED_TIERS,
|
|
181
|
+
defaultSpeedTier = 'standard'
|
|
182
|
+
}) {
|
|
183
|
+
const normalizedEfforts = normalizeReasoningEfforts(reasoningEfforts);
|
|
184
|
+
const normalizedDefault = getString(defaultReasoningEffort) || 'medium';
|
|
185
|
+
const normalizedSpeedTiers = normalizeSpeedTiers(speedTiers);
|
|
186
|
+
const normalizedDefaultSpeedTier = getString(defaultSpeedTier) || 'standard';
|
|
187
|
+
return {
|
|
188
|
+
id,
|
|
189
|
+
label,
|
|
190
|
+
reasoningEfforts: normalizedEfforts.length ? normalizedEfforts : DEFAULT_REASONING_EFFORTS.slice(),
|
|
191
|
+
defaultReasoningEffort: normalizedDefault,
|
|
192
|
+
speedTiers: normalizedSpeedTiers.length ? normalizedSpeedTiers : DEFAULT_SPEED_TIERS.slice(),
|
|
193
|
+
defaultSpeedTier: normalizedDefaultSpeedTier
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function normalizeReasoningEfforts(rawEfforts) {
|
|
198
|
+
if (!Array.isArray(rawEfforts)) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const seen = new Set();
|
|
203
|
+
const result = [];
|
|
204
|
+
for (const rawEffort of rawEfforts) {
|
|
205
|
+
const effort = getString(typeof rawEffort === 'string' ? rawEffort : rawEffort?.effort);
|
|
206
|
+
if (!effort || seen.has(effort)) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
seen.add(effort);
|
|
210
|
+
result.push(effort);
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function normalizeSpeedTiers(rawTiers) {
|
|
216
|
+
const tiers = ['standard'];
|
|
217
|
+
if (!Array.isArray(rawTiers)) {
|
|
218
|
+
return tiers;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const rawTier of rawTiers) {
|
|
222
|
+
const tier = getString(typeof rawTier === 'string' ? rawTier : rawTier?.tier || rawTier?.id || rawTier?.name);
|
|
223
|
+
if (tier && tier !== 'standard' && !tiers.includes(tier)) {
|
|
224
|
+
tiers.push(tier);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return tiers;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function buildFallbackModels() {
|
|
231
|
+
return FALLBACK_MODELS.map(model => buildModelEntry({
|
|
232
|
+
id: model.id,
|
|
233
|
+
label: model.label,
|
|
234
|
+
reasoningEfforts: DEFAULT_REASONING_EFFORTS,
|
|
235
|
+
defaultReasoningEffort: 'medium',
|
|
236
|
+
speedTiers: DEFAULT_SPEED_TIERS,
|
|
237
|
+
defaultSpeedTier: 'standard'
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getString(value) {
|
|
242
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = {
|
|
246
|
+
resolveCodexModels
|
|
247
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function buildCodexPrompt(request) {
|
|
4
|
+
const project = request.project || {};
|
|
5
|
+
const files = Array.isArray(project.files) ? project.files : [];
|
|
6
|
+
const focusFiles = normalizeFocusFiles(request.focusFiles || request.session?.focusFiles);
|
|
7
|
+
const orderedFiles = orderFilesByFocus(files, focusFiles);
|
|
8
|
+
const inventory = orderedFiles.map(file => `- ${file.path} (${String(file.content || '').length} chars)`).join('\n') || '- no files supplied';
|
|
9
|
+
|
|
10
|
+
return [
|
|
11
|
+
'You are generating edit operations for an Overleaf project.',
|
|
12
|
+
'',
|
|
13
|
+
`Mode: ${request.mode}`,
|
|
14
|
+
`Model: ${request.model || 'default'}`,
|
|
15
|
+
`Reasoning effort: ${request.reasoningEffort || 'default'}`,
|
|
16
|
+
`Session: ${request.session?.id || 'none'}`,
|
|
17
|
+
`Task: ${request.task}`,
|
|
18
|
+
`Active file: ${project.activePath || 'unknown'}`,
|
|
19
|
+
'',
|
|
20
|
+
'Recent session history:',
|
|
21
|
+
formatSessionHistory(request.session?.history),
|
|
22
|
+
'',
|
|
23
|
+
'Focus files:',
|
|
24
|
+
formatFocusFiles(focusFiles, files),
|
|
25
|
+
'',
|
|
26
|
+
'Files:',
|
|
27
|
+
inventory,
|
|
28
|
+
'',
|
|
29
|
+
'Return JSON only. Do not include Markdown fences.',
|
|
30
|
+
'Set status to "completed", "requires_task_confirmation", or "delete_plan_required".',
|
|
31
|
+
'Return userReport as natural user-facing language for the Overleaf panel.',
|
|
32
|
+
'Never put raw JSON, schema, stdout, stderr, or implementation details in userReport.',
|
|
33
|
+
'Always include notes as one concise user-facing sentence summarizing what changed or what was checked. For analysis-only or check-only tasks, set operations to [] and put concise findings in notes.',
|
|
34
|
+
'During the run, write short user-facing progress messages that say what you are checking, what you found, and what you will do next. Do not expose hidden chain-of-thought.',
|
|
35
|
+
'For Ask Mode, do not propose or return write operations. Return operations as [] and use notes for findings, explanations, and suggested next steps.',
|
|
36
|
+
'For userReport, include conclusion, checked files, findings, planned changes, applied changes, unchanged reason, and next step.',
|
|
37
|
+
'The schema is strict: every operation must include type, path, to, find, replace, replaceAll, content, and reason; use null for unused fields.',
|
|
38
|
+
'Use only these operation types:',
|
|
39
|
+
'- edit: { "type": "edit", "path": "...", "find": "...", "replace": "..." }',
|
|
40
|
+
'- edit replace all: { "type": "edit", "path": "...", "replaceAll": "..." }',
|
|
41
|
+
'- create: { "type": "create", "path": "...", "content": "..." }',
|
|
42
|
+
'- rename: { "type": "rename", "path": "...", "to": "..." }',
|
|
43
|
+
'- move: { "type": "move", "path": "...", "to": "..." }',
|
|
44
|
+
'- delete: { "type": "delete", "path": "...", "reason": "..." }',
|
|
45
|
+
'',
|
|
46
|
+
'Do not invent files that are not needed. Prefer minimal find/replace edits over replaceAll.',
|
|
47
|
+
'When focus files are provided, treat them as the primary context for reasoning and edits. Use other project files only when the task requires cross-file consistency, references, imports, or compilation context.',
|
|
48
|
+
'For Ask Mode, focus files still guide analysis, but never write.',
|
|
49
|
+
'For Confirm Mode, operations may include deletes because the user confirms the whole task summary before application.',
|
|
50
|
+
'For Auto Mode, deletes will be paused and confirmed separately by the bridge.',
|
|
51
|
+
'',
|
|
52
|
+
'Project file contents:',
|
|
53
|
+
...orderedFiles.map(file => [
|
|
54
|
+
`--- FILE: ${file.path} ---`,
|
|
55
|
+
String(file.content || ''),
|
|
56
|
+
`--- END FILE: ${file.path} ---`
|
|
57
|
+
].join('\n'))
|
|
58
|
+
].join('\n');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeFocusFiles(value) {
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
const files = [];
|
|
64
|
+
for (const item of Array.isArray(value) ? value : []) {
|
|
65
|
+
if (typeof item !== 'string') {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const path = item.trim();
|
|
69
|
+
if (!path || seen.has(path)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
seen.add(path);
|
|
73
|
+
files.push(path);
|
|
74
|
+
}
|
|
75
|
+
return files.slice(0, 5);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatFocusFiles(focusFiles, files) {
|
|
79
|
+
if (!focusFiles.length) {
|
|
80
|
+
return '- none (default to whole project)';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const fileByPath = new Map(files.map(file => [file.path, file]));
|
|
84
|
+
return focusFiles.map(path => {
|
|
85
|
+
const file = fileByPath.get(path);
|
|
86
|
+
if (!file) {
|
|
87
|
+
return `- ${path} (primary context, not present in current snapshot)`;
|
|
88
|
+
}
|
|
89
|
+
return `- ${path} (primary context, ${String(file.content || '').length} chars)`;
|
|
90
|
+
}).join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function orderFilesByFocus(files, focusFiles) {
|
|
94
|
+
const focusSet = new Set(focusFiles);
|
|
95
|
+
return [...files].sort((left, right) => {
|
|
96
|
+
const leftFocus = focusSet.has(left.path);
|
|
97
|
+
const rightFocus = focusSet.has(right.path);
|
|
98
|
+
if (leftFocus !== rightFocus) {
|
|
99
|
+
return leftFocus ? -1 : 1;
|
|
100
|
+
}
|
|
101
|
+
return String(left.path || '').localeCompare(String(right.path || ''));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatSessionHistory(history) {
|
|
106
|
+
if (!Array.isArray(history) || history.length === 0) {
|
|
107
|
+
return '- none';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return history.slice(-8).map((item, index) => {
|
|
111
|
+
const task = item.task || 'untitled task';
|
|
112
|
+
const result = item.result || item.status || 'no result recorded';
|
|
113
|
+
return `${index + 1}. ${task}: ${result}`;
|
|
114
|
+
}).join('\n');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildOutputSchema() {
|
|
118
|
+
return {
|
|
119
|
+
type: 'object',
|
|
120
|
+
additionalProperties: false,
|
|
121
|
+
required: ['status', 'operations', 'notes', 'userReport'],
|
|
122
|
+
properties: {
|
|
123
|
+
status: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
enum: ['requires_task_confirmation', 'completed', 'delete_plan_required']
|
|
126
|
+
},
|
|
127
|
+
notes: {
|
|
128
|
+
type: 'string'
|
|
129
|
+
},
|
|
130
|
+
userReport: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
additionalProperties: false,
|
|
133
|
+
required: [
|
|
134
|
+
'conclusion',
|
|
135
|
+
'checked',
|
|
136
|
+
'findings',
|
|
137
|
+
'plannedChanges',
|
|
138
|
+
'appliedChanges',
|
|
139
|
+
'unchangedReason',
|
|
140
|
+
'nextStep'
|
|
141
|
+
],
|
|
142
|
+
properties: {
|
|
143
|
+
conclusion: { type: 'string' },
|
|
144
|
+
checked: {
|
|
145
|
+
type: 'array',
|
|
146
|
+
items: { type: 'string' }
|
|
147
|
+
},
|
|
148
|
+
findings: {
|
|
149
|
+
type: 'array',
|
|
150
|
+
items: { type: 'string' }
|
|
151
|
+
},
|
|
152
|
+
plannedChanges: {
|
|
153
|
+
type: 'array',
|
|
154
|
+
items: { type: 'string' }
|
|
155
|
+
},
|
|
156
|
+
appliedChanges: {
|
|
157
|
+
type: 'array',
|
|
158
|
+
items: { type: 'string' }
|
|
159
|
+
},
|
|
160
|
+
unchangedReason: { type: 'string' },
|
|
161
|
+
nextStep: { type: 'string' }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
operations: {
|
|
165
|
+
type: 'array',
|
|
166
|
+
items: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
additionalProperties: false,
|
|
169
|
+
required: ['type', 'path', 'to', 'find', 'replace', 'replaceAll', 'content', 'reason'],
|
|
170
|
+
properties: {
|
|
171
|
+
type: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
enum: ['edit', 'create', 'rename', 'move', 'delete']
|
|
174
|
+
},
|
|
175
|
+
path: { type: 'string' },
|
|
176
|
+
to: { type: ['string', 'null'] },
|
|
177
|
+
find: { type: ['string', 'null'] },
|
|
178
|
+
replace: { type: ['string', 'null'] },
|
|
179
|
+
replaceAll: { type: ['string', 'null'] },
|
|
180
|
+
content: { type: ['string', 'null'] },
|
|
181
|
+
reason: { type: ['string', 'null'] }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
buildCodexPrompt,
|
|
191
|
+
buildOutputSchema
|
|
192
|
+
};
|