@technomoron/agent-run 0.99.24 → 0.99.26

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.
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isProfileConfigured = isProfileConfigured;
4
+ exports.createProfileMarker = createProfileMarker;
5
+ exports.ensureRootDefaultsFile = ensureRootDefaultsFile;
6
+ exports.createDefaultLocalFile = createDefaultLocalFile;
7
+ exports.convertLegacyProfileIfNeeded = convertLegacyProfileIfNeeded;
8
+ exports.loadManifest = loadManifest;
9
+ exports.normalizeManifest = normalizeManifest;
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const jsonc_parser_1 = require("jsonc-parser");
13
+ const constants_1 = require("./constants");
14
+ const defaults_1 = require("./defaults");
15
+ const utils_1 = require("./utils");
16
+ function isProfileConfigured(agentDir) {
17
+ return [constants_1.MANIFEST_FILE_NAME, constants_1.LOCAL_TEMPLATE_FILE_NAME, 'AGENTS-MODS.md'].some((name) => fs.existsSync(path.join(agentDir, name)));
18
+ }
19
+ function createProfileMarker(agentDir) {
20
+ const manifestPath = path.join(agentDir, constants_1.MANIFEST_FILE_NAME);
21
+ if (fs.existsSync(manifestPath)) {
22
+ return;
23
+ }
24
+ fs.mkdirSync(agentDir, { recursive: true });
25
+ fs.writeFileSync(manifestPath, '{}\n', 'utf8');
26
+ (0, utils_1.verbose)(`created ${manifestPath}`);
27
+ }
28
+ function ensureRootDefaultsFile(configRoot) {
29
+ const defaultsPath = path.join(configRoot, constants_1.CONFIG_ROOT_DEFAULTS_FILE_NAME);
30
+ if (fs.existsSync(defaultsPath)) {
31
+ return;
32
+ }
33
+ fs.mkdirSync(configRoot, { recursive: true });
34
+ fs.writeFileSync(defaultsPath, (0, defaults_1.stringifyRootDefaults)(), 'utf8');
35
+ (0, utils_1.verbose)(`created ${defaultsPath}`);
36
+ }
37
+ function createDefaultLocalFile(agentDir) {
38
+ const localPath = path.join(agentDir, constants_1.LOCAL_TEMPLATE_FILE_NAME);
39
+ if (fs.existsSync(localPath)) {
40
+ return localPath;
41
+ }
42
+ const legacyPath = path.join(agentDir, 'AGENTS-MODS.md');
43
+ const content = fs.existsSync(legacyPath) ? fs.readFileSync(legacyPath, 'utf8') : (0, defaults_1.defaultLocalTemplate)();
44
+ fs.mkdirSync(agentDir, { recursive: true });
45
+ fs.writeFileSync(localPath, content, 'utf8');
46
+ (0, utils_1.verbose)(`created ${localPath}`);
47
+ return localPath;
48
+ }
49
+ function convertLegacyProfileIfNeeded(agentDir) {
50
+ const legacyPath = path.join(agentDir, 'AGENTS-MODS.md');
51
+ const localPath = path.join(agentDir, constants_1.LOCAL_TEMPLATE_FILE_NAME);
52
+ if (fs.existsSync(legacyPath) && !fs.existsSync(localPath)) {
53
+ fs.mkdirSync(agentDir, { recursive: true });
54
+ fs.writeFileSync(localPath, fs.readFileSync(legacyPath, 'utf8'), 'utf8');
55
+ (0, utils_1.verbose)(`converted ${legacyPath} -> ${localPath}`);
56
+ }
57
+ }
58
+ function loadManifest(configRoot, agentDir, profile, trace) {
59
+ const rootDefaultsPath = path.join(configRoot, constants_1.CONFIG_ROOT_DEFAULTS_FILE_NAME);
60
+ const profileManifestPath = path.join(agentDir, constants_1.MANIFEST_FILE_NAME);
61
+ const rootDefaults = fs.existsSync(rootDefaultsPath) ? parseManifestFile(rootDefaultsPath, trace) : {};
62
+ if (rootDefaults.profile !== undefined) {
63
+ throw new Error(`${rootDefaultsPath} must not set profile; profiles are inferred from project mapping`);
64
+ }
65
+ let profileManifest = fs.existsSync(profileManifestPath) ? parseManifestFile(profileManifestPath, trace) : {};
66
+ if (!fs.existsSync(profileManifestPath)) {
67
+ profileManifest = applyLegacyIncludeFallback(agentDir, profileManifest);
68
+ }
69
+ const builtIn = (0, defaults_1.defaultManifest)(profile);
70
+ const mergedDefaults = mergeManifest(builtIn, rootDefaults, false);
71
+ return mergeManifest(mergedDefaults, profileManifest, true, profile);
72
+ }
73
+ function parseManifestFile(filePath, trace) {
74
+ trace?.sourceFiles.add(filePath);
75
+ const text = fs.readFileSync(filePath, 'utf8');
76
+ const errors = [];
77
+ const parsed = (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true });
78
+ if (errors.length > 0) {
79
+ const first = errors[0];
80
+ const detail = first ? `${(0, jsonc_parser_1.printParseErrorCode)(first.error)} at offset ${first.offset}` : 'unknown JSONC parse error';
81
+ throw new Error(`invalid JSONC in ${filePath}: ${detail}`);
82
+ }
83
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
84
+ throw new Error(`manifest must be an object: ${filePath}`);
85
+ }
86
+ return parsed;
87
+ }
88
+ function applyLegacyIncludeFallback(agentDir, manifest) {
89
+ const localPath = path.join(agentDir, constants_1.LOCAL_TEMPLATE_FILE_NAME);
90
+ const legacyPath = path.join(agentDir, 'AGENTS-MODS.md');
91
+ if (!fs.existsSync(localPath) && fs.existsSync(legacyPath)) {
92
+ return { ...manifest, agent: { ...manifest.agent, includes: ['{{ profile }}/AGENTS-MODS.md'] } };
93
+ }
94
+ return manifest;
95
+ }
96
+ function mergeManifest(base, override, allowProfile, inferredProfile) {
97
+ const merged = {
98
+ ...base,
99
+ ...override,
100
+ agent: { ...base.agent, ...override.agent },
101
+ skills: mergeSkills(base.skills, override.skills),
102
+ tools: { ...base.tools, ...override.tools },
103
+ guardrails: { ...base.guardrails, ...override.guardrails },
104
+ paths: { ...base.paths, ...override.paths }
105
+ };
106
+ if (!allowProfile) {
107
+ merged.profile = base.profile;
108
+ }
109
+ else {
110
+ merged.profile = override.profile ?? inferredProfile ?? base.profile;
111
+ }
112
+ return merged;
113
+ }
114
+ function mergeSkills(base, override) {
115
+ if (override === undefined) {
116
+ return base;
117
+ }
118
+ if (Array.isArray(override)) {
119
+ return [...override];
120
+ }
121
+ const baseObject = asSkillObject(base);
122
+ return {
123
+ install: override.install ?? baseObject.install,
124
+ overrides: { ...baseObject.overrides, ...override.overrides }
125
+ };
126
+ }
127
+ function asSkillObject(skills) {
128
+ if (Array.isArray(skills)) {
129
+ return { install: skills, overrides: {} };
130
+ }
131
+ return {
132
+ install: skills?.install ?? [],
133
+ overrides: skills?.overrides ?? {}
134
+ };
135
+ }
136
+ function normalizeManifest(manifest, profile) {
137
+ const fallback = (0, defaults_1.defaultManifest)(profile);
138
+ const skills = asSkillObject(manifest.skills ?? fallback.skills);
139
+ const fallbackPaths = fallback.paths ?? {};
140
+ const paths = manifest.paths ?? {};
141
+ return {
142
+ profile: manifest.profile ?? profile,
143
+ kind: manifest.kind ?? fallback.kind ?? 'code',
144
+ agent: {
145
+ base: manifest.agent?.base ?? fallback.agent?.base ?? 'global/agents/code.md.njk',
146
+ includes: manifest.agent?.includes ?? [`{{ profile }}/${constants_1.LOCAL_TEMPLATE_FILE_NAME}`]
147
+ },
148
+ skills: {
149
+ install: normalizeInstalledSkills(skills.install),
150
+ overrides: skills.overrides
151
+ },
152
+ tools: {
153
+ codex: manifest.tools?.codex ?? true,
154
+ claude: manifest.tools?.claude ?? true
155
+ },
156
+ checks: manifest.checks ?? fallback.checks ?? [],
157
+ guardrails: {
158
+ blockGitWrite: manifest.guardrails?.blockGitWrite ?? true,
159
+ blockPublish: manifest.guardrails?.blockPublish ?? true,
160
+ blockGithubRelease: manifest.guardrails?.blockGithubRelease ?? true,
161
+ forbidRepoAiFiles: manifest.guardrails?.forbidRepoAiFiles ?? true
162
+ },
163
+ paths: {
164
+ changesFile: paths.changesFile ?? fallbackPaths.changesFile ?? '{{ projectRoot }}/CHANGES',
165
+ reviewDir: normalizeLegacyReviewPath(paths.reviewDir ?? fallbackPaths.reviewDir ?? '{{ profileDir }}/reviews'),
166
+ reviewFile: normalizeLegacyReviewPath(paths.reviewFile ?? fallbackPaths.reviewFile ?? '{{ profileDir }}/reviews/REVIEW-{{ date }}.md'),
167
+ reviewConsolidatedFile: paths.reviewConsolidatedFile ?? fallbackPaths.reviewConsolidatedFile,
168
+ memoriesDir: paths.memoriesDir ?? fallbackPaths.memoriesDir ?? '{{ agentDir }}/memories'
169
+ }
170
+ };
171
+ }
172
+ function normalizeInstalledSkills(names) {
173
+ return [...new Set(names.map(normalizeSkillName))];
174
+ }
175
+ function normalizeSkillName(name) {
176
+ const normalized = name === 'code-review' ? 'code-review-organizer' : name;
177
+ if (!/^[A-Za-z0-9._-]+$/.test(normalized)) {
178
+ throw new Error(`invalid skill name: ${name}`);
179
+ }
180
+ return normalized;
181
+ }
182
+ function normalizeLegacyReviewPath(value) {
183
+ if (value === '{{ agentDir }}/reviews') {
184
+ return '{{ profileDir }}/reviews';
185
+ }
186
+ if (value === '{{ agentDir }}/reviews/REVIEW-{{ date }}.md') {
187
+ return '{{ profileDir }}/reviews/REVIEW-{{ date }}.md';
188
+ }
189
+ return value;
190
+ }
package/dist/model.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findRealBinary = findRealBinary;
4
+ exports.execTool = execTool;
5
+ exports.execCommand = execCommand;
6
+ exports.openEditor = openEditor;
7
+ exports.parseEditorCommand = parseEditorCommand;
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const constants_1 = require("./constants");
11
+ const utils_1 = require("./utils");
12
+ const crossSpawn = require('cross-spawn');
13
+ function findRealBinary(tool) {
14
+ const pathDirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
15
+ const currentScriptArg = process.argv[1];
16
+ const currentScript = currentScriptArg ? fs.realpathSync(currentScriptArg) : '';
17
+ const wrappers = wrapperCandidates(tool);
18
+ for (const dir of pathDirs) {
19
+ for (const extension of (0, utils_1.getExecutableExtensions)(tool)) {
20
+ const candidate = path.join(dir, `${tool}${extension}`);
21
+ if (!fs.existsSync(candidate) || !(0, utils_1.isExecutable)(candidate)) {
22
+ continue;
23
+ }
24
+ const resolvedCandidate = safeRealpath(candidate);
25
+ if (resolvedCandidate === currentScript || wrappers.has(resolvedCandidate)) {
26
+ continue;
27
+ }
28
+ if (isAgentRunRedirectShim(candidate)) {
29
+ (0, utils_1.verbose)(`skip ${tool} redirect shim: ${candidate}`);
30
+ continue;
31
+ }
32
+ return candidate;
33
+ }
34
+ }
35
+ (0, utils_1.fail)(`no ${tool} binary found in PATH`);
36
+ }
37
+ function wrapperCandidates(tool) {
38
+ const names = [
39
+ tool,
40
+ `${tool}.js`,
41
+ `${tool}.cmd`,
42
+ `${tool}.bat`,
43
+ `${tool}.exe`,
44
+ 'agent-run',
45
+ 'agent-run.js',
46
+ 'agent-run.cmd',
47
+ 'agent-run.bat',
48
+ 'agent-run.exe'
49
+ ];
50
+ return new Set(names.map((name) => path.join(__dirname, name)).filter(fs.existsSync).map(safeRealpath));
51
+ }
52
+ function safeRealpath(filePath) {
53
+ try {
54
+ return fs.realpathSync(filePath);
55
+ }
56
+ catch {
57
+ return filePath;
58
+ }
59
+ }
60
+ function isAgentRunRedirectShim(filePath) {
61
+ try {
62
+ const handle = fs.openSync(filePath, 'r');
63
+ try {
64
+ const buffer = Buffer.alloc(4096);
65
+ const bytesRead = fs.readSync(handle, buffer, 0, buffer.length, 0);
66
+ return buffer.subarray(0, bytesRead).toString('utf8').includes('Run agent-run instead');
67
+ }
68
+ finally {
69
+ fs.closeSync(handle);
70
+ }
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ function execTool(command, args) {
77
+ (0, utils_1.verbose)(`exec tool: ${(0, utils_1.formatCommand)(command, args)}`);
78
+ execCommand(command, args);
79
+ }
80
+ function execCommand(command, args, env, cwd, onExit) {
81
+ (0, utils_1.verbose)(`spawn: ${(0, utils_1.formatCommand)(command, args)} cwd=${cwd ?? process.cwd()}`);
82
+ const child = crossSpawn(command, args, { cwd, env, stdio: 'inherit' });
83
+ child.on('exit', (code, signal) => {
84
+ if (signal) {
85
+ process.kill(process.pid, signal);
86
+ return;
87
+ }
88
+ const originalCode = code === null ? 1 : code;
89
+ process.exit(onExit ? onExit(originalCode) : originalCode);
90
+ });
91
+ child.on('error', (error) => (0, utils_1.fail)(error.message));
92
+ }
93
+ function openEditor(filePath) {
94
+ const configuredEditor = process.env.VISUAL?.trim() || process.env.EDITOR?.trim();
95
+ if (configuredEditor) {
96
+ runConfiguredEditor(configuredEditor, filePath);
97
+ return;
98
+ }
99
+ const vscodeCommand = findVsCodeEditorCommand();
100
+ if (vscodeCommand !== null) {
101
+ execCommand(vscodeCommand, ['--reuse-window', filePath]);
102
+ return;
103
+ }
104
+ const fallbackEditor = findFallbackEditor();
105
+ if (fallbackEditor !== null) {
106
+ execCommand(fallbackEditor, [filePath]);
107
+ return;
108
+ }
109
+ if (constants_1.IS_WINDOWS) {
110
+ execCommand('explorer.exe', [filePath]);
111
+ }
112
+ else if (process.platform === 'darwin') {
113
+ execCommand('open', [filePath]);
114
+ }
115
+ else {
116
+ execCommand('xdg-open', [filePath]);
117
+ }
118
+ }
119
+ function runConfiguredEditor(commandLine, filePath) {
120
+ let editorArgs;
121
+ try {
122
+ editorArgs = parseEditorCommand(commandLine);
123
+ }
124
+ catch (error) {
125
+ (0, utils_1.fail)(`invalid editor command: ${(0, utils_1.formatError)(error)}`);
126
+ }
127
+ const [command, ...args] = editorArgs;
128
+ if (!command) {
129
+ (0, utils_1.fail)('invalid editor command: command is empty');
130
+ }
131
+ execCommand(command, [...args, filePath]);
132
+ }
133
+ function parseEditorCommand(commandLine) {
134
+ const args = [];
135
+ let current = '';
136
+ let quote = null;
137
+ let tokenStarted = false;
138
+ for (let index = 0; index < commandLine.length; index += 1) {
139
+ const character = commandLine[index] ?? '';
140
+ if (quote !== null) {
141
+ if (character === quote) {
142
+ quote = null;
143
+ continue;
144
+ }
145
+ if (character === '\\' && quote === '"') {
146
+ const next = commandLine[index + 1];
147
+ if (next === '"' || next === '\\') {
148
+ current += next;
149
+ index += 1;
150
+ continue;
151
+ }
152
+ }
153
+ current += character;
154
+ continue;
155
+ }
156
+ if (character === "'" || character === '"') {
157
+ quote = character;
158
+ tokenStarted = true;
159
+ continue;
160
+ }
161
+ if (/\s/.test(character)) {
162
+ if (tokenStarted) {
163
+ args.push(current);
164
+ current = '';
165
+ tokenStarted = false;
166
+ }
167
+ continue;
168
+ }
169
+ if (character === '\\') {
170
+ const next = commandLine[index + 1];
171
+ if (next !== undefined && (/\s/.test(next) || next === "'" || next === '"' || next === '\\')) {
172
+ current += next;
173
+ index += 1;
174
+ tokenStarted = true;
175
+ continue;
176
+ }
177
+ }
178
+ current += character;
179
+ tokenStarted = true;
180
+ }
181
+ if (quote !== null) {
182
+ throw new Error(`unterminated ${quote} quote`);
183
+ }
184
+ if (tokenStarted) {
185
+ args.push(current);
186
+ }
187
+ if (args.length === 0) {
188
+ throw new Error('command is empty');
189
+ }
190
+ return args;
191
+ }
192
+ function findVsCodeEditorCommand() {
193
+ if (!isRunningInVsCodeTerminal()) {
194
+ return null;
195
+ }
196
+ return ['code', 'codium'].map(utils_1.findExecutable).find((command) => command !== null) ?? null;
197
+ }
198
+ function isRunningInVsCodeTerminal() {
199
+ const termProgram = process.env.TERM_PROGRAM?.trim().toLowerCase();
200
+ return (termProgram === 'vscode' ||
201
+ Boolean(process.env.VSCODE_GIT_IPC_HANDLE) ||
202
+ Boolean(process.env.VSCODE_IPC_HOOK) ||
203
+ Boolean(process.env.VSCODE_IPC_HOOK_CLI));
204
+ }
205
+ function findFallbackEditor() {
206
+ const candidates = constants_1.IS_WINDOWS ? ['notepad.exe'] : ['joe', 'sensible-editor', 'editor', 'nano', 'nvim', 'vim', 'vi'];
207
+ return candidates.map(utils_1.findExecutable).find((command) => command !== null) ?? null;
208
+ }