@zhixuan92/multi-model-agent 3.0.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 (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/dist/cli/index.d.ts +61 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +252 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/install-skill.d.ts +158 -0
  8. package/dist/cli/install-skill.d.ts.map +1 -0
  9. package/dist/cli/install-skill.js +425 -0
  10. package/dist/cli/install-skill.js.map +1 -0
  11. package/dist/cli/print-token.d.ts +18 -0
  12. package/dist/cli/print-token.d.ts.map +1 -0
  13. package/dist/cli/print-token.js +60 -0
  14. package/dist/cli/print-token.js.map +1 -0
  15. package/dist/cli/serve.d.ts +44 -0
  16. package/dist/cli/serve.d.ts.map +1 -0
  17. package/dist/cli/serve.js +61 -0
  18. package/dist/cli/serve.js.map +1 -0
  19. package/dist/cli/status.d.ts +49 -0
  20. package/dist/cli/status.d.ts.map +1 -0
  21. package/dist/cli/status.js +155 -0
  22. package/dist/cli/status.js.map +1 -0
  23. package/dist/http/async-dispatch.d.ts +32 -0
  24. package/dist/http/async-dispatch.d.ts.map +1 -0
  25. package/dist/http/async-dispatch.js +53 -0
  26. package/dist/http/async-dispatch.js.map +1 -0
  27. package/dist/http/auth.d.ts +26 -0
  28. package/dist/http/auth.d.ts.map +1 -0
  29. package/dist/http/auth.js +64 -0
  30. package/dist/http/auth.js.map +1 -0
  31. package/dist/http/cwd-validator.d.ts +11 -0
  32. package/dist/http/cwd-validator.d.ts.map +1 -0
  33. package/dist/http/cwd-validator.js +115 -0
  34. package/dist/http/cwd-validator.js.map +1 -0
  35. package/dist/http/errors.d.ts +4 -0
  36. package/dist/http/errors.d.ts.map +1 -0
  37. package/dist/http/errors.js +9 -0
  38. package/dist/http/errors.js.map +1 -0
  39. package/dist/http/execution-context.d.ts +15 -0
  40. package/dist/http/execution-context.d.ts.map +1 -0
  41. package/dist/http/execution-context.js +35 -0
  42. package/dist/http/execution-context.js.map +1 -0
  43. package/dist/http/handler-deps.d.ts +16 -0
  44. package/dist/http/handler-deps.d.ts.map +1 -0
  45. package/dist/http/handler-deps.js +2 -0
  46. package/dist/http/handler-deps.js.map +1 -0
  47. package/dist/http/handlers/control/batch.d.ts +24 -0
  48. package/dist/http/handlers/control/batch.d.ts.map +1 -0
  49. package/dist/http/handlers/control/batch.js +81 -0
  50. package/dist/http/handlers/control/batch.js.map +1 -0
  51. package/dist/http/handlers/control/clarifications.d.ts +19 -0
  52. package/dist/http/handlers/control/clarifications.d.ts.map +1 -0
  53. package/dist/http/handlers/control/clarifications.js +58 -0
  54. package/dist/http/handlers/control/clarifications.js.map +1 -0
  55. package/dist/http/handlers/control/context-blocks.d.ts +22 -0
  56. package/dist/http/handlers/control/context-blocks.d.ts.map +1 -0
  57. package/dist/http/handlers/control/context-blocks.js +88 -0
  58. package/dist/http/handlers/control/context-blocks.js.map +1 -0
  59. package/dist/http/handlers/introspection/health.d.ts +13 -0
  60. package/dist/http/handlers/introspection/health.d.ts.map +1 -0
  61. package/dist/http/handlers/introspection/health.js +17 -0
  62. package/dist/http/handlers/introspection/health.js.map +1 -0
  63. package/dist/http/handlers/introspection/status.d.ts +26 -0
  64. package/dist/http/handlers/introspection/status.d.ts.map +1 -0
  65. package/dist/http/handlers/introspection/status.js +136 -0
  66. package/dist/http/handlers/introspection/status.js.map +1 -0
  67. package/dist/http/handlers/introspection/tools-list.d.ts +9 -0
  68. package/dist/http/handlers/introspection/tools-list.d.ts.map +1 -0
  69. package/dist/http/handlers/introspection/tools-list.js +28 -0
  70. package/dist/http/handlers/introspection/tools-list.js.map +1 -0
  71. package/dist/http/handlers/tools/audit.d.ts +4 -0
  72. package/dist/http/handlers/tools/audit.d.ts.map +1 -0
  73. package/dist/http/handlers/tools/audit.js +39 -0
  74. package/dist/http/handlers/tools/audit.js.map +1 -0
  75. package/dist/http/handlers/tools/debug.d.ts +4 -0
  76. package/dist/http/handlers/tools/debug.d.ts.map +1 -0
  77. package/dist/http/handlers/tools/debug.js +39 -0
  78. package/dist/http/handlers/tools/debug.js.map +1 -0
  79. package/dist/http/handlers/tools/delegate.d.ts +4 -0
  80. package/dist/http/handlers/tools/delegate.d.ts.map +1 -0
  81. package/dist/http/handlers/tools/delegate.js +57 -0
  82. package/dist/http/handlers/tools/delegate.js.map +1 -0
  83. package/dist/http/handlers/tools/execute-plan.d.ts +4 -0
  84. package/dist/http/handlers/tools/execute-plan.d.ts.map +1 -0
  85. package/dist/http/handlers/tools/execute-plan.js +39 -0
  86. package/dist/http/handlers/tools/execute-plan.js.map +1 -0
  87. package/dist/http/handlers/tools/retry.d.ts +4 -0
  88. package/dist/http/handlers/tools/retry.d.ts.map +1 -0
  89. package/dist/http/handlers/tools/retry.js +52 -0
  90. package/dist/http/handlers/tools/retry.js.map +1 -0
  91. package/dist/http/handlers/tools/review.d.ts +4 -0
  92. package/dist/http/handlers/tools/review.d.ts.map +1 -0
  93. package/dist/http/handlers/tools/review.js +39 -0
  94. package/dist/http/handlers/tools/review.js.map +1 -0
  95. package/dist/http/handlers/tools/verify.d.ts +4 -0
  96. package/dist/http/handlers/tools/verify.d.ts.map +1 -0
  97. package/dist/http/handlers/tools/verify.js +39 -0
  98. package/dist/http/handlers/tools/verify.js.map +1 -0
  99. package/dist/http/loopback.d.ts +17 -0
  100. package/dist/http/loopback.d.ts.map +1 -0
  101. package/dist/http/loopback.js +43 -0
  102. package/dist/http/loopback.js.map +1 -0
  103. package/dist/http/middleware/body-reader.d.ts +16 -0
  104. package/dist/http/middleware/body-reader.d.ts.map +1 -0
  105. package/dist/http/middleware/body-reader.js +44 -0
  106. package/dist/http/middleware/body-reader.js.map +1 -0
  107. package/dist/http/project-registry.d.ts +54 -0
  108. package/dist/http/project-registry.d.ts.map +1 -0
  109. package/dist/http/project-registry.js +132 -0
  110. package/dist/http/project-registry.js.map +1 -0
  111. package/dist/http/router.d.ts +14 -0
  112. package/dist/http/router.d.ts.map +1 -0
  113. package/dist/http/router.js +41 -0
  114. package/dist/http/router.js.map +1 -0
  115. package/dist/http/server.d.ts +16 -0
  116. package/dist/http/server.d.ts.map +1 -0
  117. package/dist/http/server.js +235 -0
  118. package/dist/http/server.js.map +1 -0
  119. package/dist/http/types.d.ts +9 -0
  120. package/dist/http/types.d.ts.map +1 -0
  121. package/dist/http/types.js +2 -0
  122. package/dist/http/types.js.map +1 -0
  123. package/dist/index.d.ts +2 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +2 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/install/claude-code.d.ts +43 -0
  128. package/dist/install/claude-code.d.ts.map +1 -0
  129. package/dist/install/claude-code.js +65 -0
  130. package/dist/install/claude-code.js.map +1 -0
  131. package/dist/install/codex-cli.d.ts +39 -0
  132. package/dist/install/codex-cli.d.ts.map +1 -0
  133. package/dist/install/codex-cli.js +318 -0
  134. package/dist/install/codex-cli.js.map +1 -0
  135. package/dist/install/cursor.d.ts +72 -0
  136. package/dist/install/cursor.d.ts.map +1 -0
  137. package/dist/install/cursor.js +81 -0
  138. package/dist/install/cursor.js.map +1 -0
  139. package/dist/install/gemini-cli.d.ts +66 -0
  140. package/dist/install/gemini-cli.d.ts.map +1 -0
  141. package/dist/install/gemini-cli.js +111 -0
  142. package/dist/install/gemini-cli.js.map +1 -0
  143. package/dist/install/include-utils.d.ts +27 -0
  144. package/dist/install/include-utils.d.ts.map +1 -0
  145. package/dist/install/include-utils.js +90 -0
  146. package/dist/install/include-utils.js.map +1 -0
  147. package/dist/install/manifest.d.ts +90 -0
  148. package/dist/install/manifest.d.ts.map +1 -0
  149. package/dist/install/manifest.js +200 -0
  150. package/dist/install/manifest.js.map +1 -0
  151. package/dist/openapi.d.ts +15 -0
  152. package/dist/openapi.d.ts.map +1 -0
  153. package/dist/openapi.js +314 -0
  154. package/dist/openapi.js.map +1 -0
  155. package/dist/skills/_shared/auth.md +32 -0
  156. package/dist/skills/_shared/error-handling.md +31 -0
  157. package/dist/skills/_shared/polling.md +40 -0
  158. package/dist/skills/_shared/response-shape.md +46 -0
  159. package/dist/skills/mma-audit/SKILL.md +55 -0
  160. package/dist/skills/mma-clarifications/SKILL.md +68 -0
  161. package/dist/skills/mma-context-blocks/SKILL.md +69 -0
  162. package/dist/skills/mma-debug/SKILL.md +59 -0
  163. package/dist/skills/mma-delegate/SKILL.md +63 -0
  164. package/dist/skills/mma-execute-plan/SKILL.md +63 -0
  165. package/dist/skills/mma-retry/SKILL.md +54 -0
  166. package/dist/skills/mma-review/SKILL.md +55 -0
  167. package/dist/skills/mma-verify/SKILL.md +57 -0
  168. package/dist/skills/multi-model-agent/SKILL.md +55 -0
  169. package/package.json +60 -0
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Codex CLI skill writer for install-skill.
3
+ *
4
+ * Task 9.7 scope: write/install and remove/uninstall of skills to
5
+ * the Codex CLI's managed block in `<homeDir>/.codex/AGENTS.md`.
6
+ *
7
+ * Managed block delimiters:
8
+ * <!-- multi-model-agent:BEGIN -->
9
+ * ... skill content ...
10
+ * <!-- multi-model-agent:END -->
11
+ *
12
+ * Install behaviour:
13
+ * - If AGENTS.md does NOT exist: create it with just the managed block.
14
+ * - If AGENTS.md exists but has NO managed block markers: append the block
15
+ * at the end with a blank-line separator.
16
+ * - If AGENTS.md exists WITH managed block markers: replace the content
17
+ * between (and including) the markers with the new managed block.
18
+ * - User content OUTSIDE the markers is preserved verbatim.
19
+ *
20
+ * Uninstall behaviour:
21
+ * - Remove the managed block (including markers) from AGENTS.md.
22
+ * - If the file becomes empty or only whitespace after removal, delete the
23
+ * file.
24
+ * - If the file does not contain the markers, do nothing (no error).
25
+ * - If the file does not exist, do nothing (no error).
26
+ *
27
+ * @include resolution:
28
+ * Lines beginning with `@include _shared/<file>.md` are replaced with the
29
+ * file content read from `<skillsRoot>/_shared/<file>.md`. If the file
30
+ * cannot be read, a warning is written to stderr and the line is omitted
31
+ * from the output (the directive is NOT preserved — matching the Claude Code
32
+ * writer behaviour).
33
+ *
34
+ * Security: @include paths must begin with `_shared/` and are resolved
35
+ * strictly within `<skillsRoot>/_shared/` to prevent path traversal.
36
+ *
37
+ * @module
38
+ */
39
+ import fs from 'node:fs';
40
+ import path from 'node:path';
41
+ // ─── Constants ────────────────────────────────────────────────────────────────
42
+ const MANAGED_BEGIN = '<!-- multi-model-agent:BEGIN -->';
43
+ const MANAGED_END = '<!-- multi-model-agent:END -->';
44
+ // ─── Managed block helpers ────────────────────────────────────────────────────
45
+ /**
46
+ * Build the complete managed block string including delimiters.
47
+ *
48
+ * Block format:
49
+ * <!-- multi-model-agent:BEGIN -->
50
+ * <content>
51
+ * <!-- multi-model-agent:END -->
52
+ *
53
+ * Key newline rules:
54
+ * - BEGIN is always followed by `\n` so content starts on its own line.
55
+ * - The content line(s) end with a newline. If content doesn't already end
56
+ * with `\n`, we add one before END.
57
+ * - END is the final line of the block; no trailing newline is added after
58
+ * END (the block ends with the `>` character of the END tag).
59
+ *
60
+ * This means the block always ends with `MANAGED_END` as the last characters
61
+ * of the file (no trailing newline after it).
62
+ */
63
+ function buildManagedBlock(inlinedContent) {
64
+ const contentEndsWithNewline = inlinedContent.endsWith('\n');
65
+ // Block format: BEGIN + "\n" + content (ending with \n) + END
66
+ // No trailing newline after END — callers join suffix directly.
67
+ return `${MANAGED_BEGIN}\n${inlinedContent}${contentEndsWithNewline ? '' : '\n'}${MANAGED_END}`;
68
+ }
69
+ // ─── @include inlining (private) ─────────────────────────────────────────────
70
+ /**
71
+ * Inline `@include _shared/<path>` directives in `content`.
72
+ *
73
+ * Each line matching `@include _shared/<path>` is replaced with the full
74
+ * content of the corresponding file under `<skillsRoot>/_shared/<path>`.
75
+ *
76
+ * Security constraints:
77
+ * - Only paths beginning with `_shared/` are accepted.
78
+ * - The resolved path must stay within `<skillsRoot>/_shared/`. Path
79
+ * traversal attempts (e.g. `_shared/../secrets.txt`) are rejected with a
80
+ * warning and the directive line is dropped.
81
+ *
82
+ * Missing shared files:
83
+ * - A warning containing "missing shared file" is written to stderr.
84
+ * - The directive line is dropped (not preserved) — matching the Claude
85
+ * Code writer behaviour as required by the brief.
86
+ *
87
+ * @param skillName Used in warning messages to identify the skill.
88
+ * @param content Raw skill content (may contain @include directives).
89
+ * @param skillsRoot Root directory containing `_shared/`.
90
+ * @returns The content with directives inlined.
91
+ */
92
+ function inlineIncludes(skillName, content, skillsRoot) {
93
+ const lines = content.split('\n');
94
+ const result = [];
95
+ for (const line of lines) {
96
+ const match = line.match(/^@include\s+(.+)$/);
97
+ if (!match) {
98
+ result.push(line);
99
+ continue;
100
+ }
101
+ // Trim trailing whitespace from the path token.
102
+ const relativePath = match[1].trimEnd();
103
+ // Security: only accept paths beginning with `_shared/`
104
+ if (!relativePath.startsWith('_shared/')) {
105
+ process.stderr.write(`Warning: Codex CLI skill writer [${skillName}]: @include path must ` +
106
+ `start with "_shared/": ${relativePath}\n`);
107
+ continue;
108
+ }
109
+ // Security: reject path traversal attempts
110
+ const resolvedPath = path.resolve(skillsRoot, relativePath);
111
+ const sharedDir = path.resolve(skillsRoot, '_shared');
112
+ if (!resolvedPath.startsWith(sharedDir + path.sep) &&
113
+ resolvedPath !== sharedDir) {
114
+ process.stderr.write(`Warning: Codex CLI skill writer [${skillName}]: @include path ` +
115
+ `rejected (path traversal): ${relativePath}\n`);
116
+ continue;
117
+ }
118
+ const sharedFilePath = path.join(skillsRoot, relativePath);
119
+ try {
120
+ const sharedContent = fs.readFileSync(sharedFilePath, 'utf-8');
121
+ result.push(sharedContent);
122
+ }
123
+ catch (err) {
124
+ const nodeErr = err;
125
+ if (nodeErr.code === 'ENOENT') {
126
+ process.stderr.write(`Warning: Codex CLI skill writer [${skillName}]: missing shared ` +
127
+ `file: ${sharedFilePath} (referenced by @include ${relativePath})\n`);
128
+ // Directive is dropped — do not push anything.
129
+ }
130
+ else {
131
+ // Permission errors, EISDIR, etc. — re-throw so the caller notices.
132
+ throw err;
133
+ }
134
+ }
135
+ }
136
+ return result.join('\n');
137
+ }
138
+ // ─── Marker helpers ────────────────────────────────────────────────────────────
139
+ /**
140
+ * Returns true only when both managed block markers are present in
141
+ * `content` and BEGIN appears before END. An orphan marker or corrupt
142
+ * ordering does NOT constitute a valid block.
143
+ */
144
+ function hasManagedBlock(content) {
145
+ const beginIdx = content.indexOf(MANAGED_BEGIN);
146
+ const endIdx = content.indexOf(MANAGED_END);
147
+ return beginIdx !== -1 && endIdx !== -1 && beginIdx < endIdx;
148
+ }
149
+ // ─── Core write logic ─────────────────────────────────────────────────────────
150
+ /**
151
+ * Find the span of the existing managed block (including both markers) in
152
+ * `content`. Returns `{ prefix, suffix }` where `prefix` is everything
153
+ * before BEGIN and `suffix` is everything after the block's final character.
154
+ *
155
+ * The block always ends with `MANAGED_END` (no trailing newline after END).
156
+ * After END, there may be a structural newline (the newline that terminates
157
+ * the END line). This structural newline is NOT part of the user suffix — it
158
+ * belongs to the block boundary and must not appear as an extra blank line
159
+ * when prefix and suffix are joined.
160
+ *
161
+ * Edge cases:
162
+ * - If END is the last character of the file (no trailing newline), suffix
163
+ * is empty.
164
+ * - If BEGIN and END are in corrupt order (END before BEGIN), returns null
165
+ * so no user content is inadvertently destroyed.
166
+ */
167
+ function splitAroundBlock(content) {
168
+ const beginIdx = content.indexOf(MANAGED_BEGIN);
169
+ const endIdx = content.indexOf(MANAGED_END);
170
+ // No valid block without both markers
171
+ if (beginIdx === -1 || endIdx === -1) {
172
+ return null;
173
+ }
174
+ // Corrupt order: END before BEGIN — treat as no valid block to protect
175
+ // user content from inadvertent removal.
176
+ if (beginIdx > endIdx) {
177
+ return null;
178
+ }
179
+ // suffix starts immediately after MANAGED_END (includes any trailing newlines).
180
+ // Callers decide how to handle the leading newlines in suffix:
181
+ // - Install (replace): suffix begins with the separator between END and
182
+ // following user content; joining block + suffix reconstructs the file.
183
+ // - Uninstall: strip all leading newlines from suffix before joining.
184
+ const suffixStart = endIdx + MANAGED_END.length;
185
+ return {
186
+ prefix: content.slice(0, beginIdx),
187
+ suffix: content.slice(suffixStart),
188
+ };
189
+ }
190
+ /**
191
+ * Write (or overwrite) the managed block in the Codex CLI's AGENTS.md.
192
+ *
193
+ * The algorithm:
194
+ * 1. Inline @include directives.
195
+ * 2. Read existing file if present.
196
+ * 3. Determine new content: create / append / replace.
197
+ * 4. Write to disk.
198
+ *
199
+ * @throws If the AGENTS.md file exists but cannot be read.
200
+ * @throws If the AGENTS.md path is a directory.
201
+ */
202
+ export function installCodexCli(opts) {
203
+ const agentsPath = path.join(opts.homeDir, '.codex', 'AGENTS.md');
204
+ // 1. Inline @include directives
205
+ const inlinedContent = inlineIncludes(opts.skillName, opts.content, opts.skillsRoot);
206
+ const block = buildManagedBlock(inlinedContent);
207
+ // 2. Read existing file
208
+ let existingContent;
209
+ let fileExists = false;
210
+ try {
211
+ const stat = fs.statSync(agentsPath);
212
+ if (stat.isDirectory()) {
213
+ throw new Error(`AGENTS.md path is a directory: ${agentsPath}`);
214
+ }
215
+ existingContent = fs.readFileSync(agentsPath, 'utf-8');
216
+ fileExists = true;
217
+ }
218
+ catch (err) {
219
+ if (err.code === 'ENOENT') {
220
+ existingContent = '';
221
+ fileExists = false;
222
+ }
223
+ else {
224
+ throw err;
225
+ }
226
+ }
227
+ // 3. Determine new content
228
+ let newContent;
229
+ if (!fileExists) {
230
+ // Case A: file does not exist → create with just the managed block
231
+ newContent = block;
232
+ }
233
+ else if (!hasManagedBlock(existingContent)) {
234
+ // Case B: file exists but no managed block → append with blank-line separator
235
+ //
236
+ // The block begins with BEGIN + "\n". To create exactly one blank line
237
+ // between existing content and the BEGIN marker:
238
+ // - If existingContent ends with "\n": add one "\n" → existing ends with
239
+ // "\n", block starts with "\n" → two newlines = one blank line. ✓
240
+ // - If existingContent does NOT end with "\n": add "\n\n" → existing ends
241
+ // with "\n", block starts with "\n" → two newlines = one blank line. ✓
242
+ newContent = existingContent.endsWith('\n')
243
+ ? `${existingContent}\n${block}`
244
+ : `${existingContent}\n\n${block}`;
245
+ }
246
+ else {
247
+ // Case C: file exists with managed block → replace it
248
+ const split = splitAroundBlock(existingContent);
249
+ if (split === null) {
250
+ // Both markers present but split returned null — corrupt marker order.
251
+ // Do not overwrite; append with separator instead.
252
+ newContent = existingContent.endsWith('\n')
253
+ ? `${existingContent}\n${block}`
254
+ : `${existingContent}\n\n${block}`;
255
+ }
256
+ else {
257
+ const { prefix, suffix } = split;
258
+ newContent = `${prefix}${block}${suffix}`;
259
+ }
260
+ }
261
+ // 4. Write to disk
262
+ fs.mkdirSync(path.join(opts.homeDir, '.codex'), { recursive: true });
263
+ fs.writeFileSync(agentsPath, newContent, 'utf-8');
264
+ }
265
+ // ─── Uninstall ────────────────────────────────────────────────────────────────
266
+ /**
267
+ * Remove the managed block (including markers) from AGENTS.md.
268
+ *
269
+ * Behaviour:
270
+ * - File does not exist → no-op.
271
+ * - File has no managed block markers → no-op (file unchanged).
272
+ * - File has managed block → remove it; write remaining content back.
273
+ * - Remaining content is empty or whitespace-only → delete the file.
274
+ *
275
+ * @throws On filesystem errors other than ENOENT when reading/writing.
276
+ */
277
+ export function uninstallCodexCli(homeDir) {
278
+ const agentsPath = path.join(homeDir, '.codex', 'AGENTS.md');
279
+ let content;
280
+ try {
281
+ content = fs.readFileSync(agentsPath, 'utf-8');
282
+ }
283
+ catch (err) {
284
+ if (err.code === 'ENOENT') {
285
+ // File does not exist — no-op.
286
+ return;
287
+ }
288
+ // Re-throw on read errors, preserving the original error context.
289
+ throw err;
290
+ }
291
+ if (!hasManagedBlock(content)) {
292
+ // No managed block to remove — file unchanged.
293
+ return;
294
+ }
295
+ const split = splitAroundBlock(content);
296
+ if (split === null) {
297
+ // Both markers present but split returned null — corrupt marker order.
298
+ // No safe block to remove — leave file unchanged.
299
+ return;
300
+ }
301
+ const { prefix, suffix } = split;
302
+ // Strip all leading newlines from suffix. The suffix begins with any
303
+ // newlines that followed MANAGED_END (including blank-line separators).
304
+ // Removing them avoids leaving orphan blank lines after the managed block
305
+ // is gone. The prefix already ends before BEGIN (including any trailing
306
+ // newlines the user placed before the block), so this does not disturb
307
+ // user-authored spacing in the prefix.
308
+ const trimmedSuffix = suffix.replace(/^\n+/, '');
309
+ const newContent = `${prefix}${trimmedSuffix}`;
310
+ if (newContent.trim() === '') {
311
+ // File is empty after removal — delete it.
312
+ fs.unlinkSync(agentsPath);
313
+ }
314
+ else {
315
+ fs.writeFileSync(agentsPath, newContent, 'utf-8');
316
+ }
317
+ }
318
+ //# sourceMappingURL=codex-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-cli.js","sourceRoot":"","sources":["../../src/install/codex-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Options for installing a Cursor skill.
3
+ *
4
+ * NOTE: `homeDir` is accepted for API parity with other skill writers
5
+ * (Claude Code, Gemini CLI, etc.) but is intentionally not used by this
6
+ * writer. The Cursor rules target path is always CWD-relative under
7
+ * `<cwd>/.cursor/rules/`, not homeDir-relative. This is intentional to
8
+ * keep Cursor rules scoped to the project directory.
9
+ */
10
+ export interface CursorInstallOpts {
11
+ /**
12
+ * Raw skill content. May contain `@include _shared/<file>.md` directives
13
+ * which are inlined before writing.
14
+ */
15
+ content: string;
16
+ /**
17
+ * Working directory — replaces `process.cwd()`.
18
+ * Used to construct the CWD-relative target path:
19
+ * `<cwd>/.cursor/rules/multi-model-agent.mdc`
20
+ */
21
+ cwd: string;
22
+ /**
23
+ * Home directory. Accepted for API signature compatibility with other
24
+ * skill writers, but NOT used by this writer. Cursor rules are always
25
+ * written relative to `cwd`, not `homeDir`.
26
+ */
27
+ homeDir: string;
28
+ /**
29
+ * Where shared files live. The writer reads `<skillsRoot>/_shared/<path>`
30
+ * when inlining `@include` directives.
31
+ */
32
+ skillsRoot: string;
33
+ /**
34
+ * If true, overwrite the existing file. If false (default), skip writing
35
+ * when the file already exists.
36
+ */
37
+ force?: boolean;
38
+ }
39
+ /**
40
+ * Result of `installCursor`.
41
+ */
42
+ export interface CursorInstallResult {
43
+ /**
44
+ * `true` if the file was written, `false` if it was skipped because it
45
+ * already exists and `force` was not set.
46
+ */
47
+ written: boolean;
48
+ /** The full path that was (or would have been) written. */
49
+ targetPath: string;
50
+ }
51
+ /**
52
+ * Install the multi-model-agent skill for Cursor.
53
+ *
54
+ * - Target path is `<cwd>/.cursor/rules/multi-model-agent.mdc` (CWD-relative).
55
+ * - Creates `<cwd>/.cursor/rules/` if it does not exist.
56
+ * - Inlines `@include` directives before writing (see `inlineIncludes`).
57
+ * - If the file already exists and `force` is not set, skips writing and
58
+ * returns `written: false`.
59
+ *
60
+ * @param opts Installation options (see `CursorInstallOpts`).
61
+ */
62
+ export declare function installCursor(opts: CursorInstallOpts): CursorInstallResult;
63
+ /**
64
+ * Uninstall the multi-model-agent Cursor skill.
65
+ *
66
+ * Removes `<cwd>/.cursor/rules/multi-model-agent.mdc`.
67
+ * This is a no-op when the file does not exist (no error is thrown).
68
+ *
69
+ * @param cwd Working directory (replaces `process.cwd()`).
70
+ */
71
+ export declare function uninstallCursor(cwd: string): void;
72
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/install/cursor.ts"],"names":[],"mappings":"AA8BA;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAmB1E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBjD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Cursor skill writer.
3
+ *
4
+ * Writes the skill content to `<cwd>/.cursor/rules/multi-model-agent.mdc`.
5
+ *
6
+ * Before writing, inlines any `@include _shared/<file>.md` directives found in
7
+ * the content. The directive line is replaced with the full content of
8
+ * the corresponding shared file sourced from `<skillsRoot>/_shared/<path>`.
9
+ * The `@include` directive is NOT preserved in the written file.
10
+ *
11
+ * If a referenced shared file is missing (ENOENT):
12
+ * - A warning is logged to stderr.
13
+ * - The include line is removed from the output (not preserved).
14
+ * - Processing continues for remaining directives.
15
+ *
16
+ * Security constraints on `@include` directives:
17
+ * - Only paths beginning with `_shared/` are accepted.
18
+ * - The resolved path must stay within `<skillsRoot>/_shared/`.
19
+ * Path traversal attempts (e.g. `_shared/../secrets.txt`) are rejected
20
+ * with a warning and the directive line is dropped.
21
+ * - Non-`_shared/` paths are rejected with a warning and the directive
22
+ * line is dropped.
23
+ *
24
+ * @module
25
+ */
26
+ import fs from 'node:fs';
27
+ import path from 'node:path';
28
+ import { inlineIncludes } from './include-utils.js';
29
+ /**
30
+ * Install the multi-model-agent skill for Cursor.
31
+ *
32
+ * - Target path is `<cwd>/.cursor/rules/multi-model-agent.mdc` (CWD-relative).
33
+ * - Creates `<cwd>/.cursor/rules/` if it does not exist.
34
+ * - Inlines `@include` directives before writing (see `inlineIncludes`).
35
+ * - If the file already exists and `force` is not set, skips writing and
36
+ * returns `written: false`.
37
+ *
38
+ * @param opts Installation options (see `CursorInstallOpts`).
39
+ */
40
+ export function installCursor(opts) {
41
+ const { content, cwd, skillsRoot, force } = opts;
42
+ const targetPath = path.join(cwd, '.cursor', 'rules', 'multi-model-agent.mdc');
43
+ if (!force && fs.existsSync(targetPath)) {
44
+ process.stderr.write(`Warning: Cursor skill writer: file already exists: ${targetPath} — skipping (use force: true to overwrite)\n`);
45
+ return { written: false, targetPath };
46
+ }
47
+ const rulesDir = path.join(cwd, '.cursor', 'rules');
48
+ fs.mkdirSync(rulesDir, { recursive: true, mode: 0o700 });
49
+ const finalContent = inlineIncludes('Cursor skill writer', content, skillsRoot);
50
+ fs.writeFileSync(targetPath, finalContent, 'utf-8');
51
+ return { written: true, targetPath };
52
+ }
53
+ /**
54
+ * Uninstall the multi-model-agent Cursor skill.
55
+ *
56
+ * Removes `<cwd>/.cursor/rules/multi-model-agent.mdc`.
57
+ * This is a no-op when the file does not exist (no error is thrown).
58
+ *
59
+ * @param cwd Working directory (replaces `process.cwd()`).
60
+ */
61
+ export function uninstallCursor(cwd) {
62
+ const targetPath = path.join(cwd, '.cursor', 'rules', 'multi-model-agent.mdc');
63
+ // Only remove the file if it exists and is a file (not a directory).
64
+ // This is a no-op when the path does not exist.
65
+ try {
66
+ const stat = fs.statSync(targetPath);
67
+ if (stat.isFile()) {
68
+ fs.unlinkSync(targetPath);
69
+ }
70
+ // If it's a directory or symlink to directory, do nothing — the brief
71
+ // specifies removing a file, not a directory tree.
72
+ }
73
+ catch (err) {
74
+ const nodeErr = err;
75
+ // ENOENT means the file doesn't exist — that's fine, we were asked to remove it
76
+ if (nodeErr.code !== 'ENOENT') {
77
+ throw err;
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/install/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAsDpD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAEjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC;IAE/E,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sDAAsD,UAAU,8CAA8C,CAC/G,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IACxC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEzD,MAAM,YAAY,GAAG,cAAc,CAAC,qBAAqB,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAChF,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAEpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC;IAE/E,qEAAqE;IACrE,gDAAgD;IAChD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAClB,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5B,CAAC;QACD,sEAAsE;QACtE,mDAAmD;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAA4B,CAAC;QAC7C,gFAAgF;QAChF,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Options for installing a skill via the Gemini CLI writer.
3
+ */
4
+ export interface GeminiCliInstallOpts {
5
+ /** Skill name (currently informational; writes always go to multi-model-agent extension). */
6
+ skillName: string;
7
+ /**
8
+ * Raw SKILL.md content. May contain `@include _shared/<file>.md` directives
9
+ * which are inlined before writing.
10
+ */
11
+ content: string;
12
+ /**
13
+ * Version string for the extension manifest's `version` field.
14
+ */
15
+ skillVersion: string;
16
+ /**
17
+ * The "home directory" that replaces `os.homedir()`.
18
+ * Must NOT default to `os.homedir()` — always required explicitly.
19
+ */
20
+ homeDir: string;
21
+ /**
22
+ * Where shared files live. The writer reads `<skillsRoot>/_shared/<file>.md`
23
+ * when inlining `@include` directives.
24
+ */
25
+ skillsRoot: string;
26
+ }
27
+ /**
28
+ * Inline `@include _shared/<file>.md` directives in `content`.
29
+ *
30
+ * Each line matching `@include <path>` (space after `@include`) is replaced with
31
+ * the full content of `<skillsRoot>/_shared/<path>`.
32
+ *
33
+ * If a shared file is missing:
34
+ * - A warning is written to stderr.
35
+ * - The include line is removed from the output (not preserved).
36
+ * - Processing continues for remaining directives.
37
+ *
38
+ * @param content Raw SKILL.md content (may contain @include directives).
39
+ * @param skillsRoot Root directory containing `_shared/` sub-directory.
40
+ * @returns The content with directives inlined.
41
+ */
42
+ export declare function inlineIncludes(content: string, skillsRoot: string): string;
43
+ /**
44
+ * Install a skill to the Gemini CLI extensions directory.
45
+ *
46
+ * Writes two files into `<homeDir>/.gemini/extensions/multi-model-agent/`:
47
+ * 1. `gemini-extension.json` — the extension manifest
48
+ * 2. `SKILL.md` — the skill content with @include directives inlined
49
+ *
50
+ * The directory (and any parent directories) are created with mode `0o700`.
51
+ * Calling this function multiple times overwrites the previous installation
52
+ * (idempotent).
53
+ *
54
+ * @param opts Installation options (see `GeminiCliInstallOpts`).
55
+ */
56
+ export declare function installGeminiCli(opts: GeminiCliInstallOpts): void;
57
+ /**
58
+ * Uninstall the multi-model-agent Gemini CLI extension.
59
+ *
60
+ * Recursively removes `<homeDir>/.gemini/extensions/multi-model-agent/`.
61
+ * This is a no-op when the directory does not exist (no error is thrown).
62
+ *
63
+ * @param homeDir The "home directory" that replaces `os.homedir()`.
64
+ */
65
+ export declare function uninstallGeminiCli(homeDir: string): void;
66
+ //# sourceMappingURL=gemini-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-cli.d.ts","sourceRoot":"","sources":["../../src/install/gemini-cli.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA6B1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAuBjE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKxD"}