@nram-ai/setup-agents 0.0.1-pre.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.
Files changed (84) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +109 -0
  3. package/dist/helpers/configure/amp.d.ts +9 -0
  4. package/dist/helpers/configure/amp.js +49 -0
  5. package/dist/helpers/configure/amp.js.map +1 -0
  6. package/dist/helpers/configure/antigravity.d.ts +12 -0
  7. package/dist/helpers/configure/antigravity.js +74 -0
  8. package/dist/helpers/configure/antigravity.js.map +1 -0
  9. package/dist/helpers/configure/claude_code.d.ts +9 -0
  10. package/dist/helpers/configure/claude_code.js +119 -0
  11. package/dist/helpers/configure/claude_code.js.map +1 -0
  12. package/dist/helpers/configure/codex.d.ts +19 -0
  13. package/dist/helpers/configure/codex.js +76 -0
  14. package/dist/helpers/configure/codex.js.map +1 -0
  15. package/dist/helpers/configure/copilot.d.ts +11 -0
  16. package/dist/helpers/configure/copilot.js +64 -0
  17. package/dist/helpers/configure/copilot.js.map +1 -0
  18. package/dist/helpers/configure/cursor.d.ts +26 -0
  19. package/dist/helpers/configure/cursor.js +90 -0
  20. package/dist/helpers/configure/cursor.js.map +1 -0
  21. package/dist/helpers/configure/droid.d.ts +18 -0
  22. package/dist/helpers/configure/droid.js +70 -0
  23. package/dist/helpers/configure/droid.js.map +1 -0
  24. package/dist/helpers/configure/grok.d.ts +25 -0
  25. package/dist/helpers/configure/grok.js +93 -0
  26. package/dist/helpers/configure/grok.js.map +1 -0
  27. package/dist/helpers/configure/hermes.d.ts +22 -0
  28. package/dist/helpers/configure/hermes.js +107 -0
  29. package/dist/helpers/configure/hermes.js.map +1 -0
  30. package/dist/helpers/configure/junie.d.ts +10 -0
  31. package/dist/helpers/configure/junie.js +52 -0
  32. package/dist/helpers/configure/junie.js.map +1 -0
  33. package/dist/helpers/configure/kilo.d.ts +19 -0
  34. package/dist/helpers/configure/kilo.js +110 -0
  35. package/dist/helpers/configure/kilo.js.map +1 -0
  36. package/dist/helpers/configure/kimi.d.ts +10 -0
  37. package/dist/helpers/configure/kimi.js +75 -0
  38. package/dist/helpers/configure/kimi.js.map +1 -0
  39. package/dist/helpers/configure/kiro.d.ts +25 -0
  40. package/dist/helpers/configure/kiro.js +77 -0
  41. package/dist/helpers/configure/kiro.js.map +1 -0
  42. package/dist/helpers/configure/openclaw.d.ts +10 -0
  43. package/dist/helpers/configure/openclaw.js +72 -0
  44. package/dist/helpers/configure/openclaw.js.map +1 -0
  45. package/dist/helpers/configure/opencode.d.ts +18 -0
  46. package/dist/helpers/configure/opencode.js +72 -0
  47. package/dist/helpers/configure/opencode.js.map +1 -0
  48. package/dist/helpers/configure/pi.d.ts +10 -0
  49. package/dist/helpers/configure/pi.js +60 -0
  50. package/dist/helpers/configure/pi.js.map +1 -0
  51. package/dist/helpers/configure/trae.d.ts +11 -0
  52. package/dist/helpers/configure/trae.js +63 -0
  53. package/dist/helpers/configure/trae.js.map +1 -0
  54. package/dist/helpers/configure/vibe.d.ts +24 -0
  55. package/dist/helpers/configure/vibe.js +106 -0
  56. package/dist/helpers/configure/vibe.js.map +1 -0
  57. package/dist/helpers/configure/vscode.d.ts +18 -0
  58. package/dist/helpers/configure/vscode.js +81 -0
  59. package/dist/helpers/configure/vscode.js.map +1 -0
  60. package/dist/helpers/harnesses.d.ts +8 -0
  61. package/dist/helpers/harnesses.js +161 -0
  62. package/dist/helpers/harnesses.js.map +1 -0
  63. package/dist/helpers/instructions.d.ts +63 -0
  64. package/dist/helpers/instructions.js +175 -0
  65. package/dist/helpers/instructions.js.map +1 -0
  66. package/dist/helpers/paths.d.ts +130 -0
  67. package/dist/helpers/paths.js +211 -0
  68. package/dist/helpers/paths.js.map +1 -0
  69. package/dist/helpers/upsert.d.ts +197 -0
  70. package/dist/helpers/upsert.js +428 -0
  71. package/dist/helpers/upsert.js.map +1 -0
  72. package/dist/helpers/url.d.ts +15 -0
  73. package/dist/helpers/url.js +51 -0
  74. package/dist/helpers/url.js.map +1 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.js +105 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/setup.d.ts +17 -0
  79. package/dist/setup.js +296 -0
  80. package/dist/setup.js.map +1 -0
  81. package/dist/types.d.ts +67 -0
  82. package/dist/types.js +50 -0
  83. package/dist/types.js.map +1 -0
  84. package/package.json +76 -0
@@ -0,0 +1,197 @@
1
+ import { ActionResult } from '../types';
2
+ /**
3
+ * Identifies our SessionStart hook entry among the user's hooks, across every
4
+ * historical command format
5
+ */
6
+ export declare const SESSION_START_MARKER = "@nram-ai/setup-agents";
7
+ /**
8
+ * The shell command injected as a SessionStart hook; its stdout (the agent
9
+ * instructions, fetched from the configured nram server) is added to the
10
+ * model's context by the harness
11
+ *
12
+ * @param base_url the normalized nram base URL the hook fetches from
13
+ */
14
+ export declare const session_start_command: (base_url: string) => string;
15
+ export type MarkerStyle = 'hash' | 'html';
16
+ /**
17
+ * Returns whether the text already carries our marker-fenced block
18
+ *
19
+ * @param text the file content
20
+ * @param style the marker comment style
21
+ */
22
+ export declare const has_marker_block: (text: string, style: MarkerStyle) => boolean;
23
+ /**
24
+ * Reads a text file, returning an empty string when the file does not exist
25
+ *
26
+ * @param path the file to read
27
+ */
28
+ export declare const read_text: (path: string) => string;
29
+ /**
30
+ * Writes a text file, creating parent directories as needed
31
+ *
32
+ * @param path the file to write
33
+ * @param text the file content
34
+ */
35
+ export declare const write_text: (path: string, text: string) => void;
36
+ export type JsonReadResult = {
37
+ ok: true;
38
+ value: any;
39
+ existed: boolean;
40
+ } | {
41
+ ok: false;
42
+ error: string;
43
+ };
44
+ /**
45
+ * Reads and parses a JSON file. A missing file is NOT an error (it yields
46
+ * `value: undefined`); an existing file that cannot be parsed is, and the
47
+ * caller MUST leave that file untouched and fall back to a manual snippet
48
+ *
49
+ * @param path the file to read
50
+ */
51
+ export declare const read_json: (path: string) => JsonReadResult;
52
+ /**
53
+ * Serializes a value as 2-space-indented JSON (with a trailing newline)
54
+ * and writes it, creating parent directories as needed
55
+ *
56
+ * @param path the file to write
57
+ * @param value the value to serialize
58
+ */
59
+ export declare const write_json: (path: string, value: unknown) => void;
60
+ /**
61
+ * Inserts a marker-fenced block into a body of text, or replaces the existing
62
+ * fenced block in place. Content outside the markers is never modified, and
63
+ * re-applying the same block is byte-stable
64
+ *
65
+ * @param text the current file content (may be empty)
66
+ * @param block the block content to place between the markers
67
+ * @param style the marker comment style for the target file type
68
+ * @param position where to place a NEW block; ignored when replacing
69
+ * @returns the updated text and whether anything changed
70
+ */
71
+ export declare const upsert_marker_block: (text: string, block: string, style: MarkerStyle, position?: "prepend" | "append") => {
72
+ text: string;
73
+ changed: boolean;
74
+ };
75
+ /**
76
+ * Upserts our SessionStart hook entry into a hooks-style configuration object
77
+ * (the SessionStart shape is identical between Claude Code's settings.json
78
+ * and Codex's hooks.json). An existing entry referencing `@nram-ai/setup-agents` is
79
+ * replaced in place when its command differs (a different server URL, or the
80
+ * URL-less command written by older package versions), left alone when it
81
+ * already matches, and appended when absent. Unrelated hooks are never touched
82
+ *
83
+ * @param config the parsed configuration object (mutated in place)
84
+ * @param base_url the normalized nram base URL embedded in the hook command
85
+ * @returns the configuration and whether it changed
86
+ */
87
+ export declare const upsert_session_start_hook: (config: any, base_url: string) => {
88
+ config: any;
89
+ changed: boolean;
90
+ };
91
+ /**
92
+ * Upserts our SessionStart hook into a hooks-style JSON file (Claude Code's
93
+ * settings.json and Codex's hooks.json share the shape) and reports the
94
+ * outcome; an unparseable file is left untouched
95
+ *
96
+ * @param path the hooks file to update
97
+ * @param base_url the normalized nram base URL embedded in the hook command
98
+ */
99
+ export declare const upsert_hooks_json: (path: string, base_url: string) => ActionResult;
100
+ export type JsonUpsertResult = {
101
+ kind: 'skipped' | 'written' | 'updated';
102
+ } | {
103
+ kind: 'manual';
104
+ error: string;
105
+ };
106
+ /**
107
+ * Merges a value into a JSON file at the given key path (creating intermediate
108
+ * objects as needed), writing only when the value differs. An unparseable
109
+ * existing file is left untouched and reported for manual handling
110
+ *
111
+ * @param path the JSON file to update
112
+ * @param key_path the nested key path, e.g. ['mcpServers', 'nram']
113
+ * @param entry the value to place at the key path
114
+ * @param fallback the configuration to start from when the file is missing
115
+ */
116
+ export declare const upsert_json_path: (path: string, key_path: string[], entry: unknown, fallback?: any) => JsonUpsertResult;
117
+ /**
118
+ * The manual follow-up shown whenever a configuration references the
119
+ * NRAM_API_KEY environment variable instead of embedding the key
120
+ */
121
+ export declare const api_key_manual_result: () => ActionResult;
122
+ /**
123
+ * The manual follow-up shown when a tool's configuration has no verified
124
+ * environment-variable substitution, so the Authorization header must be
125
+ * added by hand rather than risking a literal key on disk
126
+ *
127
+ * @param path the configuration file the header belongs in
128
+ */
129
+ export declare const api_key_header_manual_result: (path: string) => ActionResult;
130
+ export interface McpJsonOptions {
131
+ /** Starting configuration when the file is missing */
132
+ fallback?: any;
133
+ /** Replaces the parse-error reason in the manual detail (e.g. a JSONC caveat) */
134
+ parse_hint?: string;
135
+ /** Appended to the written/updated detail */
136
+ note?: string;
137
+ /** The key path addresses an array merged by entry name instead of a keyed object */
138
+ array?: boolean;
139
+ }
140
+ /**
141
+ * Merges an MCP server entry into a JSON configuration and maps the outcome
142
+ * onto the standard `MCP registration` ActionResult, including the manual
143
+ * snippet for unparseable files; this is the shared report flow every
144
+ * JSON-configured harness uses
145
+ *
146
+ * @param path the JSON file to update
147
+ * @param key_path the nested key path (the leaf is the entry key, or the array itself with `array`)
148
+ * @param entry the entry value
149
+ * @param options report and merge options
150
+ */
151
+ export declare const mcp_json_result: (path: string, key_path: string[], entry: any, options?: McpJsonOptions) => ActionResult;
152
+ /**
153
+ * Upserts a marker-fenced configuration block into a structured text file
154
+ * (TOML, YAML): parse the existing content (manual on failure), let the
155
+ * caller veto via a conflict check, apply the marker upsert, validate that
156
+ * the result re-parses with the expected entry, and only then write. The
157
+ * file is never re-serialized as a whole, so user comments survive
158
+ *
159
+ * @param path the configuration file
160
+ * @param block the marker-fenced block content
161
+ * @param parse the format parser (throws on invalid input)
162
+ * @param format the format name for messaging
163
+ * @param conflict returns a result to report instead of editing (e.g. the key exists outside our markers)
164
+ * @param valid post-edit check that the entry is present in the re-parsed file
165
+ */
166
+ export declare const upsert_block_with_validation: (path: string, block: string, parse: (text: string) => any, format: string, conflict: (parsed: any, text: string) => ActionResult | undefined, valid: (parsed: any) => boolean) => ActionResult;
167
+ /**
168
+ * Merges an entry into an array-shaped JSON configuration (e.g. Factory's
169
+ * `servers: []`), keyed by a name field: replace in place when an element
170
+ * with the same name exists and differs, append when absent, skip when
171
+ * identical. An unparseable existing file is left untouched
172
+ *
173
+ * @param path the JSON file to update
174
+ * @param array_key_path the nested key path holding the array
175
+ * @param entry the element to place (must carry the name key)
176
+ * @param name_key the field that identifies elements (default `name`)
177
+ */
178
+ export declare const upsert_json_array_entry: (path: string, array_key_path: string[], entry: any, name_key?: string) => JsonUpsertResult;
179
+ /**
180
+ * Writes a file the tool owns outright (rule files, steering docs,
181
+ * instruction files): compare, write when different, and report
182
+ *
183
+ * @param path the file to write
184
+ * @param content the full file content
185
+ * @param action the action name for the result
186
+ */
187
+ export declare const upsert_own_file: (path: string, content: string, action: string) => ActionResult;
188
+ /**
189
+ * Upserts the marker-fenced agent-instructions block at the top of an
190
+ * AGENTS.md-style file and reports the outcome; tools sharing the same file
191
+ * (the project-root AGENTS.md) dedupe automatically because the second
192
+ * upsert finds the block unchanged
193
+ *
194
+ * @param path the markdown file to update
195
+ * @param instructions the agent instructions text to place in the block
196
+ */
197
+ export declare const agents_md_result: (path: string, instructions: string) => ActionResult;
@@ -0,0 +1,428 @@
1
+ "use strict";
2
+ // Copyright (c) 2026, Brandon Lehmann <brandonlehmann@gmail.com>
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.agents_md_result = exports.upsert_own_file = exports.upsert_json_array_entry = exports.upsert_block_with_validation = exports.mcp_json_result = exports.api_key_header_manual_result = exports.api_key_manual_result = exports.upsert_json_path = exports.upsert_hooks_json = exports.upsert_session_start_hook = exports.upsert_marker_block = exports.write_json = exports.read_json = exports.write_text = exports.read_text = exports.has_marker_block = exports.session_start_command = exports.SESSION_START_MARKER = void 0;
23
+ const fs_1 = require("fs");
24
+ const path_1 = require("path");
25
+ /**
26
+ * Identifies our SessionStart hook entry among the user's hooks, across every
27
+ * historical command format
28
+ */
29
+ exports.SESSION_START_MARKER = '@nram-ai/setup-agents';
30
+ /**
31
+ * The shell command injected as a SessionStart hook; its stdout (the agent
32
+ * instructions, fetched from the configured nram server) is added to the
33
+ * model's context by the harness
34
+ *
35
+ * @param base_url the normalized nram base URL the hook fetches from
36
+ */
37
+ const session_start_command = (base_url) => `npx -y @nram-ai/setup-agents --session-start --url ${base_url}`;
38
+ exports.session_start_command = session_start_command;
39
+ const MARKERS = {
40
+ hash: ['# >>> nram setup >>>', '# <<< nram setup <<<'],
41
+ html: ['<!-- nram:start -->', '<!-- nram:end -->']
42
+ };
43
+ /**
44
+ * Returns whether the text already carries our marker-fenced block
45
+ *
46
+ * @param text the file content
47
+ * @param style the marker comment style
48
+ */
49
+ const has_marker_block = (text, style) => text.includes(MARKERS[style][0]);
50
+ exports.has_marker_block = has_marker_block;
51
+ /**
52
+ * Reads a text file, returning an empty string when the file does not exist
53
+ *
54
+ * @param path the file to read
55
+ */
56
+ const read_text = (path) => (0, fs_1.existsSync)(path) ? (0, fs_1.readFileSync)(path, 'utf8') : '';
57
+ exports.read_text = read_text;
58
+ /**
59
+ * Writes a text file, creating parent directories as needed
60
+ *
61
+ * @param path the file to write
62
+ * @param text the file content
63
+ */
64
+ const write_text = (path, text) => {
65
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(path), { recursive: true });
66
+ (0, fs_1.writeFileSync)(path, text);
67
+ };
68
+ exports.write_text = write_text;
69
+ /**
70
+ * Reads and parses a JSON file. A missing file is NOT an error (it yields
71
+ * `value: undefined`); an existing file that cannot be parsed is, and the
72
+ * caller MUST leave that file untouched and fall back to a manual snippet
73
+ *
74
+ * @param path the file to read
75
+ */
76
+ const read_json = (path) => {
77
+ if (!(0, fs_1.existsSync)(path)) {
78
+ return { ok: true, value: undefined, existed: false };
79
+ }
80
+ try {
81
+ return { ok: true, value: JSON.parse((0, fs_1.readFileSync)(path, 'utf8')), existed: true };
82
+ }
83
+ catch (error) {
84
+ return { ok: false, error: error.toString() };
85
+ }
86
+ };
87
+ exports.read_json = read_json;
88
+ /**
89
+ * Serializes a value as 2-space-indented JSON (with a trailing newline)
90
+ * and writes it, creating parent directories as needed
91
+ *
92
+ * @param path the file to write
93
+ * @param value the value to serialize
94
+ */
95
+ const write_json = (path, value) => (0, exports.write_text)(path, `${JSON.stringify(value, null, 2)}\n`);
96
+ exports.write_json = write_json;
97
+ /**
98
+ * Inserts a marker-fenced block into a body of text, or replaces the existing
99
+ * fenced block in place. Content outside the markers is never modified, and
100
+ * re-applying the same block is byte-stable
101
+ *
102
+ * @param text the current file content (may be empty)
103
+ * @param block the block content to place between the markers
104
+ * @param style the marker comment style for the target file type
105
+ * @param position where to place a NEW block; ignored when replacing
106
+ * @returns the updated text and whether anything changed
107
+ */
108
+ const upsert_marker_block = (text, block, style, position = 'append') => {
109
+ const [begin, end] = MARKERS[style];
110
+ const fenced = `${begin}\n${block.trimEnd()}\n${end}\n`;
111
+ const begin_idx = text.indexOf(begin);
112
+ if (begin_idx !== -1) {
113
+ const end_idx = text.indexOf(end, begin_idx);
114
+ if (end_idx !== -1) {
115
+ const after = text.slice(end_idx + end.length).replace(/^\r?\n/, '');
116
+ const updated = `${text.slice(0, begin_idx)}${fenced}${after}`;
117
+ return { text: updated, changed: updated !== text };
118
+ }
119
+ }
120
+ if (text.trim().length === 0) {
121
+ return { text: fenced, changed: true };
122
+ }
123
+ if (position === 'prepend') {
124
+ return { text: `${fenced}\n${text.replace(/^\s*\n/, '')}`, changed: true };
125
+ }
126
+ return { text: `${text.trimEnd()}\n\n${fenced}`, changed: true };
127
+ };
128
+ exports.upsert_marker_block = upsert_marker_block;
129
+ /**
130
+ * Upserts our SessionStart hook entry into a hooks-style configuration object
131
+ * (the SessionStart shape is identical between Claude Code's settings.json
132
+ * and Codex's hooks.json). An existing entry referencing `@nram-ai/setup-agents` is
133
+ * replaced in place when its command differs (a different server URL, or the
134
+ * URL-less command written by older package versions), left alone when it
135
+ * already matches, and appended when absent. Unrelated hooks are never touched
136
+ *
137
+ * @param config the parsed configuration object (mutated in place)
138
+ * @param base_url the normalized nram base URL embedded in the hook command
139
+ * @returns the configuration and whether it changed
140
+ */
141
+ const upsert_session_start_hook = (config, base_url) => {
142
+ config ??= {};
143
+ config.hooks ??= {};
144
+ config.hooks.SessionStart ??= [];
145
+ const command = (0, exports.session_start_command)(base_url);
146
+ for (const group of config.hooks.SessionStart) {
147
+ for (const hook of group?.hooks ?? []) {
148
+ if (typeof hook?.command === 'string' && hook.command.includes(exports.SESSION_START_MARKER)) {
149
+ if (hook.command === command) {
150
+ return { config, changed: false };
151
+ }
152
+ hook.command = command;
153
+ return { config, changed: true };
154
+ }
155
+ }
156
+ }
157
+ config.hooks.SessionStart.push({
158
+ matcher: '',
159
+ hooks: [{
160
+ type: 'command',
161
+ command
162
+ }]
163
+ });
164
+ return { config, changed: true };
165
+ };
166
+ exports.upsert_session_start_hook = upsert_session_start_hook;
167
+ /**
168
+ * Upserts our SessionStart hook into a hooks-style JSON file (Claude Code's
169
+ * settings.json and Codex's hooks.json share the shape) and reports the
170
+ * outcome; an unparseable file is left untouched
171
+ *
172
+ * @param path the hooks file to update
173
+ * @param base_url the normalized nram base URL embedded in the hook command
174
+ */
175
+ const upsert_hooks_json = (path, base_url) => {
176
+ const file = (0, exports.read_json)(path);
177
+ if (!file.ok) {
178
+ return {
179
+ action: 'SessionStart hook',
180
+ kind: 'manual',
181
+ detail: `${path} exists but could not be parsed (${file.error}); ` +
182
+ 'add this to hooks.SessionStart yourself: ' +
183
+ `{ "matcher": "", "hooks": [{ "type": "command", "command": "${(0, exports.session_start_command)(base_url)}" }] }`
184
+ };
185
+ }
186
+ const { config, changed } = (0, exports.upsert_session_start_hook)(file.value ?? {}, base_url);
187
+ if (!changed) {
188
+ return { action: 'SessionStart hook', kind: 'skipped', detail: `already present in ${path}` };
189
+ }
190
+ (0, exports.write_json)(path, config);
191
+ return { action: 'SessionStart hook', kind: file.existed ? 'updated' : 'written', detail: path };
192
+ };
193
+ exports.upsert_hooks_json = upsert_hooks_json;
194
+ /**
195
+ * Merges a value into a JSON file at the given key path (creating intermediate
196
+ * objects as needed), writing only when the value differs. An unparseable
197
+ * existing file is left untouched and reported for manual handling
198
+ *
199
+ * @param path the JSON file to update
200
+ * @param key_path the nested key path, e.g. ['mcpServers', 'nram']
201
+ * @param entry the value to place at the key path
202
+ * @param fallback the configuration to start from when the file is missing
203
+ */
204
+ const upsert_json_path = (path, key_path, entry, fallback = {}) => {
205
+ const file = (0, exports.read_json)(path);
206
+ if (!file.ok) {
207
+ return { kind: 'manual', error: file.error };
208
+ }
209
+ const config = file.value ?? fallback;
210
+ let node = config;
211
+ for (const key of key_path.slice(0, -1)) {
212
+ node[key] ??= {};
213
+ node = node[key];
214
+ }
215
+ const leaf = key_path[key_path.length - 1];
216
+ if (JSON.stringify(node[leaf]) === JSON.stringify(entry)) {
217
+ return { kind: 'skipped' };
218
+ }
219
+ node[leaf] = entry;
220
+ (0, exports.write_json)(path, config);
221
+ return { kind: file.existed ? 'updated' : 'written' };
222
+ };
223
+ exports.upsert_json_path = upsert_json_path;
224
+ /**
225
+ * The manual follow-up shown whenever a configuration references the
226
+ * NRAM_API_KEY environment variable instead of embedding the key
227
+ */
228
+ const api_key_manual_result = () => ({
229
+ action: 'API key',
230
+ kind: 'manual',
231
+ detail: 'set the NRAM_API_KEY environment variable to your nram API key; ' +
232
+ 'the key is never written to disk'
233
+ });
234
+ exports.api_key_manual_result = api_key_manual_result;
235
+ /**
236
+ * The manual follow-up shown when a tool's configuration has no verified
237
+ * environment-variable substitution, so the Authorization header must be
238
+ * added by hand rather than risking a literal key on disk
239
+ *
240
+ * @param path the configuration file the header belongs in
241
+ */
242
+ const api_key_header_manual_result = (path) => ({
243
+ action: 'API key',
244
+ kind: 'manual',
245
+ detail: `this tool has no verified env-var substitution, so add the header yourself in ${path}: ` +
246
+ '"headers": { "Authorization": "Bearer <your nram_k_ key>" }'
247
+ });
248
+ exports.api_key_header_manual_result = api_key_header_manual_result;
249
+ /**
250
+ * Merges an MCP server entry into a JSON configuration and maps the outcome
251
+ * onto the standard `MCP registration` ActionResult, including the manual
252
+ * snippet for unparseable files; this is the shared report flow every
253
+ * JSON-configured harness uses
254
+ *
255
+ * @param path the JSON file to update
256
+ * @param key_path the nested key path (the leaf is the entry key, or the array itself with `array`)
257
+ * @param entry the entry value
258
+ * @param options report and merge options
259
+ */
260
+ const mcp_json_result = (path, key_path, entry, options = {}) => {
261
+ const merged = options.array
262
+ ? (0, exports.upsert_json_array_entry)(path, key_path, entry)
263
+ : (0, exports.upsert_json_path)(path, key_path, entry, options.fallback ?? {});
264
+ if (merged.kind === 'manual') {
265
+ const reason = options.parse_hint ?? merged.error;
266
+ const target = options.array
267
+ ? `add this to the "${key_path.join('.')}" array yourself`
268
+ : `add this under "${key_path.slice(0, -1).join('.')}" yourself: "${key_path[key_path.length - 1]}"`;
269
+ return {
270
+ action: 'MCP registration',
271
+ kind: 'manual',
272
+ detail: `${path} exists but could not be parsed (${reason}); ${target}: ${JSON.stringify(entry)}`
273
+ };
274
+ }
275
+ if (merged.kind === 'skipped') {
276
+ return { action: 'MCP registration', kind: 'skipped', detail: `already present in ${path}` };
277
+ }
278
+ return {
279
+ action: 'MCP registration',
280
+ kind: merged.kind,
281
+ detail: options.note === undefined ? path : `${path} (${options.note})`
282
+ };
283
+ };
284
+ exports.mcp_json_result = mcp_json_result;
285
+ /**
286
+ * Upserts a marker-fenced configuration block into a structured text file
287
+ * (TOML, YAML): parse the existing content (manual on failure), let the
288
+ * caller veto via a conflict check, apply the marker upsert, validate that
289
+ * the result re-parses with the expected entry, and only then write. The
290
+ * file is never re-serialized as a whole, so user comments survive
291
+ *
292
+ * @param path the configuration file
293
+ * @param block the marker-fenced block content
294
+ * @param parse the format parser (throws on invalid input)
295
+ * @param format the format name for messaging
296
+ * @param conflict returns a result to report instead of editing (e.g. the key exists outside our markers)
297
+ * @param valid post-edit check that the entry is present in the re-parsed file
298
+ */
299
+ const upsert_block_with_validation = (path, block, parse, format, conflict, valid) => {
300
+ const text = (0, exports.read_text)(path);
301
+ if (text.trim().length !== 0) {
302
+ let parsed;
303
+ try {
304
+ parsed = parse(text);
305
+ }
306
+ catch {
307
+ return {
308
+ action: 'MCP registration',
309
+ kind: 'manual',
310
+ detail: `${path} exists but could not be parsed as ${format}; add this yourself:\n${block}`
311
+ };
312
+ }
313
+ const vetoed = conflict(parsed, text);
314
+ if (vetoed !== undefined) {
315
+ return vetoed;
316
+ }
317
+ }
318
+ const { text: updated, changed } = (0, exports.upsert_marker_block)(text, block, 'hash', 'append');
319
+ if (!changed) {
320
+ return { action: 'MCP registration', kind: 'skipped', detail: `already present in ${path}` };
321
+ }
322
+ let ok;
323
+ try {
324
+ ok = valid(parse(updated));
325
+ }
326
+ catch {
327
+ ok = false;
328
+ }
329
+ if (!ok) {
330
+ return {
331
+ action: 'MCP registration',
332
+ kind: 'failed',
333
+ detail: `refusing to write ${path}: the updated file did not re-parse cleanly; ` +
334
+ `add this yourself:\n${block}`
335
+ };
336
+ }
337
+ (0, exports.write_text)(path, updated);
338
+ return {
339
+ action: 'MCP registration',
340
+ kind: text.length === 0 ? 'written' : 'updated',
341
+ detail: path
342
+ };
343
+ };
344
+ exports.upsert_block_with_validation = upsert_block_with_validation;
345
+ /**
346
+ * Merges an entry into an array-shaped JSON configuration (e.g. Factory's
347
+ * `servers: []`), keyed by a name field: replace in place when an element
348
+ * with the same name exists and differs, append when absent, skip when
349
+ * identical. An unparseable existing file is left untouched
350
+ *
351
+ * @param path the JSON file to update
352
+ * @param array_key_path the nested key path holding the array
353
+ * @param entry the element to place (must carry the name key)
354
+ * @param name_key the field that identifies elements (default `name`)
355
+ */
356
+ const upsert_json_array_entry = (path, array_key_path, entry, name_key = 'name') => {
357
+ const file = (0, exports.read_json)(path);
358
+ if (!file.ok) {
359
+ return { kind: 'manual', error: file.error };
360
+ }
361
+ const config = file.value ?? {};
362
+ let node = config;
363
+ for (const key of array_key_path.slice(0, -1)) {
364
+ node[key] ??= {};
365
+ node = node[key];
366
+ }
367
+ const leaf = array_key_path[array_key_path.length - 1];
368
+ node[leaf] ??= [];
369
+ const list = node[leaf];
370
+ const index = list.findIndex(element => element?.[name_key] === entry[name_key]);
371
+ if (index !== -1 && JSON.stringify(list[index]) === JSON.stringify(entry)) {
372
+ return { kind: 'skipped' };
373
+ }
374
+ if (index !== -1) {
375
+ list[index] = entry;
376
+ }
377
+ else {
378
+ list.push(entry);
379
+ }
380
+ (0, exports.write_json)(path, config);
381
+ return { kind: file.existed ? 'updated' : 'written' };
382
+ };
383
+ exports.upsert_json_array_entry = upsert_json_array_entry;
384
+ /**
385
+ * Writes a file the tool owns outright (rule files, steering docs,
386
+ * instruction files): compare, write when different, and report
387
+ *
388
+ * @param path the file to write
389
+ * @param content the full file content
390
+ * @param action the action name for the result
391
+ */
392
+ const upsert_own_file = (path, content, action) => {
393
+ const existing = (0, exports.read_text)(path);
394
+ if (existing === content) {
395
+ return { action, kind: 'skipped', detail: `already present at ${path}` };
396
+ }
397
+ (0, exports.write_text)(path, content);
398
+ return {
399
+ action,
400
+ kind: existing.length === 0 ? 'written' : 'updated',
401
+ detail: path
402
+ };
403
+ };
404
+ exports.upsert_own_file = upsert_own_file;
405
+ /**
406
+ * Upserts the marker-fenced agent-instructions block at the top of an
407
+ * AGENTS.md-style file and reports the outcome; tools sharing the same file
408
+ * (the project-root AGENTS.md) dedupe automatically because the second
409
+ * upsert finds the block unchanged
410
+ *
411
+ * @param path the markdown file to update
412
+ * @param instructions the agent instructions text to place in the block
413
+ */
414
+ const agents_md_result = (path, instructions) => {
415
+ const text = (0, exports.read_text)(path);
416
+ const { text: updated, changed } = (0, exports.upsert_marker_block)(text, instructions, 'html', 'prepend');
417
+ if (!changed) {
418
+ return { action: 'Agent instructions', kind: 'skipped', detail: `already present in ${path}` };
419
+ }
420
+ (0, exports.write_text)(path, updated);
421
+ return {
422
+ action: 'Agent instructions',
423
+ kind: text.length === 0 ? 'written' : 'updated',
424
+ detail: path
425
+ };
426
+ };
427
+ exports.agents_md_result = agents_md_result;
428
+ //# sourceMappingURL=upsert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upsert.js","sourceRoot":"","sources":["../../src/helpers/upsert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Normalizes an nram base URL: trims whitespace, requires http/https,
3
+ * and strips any trailing slash so that paths can be appended safely
4
+ *
5
+ * @param input the user-supplied base URL
6
+ * @returns the normalized base URL
7
+ * @throws Error when the input is not a valid http(s) URL
8
+ */
9
+ export declare const normalize_base_url: (input: string) => string;
10
+ /**
11
+ * Derives the MCP endpoint URL from a normalized base URL
12
+ *
13
+ * @param base_url the normalized nram base URL
14
+ */
15
+ export declare const mcp_url: (base_url: string) => string;