@omnidev-ai/cli 0.4.0 → 0.5.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/dist/index.js +986 -696
- package/dist/shared/chunk-7txw9v1e.js +3017 -0
- package/package.json +35 -36
|
@@ -0,0 +1,3017 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
|
+
|
|
21
|
+
// ../core/dist/shared/chunk-1dqs11h6.js
|
|
22
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
23
|
+
|
|
24
|
+
// ../core/dist/index.js
|
|
25
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
26
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
29
|
+
import { basename, join as join2 } from "node:path";
|
|
30
|
+
import { existsSync as existsSync7, readdirSync as readdirSync6 } from "node:fs";
|
|
31
|
+
import { join as join6 } from "node:path";
|
|
32
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
33
|
+
|
|
34
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
35
|
+
/*!
|
|
36
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
37
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
38
|
+
*
|
|
39
|
+
* Redistribution and use in source and binary forms, with or without
|
|
40
|
+
* modification, are permitted provided that the following conditions are met:
|
|
41
|
+
*
|
|
42
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
43
|
+
* list of conditions and the following disclaimer.
|
|
44
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
45
|
+
* this list of conditions and the following disclaimer in the
|
|
46
|
+
* documentation and/or other materials provided with the distribution.
|
|
47
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
48
|
+
* may be used to endorse or promote products derived from this software without
|
|
49
|
+
* specific prior written permission.
|
|
50
|
+
*
|
|
51
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
52
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
53
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
54
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
55
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
56
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
57
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
58
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
59
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
60
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
61
|
+
*/
|
|
62
|
+
function getLineColFromPtr(string, ptr) {
|
|
63
|
+
let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
64
|
+
return [lines.length, lines.pop().length + 1];
|
|
65
|
+
}
|
|
66
|
+
function makeCodeBlock(string, line, column) {
|
|
67
|
+
let lines = string.split(/\r\n|\n|\r/g);
|
|
68
|
+
let codeblock = "";
|
|
69
|
+
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
70
|
+
for (let i = line - 1;i <= line + 1; i++) {
|
|
71
|
+
let l = lines[i - 1];
|
|
72
|
+
if (!l)
|
|
73
|
+
continue;
|
|
74
|
+
codeblock += i.toString().padEnd(numberLen, " ");
|
|
75
|
+
codeblock += ": ";
|
|
76
|
+
codeblock += l;
|
|
77
|
+
codeblock += `
|
|
78
|
+
`;
|
|
79
|
+
if (i === line) {
|
|
80
|
+
codeblock += " ".repeat(numberLen + column + 2);
|
|
81
|
+
codeblock += `^
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return codeblock;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class TomlError extends Error {
|
|
89
|
+
line;
|
|
90
|
+
column;
|
|
91
|
+
codeblock;
|
|
92
|
+
constructor(message, options) {
|
|
93
|
+
const [line, column] = getLineColFromPtr(options.toml, options.ptr);
|
|
94
|
+
const codeblock = makeCodeBlock(options.toml, line, column);
|
|
95
|
+
super(`Invalid TOML document: ${message}
|
|
96
|
+
|
|
97
|
+
${codeblock}`, options);
|
|
98
|
+
this.line = line;
|
|
99
|
+
this.column = column;
|
|
100
|
+
this.codeblock = codeblock;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/util.js
|
|
105
|
+
/*!
|
|
106
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
107
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
108
|
+
*
|
|
109
|
+
* Redistribution and use in source and binary forms, with or without
|
|
110
|
+
* modification, are permitted provided that the following conditions are met:
|
|
111
|
+
*
|
|
112
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
113
|
+
* list of conditions and the following disclaimer.
|
|
114
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
115
|
+
* this list of conditions and the following disclaimer in the
|
|
116
|
+
* documentation and/or other materials provided with the distribution.
|
|
117
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
118
|
+
* may be used to endorse or promote products derived from this software without
|
|
119
|
+
* specific prior written permission.
|
|
120
|
+
*
|
|
121
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
122
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
123
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
124
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
125
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
126
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
127
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
128
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
129
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
130
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
131
|
+
*/
|
|
132
|
+
function isEscaped(str, ptr) {
|
|
133
|
+
let i = 0;
|
|
134
|
+
while (str[ptr - ++i] === "\\")
|
|
135
|
+
;
|
|
136
|
+
return --i && i % 2;
|
|
137
|
+
}
|
|
138
|
+
function indexOfNewline(str, start = 0, end = str.length) {
|
|
139
|
+
let idx = str.indexOf(`
|
|
140
|
+
`, start);
|
|
141
|
+
if (str[idx - 1] === "\r")
|
|
142
|
+
idx--;
|
|
143
|
+
return idx <= end ? idx : -1;
|
|
144
|
+
}
|
|
145
|
+
function skipComment(str, ptr) {
|
|
146
|
+
for (let i = ptr;i < str.length; i++) {
|
|
147
|
+
let c = str[i];
|
|
148
|
+
if (c === `
|
|
149
|
+
`)
|
|
150
|
+
return i;
|
|
151
|
+
if (c === "\r" && str[i + 1] === `
|
|
152
|
+
`)
|
|
153
|
+
return i + 1;
|
|
154
|
+
if (c < " " && c !== "\t" || c === "") {
|
|
155
|
+
throw new TomlError("control characters are not allowed in comments", {
|
|
156
|
+
toml: str,
|
|
157
|
+
ptr
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return str.length;
|
|
162
|
+
}
|
|
163
|
+
function skipVoid(str, ptr, banNewLines, banComments) {
|
|
164
|
+
let c;
|
|
165
|
+
while ((c = str[ptr]) === " " || c === "\t" || !banNewLines && (c === `
|
|
166
|
+
` || c === "\r" && str[ptr + 1] === `
|
|
167
|
+
`))
|
|
168
|
+
ptr++;
|
|
169
|
+
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
170
|
+
}
|
|
171
|
+
function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
172
|
+
if (!end) {
|
|
173
|
+
ptr = indexOfNewline(str, ptr);
|
|
174
|
+
return ptr < 0 ? str.length : ptr;
|
|
175
|
+
}
|
|
176
|
+
for (let i = ptr;i < str.length; i++) {
|
|
177
|
+
let c = str[i];
|
|
178
|
+
if (c === "#") {
|
|
179
|
+
i = indexOfNewline(str, i);
|
|
180
|
+
} else if (c === sep) {
|
|
181
|
+
return i + 1;
|
|
182
|
+
} else if (c === end || banNewLines && (c === `
|
|
183
|
+
` || c === "\r" && str[i + 1] === `
|
|
184
|
+
`)) {
|
|
185
|
+
return i;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
throw new TomlError("cannot find end of structure", {
|
|
189
|
+
toml: str,
|
|
190
|
+
ptr
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function getStringEnd(str, seek) {
|
|
194
|
+
let first = str[seek];
|
|
195
|
+
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first;
|
|
196
|
+
seek += target.length - 1;
|
|
197
|
+
do
|
|
198
|
+
seek = str.indexOf(target, ++seek);
|
|
199
|
+
while (seek > -1 && first !== "'" && isEscaped(str, seek));
|
|
200
|
+
if (seek > -1) {
|
|
201
|
+
seek += target.length;
|
|
202
|
+
if (target.length > 1) {
|
|
203
|
+
if (str[seek] === first)
|
|
204
|
+
seek++;
|
|
205
|
+
if (str[seek] === first)
|
|
206
|
+
seek++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return seek;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/date.js
|
|
213
|
+
/*!
|
|
214
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
215
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
216
|
+
*
|
|
217
|
+
* Redistribution and use in source and binary forms, with or without
|
|
218
|
+
* modification, are permitted provided that the following conditions are met:
|
|
219
|
+
*
|
|
220
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
221
|
+
* list of conditions and the following disclaimer.
|
|
222
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
223
|
+
* this list of conditions and the following disclaimer in the
|
|
224
|
+
* documentation and/or other materials provided with the distribution.
|
|
225
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
226
|
+
* may be used to endorse or promote products derived from this software without
|
|
227
|
+
* specific prior written permission.
|
|
228
|
+
*
|
|
229
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
230
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
231
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
232
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
233
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
234
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
235
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
236
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
237
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
238
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
239
|
+
*/
|
|
240
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
241
|
+
|
|
242
|
+
class TomlDate extends Date {
|
|
243
|
+
#hasDate = false;
|
|
244
|
+
#hasTime = false;
|
|
245
|
+
#offset = null;
|
|
246
|
+
constructor(date) {
|
|
247
|
+
let hasDate = true;
|
|
248
|
+
let hasTime = true;
|
|
249
|
+
let offset = "Z";
|
|
250
|
+
if (typeof date === "string") {
|
|
251
|
+
let match = date.match(DATE_TIME_RE);
|
|
252
|
+
if (match) {
|
|
253
|
+
if (!match[1]) {
|
|
254
|
+
hasDate = false;
|
|
255
|
+
date = `0000-01-01T${date}`;
|
|
256
|
+
}
|
|
257
|
+
hasTime = !!match[2];
|
|
258
|
+
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
259
|
+
if (match[2] && +match[2] > 23) {
|
|
260
|
+
date = "";
|
|
261
|
+
} else {
|
|
262
|
+
offset = match[3] || null;
|
|
263
|
+
date = date.toUpperCase();
|
|
264
|
+
if (!offset && hasTime)
|
|
265
|
+
date += "Z";
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
date = "";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
super(date);
|
|
272
|
+
if (!isNaN(this.getTime())) {
|
|
273
|
+
this.#hasDate = hasDate;
|
|
274
|
+
this.#hasTime = hasTime;
|
|
275
|
+
this.#offset = offset;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
isDateTime() {
|
|
279
|
+
return this.#hasDate && this.#hasTime;
|
|
280
|
+
}
|
|
281
|
+
isLocal() {
|
|
282
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
283
|
+
}
|
|
284
|
+
isDate() {
|
|
285
|
+
return this.#hasDate && !this.#hasTime;
|
|
286
|
+
}
|
|
287
|
+
isTime() {
|
|
288
|
+
return this.#hasTime && !this.#hasDate;
|
|
289
|
+
}
|
|
290
|
+
isValid() {
|
|
291
|
+
return this.#hasDate || this.#hasTime;
|
|
292
|
+
}
|
|
293
|
+
toISOString() {
|
|
294
|
+
let iso = super.toISOString();
|
|
295
|
+
if (this.isDate())
|
|
296
|
+
return iso.slice(0, 10);
|
|
297
|
+
if (this.isTime())
|
|
298
|
+
return iso.slice(11, 23);
|
|
299
|
+
if (this.#offset === null)
|
|
300
|
+
return iso.slice(0, -1);
|
|
301
|
+
if (this.#offset === "Z")
|
|
302
|
+
return iso;
|
|
303
|
+
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
304
|
+
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
305
|
+
let offsetDate = new Date(this.getTime() - offset * 60000);
|
|
306
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
307
|
+
}
|
|
308
|
+
static wrapAsOffsetDateTime(jsDate, offset = "Z") {
|
|
309
|
+
let date = new TomlDate(jsDate);
|
|
310
|
+
date.#offset = offset;
|
|
311
|
+
return date;
|
|
312
|
+
}
|
|
313
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
314
|
+
let date = new TomlDate(jsDate);
|
|
315
|
+
date.#offset = null;
|
|
316
|
+
return date;
|
|
317
|
+
}
|
|
318
|
+
static wrapAsLocalDate(jsDate) {
|
|
319
|
+
let date = new TomlDate(jsDate);
|
|
320
|
+
date.#hasTime = false;
|
|
321
|
+
date.#offset = null;
|
|
322
|
+
return date;
|
|
323
|
+
}
|
|
324
|
+
static wrapAsLocalTime(jsDate) {
|
|
325
|
+
let date = new TomlDate(jsDate);
|
|
326
|
+
date.#hasDate = false;
|
|
327
|
+
date.#offset = null;
|
|
328
|
+
return date;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/primitive.js
|
|
333
|
+
/*!
|
|
334
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
335
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
336
|
+
*
|
|
337
|
+
* Redistribution and use in source and binary forms, with or without
|
|
338
|
+
* modification, are permitted provided that the following conditions are met:
|
|
339
|
+
*
|
|
340
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
341
|
+
* list of conditions and the following disclaimer.
|
|
342
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
343
|
+
* this list of conditions and the following disclaimer in the
|
|
344
|
+
* documentation and/or other materials provided with the distribution.
|
|
345
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
346
|
+
* may be used to endorse or promote products derived from this software without
|
|
347
|
+
* specific prior written permission.
|
|
348
|
+
*
|
|
349
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
350
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
351
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
352
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
353
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
354
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
355
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
356
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
357
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
358
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
359
|
+
*/
|
|
360
|
+
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
361
|
+
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
362
|
+
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
363
|
+
var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
|
|
364
|
+
var ESC_MAP = {
|
|
365
|
+
b: "\b",
|
|
366
|
+
t: "\t",
|
|
367
|
+
n: `
|
|
368
|
+
`,
|
|
369
|
+
f: "\f",
|
|
370
|
+
r: "\r",
|
|
371
|
+
e: "\x1B",
|
|
372
|
+
'"': '"',
|
|
373
|
+
"\\": "\\"
|
|
374
|
+
};
|
|
375
|
+
function parseString(str, ptr = 0, endPtr = str.length) {
|
|
376
|
+
let isLiteral = str[ptr] === "'";
|
|
377
|
+
let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
|
|
378
|
+
if (isMultiline) {
|
|
379
|
+
endPtr -= 2;
|
|
380
|
+
if (str[ptr += 2] === "\r")
|
|
381
|
+
ptr++;
|
|
382
|
+
if (str[ptr] === `
|
|
383
|
+
`)
|
|
384
|
+
ptr++;
|
|
385
|
+
}
|
|
386
|
+
let tmp = 0;
|
|
387
|
+
let isEscape;
|
|
388
|
+
let parsed = "";
|
|
389
|
+
let sliceStart = ptr;
|
|
390
|
+
while (ptr < endPtr - 1) {
|
|
391
|
+
let c = str[ptr++];
|
|
392
|
+
if (c === `
|
|
393
|
+
` || c === "\r" && str[ptr] === `
|
|
394
|
+
`) {
|
|
395
|
+
if (!isMultiline) {
|
|
396
|
+
throw new TomlError("newlines are not allowed in strings", {
|
|
397
|
+
toml: str,
|
|
398
|
+
ptr: ptr - 1
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
} else if (c < " " && c !== "\t" || c === "") {
|
|
402
|
+
throw new TomlError("control characters are not allowed in strings", {
|
|
403
|
+
toml: str,
|
|
404
|
+
ptr: ptr - 1
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (isEscape) {
|
|
408
|
+
isEscape = false;
|
|
409
|
+
if (c === "x" || c === "u" || c === "U") {
|
|
410
|
+
let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
|
|
411
|
+
if (!ESCAPE_REGEX.test(code)) {
|
|
412
|
+
throw new TomlError("invalid unicode escape", {
|
|
413
|
+
toml: str,
|
|
414
|
+
ptr: tmp
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
parsed += String.fromCodePoint(parseInt(code, 16));
|
|
419
|
+
} catch {
|
|
420
|
+
throw new TomlError("invalid unicode escape", {
|
|
421
|
+
toml: str,
|
|
422
|
+
ptr: tmp
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
} else if (isMultiline && (c === `
|
|
426
|
+
` || c === " " || c === "\t" || c === "\r")) {
|
|
427
|
+
ptr = skipVoid(str, ptr - 1, true);
|
|
428
|
+
if (str[ptr] !== `
|
|
429
|
+
` && str[ptr] !== "\r") {
|
|
430
|
+
throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
|
|
431
|
+
toml: str,
|
|
432
|
+
ptr: tmp
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
ptr = skipVoid(str, ptr);
|
|
436
|
+
} else if (c in ESC_MAP) {
|
|
437
|
+
parsed += ESC_MAP[c];
|
|
438
|
+
} else {
|
|
439
|
+
throw new TomlError("unrecognized escape sequence", {
|
|
440
|
+
toml: str,
|
|
441
|
+
ptr: tmp
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
sliceStart = ptr;
|
|
445
|
+
} else if (!isLiteral && c === "\\") {
|
|
446
|
+
tmp = ptr - 1;
|
|
447
|
+
isEscape = true;
|
|
448
|
+
parsed += str.slice(sliceStart, tmp);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return parsed + str.slice(sliceStart, endPtr - 1);
|
|
452
|
+
}
|
|
453
|
+
function parseValue(value, toml, ptr, integersAsBigInt) {
|
|
454
|
+
if (value === "true")
|
|
455
|
+
return true;
|
|
456
|
+
if (value === "false")
|
|
457
|
+
return false;
|
|
458
|
+
if (value === "-inf")
|
|
459
|
+
return -Infinity;
|
|
460
|
+
if (value === "inf" || value === "+inf")
|
|
461
|
+
return Infinity;
|
|
462
|
+
if (value === "nan" || value === "+nan" || value === "-nan")
|
|
463
|
+
return NaN;
|
|
464
|
+
if (value === "-0")
|
|
465
|
+
return integersAsBigInt ? 0n : 0;
|
|
466
|
+
let isInt = INT_REGEX.test(value);
|
|
467
|
+
if (isInt || FLOAT_REGEX.test(value)) {
|
|
468
|
+
if (LEADING_ZERO.test(value)) {
|
|
469
|
+
throw new TomlError("leading zeroes are not allowed", {
|
|
470
|
+
toml,
|
|
471
|
+
ptr
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
value = value.replace(/_/g, "");
|
|
475
|
+
let numeric = +value;
|
|
476
|
+
if (isNaN(numeric)) {
|
|
477
|
+
throw new TomlError("invalid number", {
|
|
478
|
+
toml,
|
|
479
|
+
ptr
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
if (isInt) {
|
|
483
|
+
if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
|
|
484
|
+
throw new TomlError("integer value cannot be represented losslessly", {
|
|
485
|
+
toml,
|
|
486
|
+
ptr
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
if (isInt || integersAsBigInt === true)
|
|
490
|
+
numeric = BigInt(value);
|
|
491
|
+
}
|
|
492
|
+
return numeric;
|
|
493
|
+
}
|
|
494
|
+
const date = new TomlDate(value);
|
|
495
|
+
if (!date.isValid()) {
|
|
496
|
+
throw new TomlError("invalid value", {
|
|
497
|
+
toml,
|
|
498
|
+
ptr
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return date;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/extract.js
|
|
505
|
+
/*!
|
|
506
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
507
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
508
|
+
*
|
|
509
|
+
* Redistribution and use in source and binary forms, with or without
|
|
510
|
+
* modification, are permitted provided that the following conditions are met:
|
|
511
|
+
*
|
|
512
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
513
|
+
* list of conditions and the following disclaimer.
|
|
514
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
515
|
+
* this list of conditions and the following disclaimer in the
|
|
516
|
+
* documentation and/or other materials provided with the distribution.
|
|
517
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
518
|
+
* may be used to endorse or promote products derived from this software without
|
|
519
|
+
* specific prior written permission.
|
|
520
|
+
*
|
|
521
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
522
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
523
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
524
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
525
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
526
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
527
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
528
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
529
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
530
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
531
|
+
*/
|
|
532
|
+
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
533
|
+
let value = str.slice(startPtr, endPtr);
|
|
534
|
+
let commentIdx = value.indexOf("#");
|
|
535
|
+
if (commentIdx > -1) {
|
|
536
|
+
skipComment(str, commentIdx);
|
|
537
|
+
value = value.slice(0, commentIdx);
|
|
538
|
+
}
|
|
539
|
+
return [value.trimEnd(), commentIdx];
|
|
540
|
+
}
|
|
541
|
+
function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
542
|
+
if (depth === 0) {
|
|
543
|
+
throw new TomlError("document contains excessively nested structures. aborting.", {
|
|
544
|
+
toml: str,
|
|
545
|
+
ptr
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
let c = str[ptr];
|
|
549
|
+
if (c === "[" || c === "{") {
|
|
550
|
+
let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
|
|
551
|
+
if (end) {
|
|
552
|
+
endPtr2 = skipVoid(str, endPtr2);
|
|
553
|
+
if (str[endPtr2] === ",")
|
|
554
|
+
endPtr2++;
|
|
555
|
+
else if (str[endPtr2] !== end) {
|
|
556
|
+
throw new TomlError("expected comma or end of structure", {
|
|
557
|
+
toml: str,
|
|
558
|
+
ptr: endPtr2
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return [value, endPtr2];
|
|
563
|
+
}
|
|
564
|
+
let endPtr;
|
|
565
|
+
if (c === '"' || c === "'") {
|
|
566
|
+
endPtr = getStringEnd(str, ptr);
|
|
567
|
+
let parsed = parseString(str, ptr, endPtr);
|
|
568
|
+
if (end) {
|
|
569
|
+
endPtr = skipVoid(str, endPtr);
|
|
570
|
+
if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== `
|
|
571
|
+
` && str[endPtr] !== "\r") {
|
|
572
|
+
throw new TomlError("unexpected character encountered", {
|
|
573
|
+
toml: str,
|
|
574
|
+
ptr: endPtr
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
endPtr += +(str[endPtr] === ",");
|
|
578
|
+
}
|
|
579
|
+
return [parsed, endPtr];
|
|
580
|
+
}
|
|
581
|
+
endPtr = skipUntil(str, ptr, ",", end);
|
|
582
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
|
|
583
|
+
if (!slice[0]) {
|
|
584
|
+
throw new TomlError("incomplete key-value declaration: no value specified", {
|
|
585
|
+
toml: str,
|
|
586
|
+
ptr
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if (end && slice[1] > -1) {
|
|
590
|
+
endPtr = skipVoid(str, ptr + slice[1]);
|
|
591
|
+
endPtr += +(str[endPtr] === ",");
|
|
592
|
+
}
|
|
593
|
+
return [
|
|
594
|
+
parseValue(slice[0], str, ptr, integersAsBigInt),
|
|
595
|
+
endPtr
|
|
596
|
+
];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/struct.js
|
|
600
|
+
/*!
|
|
601
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
602
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
603
|
+
*
|
|
604
|
+
* Redistribution and use in source and binary forms, with or without
|
|
605
|
+
* modification, are permitted provided that the following conditions are met:
|
|
606
|
+
*
|
|
607
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
608
|
+
* list of conditions and the following disclaimer.
|
|
609
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
610
|
+
* this list of conditions and the following disclaimer in the
|
|
611
|
+
* documentation and/or other materials provided with the distribution.
|
|
612
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
613
|
+
* may be used to endorse or promote products derived from this software without
|
|
614
|
+
* specific prior written permission.
|
|
615
|
+
*
|
|
616
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
617
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
618
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
619
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
620
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
621
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
622
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
623
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
624
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
625
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
626
|
+
*/
|
|
627
|
+
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
628
|
+
function parseKey(str, ptr, end = "=") {
|
|
629
|
+
let dot = ptr - 1;
|
|
630
|
+
let parsed = [];
|
|
631
|
+
let endPtr = str.indexOf(end, ptr);
|
|
632
|
+
if (endPtr < 0) {
|
|
633
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
634
|
+
toml: str,
|
|
635
|
+
ptr
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
do {
|
|
639
|
+
let c = str[ptr = ++dot];
|
|
640
|
+
if (c !== " " && c !== "\t") {
|
|
641
|
+
if (c === '"' || c === "'") {
|
|
642
|
+
if (c === str[ptr + 1] && c === str[ptr + 2]) {
|
|
643
|
+
throw new TomlError("multiline strings are not allowed in keys", {
|
|
644
|
+
toml: str,
|
|
645
|
+
ptr
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
let eos = getStringEnd(str, ptr);
|
|
649
|
+
if (eos < 0) {
|
|
650
|
+
throw new TomlError("unfinished string encountered", {
|
|
651
|
+
toml: str,
|
|
652
|
+
ptr
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
dot = str.indexOf(".", eos);
|
|
656
|
+
let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
657
|
+
let newLine = indexOfNewline(strEnd);
|
|
658
|
+
if (newLine > -1) {
|
|
659
|
+
throw new TomlError("newlines are not allowed in keys", {
|
|
660
|
+
toml: str,
|
|
661
|
+
ptr: ptr + dot + newLine
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
if (strEnd.trimStart()) {
|
|
665
|
+
throw new TomlError("found extra tokens after the string part", {
|
|
666
|
+
toml: str,
|
|
667
|
+
ptr: eos
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
if (endPtr < eos) {
|
|
671
|
+
endPtr = str.indexOf(end, eos);
|
|
672
|
+
if (endPtr < 0) {
|
|
673
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
674
|
+
toml: str,
|
|
675
|
+
ptr
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
parsed.push(parseString(str, ptr, eos));
|
|
680
|
+
} else {
|
|
681
|
+
dot = str.indexOf(".", ptr);
|
|
682
|
+
let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
683
|
+
if (!KEY_PART_RE.test(part)) {
|
|
684
|
+
throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
|
|
685
|
+
toml: str,
|
|
686
|
+
ptr
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
parsed.push(part.trimEnd());
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} while (dot + 1 && dot < endPtr);
|
|
693
|
+
return [parsed, skipVoid(str, endPtr + 1, true, true)];
|
|
694
|
+
}
|
|
695
|
+
function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
696
|
+
let res = {};
|
|
697
|
+
let seen = new Set;
|
|
698
|
+
let c;
|
|
699
|
+
ptr++;
|
|
700
|
+
while ((c = str[ptr++]) !== "}" && c) {
|
|
701
|
+
if (c === ",") {
|
|
702
|
+
throw new TomlError("expected value, found comma", {
|
|
703
|
+
toml: str,
|
|
704
|
+
ptr: ptr - 1
|
|
705
|
+
});
|
|
706
|
+
} else if (c === "#")
|
|
707
|
+
ptr = skipComment(str, ptr);
|
|
708
|
+
else if (c !== " " && c !== "\t" && c !== `
|
|
709
|
+
` && c !== "\r") {
|
|
710
|
+
let k;
|
|
711
|
+
let t = res;
|
|
712
|
+
let hasOwn = false;
|
|
713
|
+
let [key, keyEndPtr] = parseKey(str, ptr - 1);
|
|
714
|
+
for (let i = 0;i < key.length; i++) {
|
|
715
|
+
if (i)
|
|
716
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
717
|
+
k = key[i];
|
|
718
|
+
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
|
|
719
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
720
|
+
toml: str,
|
|
721
|
+
ptr
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
if (!hasOwn && k === "__proto__") {
|
|
725
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (hasOwn) {
|
|
729
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
730
|
+
toml: str,
|
|
731
|
+
ptr
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
|
|
735
|
+
seen.add(value);
|
|
736
|
+
t[k] = value;
|
|
737
|
+
ptr = valueEndPtr;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (!c) {
|
|
741
|
+
throw new TomlError("unfinished table encountered", {
|
|
742
|
+
toml: str,
|
|
743
|
+
ptr
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
return [res, ptr];
|
|
747
|
+
}
|
|
748
|
+
function parseArray(str, ptr, depth, integersAsBigInt) {
|
|
749
|
+
let res = [];
|
|
750
|
+
let c;
|
|
751
|
+
ptr++;
|
|
752
|
+
while ((c = str[ptr++]) !== "]" && c) {
|
|
753
|
+
if (c === ",") {
|
|
754
|
+
throw new TomlError("expected value, found comma", {
|
|
755
|
+
toml: str,
|
|
756
|
+
ptr: ptr - 1
|
|
757
|
+
});
|
|
758
|
+
} else if (c === "#")
|
|
759
|
+
ptr = skipComment(str, ptr);
|
|
760
|
+
else if (c !== " " && c !== "\t" && c !== `
|
|
761
|
+
` && c !== "\r") {
|
|
762
|
+
let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
|
|
763
|
+
res.push(e[0]);
|
|
764
|
+
ptr = e[1];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (!c) {
|
|
768
|
+
throw new TomlError("unfinished array encountered", {
|
|
769
|
+
toml: str,
|
|
770
|
+
ptr
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
return [res, ptr];
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/parse.js
|
|
777
|
+
/*!
|
|
778
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
779
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
780
|
+
*
|
|
781
|
+
* Redistribution and use in source and binary forms, with or without
|
|
782
|
+
* modification, are permitted provided that the following conditions are met:
|
|
783
|
+
*
|
|
784
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
785
|
+
* list of conditions and the following disclaimer.
|
|
786
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
787
|
+
* this list of conditions and the following disclaimer in the
|
|
788
|
+
* documentation and/or other materials provided with the distribution.
|
|
789
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
790
|
+
* may be used to endorse or promote products derived from this software without
|
|
791
|
+
* specific prior written permission.
|
|
792
|
+
*
|
|
793
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
794
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
795
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
796
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
797
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
798
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
799
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
800
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
801
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
802
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
803
|
+
*/
|
|
804
|
+
function peekTable(key, table, meta, type) {
|
|
805
|
+
let t = table;
|
|
806
|
+
let m = meta;
|
|
807
|
+
let k;
|
|
808
|
+
let hasOwn = false;
|
|
809
|
+
let state;
|
|
810
|
+
for (let i = 0;i < key.length; i++) {
|
|
811
|
+
if (i) {
|
|
812
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
813
|
+
m = (state = m[k]).c;
|
|
814
|
+
if (type === 0 && (state.t === 1 || state.t === 2)) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
if (state.t === 2) {
|
|
818
|
+
let l = t.length - 1;
|
|
819
|
+
t = t[l];
|
|
820
|
+
m = m[l].c;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
k = key[i];
|
|
824
|
+
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
if (!hasOwn) {
|
|
828
|
+
if (k === "__proto__") {
|
|
829
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
830
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
831
|
+
}
|
|
832
|
+
m[k] = {
|
|
833
|
+
t: i < key.length - 1 && type === 2 ? 3 : type,
|
|
834
|
+
d: false,
|
|
835
|
+
i: 0,
|
|
836
|
+
c: {}
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
state = m[k];
|
|
841
|
+
if (state.t !== type && !(type === 1 && state.t === 3)) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
if (type === 2) {
|
|
845
|
+
if (!state.d) {
|
|
846
|
+
state.d = true;
|
|
847
|
+
t[k] = [];
|
|
848
|
+
}
|
|
849
|
+
t[k].push(t = {});
|
|
850
|
+
state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
|
|
851
|
+
}
|
|
852
|
+
if (state.d) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
state.d = true;
|
|
856
|
+
if (type === 1) {
|
|
857
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
858
|
+
} else if (type === 0 && hasOwn) {
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
return [k, t, state.c];
|
|
862
|
+
}
|
|
863
|
+
function parse(toml, { maxDepth = 1000, integersAsBigInt } = {}) {
|
|
864
|
+
let res = {};
|
|
865
|
+
let meta = {};
|
|
866
|
+
let tbl = res;
|
|
867
|
+
let m = meta;
|
|
868
|
+
for (let ptr = skipVoid(toml, 0);ptr < toml.length; ) {
|
|
869
|
+
if (toml[ptr] === "[") {
|
|
870
|
+
let isTableArray = toml[++ptr] === "[";
|
|
871
|
+
let k = parseKey(toml, ptr += +isTableArray, "]");
|
|
872
|
+
if (isTableArray) {
|
|
873
|
+
if (toml[k[1] - 1] !== "]") {
|
|
874
|
+
throw new TomlError("expected end of table declaration", {
|
|
875
|
+
toml,
|
|
876
|
+
ptr: k[1] - 1
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
k[1]++;
|
|
880
|
+
}
|
|
881
|
+
let p = peekTable(k[0], res, meta, isTableArray ? 2 : 1);
|
|
882
|
+
if (!p) {
|
|
883
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
884
|
+
toml,
|
|
885
|
+
ptr
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
m = p[2];
|
|
889
|
+
tbl = p[1];
|
|
890
|
+
ptr = k[1];
|
|
891
|
+
} else {
|
|
892
|
+
let k = parseKey(toml, ptr);
|
|
893
|
+
let p = peekTable(k[0], tbl, m, 0);
|
|
894
|
+
if (!p) {
|
|
895
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
896
|
+
toml,
|
|
897
|
+
ptr
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
let v = extractValue(toml, k[1], undefined, maxDepth, integersAsBigInt);
|
|
901
|
+
p[1][p[0]] = v[0];
|
|
902
|
+
ptr = v[1];
|
|
903
|
+
}
|
|
904
|
+
ptr = skipVoid(toml, ptr, true);
|
|
905
|
+
if (toml[ptr] && toml[ptr] !== `
|
|
906
|
+
` && toml[ptr] !== "\r") {
|
|
907
|
+
throw new TomlError("each key-value declaration must be followed by an end-of-line", {
|
|
908
|
+
toml,
|
|
909
|
+
ptr
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
ptr = skipVoid(toml, ptr);
|
|
913
|
+
}
|
|
914
|
+
return res;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/stringify.js
|
|
918
|
+
/*!
|
|
919
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
920
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
921
|
+
*
|
|
922
|
+
* Redistribution and use in source and binary forms, with or without
|
|
923
|
+
* modification, are permitted provided that the following conditions are met:
|
|
924
|
+
*
|
|
925
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
926
|
+
* list of conditions and the following disclaimer.
|
|
927
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
928
|
+
* this list of conditions and the following disclaimer in the
|
|
929
|
+
* documentation and/or other materials provided with the distribution.
|
|
930
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
931
|
+
* may be used to endorse or promote products derived from this software without
|
|
932
|
+
* specific prior written permission.
|
|
933
|
+
*
|
|
934
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
935
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
936
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
937
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
938
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
939
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
940
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
941
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
942
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
943
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
944
|
+
*/
|
|
945
|
+
|
|
946
|
+
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/index.js
|
|
947
|
+
/*!
|
|
948
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
949
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
950
|
+
*
|
|
951
|
+
* Redistribution and use in source and binary forms, with or without
|
|
952
|
+
* modification, are permitted provided that the following conditions are met:
|
|
953
|
+
*
|
|
954
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
955
|
+
* list of conditions and the following disclaimer.
|
|
956
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
957
|
+
* this list of conditions and the following disclaimer in the
|
|
958
|
+
* documentation and/or other materials provided with the distribution.
|
|
959
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
960
|
+
* may be used to endorse or promote products derived from this software without
|
|
961
|
+
* specific prior written permission.
|
|
962
|
+
*
|
|
963
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
964
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
965
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
966
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
967
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
968
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
969
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
970
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
971
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
972
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
973
|
+
*/
|
|
974
|
+
|
|
975
|
+
// ../core/dist/index.js
|
|
976
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3 } from "node:fs";
|
|
977
|
+
import { basename as basename2, join as join3 } from "node:path";
|
|
978
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
979
|
+
import { join as join4 } from "node:path";
|
|
980
|
+
import { existsSync as existsSync6, readdirSync as readdirSync5 } from "node:fs";
|
|
981
|
+
import { join as join5 } from "node:path";
|
|
982
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
983
|
+
import { existsSync as existsSync9, mkdirSync } from "node:fs";
|
|
984
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
985
|
+
import { cp, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
986
|
+
import { join as join7 } from "node:path";
|
|
987
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
988
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
989
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync2, rmSync } from "node:fs";
|
|
990
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3 } from "node:fs";
|
|
991
|
+
import { spawn } from "node:child_process";
|
|
992
|
+
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
993
|
+
function parseSimpleYamlFrontmatter(yaml) {
|
|
994
|
+
const result = {};
|
|
995
|
+
for (const line of yaml.split(`
|
|
996
|
+
`)) {
|
|
997
|
+
const trimmed = line.trim();
|
|
998
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
const colonIndex = trimmed.indexOf(":");
|
|
1002
|
+
if (colonIndex === -1) {
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const rawKey = trimmed.slice(0, colonIndex).trim();
|
|
1006
|
+
let value = trimmed.slice(colonIndex + 1).trim();
|
|
1007
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1008
|
+
value = value.slice(1, -1);
|
|
1009
|
+
}
|
|
1010
|
+
const key = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1011
|
+
result[key] = value;
|
|
1012
|
+
}
|
|
1013
|
+
return result;
|
|
1014
|
+
}
|
|
1015
|
+
function parseFrontmatterWithMarkdown(content) {
|
|
1016
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/s);
|
|
1017
|
+
if (!match?.[1] || match[2] === undefined) {
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
const frontmatter = parseSimpleYamlFrontmatter(match[1]);
|
|
1021
|
+
const markdown = match[2];
|
|
1022
|
+
return { frontmatter, markdown };
|
|
1023
|
+
}
|
|
1024
|
+
async function loadCommands(capabilityPath, capabilityId) {
|
|
1025
|
+
const commandsDir = join(capabilityPath, "commands");
|
|
1026
|
+
if (!existsSync(commandsDir)) {
|
|
1027
|
+
return [];
|
|
1028
|
+
}
|
|
1029
|
+
const commands = [];
|
|
1030
|
+
const entries = readdirSync(commandsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1031
|
+
for (const entry of entries) {
|
|
1032
|
+
if (entry.isDirectory()) {
|
|
1033
|
+
const commandPath = join(commandsDir, entry.name, "COMMAND.md");
|
|
1034
|
+
if (existsSync(commandPath)) {
|
|
1035
|
+
const command = await parseCommandFile(commandPath, capabilityId);
|
|
1036
|
+
commands.push(command);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return commands;
|
|
1041
|
+
}
|
|
1042
|
+
async function parseCommandFile(filePath, capabilityId) {
|
|
1043
|
+
const content = await Bun.file(filePath).text();
|
|
1044
|
+
const parsed = parseFrontmatterWithMarkdown(content);
|
|
1045
|
+
if (!parsed) {
|
|
1046
|
+
throw new Error(`Invalid COMMAND.md format at ${filePath}: missing YAML frontmatter`);
|
|
1047
|
+
}
|
|
1048
|
+
const frontmatter = parsed.frontmatter;
|
|
1049
|
+
const prompt = parsed.markdown;
|
|
1050
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
1051
|
+
throw new Error(`Invalid COMMAND.md at ${filePath}: name and description required`);
|
|
1052
|
+
}
|
|
1053
|
+
const result = {
|
|
1054
|
+
name: frontmatter.name,
|
|
1055
|
+
description: frontmatter.description,
|
|
1056
|
+
prompt: prompt.trim(),
|
|
1057
|
+
capabilityId
|
|
1058
|
+
};
|
|
1059
|
+
if (frontmatter.allowedTools) {
|
|
1060
|
+
result.allowedTools = frontmatter.allowedTools;
|
|
1061
|
+
}
|
|
1062
|
+
return result;
|
|
1063
|
+
}
|
|
1064
|
+
async function loadDocs(capabilityPath, capabilityId) {
|
|
1065
|
+
const docs = [];
|
|
1066
|
+
const definitionPath = join2(capabilityPath, "definition.md");
|
|
1067
|
+
if (existsSync2(definitionPath)) {
|
|
1068
|
+
const content = await Bun.file(definitionPath).text();
|
|
1069
|
+
docs.push({
|
|
1070
|
+
name: "definition",
|
|
1071
|
+
content: content.trim(),
|
|
1072
|
+
capabilityId
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
const docsDir = join2(capabilityPath, "docs");
|
|
1076
|
+
if (existsSync2(docsDir)) {
|
|
1077
|
+
const entries = readdirSync2(docsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1078
|
+
for (const entry of entries) {
|
|
1079
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1080
|
+
const docPath = join2(docsDir, entry.name);
|
|
1081
|
+
const content = await Bun.file(docPath).text();
|
|
1082
|
+
docs.push({
|
|
1083
|
+
name: basename(entry.name, ".md"),
|
|
1084
|
+
content: content.trim(),
|
|
1085
|
+
capabilityId
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return docs;
|
|
1091
|
+
}
|
|
1092
|
+
var ENV_FILE = ".omni/.env";
|
|
1093
|
+
async function loadEnvironment() {
|
|
1094
|
+
const env = {};
|
|
1095
|
+
if (existsSync3(ENV_FILE)) {
|
|
1096
|
+
const content = await Bun.file(ENV_FILE).text();
|
|
1097
|
+
for (const line of content.split(`
|
|
1098
|
+
`)) {
|
|
1099
|
+
const trimmed = line.trim();
|
|
1100
|
+
if (trimmed && !trimmed.startsWith("#")) {
|
|
1101
|
+
const eqIndex = trimmed.indexOf("=");
|
|
1102
|
+
if (eqIndex > 0) {
|
|
1103
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
1104
|
+
const value = trimmed.slice(eqIndex + 1).trim();
|
|
1105
|
+
const unquotedValue = value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'") ? value.slice(1, -1) : value;
|
|
1106
|
+
env[key] = unquotedValue;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const processEnv = {};
|
|
1112
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
1113
|
+
if (value !== undefined) {
|
|
1114
|
+
processEnv[key] = value;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return { ...env, ...processEnv };
|
|
1118
|
+
}
|
|
1119
|
+
function validateEnv(declarations, env, capabilityId) {
|
|
1120
|
+
const missing = [];
|
|
1121
|
+
for (const [key, decl] of Object.entries(declarations)) {
|
|
1122
|
+
const declaration = decl;
|
|
1123
|
+
const value = env[key] ?? declaration.default;
|
|
1124
|
+
if (declaration.required && !value) {
|
|
1125
|
+
missing.push(key);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (missing.length > 0) {
|
|
1129
|
+
throw new Error(`Missing required environment variable${missing.length > 1 ? "s" : ""} for capability "${capabilityId}": ${missing.join(", ")}. ` + `Set ${missing.length > 1 ? "them" : "it"} in .omni/.env or as environment variable${missing.length > 1 ? "s" : ""}.`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
function isSecretEnvVar(key, declarations) {
|
|
1133
|
+
const decl = declarations[key];
|
|
1134
|
+
return decl?.secret === true;
|
|
1135
|
+
}
|
|
1136
|
+
function parseOmniConfig(tomlContent) {
|
|
1137
|
+
try {
|
|
1138
|
+
return parse(tomlContent);
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1141
|
+
throw new Error(`Invalid TOML in config: ${message}`);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function validateCapabilityConfig(parsed) {
|
|
1145
|
+
const cap = parsed["capability"];
|
|
1146
|
+
if (typeof cap !== "object" || cap === null) {
|
|
1147
|
+
throw new Error("capability.id is required in capability.toml");
|
|
1148
|
+
}
|
|
1149
|
+
const capability = cap;
|
|
1150
|
+
if (typeof capability["id"] !== "string") {
|
|
1151
|
+
throw new Error("capability.id is required in capability.toml");
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof capability["name"] !== "string") {
|
|
1154
|
+
throw new Error("capability.name is required in capability.toml");
|
|
1155
|
+
}
|
|
1156
|
+
if (typeof capability["version"] !== "string") {
|
|
1157
|
+
throw new Error("capability.version is required in capability.toml");
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function parseCapabilityConfig(tomlContent) {
|
|
1161
|
+
try {
|
|
1162
|
+
const parsed = parse(tomlContent);
|
|
1163
|
+
validateCapabilityConfig(parsed);
|
|
1164
|
+
return parsed;
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1167
|
+
throw new Error(`Invalid capability.toml: ${message}`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
async function loadRules(capabilityPath, capabilityId) {
|
|
1171
|
+
const rulesDir = join3(capabilityPath, "rules");
|
|
1172
|
+
if (!existsSync4(rulesDir)) {
|
|
1173
|
+
return [];
|
|
1174
|
+
}
|
|
1175
|
+
const rules = [];
|
|
1176
|
+
const entries = readdirSync3(rulesDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1177
|
+
for (const entry of entries) {
|
|
1178
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1179
|
+
const rulePath = join3(rulesDir, entry.name);
|
|
1180
|
+
const content = await Bun.file(rulePath).text();
|
|
1181
|
+
rules.push({
|
|
1182
|
+
name: basename2(entry.name, ".md"),
|
|
1183
|
+
content: content.trim(),
|
|
1184
|
+
capabilityId
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return rules;
|
|
1189
|
+
}
|
|
1190
|
+
async function writeRules(rules, docs = []) {
|
|
1191
|
+
const instructionsPath = ".omni/instructions.md";
|
|
1192
|
+
const rulesContent = generateRulesContent(rules, docs);
|
|
1193
|
+
let content;
|
|
1194
|
+
if (existsSync4(instructionsPath)) {
|
|
1195
|
+
content = await Bun.file(instructionsPath).text();
|
|
1196
|
+
} else {
|
|
1197
|
+
content = `# OmniDev Instructions
|
|
1198
|
+
|
|
1199
|
+
## Project Description
|
|
1200
|
+
<!-- TODO: Add 2-3 sentences describing your project -->
|
|
1201
|
+
[Describe what this project does and its main purpose]
|
|
1202
|
+
|
|
1203
|
+
<!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->
|
|
1204
|
+
<!-- END OMNIDEV GENERATED CONTENT -->
|
|
1205
|
+
`;
|
|
1206
|
+
}
|
|
1207
|
+
const beginMarker = "<!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->";
|
|
1208
|
+
const endMarker = "<!-- END OMNIDEV GENERATED CONTENT -->";
|
|
1209
|
+
const beginIndex = content.indexOf(beginMarker);
|
|
1210
|
+
const endIndex = content.indexOf(endMarker);
|
|
1211
|
+
if (beginIndex === -1 || endIndex === -1) {
|
|
1212
|
+
content += `
|
|
1213
|
+
|
|
1214
|
+
${beginMarker}
|
|
1215
|
+
${rulesContent}
|
|
1216
|
+
${endMarker}
|
|
1217
|
+
`;
|
|
1218
|
+
} else {
|
|
1219
|
+
content = content.substring(0, beginIndex + beginMarker.length) + `
|
|
1220
|
+
` + rulesContent + `
|
|
1221
|
+
` + content.substring(endIndex);
|
|
1222
|
+
}
|
|
1223
|
+
await Bun.write(instructionsPath, content);
|
|
1224
|
+
}
|
|
1225
|
+
function generateRulesContent(rules, docs = []) {
|
|
1226
|
+
if (rules.length === 0 && docs.length === 0) {
|
|
1227
|
+
return `<!-- This section is automatically updated when capabilities change -->
|
|
1228
|
+
|
|
1229
|
+
## Capabilities
|
|
1230
|
+
|
|
1231
|
+
No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.`;
|
|
1232
|
+
}
|
|
1233
|
+
let content = `<!-- This section is automatically updated when capabilities change -->
|
|
1234
|
+
|
|
1235
|
+
## Capabilities
|
|
1236
|
+
|
|
1237
|
+
`;
|
|
1238
|
+
if (docs.length > 0) {
|
|
1239
|
+
content += `### Documentation
|
|
1240
|
+
|
|
1241
|
+
`;
|
|
1242
|
+
for (const doc of docs) {
|
|
1243
|
+
content += `#### ${doc.name} (from ${doc.capabilityId})
|
|
1244
|
+
|
|
1245
|
+
${doc.content}
|
|
1246
|
+
|
|
1247
|
+
`;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (rules.length > 0) {
|
|
1251
|
+
content += `### Rules
|
|
1252
|
+
|
|
1253
|
+
`;
|
|
1254
|
+
for (const rule of rules) {
|
|
1255
|
+
content += `#### ${rule.name} (from ${rule.capabilityId})
|
|
1256
|
+
|
|
1257
|
+
${rule.content}
|
|
1258
|
+
|
|
1259
|
+
`;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
return content.trim();
|
|
1263
|
+
}
|
|
1264
|
+
async function loadSkills(capabilityPath, capabilityId) {
|
|
1265
|
+
const skillsDir = join4(capabilityPath, "skills");
|
|
1266
|
+
if (!existsSync5(skillsDir)) {
|
|
1267
|
+
return [];
|
|
1268
|
+
}
|
|
1269
|
+
const skills = [];
|
|
1270
|
+
const entries = readdirSync4(skillsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1271
|
+
for (const entry of entries) {
|
|
1272
|
+
if (entry.isDirectory()) {
|
|
1273
|
+
const skillPath = join4(skillsDir, entry.name, "SKILL.md");
|
|
1274
|
+
if (existsSync5(skillPath)) {
|
|
1275
|
+
const skill = await parseSkillFile(skillPath, capabilityId);
|
|
1276
|
+
skills.push(skill);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return skills;
|
|
1281
|
+
}
|
|
1282
|
+
async function parseSkillFile(filePath, capabilityId) {
|
|
1283
|
+
const content = await Bun.file(filePath).text();
|
|
1284
|
+
const parsed = parseFrontmatterWithMarkdown(content);
|
|
1285
|
+
if (!parsed) {
|
|
1286
|
+
throw new Error(`Invalid SKILL.md format at ${filePath}: missing YAML frontmatter`);
|
|
1287
|
+
}
|
|
1288
|
+
const frontmatter = parsed.frontmatter;
|
|
1289
|
+
const instructions = parsed.markdown;
|
|
1290
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
1291
|
+
throw new Error(`Invalid SKILL.md at ${filePath}: name and description required`);
|
|
1292
|
+
}
|
|
1293
|
+
return {
|
|
1294
|
+
name: frontmatter.name,
|
|
1295
|
+
description: frontmatter.description,
|
|
1296
|
+
instructions: instructions.trim(),
|
|
1297
|
+
capabilityId
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
async function loadSubagents(capabilityPath, capabilityId) {
|
|
1301
|
+
const subagentsDir = join5(capabilityPath, "subagents");
|
|
1302
|
+
if (!existsSync6(subagentsDir)) {
|
|
1303
|
+
return [];
|
|
1304
|
+
}
|
|
1305
|
+
const subagents = [];
|
|
1306
|
+
const entries = readdirSync5(subagentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1307
|
+
for (const entry of entries) {
|
|
1308
|
+
if (entry.isDirectory()) {
|
|
1309
|
+
const subagentPath = join5(subagentsDir, entry.name, "SUBAGENT.md");
|
|
1310
|
+
if (existsSync6(subagentPath)) {
|
|
1311
|
+
const subagent = await parseSubagentFile(subagentPath, capabilityId);
|
|
1312
|
+
subagents.push(subagent);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return subagents;
|
|
1317
|
+
}
|
|
1318
|
+
async function parseSubagentFile(filePath, capabilityId) {
|
|
1319
|
+
const content = await Bun.file(filePath).text();
|
|
1320
|
+
const parsed = parseFrontmatterWithMarkdown(content);
|
|
1321
|
+
if (!parsed) {
|
|
1322
|
+
throw new Error(`Invalid SUBAGENT.md format at ${filePath}: missing YAML frontmatter`);
|
|
1323
|
+
}
|
|
1324
|
+
const frontmatter = parsed.frontmatter;
|
|
1325
|
+
const systemPrompt = parsed.markdown;
|
|
1326
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
1327
|
+
throw new Error(`Invalid SUBAGENT.md at ${filePath}: name and description required`);
|
|
1328
|
+
}
|
|
1329
|
+
const result = {
|
|
1330
|
+
name: frontmatter.name,
|
|
1331
|
+
description: frontmatter.description,
|
|
1332
|
+
systemPrompt: systemPrompt.trim(),
|
|
1333
|
+
capabilityId
|
|
1334
|
+
};
|
|
1335
|
+
if (frontmatter.tools) {
|
|
1336
|
+
result.tools = parseCommaSeparatedList(frontmatter.tools);
|
|
1337
|
+
}
|
|
1338
|
+
if (frontmatter.disallowedTools) {
|
|
1339
|
+
result.disallowedTools = parseCommaSeparatedList(frontmatter.disallowedTools);
|
|
1340
|
+
}
|
|
1341
|
+
if (frontmatter.model) {
|
|
1342
|
+
result.model = frontmatter.model;
|
|
1343
|
+
}
|
|
1344
|
+
if (frontmatter.permissionMode) {
|
|
1345
|
+
result.permissionMode = frontmatter.permissionMode;
|
|
1346
|
+
}
|
|
1347
|
+
if (frontmatter.skills) {
|
|
1348
|
+
result.skills = parseCommaSeparatedList(frontmatter.skills);
|
|
1349
|
+
}
|
|
1350
|
+
if (frontmatter.hooks) {
|
|
1351
|
+
result.hooks = frontmatter.hooks;
|
|
1352
|
+
}
|
|
1353
|
+
return result;
|
|
1354
|
+
}
|
|
1355
|
+
function parseCommaSeparatedList(value) {
|
|
1356
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1357
|
+
}
|
|
1358
|
+
var CAPABILITIES_DIR = ".omni/capabilities";
|
|
1359
|
+
async function discoverCapabilities() {
|
|
1360
|
+
const capabilities = [];
|
|
1361
|
+
if (existsSync7(CAPABILITIES_DIR)) {
|
|
1362
|
+
const entries = readdirSync6(CAPABILITIES_DIR, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1363
|
+
for (const entry of entries) {
|
|
1364
|
+
if (entry.isDirectory()) {
|
|
1365
|
+
const entryPath = join6(CAPABILITIES_DIR, entry.name);
|
|
1366
|
+
const configPath = join6(entryPath, "capability.toml");
|
|
1367
|
+
if (existsSync7(configPath)) {
|
|
1368
|
+
capabilities.push(entryPath);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return capabilities;
|
|
1374
|
+
}
|
|
1375
|
+
async function loadCapabilityConfig(capabilityPath) {
|
|
1376
|
+
const configPath = join6(capabilityPath, "capability.toml");
|
|
1377
|
+
const content = await Bun.file(configPath).text();
|
|
1378
|
+
const config = parseCapabilityConfig(content);
|
|
1379
|
+
return config;
|
|
1380
|
+
}
|
|
1381
|
+
async function importCapabilityExports(capabilityPath) {
|
|
1382
|
+
const indexPath = join6(capabilityPath, "index.ts");
|
|
1383
|
+
if (!existsSync7(indexPath)) {
|
|
1384
|
+
return {};
|
|
1385
|
+
}
|
|
1386
|
+
try {
|
|
1387
|
+
const absolutePath = join6(process.cwd(), indexPath);
|
|
1388
|
+
const module = await import(absolutePath);
|
|
1389
|
+
return module;
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
const errorMessage = String(error);
|
|
1392
|
+
if (errorMessage.includes("Cannot find module")) {
|
|
1393
|
+
const match = errorMessage.match(/Cannot find module '([^']+)'/);
|
|
1394
|
+
const missingModule = match ? match[1] : "unknown";
|
|
1395
|
+
throw new Error(`Missing dependency '${missingModule}' for capability at ${capabilityPath}.
|
|
1396
|
+
If this is a project-specific capability, install dependencies or remove it from .omni/capabilities/`);
|
|
1397
|
+
}
|
|
1398
|
+
throw new Error(`Failed to import capability at ${capabilityPath}: ${error}`);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
async function loadTypeDefinitions(capabilityPath) {
|
|
1402
|
+
const typesPath = join6(capabilityPath, "types.d.ts");
|
|
1403
|
+
if (!existsSync7(typesPath)) {
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
return Bun.file(typesPath).text();
|
|
1407
|
+
}
|
|
1408
|
+
function convertSkillExports(skillExports, capabilityId) {
|
|
1409
|
+
return skillExports.map((skillExport) => {
|
|
1410
|
+
const exportObj = skillExport;
|
|
1411
|
+
const lines = exportObj.skillMd.split(`
|
|
1412
|
+
`);
|
|
1413
|
+
let name = "unnamed";
|
|
1414
|
+
let description = "";
|
|
1415
|
+
let instructions = exportObj.skillMd;
|
|
1416
|
+
if (lines[0]?.trim() === "---") {
|
|
1417
|
+
const endIndex = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
1418
|
+
if (endIndex > 0) {
|
|
1419
|
+
const frontmatter = lines.slice(1, endIndex);
|
|
1420
|
+
instructions = lines.slice(endIndex + 1).join(`
|
|
1421
|
+
`).trim();
|
|
1422
|
+
for (const line of frontmatter) {
|
|
1423
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
1424
|
+
if (match?.[1] && match[2]) {
|
|
1425
|
+
const key = match[1];
|
|
1426
|
+
const value = match[2];
|
|
1427
|
+
if (key === "name") {
|
|
1428
|
+
name = value.replace(/^["']|["']$/g, "");
|
|
1429
|
+
} else if (key === "description") {
|
|
1430
|
+
description = value.replace(/^["']|["']$/g, "");
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return {
|
|
1437
|
+
name,
|
|
1438
|
+
description,
|
|
1439
|
+
instructions,
|
|
1440
|
+
capabilityId
|
|
1441
|
+
};
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
function convertRuleExports(ruleExports, capabilityId) {
|
|
1445
|
+
return ruleExports.map((ruleExport, index) => {
|
|
1446
|
+
return {
|
|
1447
|
+
name: `rule-${index + 1}`,
|
|
1448
|
+
content: String(ruleExport).trim(),
|
|
1449
|
+
capabilityId
|
|
1450
|
+
};
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
function convertDocExports(docExports, capabilityId) {
|
|
1454
|
+
return docExports.map((docExport) => {
|
|
1455
|
+
const exportObj = docExport;
|
|
1456
|
+
return {
|
|
1457
|
+
name: exportObj.title,
|
|
1458
|
+
content: exportObj.content.trim(),
|
|
1459
|
+
capabilityId
|
|
1460
|
+
};
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
function convertSubagentExports(subagentExports, capabilityId) {
|
|
1464
|
+
return subagentExports.map((subagentExport) => {
|
|
1465
|
+
const exportObj = subagentExport;
|
|
1466
|
+
const lines = exportObj.subagentMd.split(`
|
|
1467
|
+
`);
|
|
1468
|
+
let name = "unnamed";
|
|
1469
|
+
let description = "";
|
|
1470
|
+
let systemPrompt = exportObj.subagentMd;
|
|
1471
|
+
let tools;
|
|
1472
|
+
let disallowedTools;
|
|
1473
|
+
let model;
|
|
1474
|
+
let permissionMode;
|
|
1475
|
+
let skills;
|
|
1476
|
+
if (lines[0]?.trim() === "---") {
|
|
1477
|
+
const endIndex = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
1478
|
+
if (endIndex > 0) {
|
|
1479
|
+
const frontmatter = lines.slice(1, endIndex);
|
|
1480
|
+
systemPrompt = lines.slice(endIndex + 1).join(`
|
|
1481
|
+
`).trim();
|
|
1482
|
+
for (const line of frontmatter) {
|
|
1483
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
1484
|
+
if (match?.[1] && match[2]) {
|
|
1485
|
+
const key = match[1];
|
|
1486
|
+
const value = match[2].replace(/^["']|["']$/g, "");
|
|
1487
|
+
switch (key) {
|
|
1488
|
+
case "name":
|
|
1489
|
+
name = value;
|
|
1490
|
+
break;
|
|
1491
|
+
case "description":
|
|
1492
|
+
description = value;
|
|
1493
|
+
break;
|
|
1494
|
+
case "tools":
|
|
1495
|
+
tools = value.split(",").map((t) => t.trim());
|
|
1496
|
+
break;
|
|
1497
|
+
case "disallowedTools":
|
|
1498
|
+
disallowedTools = value.split(",").map((t) => t.trim());
|
|
1499
|
+
break;
|
|
1500
|
+
case "model":
|
|
1501
|
+
model = value;
|
|
1502
|
+
break;
|
|
1503
|
+
case "permissionMode":
|
|
1504
|
+
permissionMode = value;
|
|
1505
|
+
break;
|
|
1506
|
+
case "skills":
|
|
1507
|
+
skills = value.split(",").map((s) => s.trim());
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
const result = {
|
|
1515
|
+
name,
|
|
1516
|
+
description,
|
|
1517
|
+
systemPrompt,
|
|
1518
|
+
capabilityId
|
|
1519
|
+
};
|
|
1520
|
+
if (tools)
|
|
1521
|
+
result.tools = tools;
|
|
1522
|
+
if (disallowedTools)
|
|
1523
|
+
result.disallowedTools = disallowedTools;
|
|
1524
|
+
if (model) {
|
|
1525
|
+
result.model = model;
|
|
1526
|
+
}
|
|
1527
|
+
if (permissionMode) {
|
|
1528
|
+
result.permissionMode = permissionMode;
|
|
1529
|
+
}
|
|
1530
|
+
if (skills)
|
|
1531
|
+
result.skills = skills;
|
|
1532
|
+
return result;
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
function convertCommandExports(commandExports, capabilityId) {
|
|
1536
|
+
return commandExports.map((commandExport) => {
|
|
1537
|
+
const exportObj = commandExport;
|
|
1538
|
+
const lines = exportObj.commandMd.split(`
|
|
1539
|
+
`);
|
|
1540
|
+
let name = "unnamed";
|
|
1541
|
+
let description = "";
|
|
1542
|
+
let prompt = exportObj.commandMd;
|
|
1543
|
+
let allowedTools;
|
|
1544
|
+
if (lines[0]?.trim() === "---") {
|
|
1545
|
+
const endIndex = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
1546
|
+
if (endIndex > 0) {
|
|
1547
|
+
const frontmatter = lines.slice(1, endIndex);
|
|
1548
|
+
prompt = lines.slice(endIndex + 1).join(`
|
|
1549
|
+
`).trim();
|
|
1550
|
+
for (const line of frontmatter) {
|
|
1551
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
1552
|
+
if (match?.[1] && match[2]) {
|
|
1553
|
+
const key = match[1];
|
|
1554
|
+
const value = match[2].replace(/^["']|["']$/g, "");
|
|
1555
|
+
switch (key) {
|
|
1556
|
+
case "name":
|
|
1557
|
+
name = value;
|
|
1558
|
+
break;
|
|
1559
|
+
case "description":
|
|
1560
|
+
description = value;
|
|
1561
|
+
break;
|
|
1562
|
+
case "allowedTools":
|
|
1563
|
+
case "allowed-tools":
|
|
1564
|
+
allowedTools = value;
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
const result = {
|
|
1572
|
+
name,
|
|
1573
|
+
description,
|
|
1574
|
+
prompt,
|
|
1575
|
+
capabilityId
|
|
1576
|
+
};
|
|
1577
|
+
if (allowedTools) {
|
|
1578
|
+
result.allowedTools = allowedTools;
|
|
1579
|
+
}
|
|
1580
|
+
return result;
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
async function loadCapability(capabilityPath, env) {
|
|
1584
|
+
const config = await loadCapabilityConfig(capabilityPath);
|
|
1585
|
+
const id = config.capability.id;
|
|
1586
|
+
if (config.env) {
|
|
1587
|
+
validateEnv(config.env, env, id);
|
|
1588
|
+
}
|
|
1589
|
+
const exports = await importCapabilityExports(capabilityPath);
|
|
1590
|
+
const exportsAny = exports;
|
|
1591
|
+
const skills = "skills" in exports && Array.isArray(exportsAny.skills) ? convertSkillExports(exportsAny.skills, id) : await loadSkills(capabilityPath, id);
|
|
1592
|
+
const rules = "rules" in exports && Array.isArray(exportsAny.rules) ? convertRuleExports(exportsAny.rules, id) : await loadRules(capabilityPath, id);
|
|
1593
|
+
const docs = "docs" in exports && Array.isArray(exportsAny.docs) ? convertDocExports(exportsAny.docs, id) : await loadDocs(capabilityPath, id);
|
|
1594
|
+
const subagents = "subagents" in exports && Array.isArray(exportsAny.subagents) ? convertSubagentExports(exportsAny.subagents, id) : await loadSubagents(capabilityPath, id);
|
|
1595
|
+
const commands = "commands" in exports && Array.isArray(exportsAny.commands) ? convertCommandExports(exportsAny.commands, id) : await loadCommands(capabilityPath, id);
|
|
1596
|
+
const typeDefinitionsFromExports = "typeDefinitions" in exports && typeof exportsAny.typeDefinitions === "string" ? exportsAny.typeDefinitions : undefined;
|
|
1597
|
+
const typeDefinitions = typeDefinitionsFromExports !== undefined ? typeDefinitionsFromExports : await loadTypeDefinitions(capabilityPath);
|
|
1598
|
+
const gitignore = "gitignore" in exports && Array.isArray(exportsAny.gitignore) ? exportsAny.gitignore : undefined;
|
|
1599
|
+
const result = {
|
|
1600
|
+
id,
|
|
1601
|
+
path: capabilityPath,
|
|
1602
|
+
config,
|
|
1603
|
+
skills,
|
|
1604
|
+
rules,
|
|
1605
|
+
docs,
|
|
1606
|
+
subagents,
|
|
1607
|
+
commands,
|
|
1608
|
+
exports
|
|
1609
|
+
};
|
|
1610
|
+
if (typeDefinitions !== undefined) {
|
|
1611
|
+
result.typeDefinitions = typeDefinitions;
|
|
1612
|
+
}
|
|
1613
|
+
if (gitignore !== undefined) {
|
|
1614
|
+
result.gitignore = gitignore;
|
|
1615
|
+
}
|
|
1616
|
+
return result;
|
|
1617
|
+
}
|
|
1618
|
+
var CONFIG_PATH = "omni.toml";
|
|
1619
|
+
var LOCAL_CONFIG = "omni.local.toml";
|
|
1620
|
+
function mergeConfigs(base, override) {
|
|
1621
|
+
const merged = { ...base, ...override };
|
|
1622
|
+
merged.env = { ...base.env, ...override.env };
|
|
1623
|
+
merged.profiles = { ...base.profiles };
|
|
1624
|
+
for (const [name, profile] of Object.entries(override.profiles || {})) {
|
|
1625
|
+
merged.profiles[name] = {
|
|
1626
|
+
...base.profiles?.[name] || {},
|
|
1627
|
+
...profile
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
if (base.mcps || override.mcps) {
|
|
1631
|
+
merged.mcps = { ...base.mcps, ...override.mcps };
|
|
1632
|
+
}
|
|
1633
|
+
return merged;
|
|
1634
|
+
}
|
|
1635
|
+
async function loadConfig() {
|
|
1636
|
+
let baseConfig = {};
|
|
1637
|
+
let localConfig = {};
|
|
1638
|
+
if (existsSync8(CONFIG_PATH)) {
|
|
1639
|
+
const content = await Bun.file(CONFIG_PATH).text();
|
|
1640
|
+
baseConfig = parseOmniConfig(content);
|
|
1641
|
+
}
|
|
1642
|
+
if (existsSync8(LOCAL_CONFIG)) {
|
|
1643
|
+
const content = await Bun.file(LOCAL_CONFIG).text();
|
|
1644
|
+
localConfig = parseOmniConfig(content);
|
|
1645
|
+
}
|
|
1646
|
+
return mergeConfigs(baseConfig, localConfig);
|
|
1647
|
+
}
|
|
1648
|
+
async function writeConfig(config) {
|
|
1649
|
+
const content = generateConfigToml(config);
|
|
1650
|
+
await Bun.write(CONFIG_PATH, content);
|
|
1651
|
+
}
|
|
1652
|
+
function generateConfigToml(config) {
|
|
1653
|
+
const lines = [];
|
|
1654
|
+
lines.push("# =============================================================================");
|
|
1655
|
+
lines.push("# OmniDev Configuration");
|
|
1656
|
+
lines.push("# =============================================================================");
|
|
1657
|
+
lines.push("# This file defines your project's capabilities, profiles, and settings.");
|
|
1658
|
+
lines.push("#");
|
|
1659
|
+
lines.push("# Files:");
|
|
1660
|
+
lines.push("# • omni.toml - Main config (commit to share with team)");
|
|
1661
|
+
lines.push("# • omni.local.toml - Local overrides (add to .gitignore)");
|
|
1662
|
+
lines.push("# • omni.lock.toml - Version lock file (commit for reproducibility)");
|
|
1663
|
+
lines.push("#");
|
|
1664
|
+
lines.push("# Quick start:");
|
|
1665
|
+
lines.push("# 1. Add capability sources to [capabilities.sources]");
|
|
1666
|
+
lines.push("# 2. Reference them in your profiles");
|
|
1667
|
+
lines.push("# 3. Run: omnidev sync");
|
|
1668
|
+
lines.push("# 4. Switch profiles: omnidev profile use <name>");
|
|
1669
|
+
lines.push("");
|
|
1670
|
+
if (config.project) {
|
|
1671
|
+
lines.push(`project = "${config.project}"`);
|
|
1672
|
+
lines.push("");
|
|
1673
|
+
}
|
|
1674
|
+
if (config.providers?.enabled && config.providers.enabled.length > 0) {
|
|
1675
|
+
lines.push("# AI providers to enable (claude, codex, or both)");
|
|
1676
|
+
lines.push("[providers]");
|
|
1677
|
+
lines.push(`enabled = [${config.providers.enabled.map((p) => `"${p}"`).join(", ")}]`);
|
|
1678
|
+
lines.push("");
|
|
1679
|
+
}
|
|
1680
|
+
lines.push("# =============================================================================");
|
|
1681
|
+
lines.push("# Environment Variables");
|
|
1682
|
+
lines.push("# =============================================================================");
|
|
1683
|
+
lines.push("# Global environment variables available to all capabilities.");
|
|
1684
|
+
lines.push("# Use ${VAR_NAME} syntax to reference shell environment variables.");
|
|
1685
|
+
lines.push("#");
|
|
1686
|
+
if (config.env && Object.keys(config.env).length > 0) {
|
|
1687
|
+
lines.push("[env]");
|
|
1688
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
1689
|
+
lines.push(`${key} = "${value}"`);
|
|
1690
|
+
}
|
|
1691
|
+
} else {
|
|
1692
|
+
lines.push("# [env]");
|
|
1693
|
+
lines.push('# DATABASE_URL = "${DATABASE_URL}"');
|
|
1694
|
+
lines.push('# API_KEY = "${MY_API_KEY}"');
|
|
1695
|
+
}
|
|
1696
|
+
lines.push("");
|
|
1697
|
+
lines.push("# =============================================================================");
|
|
1698
|
+
lines.push("# Capability Sources");
|
|
1699
|
+
lines.push("# =============================================================================");
|
|
1700
|
+
lines.push("# Fetch capabilities from Git repositories. On sync, these are");
|
|
1701
|
+
lines.push("# cloned/updated and made available to your profiles.");
|
|
1702
|
+
lines.push("#");
|
|
1703
|
+
lines.push("# [capabilities.sources]");
|
|
1704
|
+
lines.push("# # GitHub shorthand (uses latest commit)");
|
|
1705
|
+
lines.push('# tasks = "github:example-org/tasks-capability"');
|
|
1706
|
+
lines.push("#");
|
|
1707
|
+
lines.push("# # Version pinning (recommended for production)");
|
|
1708
|
+
lines.push('# ralph = { source = "github:example-org/ralph", ref = "v1.2.0" }');
|
|
1709
|
+
lines.push("#");
|
|
1710
|
+
lines.push("# # Other Git sources");
|
|
1711
|
+
lines.push('# private = "git@github.com:company/private-cap.git"');
|
|
1712
|
+
lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
|
|
1713
|
+
lines.push("");
|
|
1714
|
+
lines.push("# =============================================================================");
|
|
1715
|
+
lines.push("# MCP Servers");
|
|
1716
|
+
lines.push("# =============================================================================");
|
|
1717
|
+
lines.push("# Define MCP servers that automatically become capabilities.");
|
|
1718
|
+
lines.push('# Reference in profiles using the MCP name directly, e.g. capabilities = ["filesystem"]');
|
|
1719
|
+
lines.push("#");
|
|
1720
|
+
lines.push("# [mcps.filesystem]");
|
|
1721
|
+
lines.push('# command = "npx"');
|
|
1722
|
+
lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
|
|
1723
|
+
lines.push('# transport = "stdio" # stdio (default), sse, or http');
|
|
1724
|
+
lines.push("#");
|
|
1725
|
+
lines.push("# [mcps.database]");
|
|
1726
|
+
lines.push('# command = "node"');
|
|
1727
|
+
lines.push('# args = ["./servers/database.js"]');
|
|
1728
|
+
lines.push('# cwd = "./mcp-servers"');
|
|
1729
|
+
lines.push("# [mcps.database.env]");
|
|
1730
|
+
lines.push('# DB_URL = "${DATABASE_URL}"');
|
|
1731
|
+
lines.push("");
|
|
1732
|
+
lines.push("# =============================================================================");
|
|
1733
|
+
lines.push("# Always Enabled Capabilities");
|
|
1734
|
+
lines.push("# =============================================================================");
|
|
1735
|
+
lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
|
|
1736
|
+
lines.push("# Useful for essential tools needed everywhere.");
|
|
1737
|
+
lines.push("#");
|
|
1738
|
+
lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
|
|
1739
|
+
lines.push("");
|
|
1740
|
+
lines.push("# =============================================================================");
|
|
1741
|
+
lines.push("# Profiles");
|
|
1742
|
+
lines.push("# =============================================================================");
|
|
1743
|
+
lines.push("# Define different capability sets for different workflows.");
|
|
1744
|
+
lines.push("# Switch profiles with: omnidev profile use <name>");
|
|
1745
|
+
lines.push("");
|
|
1746
|
+
if (config.profiles && Object.keys(config.profiles).length > 0) {
|
|
1747
|
+
for (const [name, profile] of Object.entries(config.profiles)) {
|
|
1748
|
+
lines.push(`[profiles.${name}]`);
|
|
1749
|
+
const capabilities = profile.capabilities ?? [];
|
|
1750
|
+
if (capabilities.length > 0) {
|
|
1751
|
+
lines.push(`capabilities = [${capabilities.map((id) => `"${id}"`).join(", ")}]`);
|
|
1752
|
+
} else {
|
|
1753
|
+
lines.push("capabilities = []");
|
|
1754
|
+
}
|
|
1755
|
+
lines.push("");
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return lines.join(`
|
|
1759
|
+
`);
|
|
1760
|
+
}
|
|
1761
|
+
var STATE_DIR = ".omni/state";
|
|
1762
|
+
var ACTIVE_PROFILE_PATH = `${STATE_DIR}/active-profile`;
|
|
1763
|
+
async function readActiveProfileState() {
|
|
1764
|
+
if (!existsSync9(ACTIVE_PROFILE_PATH)) {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
try {
|
|
1768
|
+
const content = await Bun.file(ACTIVE_PROFILE_PATH).text();
|
|
1769
|
+
const trimmed = content.trim();
|
|
1770
|
+
return trimmed || null;
|
|
1771
|
+
} catch {
|
|
1772
|
+
return null;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
async function writeActiveProfileState(profileName) {
|
|
1776
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
1777
|
+
await Bun.write(ACTIVE_PROFILE_PATH, profileName);
|
|
1778
|
+
}
|
|
1779
|
+
async function clearActiveProfileState() {
|
|
1780
|
+
if (existsSync9(ACTIVE_PROFILE_PATH)) {
|
|
1781
|
+
const file = Bun.file(ACTIVE_PROFILE_PATH);
|
|
1782
|
+
await file.delete();
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async function getActiveProfile() {
|
|
1786
|
+
const stateProfile = await readActiveProfileState();
|
|
1787
|
+
if (stateProfile) {
|
|
1788
|
+
return stateProfile;
|
|
1789
|
+
}
|
|
1790
|
+
const config = await loadConfig();
|
|
1791
|
+
return config.active_profile ?? null;
|
|
1792
|
+
}
|
|
1793
|
+
async function setActiveProfile(name) {
|
|
1794
|
+
await writeActiveProfileState(name);
|
|
1795
|
+
}
|
|
1796
|
+
function resolveEnabledCapabilities(config, profileName) {
|
|
1797
|
+
const profile = profileName ? config.profiles?.[profileName] : config.profiles?.[config.active_profile ?? "default"];
|
|
1798
|
+
const profileCapabilities = profile?.capabilities ?? [];
|
|
1799
|
+
const alwaysEnabled = config.always_enabled_capabilities ?? [];
|
|
1800
|
+
return [...new Set([...alwaysEnabled, ...profileCapabilities])];
|
|
1801
|
+
}
|
|
1802
|
+
async function loadProfileConfig(profileName) {
|
|
1803
|
+
const config = await loadConfig();
|
|
1804
|
+
return config.profiles?.[profileName];
|
|
1805
|
+
}
|
|
1806
|
+
async function setProfile(profileName, profileConfig) {
|
|
1807
|
+
const config = await loadConfig();
|
|
1808
|
+
if (!config.profiles) {
|
|
1809
|
+
config.profiles = {};
|
|
1810
|
+
}
|
|
1811
|
+
config.profiles[profileName] = profileConfig;
|
|
1812
|
+
await writeConfig(config);
|
|
1813
|
+
}
|
|
1814
|
+
async function getEnabledCapabilities() {
|
|
1815
|
+
const config = await loadConfig();
|
|
1816
|
+
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
1817
|
+
return resolveEnabledCapabilities(config, activeProfile);
|
|
1818
|
+
}
|
|
1819
|
+
async function enableCapability(capabilityId) {
|
|
1820
|
+
const config = await loadConfig();
|
|
1821
|
+
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
1822
|
+
if (!config.profiles) {
|
|
1823
|
+
config.profiles = {};
|
|
1824
|
+
}
|
|
1825
|
+
if (!config.profiles[activeProfile]) {
|
|
1826
|
+
config.profiles[activeProfile] = { capabilities: [] };
|
|
1827
|
+
}
|
|
1828
|
+
const capabilities = new Set(config.profiles[activeProfile].capabilities ?? []);
|
|
1829
|
+
capabilities.add(capabilityId);
|
|
1830
|
+
config.profiles[activeProfile].capabilities = Array.from(capabilities);
|
|
1831
|
+
await writeConfig(config);
|
|
1832
|
+
}
|
|
1833
|
+
async function disableCapability(capabilityId) {
|
|
1834
|
+
const config = await loadConfig();
|
|
1835
|
+
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
1836
|
+
if (!config.profiles?.[activeProfile]) {
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
const capabilities = new Set(config.profiles[activeProfile].capabilities ?? []);
|
|
1840
|
+
capabilities.delete(capabilityId);
|
|
1841
|
+
config.profiles[activeProfile].capabilities = Array.from(capabilities);
|
|
1842
|
+
await writeConfig(config);
|
|
1843
|
+
}
|
|
1844
|
+
async function buildCapabilityRegistry() {
|
|
1845
|
+
const env = await loadEnvironment();
|
|
1846
|
+
const enabledIds = await getEnabledCapabilities();
|
|
1847
|
+
const capabilityPaths = await discoverCapabilities();
|
|
1848
|
+
const capabilities = new Map;
|
|
1849
|
+
for (const path of capabilityPaths) {
|
|
1850
|
+
try {
|
|
1851
|
+
const cap = await loadCapability(path, env);
|
|
1852
|
+
if (enabledIds.includes(cap.id)) {
|
|
1853
|
+
capabilities.set(cap.id, cap);
|
|
1854
|
+
}
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1857
|
+
console.warn(`Warning: Skipping capability at ${path}`);
|
|
1858
|
+
console.warn(` ${errorMessage}`);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
return {
|
|
1862
|
+
capabilities,
|
|
1863
|
+
getCapability: (id) => capabilities.get(id),
|
|
1864
|
+
getAllCapabilities: () => [...capabilities.values()],
|
|
1865
|
+
getAllSkills: () => [...capabilities.values()].flatMap((c) => c.skills),
|
|
1866
|
+
getAllRules: () => [...capabilities.values()].flatMap((c) => c.rules),
|
|
1867
|
+
getAllDocs: () => [...capabilities.values()].flatMap((c) => c.docs)
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
var OMNI_LOCAL = ".omni";
|
|
1871
|
+
var SKILL_DIRS = ["skills", "skill"];
|
|
1872
|
+
var AGENT_DIRS = ["agents", "agent", "subagents", "subagent"];
|
|
1873
|
+
var COMMAND_DIRS = ["commands", "command"];
|
|
1874
|
+
var RULE_DIRS = ["rules", "rule"];
|
|
1875
|
+
var DOC_DIRS = ["docs", "doc", "documentation"];
|
|
1876
|
+
var SKILL_FILES = ["SKILL.md", "skill.md", "Skill.md"];
|
|
1877
|
+
var AGENT_FILES = ["AGENT.md", "agent.md", "Agent.md", "SUBAGENT.md", "subagent.md"];
|
|
1878
|
+
var COMMAND_FILES = ["COMMAND.md", "command.md", "Command.md"];
|
|
1879
|
+
function parseSourceConfig(source) {
|
|
1880
|
+
if (typeof source === "string") {
|
|
1881
|
+
let sourceUrl = source;
|
|
1882
|
+
let ref;
|
|
1883
|
+
if (source.startsWith("github:") && source.includes("#")) {
|
|
1884
|
+
const parts = source.split("#");
|
|
1885
|
+
sourceUrl = parts[0] ?? source;
|
|
1886
|
+
ref = parts[1];
|
|
1887
|
+
}
|
|
1888
|
+
const result = { source: sourceUrl };
|
|
1889
|
+
if (ref) {
|
|
1890
|
+
result.ref = ref;
|
|
1891
|
+
}
|
|
1892
|
+
return result;
|
|
1893
|
+
}
|
|
1894
|
+
return source;
|
|
1895
|
+
}
|
|
1896
|
+
function sourceToGitUrl(source) {
|
|
1897
|
+
if (source.startsWith("github:")) {
|
|
1898
|
+
const repo = source.replace("github:", "");
|
|
1899
|
+
return `https://github.com/${repo}.git`;
|
|
1900
|
+
}
|
|
1901
|
+
return source;
|
|
1902
|
+
}
|
|
1903
|
+
function getSourceCapabilityPath(id) {
|
|
1904
|
+
return join7(OMNI_LOCAL, "capabilities", id);
|
|
1905
|
+
}
|
|
1906
|
+
function getLockFilePath() {
|
|
1907
|
+
return "omni.lock.toml";
|
|
1908
|
+
}
|
|
1909
|
+
async function loadLockFile() {
|
|
1910
|
+
const lockPath = getLockFilePath();
|
|
1911
|
+
if (!existsSync10(lockPath)) {
|
|
1912
|
+
return { capabilities: {} };
|
|
1913
|
+
}
|
|
1914
|
+
try {
|
|
1915
|
+
const content = await readFile(lockPath, "utf-8");
|
|
1916
|
+
const parsed = parse(content);
|
|
1917
|
+
const capabilities = parsed["capabilities"];
|
|
1918
|
+
return {
|
|
1919
|
+
capabilities: capabilities || {}
|
|
1920
|
+
};
|
|
1921
|
+
} catch {
|
|
1922
|
+
return { capabilities: {} };
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
function stringifyLockFile(lockFile) {
|
|
1926
|
+
const lines = [];
|
|
1927
|
+
for (const [id, entry] of Object.entries(lockFile.capabilities)) {
|
|
1928
|
+
lines.push(`[capabilities.${id}]`);
|
|
1929
|
+
lines.push(`source = "${entry.source}"`);
|
|
1930
|
+
lines.push(`version = "${entry.version}"`);
|
|
1931
|
+
if (entry.commit) {
|
|
1932
|
+
lines.push(`commit = "${entry.commit}"`);
|
|
1933
|
+
}
|
|
1934
|
+
if (entry.ref) {
|
|
1935
|
+
lines.push(`ref = "${entry.ref}"`);
|
|
1936
|
+
}
|
|
1937
|
+
lines.push(`updated_at = "${entry.updated_at}"`);
|
|
1938
|
+
lines.push("");
|
|
1939
|
+
}
|
|
1940
|
+
return lines.join(`
|
|
1941
|
+
`);
|
|
1942
|
+
}
|
|
1943
|
+
async function saveLockFile(lockFile) {
|
|
1944
|
+
const lockPath = getLockFilePath();
|
|
1945
|
+
await mkdir(join7(OMNI_LOCAL, "capabilities"), { recursive: true });
|
|
1946
|
+
const header = `# Auto-generated by OmniDev - DO NOT EDIT
|
|
1947
|
+
# Records installed capability versions for reproducibility
|
|
1948
|
+
# Last updated: ${new Date().toISOString()}
|
|
1949
|
+
|
|
1950
|
+
`;
|
|
1951
|
+
const content = header + stringifyLockFile(lockFile);
|
|
1952
|
+
await writeFile(lockPath, content, "utf-8");
|
|
1953
|
+
}
|
|
1954
|
+
async function getRepoCommit(repoPath) {
|
|
1955
|
+
const proc = Bun.spawn(["git", "rev-parse", "HEAD"], {
|
|
1956
|
+
cwd: repoPath,
|
|
1957
|
+
stdout: "pipe",
|
|
1958
|
+
stderr: "pipe"
|
|
1959
|
+
});
|
|
1960
|
+
const output = await new Response(proc.stdout).text();
|
|
1961
|
+
await proc.exited;
|
|
1962
|
+
return output.trim();
|
|
1963
|
+
}
|
|
1964
|
+
function shortCommit(commit) {
|
|
1965
|
+
return commit.substring(0, 7);
|
|
1966
|
+
}
|
|
1967
|
+
async function cloneRepo(gitUrl, targetPath, ref) {
|
|
1968
|
+
await mkdir(join7(targetPath, ".."), { recursive: true });
|
|
1969
|
+
const args = ["clone", "--depth", "1"];
|
|
1970
|
+
if (ref) {
|
|
1971
|
+
args.push("--branch", ref);
|
|
1972
|
+
}
|
|
1973
|
+
args.push(gitUrl, targetPath);
|
|
1974
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
1975
|
+
stdout: "pipe",
|
|
1976
|
+
stderr: "pipe"
|
|
1977
|
+
});
|
|
1978
|
+
await proc.exited;
|
|
1979
|
+
if (proc.exitCode !== 0) {
|
|
1980
|
+
const stderr = await new Response(proc.stderr).text();
|
|
1981
|
+
throw new Error(`Failed to clone ${gitUrl}: ${stderr}`);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
async function fetchRepo(repoPath, ref) {
|
|
1985
|
+
const fetchProc = Bun.spawn(["git", "fetch", "--depth", "1", "origin"], {
|
|
1986
|
+
cwd: repoPath,
|
|
1987
|
+
stdout: "pipe",
|
|
1988
|
+
stderr: "pipe"
|
|
1989
|
+
});
|
|
1990
|
+
await fetchProc.exited;
|
|
1991
|
+
const currentCommit = await getRepoCommit(repoPath);
|
|
1992
|
+
const targetRef = ref || "HEAD";
|
|
1993
|
+
const lsProc = Bun.spawn(["git", "ls-remote", "origin", targetRef], {
|
|
1994
|
+
cwd: repoPath,
|
|
1995
|
+
stdout: "pipe",
|
|
1996
|
+
stderr: "pipe"
|
|
1997
|
+
});
|
|
1998
|
+
const lsOutput = await new Response(lsProc.stdout).text();
|
|
1999
|
+
await lsProc.exited;
|
|
2000
|
+
const remoteCommit = lsOutput.split("\t")[0];
|
|
2001
|
+
if (currentCommit === remoteCommit) {
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
const pullProc = Bun.spawn(["git", "pull", "--ff-only"], {
|
|
2005
|
+
cwd: repoPath,
|
|
2006
|
+
stdout: "pipe",
|
|
2007
|
+
stderr: "pipe"
|
|
2008
|
+
});
|
|
2009
|
+
await pullProc.exited;
|
|
2010
|
+
return true;
|
|
2011
|
+
}
|
|
2012
|
+
function hasCapabilityToml(dirPath) {
|
|
2013
|
+
return existsSync10(join7(dirPath, "capability.toml"));
|
|
2014
|
+
}
|
|
2015
|
+
async function shouldWrapDirectory(dirPath) {
|
|
2016
|
+
if (existsSync10(join7(dirPath, ".claude-plugin", "plugin.json"))) {
|
|
2017
|
+
return true;
|
|
2018
|
+
}
|
|
2019
|
+
const allDirs = [...SKILL_DIRS, ...AGENT_DIRS, ...COMMAND_DIRS, ...RULE_DIRS, ...DOC_DIRS];
|
|
2020
|
+
for (const dirName of allDirs) {
|
|
2021
|
+
const checkPath = join7(dirPath, dirName);
|
|
2022
|
+
if (existsSync10(checkPath)) {
|
|
2023
|
+
const stats = await stat(checkPath);
|
|
2024
|
+
if (stats.isDirectory()) {
|
|
2025
|
+
return true;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return false;
|
|
2030
|
+
}
|
|
2031
|
+
async function findMatchingDirs(basePath, names) {
|
|
2032
|
+
for (const name of names) {
|
|
2033
|
+
const dirPath = join7(basePath, name);
|
|
2034
|
+
if (existsSync10(dirPath)) {
|
|
2035
|
+
const stats = await stat(dirPath);
|
|
2036
|
+
if (stats.isDirectory()) {
|
|
2037
|
+
return dirPath;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
async function findContentItems(dirPath, filePatterns) {
|
|
2044
|
+
const items = [];
|
|
2045
|
+
if (!existsSync10(dirPath)) {
|
|
2046
|
+
return items;
|
|
2047
|
+
}
|
|
2048
|
+
const entries = (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
2049
|
+
for (const entry of entries) {
|
|
2050
|
+
const entryPath = join7(dirPath, entry.name);
|
|
2051
|
+
if (entry.isDirectory()) {
|
|
2052
|
+
for (const pattern of filePatterns) {
|
|
2053
|
+
if (existsSync10(join7(entryPath, pattern))) {
|
|
2054
|
+
items.push({
|
|
2055
|
+
name: entry.name,
|
|
2056
|
+
path: entryPath,
|
|
2057
|
+
isFolder: true
|
|
2058
|
+
});
|
|
2059
|
+
break;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2063
|
+
const name = entry.name.replace(/\.md$/i, "");
|
|
2064
|
+
items.push({
|
|
2065
|
+
name,
|
|
2066
|
+
path: entryPath,
|
|
2067
|
+
isFolder: false
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
return items;
|
|
2072
|
+
}
|
|
2073
|
+
async function parsePluginJson(dirPath) {
|
|
2074
|
+
const pluginJsonPath = join7(dirPath, ".claude-plugin", "plugin.json");
|
|
2075
|
+
if (!existsSync10(pluginJsonPath)) {
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
try {
|
|
2079
|
+
const content = await readFile(pluginJsonPath, "utf-8");
|
|
2080
|
+
const data = JSON.parse(content);
|
|
2081
|
+
const result = {
|
|
2082
|
+
name: data.name,
|
|
2083
|
+
version: data.version,
|
|
2084
|
+
description: data.description
|
|
2085
|
+
};
|
|
2086
|
+
if (data.author) {
|
|
2087
|
+
result.author = {
|
|
2088
|
+
name: data.author.name,
|
|
2089
|
+
email: data.author.email
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
return result;
|
|
2093
|
+
} catch (error) {
|
|
2094
|
+
console.warn(`Failed to parse plugin.json in ${dirPath}:`, error);
|
|
2095
|
+
return null;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
async function readReadmeDescription(dirPath) {
|
|
2099
|
+
const readmePath = join7(dirPath, "README.md");
|
|
2100
|
+
if (!existsSync10(readmePath)) {
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
try {
|
|
2104
|
+
const content = await readFile(readmePath, "utf-8");
|
|
2105
|
+
const lines = content.split(`
|
|
2106
|
+
`);
|
|
2107
|
+
let description = "";
|
|
2108
|
+
let inCodeBlock = false;
|
|
2109
|
+
for (const line of lines) {
|
|
2110
|
+
const trimmed = line.trim();
|
|
2111
|
+
if (trimmed.startsWith("```")) {
|
|
2112
|
+
inCodeBlock = !inCodeBlock;
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
if (inCodeBlock || trimmed.startsWith("#") || trimmed.length === 0 || trimmed.startsWith("![")) {
|
|
2116
|
+
continue;
|
|
2117
|
+
}
|
|
2118
|
+
description += (description ? " " : "") + trimmed;
|
|
2119
|
+
if (description.length >= 200) {
|
|
2120
|
+
break;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
if (description.length > 200) {
|
|
2124
|
+
description = `${description.substring(0, 197)}...`;
|
|
2125
|
+
}
|
|
2126
|
+
return description || null;
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
console.warn(`Failed to read README.md in ${dirPath}:`, error);
|
|
2129
|
+
return null;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
async function discoverContent(repoPath) {
|
|
2133
|
+
const result = {
|
|
2134
|
+
skills: [],
|
|
2135
|
+
agents: [],
|
|
2136
|
+
commands: [],
|
|
2137
|
+
rulesDir: null,
|
|
2138
|
+
docsDir: null
|
|
2139
|
+
};
|
|
2140
|
+
const skillsDir = await findMatchingDirs(repoPath, SKILL_DIRS);
|
|
2141
|
+
if (skillsDir) {
|
|
2142
|
+
result.skills = await findContentItems(skillsDir, SKILL_FILES);
|
|
2143
|
+
}
|
|
2144
|
+
const agentsDir = await findMatchingDirs(repoPath, AGENT_DIRS);
|
|
2145
|
+
if (agentsDir) {
|
|
2146
|
+
result.agents = await findContentItems(agentsDir, AGENT_FILES);
|
|
2147
|
+
}
|
|
2148
|
+
const commandsDir = await findMatchingDirs(repoPath, COMMAND_DIRS);
|
|
2149
|
+
if (commandsDir) {
|
|
2150
|
+
result.commands = await findContentItems(commandsDir, COMMAND_FILES);
|
|
2151
|
+
}
|
|
2152
|
+
result.rulesDir = await findMatchingDirs(repoPath, RULE_DIRS);
|
|
2153
|
+
result.docsDir = await findMatchingDirs(repoPath, DOC_DIRS);
|
|
2154
|
+
return result;
|
|
2155
|
+
}
|
|
2156
|
+
async function generateCapabilityToml(id, repoPath, source, commit, content) {
|
|
2157
|
+
const shortHash = shortCommit(commit);
|
|
2158
|
+
const pluginMeta = await parsePluginJson(repoPath);
|
|
2159
|
+
const readmeDesc = await readReadmeDescription(repoPath);
|
|
2160
|
+
let description;
|
|
2161
|
+
if (pluginMeta?.description) {
|
|
2162
|
+
description = pluginMeta.description;
|
|
2163
|
+
} else if (readmeDesc) {
|
|
2164
|
+
description = readmeDesc;
|
|
2165
|
+
} else {
|
|
2166
|
+
const parts = [];
|
|
2167
|
+
if (content.skills.length > 0) {
|
|
2168
|
+
parts.push(`${content.skills.length} skill${content.skills.length > 1 ? "s" : ""}`);
|
|
2169
|
+
}
|
|
2170
|
+
if (content.agents.length > 0) {
|
|
2171
|
+
parts.push(`${content.agents.length} agent${content.agents.length > 1 ? "s" : ""}`);
|
|
2172
|
+
}
|
|
2173
|
+
if (content.commands.length > 0) {
|
|
2174
|
+
parts.push(`${content.commands.length} command${content.commands.length > 1 ? "s" : ""}`);
|
|
2175
|
+
}
|
|
2176
|
+
description = parts.length > 0 ? `${parts.join(", ")}` : `Wrapped from ${source}`;
|
|
2177
|
+
}
|
|
2178
|
+
const name = pluginMeta?.name || `${id} (wrapped)`;
|
|
2179
|
+
const version = pluginMeta?.version || shortHash;
|
|
2180
|
+
const repoUrl = source.startsWith("github:") ? `https://github.com/${source.replace("github:", "")}` : source;
|
|
2181
|
+
let tomlContent = `# Auto-generated by OmniDev - DO NOT EDIT
|
|
2182
|
+
# This capability was wrapped from an external repository
|
|
2183
|
+
|
|
2184
|
+
[capability]
|
|
2185
|
+
id = "${id}"
|
|
2186
|
+
name = "${name}"
|
|
2187
|
+
version = "${version}"
|
|
2188
|
+
description = "${description}"
|
|
2189
|
+
`;
|
|
2190
|
+
if (pluginMeta?.author?.name || pluginMeta?.author?.email) {
|
|
2191
|
+
tomlContent += `
|
|
2192
|
+
[capability.author]
|
|
2193
|
+
`;
|
|
2194
|
+
if (pluginMeta.author.name) {
|
|
2195
|
+
tomlContent += `name = "${pluginMeta.author.name}"
|
|
2196
|
+
`;
|
|
2197
|
+
}
|
|
2198
|
+
if (pluginMeta.author.email) {
|
|
2199
|
+
tomlContent += `email = "${pluginMeta.author.email}"
|
|
2200
|
+
`;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
tomlContent += `
|
|
2204
|
+
[capability.metadata]
|
|
2205
|
+
repository = "${repoUrl}"
|
|
2206
|
+
wrapped = true
|
|
2207
|
+
commit = "${commit}"
|
|
2208
|
+
`;
|
|
2209
|
+
await writeFile(join7(repoPath, "capability.toml"), tomlContent, "utf-8");
|
|
2210
|
+
}
|
|
2211
|
+
async function fetchGitCapabilitySource(id, config, options) {
|
|
2212
|
+
const gitUrl = sourceToGitUrl(config.source);
|
|
2213
|
+
const targetPath = getSourceCapabilityPath(id);
|
|
2214
|
+
let updated = false;
|
|
2215
|
+
let commit;
|
|
2216
|
+
let repoPath;
|
|
2217
|
+
if (config.path) {
|
|
2218
|
+
const tempPath = join7(OMNI_LOCAL, "_temp", `${id}-repo`);
|
|
2219
|
+
if (existsSync10(join7(tempPath, ".git"))) {
|
|
2220
|
+
if (!options?.silent) {
|
|
2221
|
+
console.log(` Checking ${id}...`);
|
|
2222
|
+
}
|
|
2223
|
+
updated = await fetchRepo(tempPath, config.ref);
|
|
2224
|
+
commit = await getRepoCommit(tempPath);
|
|
2225
|
+
} else {
|
|
2226
|
+
if (!options?.silent) {
|
|
2227
|
+
console.log(` Cloning ${id} from ${config.source}...`);
|
|
2228
|
+
}
|
|
2229
|
+
await mkdir(join7(tempPath, ".."), { recursive: true });
|
|
2230
|
+
await cloneRepo(gitUrl, tempPath, config.ref);
|
|
2231
|
+
commit = await getRepoCommit(tempPath);
|
|
2232
|
+
updated = true;
|
|
2233
|
+
}
|
|
2234
|
+
const sourcePath = join7(tempPath, config.path);
|
|
2235
|
+
if (!existsSync10(sourcePath)) {
|
|
2236
|
+
throw new Error(`Path not found in repository: ${config.path}`);
|
|
2237
|
+
}
|
|
2238
|
+
if (existsSync10(targetPath)) {
|
|
2239
|
+
await rm(targetPath, { recursive: true });
|
|
2240
|
+
}
|
|
2241
|
+
await mkdir(join7(targetPath, ".."), { recursive: true });
|
|
2242
|
+
await cp(sourcePath, targetPath, { recursive: true });
|
|
2243
|
+
repoPath = targetPath;
|
|
2244
|
+
} else {
|
|
2245
|
+
if (existsSync10(join7(targetPath, ".git"))) {
|
|
2246
|
+
if (!options?.silent) {
|
|
2247
|
+
console.log(` Checking ${id}...`);
|
|
2248
|
+
}
|
|
2249
|
+
updated = await fetchRepo(targetPath, config.ref);
|
|
2250
|
+
commit = await getRepoCommit(targetPath);
|
|
2251
|
+
} else {
|
|
2252
|
+
if (!options?.silent) {
|
|
2253
|
+
console.log(` Cloning ${id} from ${config.source}...`);
|
|
2254
|
+
}
|
|
2255
|
+
await cloneRepo(gitUrl, targetPath, config.ref);
|
|
2256
|
+
commit = await getRepoCommit(targetPath);
|
|
2257
|
+
updated = true;
|
|
2258
|
+
}
|
|
2259
|
+
repoPath = targetPath;
|
|
2260
|
+
}
|
|
2261
|
+
let needsWrap = false;
|
|
2262
|
+
if (!hasCapabilityToml(repoPath)) {
|
|
2263
|
+
needsWrap = await shouldWrapDirectory(repoPath);
|
|
2264
|
+
}
|
|
2265
|
+
if (needsWrap) {
|
|
2266
|
+
const content = await discoverContent(repoPath);
|
|
2267
|
+
await generateCapabilityToml(id, repoPath, config.source, commit, content);
|
|
2268
|
+
if (!options?.silent) {
|
|
2269
|
+
const parts = [];
|
|
2270
|
+
if (content.skills.length > 0)
|
|
2271
|
+
parts.push(`${content.skills.length} skills`);
|
|
2272
|
+
if (content.agents.length > 0)
|
|
2273
|
+
parts.push(`${content.agents.length} agents`);
|
|
2274
|
+
if (content.commands.length > 0)
|
|
2275
|
+
parts.push(`${content.commands.length} commands`);
|
|
2276
|
+
if (parts.length > 0) {
|
|
2277
|
+
console.log(` Wrapped: ${parts.join(", ")}`);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
let version = shortCommit(commit);
|
|
2282
|
+
const pkgJsonPath = join7(repoPath, "package.json");
|
|
2283
|
+
if (existsSync10(pkgJsonPath)) {
|
|
2284
|
+
try {
|
|
2285
|
+
const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
|
|
2286
|
+
if (pkgJson.version) {
|
|
2287
|
+
version = pkgJson.version;
|
|
2288
|
+
}
|
|
2289
|
+
} catch {}
|
|
2290
|
+
}
|
|
2291
|
+
return {
|
|
2292
|
+
id,
|
|
2293
|
+
path: targetPath,
|
|
2294
|
+
version,
|
|
2295
|
+
commit,
|
|
2296
|
+
updated,
|
|
2297
|
+
wrapped: needsWrap
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
async function fetchCapabilitySource(id, sourceConfig, options) {
|
|
2301
|
+
const config = parseSourceConfig(sourceConfig);
|
|
2302
|
+
return fetchGitCapabilitySource(id, config, options);
|
|
2303
|
+
}
|
|
2304
|
+
function generateMcpCapabilityTomlContent(id, mcpConfig) {
|
|
2305
|
+
let tomlContent = `# Auto-generated by OmniDev from omni.toml [mcps] section - DO NOT EDIT
|
|
2306
|
+
|
|
2307
|
+
[capability]
|
|
2308
|
+
id = "${id}"
|
|
2309
|
+
name = "${id} (MCP)"
|
|
2310
|
+
version = "1.0.0"
|
|
2311
|
+
description = "MCP server defined in omni.toml"
|
|
2312
|
+
|
|
2313
|
+
[capability.metadata]
|
|
2314
|
+
wrapped = true
|
|
2315
|
+
generated_from_omni_toml = true
|
|
2316
|
+
|
|
2317
|
+
[mcp]
|
|
2318
|
+
command = "${mcpConfig.command}"
|
|
2319
|
+
`;
|
|
2320
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
2321
|
+
tomlContent += `args = ${JSON.stringify(mcpConfig.args)}
|
|
2322
|
+
`;
|
|
2323
|
+
}
|
|
2324
|
+
if (mcpConfig.transport) {
|
|
2325
|
+
tomlContent += `transport = "${mcpConfig.transport}"
|
|
2326
|
+
`;
|
|
2327
|
+
}
|
|
2328
|
+
if (mcpConfig.cwd) {
|
|
2329
|
+
tomlContent += `cwd = "${mcpConfig.cwd}"
|
|
2330
|
+
`;
|
|
2331
|
+
}
|
|
2332
|
+
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
2333
|
+
tomlContent += `
|
|
2334
|
+
[mcp.env]
|
|
2335
|
+
`;
|
|
2336
|
+
for (const [key, value] of Object.entries(mcpConfig.env)) {
|
|
2337
|
+
tomlContent += `${key} = "${value}"
|
|
2338
|
+
`;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
return tomlContent;
|
|
2342
|
+
}
|
|
2343
|
+
async function generateMcpCapabilityToml(id, mcpConfig, targetPath) {
|
|
2344
|
+
const tomlContent = generateMcpCapabilityTomlContent(id, mcpConfig);
|
|
2345
|
+
await writeFile(join7(targetPath, "capability.toml"), tomlContent, "utf-8");
|
|
2346
|
+
}
|
|
2347
|
+
async function isGeneratedMcpCapability(capabilityDir) {
|
|
2348
|
+
const tomlPath = join7(capabilityDir, "capability.toml");
|
|
2349
|
+
if (!existsSync10(tomlPath)) {
|
|
2350
|
+
console.warn("no capability.toml found in", capabilityDir);
|
|
2351
|
+
return false;
|
|
2352
|
+
}
|
|
2353
|
+
try {
|
|
2354
|
+
const content = await readFile(tomlPath, "utf-8");
|
|
2355
|
+
const parsed = parse(content);
|
|
2356
|
+
const capability = parsed["capability"];
|
|
2357
|
+
const metadata = capability?.["metadata"];
|
|
2358
|
+
return metadata?.["generated_from_omni_toml"] === true;
|
|
2359
|
+
} catch {
|
|
2360
|
+
return false;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
async function cleanupStaleMcpCapabilities(currentMcpIds) {
|
|
2364
|
+
const capabilitiesDir = join7(OMNI_LOCAL, "capabilities");
|
|
2365
|
+
if (!existsSync10(capabilitiesDir)) {
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
const entries = await readdir(capabilitiesDir, { withFileTypes: true });
|
|
2369
|
+
for (const entry of entries) {
|
|
2370
|
+
if (entry.isDirectory()) {
|
|
2371
|
+
const capDir = join7(capabilitiesDir, entry.name);
|
|
2372
|
+
const isGenerated = await isGeneratedMcpCapability(capDir);
|
|
2373
|
+
if (isGenerated && !currentMcpIds.has(entry.name)) {
|
|
2374
|
+
await rm(capDir, { recursive: true });
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
async function generateMcpCapabilities(config) {
|
|
2380
|
+
if (!config.mcps || Object.keys(config.mcps).length === 0) {
|
|
2381
|
+
await cleanupStaleMcpCapabilities(new Set);
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
const mcpCapabilitiesDir = join7(OMNI_LOCAL, "capabilities");
|
|
2385
|
+
const currentMcpIds = new Set;
|
|
2386
|
+
for (const [id, mcpConfig] of Object.entries(config.mcps)) {
|
|
2387
|
+
const targetPath = join7(mcpCapabilitiesDir, id);
|
|
2388
|
+
currentMcpIds.add(id);
|
|
2389
|
+
await mkdir(targetPath, { recursive: true });
|
|
2390
|
+
await generateMcpCapabilityToml(id, mcpConfig, targetPath);
|
|
2391
|
+
}
|
|
2392
|
+
await cleanupStaleMcpCapabilities(currentMcpIds);
|
|
2393
|
+
}
|
|
2394
|
+
async function fetchAllCapabilitySources(config, options) {
|
|
2395
|
+
await generateMcpCapabilities(config);
|
|
2396
|
+
const sources = config.capabilities?.sources;
|
|
2397
|
+
if (!sources || Object.keys(sources).length === 0) {
|
|
2398
|
+
return [];
|
|
2399
|
+
}
|
|
2400
|
+
if (!options?.silent) {
|
|
2401
|
+
console.log("Fetching capability sources...");
|
|
2402
|
+
}
|
|
2403
|
+
const results = [];
|
|
2404
|
+
const lockFile = await loadLockFile();
|
|
2405
|
+
let lockUpdated = false;
|
|
2406
|
+
for (const [id, source] of Object.entries(sources)) {
|
|
2407
|
+
try {
|
|
2408
|
+
const result = await fetchCapabilitySource(id, source, options);
|
|
2409
|
+
results.push(result);
|
|
2410
|
+
const lockEntry = {
|
|
2411
|
+
source: typeof source === "string" ? source : source.source,
|
|
2412
|
+
version: result.version,
|
|
2413
|
+
updated_at: new Date().toISOString()
|
|
2414
|
+
};
|
|
2415
|
+
const gitConfig = parseSourceConfig(source);
|
|
2416
|
+
if (result.commit) {
|
|
2417
|
+
lockEntry.commit = result.commit;
|
|
2418
|
+
}
|
|
2419
|
+
if (gitConfig.ref) {
|
|
2420
|
+
lockEntry.ref = gitConfig.ref;
|
|
2421
|
+
}
|
|
2422
|
+
const existing = lockFile.capabilities[id];
|
|
2423
|
+
const hasChanged = !existing || existing.commit !== result.commit;
|
|
2424
|
+
if (hasChanged) {
|
|
2425
|
+
lockFile.capabilities[id] = lockEntry;
|
|
2426
|
+
lockUpdated = true;
|
|
2427
|
+
if (!options?.silent && result.updated) {
|
|
2428
|
+
const oldVersion = existing?.version || "new";
|
|
2429
|
+
console.log(` ${result.wrapped ? "+" : "~"} ${id}: ${oldVersion} -> ${result.version}`);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
} catch (error) {
|
|
2433
|
+
console.error(` Failed to fetch ${id}: ${error}`);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
if (lockUpdated) {
|
|
2437
|
+
await saveLockFile(lockFile);
|
|
2438
|
+
}
|
|
2439
|
+
if (!options?.silent && results.length > 0) {
|
|
2440
|
+
const updated = results.filter((r) => r.updated).length;
|
|
2441
|
+
if (updated > 0) {
|
|
2442
|
+
console.log(` Updated ${updated} capability source(s)`);
|
|
2443
|
+
} else {
|
|
2444
|
+
console.log(` All ${results.length} source(s) up to date`);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
return results;
|
|
2448
|
+
}
|
|
2449
|
+
async function checkForUpdates(config) {
|
|
2450
|
+
const sources = config.capabilities?.sources;
|
|
2451
|
+
if (!sources || Object.keys(sources).length === 0) {
|
|
2452
|
+
return [];
|
|
2453
|
+
}
|
|
2454
|
+
const lockFile = await loadLockFile();
|
|
2455
|
+
const updates = [];
|
|
2456
|
+
for (const [id, source] of Object.entries(sources)) {
|
|
2457
|
+
const sourceConfig = parseSourceConfig(source);
|
|
2458
|
+
const targetPath = getSourceCapabilityPath(id);
|
|
2459
|
+
const existing = lockFile.capabilities[id];
|
|
2460
|
+
const gitConfig = sourceConfig;
|
|
2461
|
+
if (!existsSync10(join7(targetPath, ".git"))) {
|
|
2462
|
+
updates.push({
|
|
2463
|
+
id,
|
|
2464
|
+
source: gitConfig.source,
|
|
2465
|
+
currentVersion: "not installed",
|
|
2466
|
+
latestVersion: "unknown",
|
|
2467
|
+
hasUpdate: true
|
|
2468
|
+
});
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
const fetchProc = Bun.spawn(["git", "fetch", "--depth", "1", "origin"], {
|
|
2472
|
+
cwd: targetPath,
|
|
2473
|
+
stdout: "pipe",
|
|
2474
|
+
stderr: "pipe"
|
|
2475
|
+
});
|
|
2476
|
+
await fetchProc.exited;
|
|
2477
|
+
const targetRef = gitConfig.ref || "HEAD";
|
|
2478
|
+
const lsProc = Bun.spawn(["git", "ls-remote", "origin", targetRef], {
|
|
2479
|
+
cwd: targetPath,
|
|
2480
|
+
stdout: "pipe",
|
|
2481
|
+
stderr: "pipe"
|
|
2482
|
+
});
|
|
2483
|
+
const lsOutput = await new Response(lsProc.stdout).text();
|
|
2484
|
+
await lsProc.exited;
|
|
2485
|
+
const remoteCommit = lsOutput.split("\t")[0] || "";
|
|
2486
|
+
const currentCommit = existing?.commit || "";
|
|
2487
|
+
updates.push({
|
|
2488
|
+
id,
|
|
2489
|
+
source: gitConfig.source,
|
|
2490
|
+
currentVersion: existing?.version || (currentCommit ? shortCommit(currentCommit) : "unknown"),
|
|
2491
|
+
latestVersion: remoteCommit ? shortCommit(remoteCommit) : "unknown",
|
|
2492
|
+
hasUpdate: currentCommit !== remoteCommit
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
return updates;
|
|
2496
|
+
}
|
|
2497
|
+
var PROVIDER_CONFIG_PATH = ".omni/provider.toml";
|
|
2498
|
+
async function loadProviderConfig() {
|
|
2499
|
+
if (!existsSync11(PROVIDER_CONFIG_PATH)) {
|
|
2500
|
+
return { provider: "claude" };
|
|
2501
|
+
}
|
|
2502
|
+
const content = await Bun.file(PROVIDER_CONFIG_PATH).text();
|
|
2503
|
+
const parsed = parse(content);
|
|
2504
|
+
return parsed;
|
|
2505
|
+
}
|
|
2506
|
+
async function writeProviderConfig(config) {
|
|
2507
|
+
const lines = [];
|
|
2508
|
+
lines.push("# OmniDev Provider Configuration");
|
|
2509
|
+
lines.push("# Selected AI provider(s) for this project");
|
|
2510
|
+
lines.push("#");
|
|
2511
|
+
lines.push("# This file controls which AI provider(s) you're using:");
|
|
2512
|
+
lines.push("# - claude: Generates .claude/claude.md instruction file");
|
|
2513
|
+
lines.push("# - codex: Generates AGENTS.md instruction file");
|
|
2514
|
+
lines.push("# - both: Generates both instruction files");
|
|
2515
|
+
lines.push("");
|
|
2516
|
+
if (config.providers && config.providers.length > 1) {
|
|
2517
|
+
lines.push("# Multiple providers enabled");
|
|
2518
|
+
lines.push(`providers = [${config.providers.map((p) => `"${p}"`).join(", ")}]`);
|
|
2519
|
+
} else if (config.providers && config.providers.length === 1) {
|
|
2520
|
+
lines.push("# Single provider");
|
|
2521
|
+
lines.push(`provider = "${config.providers[0]}"`);
|
|
2522
|
+
} else if (config.provider) {
|
|
2523
|
+
lines.push("# Single provider");
|
|
2524
|
+
lines.push(`provider = "${config.provider}"`);
|
|
2525
|
+
} else {
|
|
2526
|
+
lines.push("# Default: Claude");
|
|
2527
|
+
lines.push('provider = "claude"');
|
|
2528
|
+
}
|
|
2529
|
+
await Bun.write(PROVIDER_CONFIG_PATH, `${lines.join(`
|
|
2530
|
+
`)}
|
|
2531
|
+
`);
|
|
2532
|
+
}
|
|
2533
|
+
function parseProviderFlag(flag) {
|
|
2534
|
+
const lower = flag.toLowerCase();
|
|
2535
|
+
if (lower === "both") {
|
|
2536
|
+
return ["claude", "codex"];
|
|
2537
|
+
}
|
|
2538
|
+
if (lower === "claude" || lower === "codex") {
|
|
2539
|
+
return [lower];
|
|
2540
|
+
}
|
|
2541
|
+
throw new Error(`Invalid provider: ${flag}. Must be 'claude', 'codex', or 'both'.`);
|
|
2542
|
+
}
|
|
2543
|
+
var MCP_JSON_PATH = ".mcp.json";
|
|
2544
|
+
async function readMcpJson() {
|
|
2545
|
+
if (!existsSync12(MCP_JSON_PATH)) {
|
|
2546
|
+
return { mcpServers: {} };
|
|
2547
|
+
}
|
|
2548
|
+
try {
|
|
2549
|
+
const content = await Bun.file(MCP_JSON_PATH).text();
|
|
2550
|
+
const parsed = JSON.parse(content);
|
|
2551
|
+
return {
|
|
2552
|
+
mcpServers: parsed.mcpServers || {}
|
|
2553
|
+
};
|
|
2554
|
+
} catch {
|
|
2555
|
+
return { mcpServers: {} };
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
async function writeMcpJson(config) {
|
|
2559
|
+
await Bun.write(MCP_JSON_PATH, JSON.stringify(config, null, 2));
|
|
2560
|
+
}
|
|
2561
|
+
function buildMcpServerConfig(mcp) {
|
|
2562
|
+
const config = {
|
|
2563
|
+
command: mcp.command
|
|
2564
|
+
};
|
|
2565
|
+
if (mcp.args) {
|
|
2566
|
+
config.args = mcp.args;
|
|
2567
|
+
}
|
|
2568
|
+
if (mcp.env) {
|
|
2569
|
+
config.env = mcp.env;
|
|
2570
|
+
}
|
|
2571
|
+
return config;
|
|
2572
|
+
}
|
|
2573
|
+
async function syncMcpJson(capabilities2, previousManifest, options = {}) {
|
|
2574
|
+
const mcpJson = await readMcpJson();
|
|
2575
|
+
const previouslyManagedMcps = new Set;
|
|
2576
|
+
for (const resources of Object.values(previousManifest.capabilities)) {
|
|
2577
|
+
for (const mcpName of resources.mcps) {
|
|
2578
|
+
previouslyManagedMcps.add(mcpName);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
for (const serverName of previouslyManagedMcps) {
|
|
2582
|
+
delete mcpJson.mcpServers[serverName];
|
|
2583
|
+
}
|
|
2584
|
+
let addedCount = 0;
|
|
2585
|
+
for (const cap of capabilities2) {
|
|
2586
|
+
if (cap.config.mcp) {
|
|
2587
|
+
mcpJson.mcpServers[cap.id] = buildMcpServerConfig(cap.config.mcp);
|
|
2588
|
+
addedCount++;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
await writeMcpJson(mcpJson);
|
|
2592
|
+
if (!options.silent) {
|
|
2593
|
+
console.log(` - .mcp.json (${addedCount} MCP server(s))`);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
var MANIFEST_PATH = ".omni/state/manifest.json";
|
|
2597
|
+
var CURRENT_VERSION = 1;
|
|
2598
|
+
async function loadManifest() {
|
|
2599
|
+
if (!existsSync13(MANIFEST_PATH)) {
|
|
2600
|
+
return {
|
|
2601
|
+
version: CURRENT_VERSION,
|
|
2602
|
+
syncedAt: new Date().toISOString(),
|
|
2603
|
+
capabilities: {}
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
const content = await Bun.file(MANIFEST_PATH).text();
|
|
2607
|
+
return JSON.parse(content);
|
|
2608
|
+
}
|
|
2609
|
+
async function saveManifest(manifest) {
|
|
2610
|
+
mkdirSync2(".omni/state", { recursive: true });
|
|
2611
|
+
await Bun.write(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
2612
|
+
}
|
|
2613
|
+
function buildManifestFromCapabilities(capabilities2) {
|
|
2614
|
+
const manifest = {
|
|
2615
|
+
version: CURRENT_VERSION,
|
|
2616
|
+
syncedAt: new Date().toISOString(),
|
|
2617
|
+
capabilities: {}
|
|
2618
|
+
};
|
|
2619
|
+
for (const cap of capabilities2) {
|
|
2620
|
+
const resources = {
|
|
2621
|
+
skills: cap.skills.map((s) => s.name),
|
|
2622
|
+
rules: cap.rules.map((r) => r.name),
|
|
2623
|
+
commands: cap.commands.map((c) => c.name),
|
|
2624
|
+
subagents: cap.subagents.map((s) => s.name),
|
|
2625
|
+
mcps: cap.config.mcp ? [cap.id] : []
|
|
2626
|
+
};
|
|
2627
|
+
manifest.capabilities[cap.id] = resources;
|
|
2628
|
+
}
|
|
2629
|
+
return manifest;
|
|
2630
|
+
}
|
|
2631
|
+
async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
|
|
2632
|
+
const result = {
|
|
2633
|
+
deletedSkills: [],
|
|
2634
|
+
deletedRules: [],
|
|
2635
|
+
deletedCommands: [],
|
|
2636
|
+
deletedSubagents: [],
|
|
2637
|
+
deletedMcps: []
|
|
2638
|
+
};
|
|
2639
|
+
for (const [capId, resources] of Object.entries(previousManifest.capabilities)) {
|
|
2640
|
+
if (currentCapabilityIds.has(capId)) {
|
|
2641
|
+
continue;
|
|
2642
|
+
}
|
|
2643
|
+
for (const skillName of resources.skills) {
|
|
2644
|
+
const skillDir = `.claude/skills/${skillName}`;
|
|
2645
|
+
if (existsSync13(skillDir)) {
|
|
2646
|
+
rmSync(skillDir, { recursive: true });
|
|
2647
|
+
result.deletedSkills.push(skillName);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
for (const ruleName of resources.rules) {
|
|
2651
|
+
const rulePath = `.cursor/rules/omnidev-${ruleName}.mdc`;
|
|
2652
|
+
if (existsSync13(rulePath)) {
|
|
2653
|
+
rmSync(rulePath);
|
|
2654
|
+
result.deletedRules.push(ruleName);
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
return result;
|
|
2659
|
+
}
|
|
2660
|
+
var STATE_DIR2 = ".omni/state";
|
|
2661
|
+
var PROVIDERS_PATH = `${STATE_DIR2}/providers.json`;
|
|
2662
|
+
var DEFAULT_PROVIDERS = ["claude-code"];
|
|
2663
|
+
async function readEnabledProviders() {
|
|
2664
|
+
if (!existsSync14(PROVIDERS_PATH)) {
|
|
2665
|
+
return DEFAULT_PROVIDERS;
|
|
2666
|
+
}
|
|
2667
|
+
try {
|
|
2668
|
+
const content = await Bun.file(PROVIDERS_PATH).text();
|
|
2669
|
+
const state = JSON.parse(content);
|
|
2670
|
+
return state.enabled.length > 0 ? state.enabled : DEFAULT_PROVIDERS;
|
|
2671
|
+
} catch {
|
|
2672
|
+
return DEFAULT_PROVIDERS;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
async function writeEnabledProviders(providers) {
|
|
2676
|
+
mkdirSync3(STATE_DIR2, { recursive: true });
|
|
2677
|
+
const state = { enabled: providers };
|
|
2678
|
+
await Bun.write(PROVIDERS_PATH, JSON.stringify(state, null, 2));
|
|
2679
|
+
}
|
|
2680
|
+
async function enableProvider(providerId) {
|
|
2681
|
+
const current = await readEnabledProviders();
|
|
2682
|
+
if (!current.includes(providerId)) {
|
|
2683
|
+
await writeEnabledProviders([...current, providerId]);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
async function disableProvider(providerId) {
|
|
2687
|
+
const current = await readEnabledProviders();
|
|
2688
|
+
const filtered = current.filter((p) => p !== providerId);
|
|
2689
|
+
await writeEnabledProviders(filtered);
|
|
2690
|
+
}
|
|
2691
|
+
async function isProviderEnabled(providerId) {
|
|
2692
|
+
const current = await readEnabledProviders();
|
|
2693
|
+
return current.includes(providerId);
|
|
2694
|
+
}
|
|
2695
|
+
async function installCapabilityDependencies(silent) {
|
|
2696
|
+
const { existsSync: existsSync15, readdirSync: readdirSync7 } = await import("node:fs");
|
|
2697
|
+
const { join: join8 } = await import("node:path");
|
|
2698
|
+
const capabilitiesDir = ".omni/capabilities";
|
|
2699
|
+
if (!existsSync15(capabilitiesDir)) {
|
|
2700
|
+
return;
|
|
2701
|
+
}
|
|
2702
|
+
const entries = readdirSync7(capabilitiesDir, { withFileTypes: true });
|
|
2703
|
+
for (const entry of entries) {
|
|
2704
|
+
if (!entry.isDirectory()) {
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
const capabilityPath = join8(capabilitiesDir, entry.name);
|
|
2708
|
+
const packageJsonPath = join8(capabilityPath, "package.json");
|
|
2709
|
+
if (!existsSync15(packageJsonPath)) {
|
|
2710
|
+
continue;
|
|
2711
|
+
}
|
|
2712
|
+
if (!silent) {
|
|
2713
|
+
console.log(`Installing dependencies for ${capabilityPath}...`);
|
|
2714
|
+
}
|
|
2715
|
+
await new Promise((resolve, reject) => {
|
|
2716
|
+
const proc = spawn("bun", ["install"], {
|
|
2717
|
+
cwd: capabilityPath,
|
|
2718
|
+
stdio: silent ? "ignore" : "inherit"
|
|
2719
|
+
});
|
|
2720
|
+
proc.on("close", (code) => {
|
|
2721
|
+
if (code === 0) {
|
|
2722
|
+
resolve();
|
|
2723
|
+
} else {
|
|
2724
|
+
reject(new Error(`Failed to install dependencies for ${capabilityPath}`));
|
|
2725
|
+
}
|
|
2726
|
+
});
|
|
2727
|
+
proc.on("error", (error) => {
|
|
2728
|
+
reject(error);
|
|
2729
|
+
});
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
async function buildSyncBundle(options) {
|
|
2734
|
+
const silent = options?.silent ?? false;
|
|
2735
|
+
const config = await loadConfig();
|
|
2736
|
+
await fetchAllCapabilitySources(config, { silent });
|
|
2737
|
+
await installCapabilityDependencies(silent);
|
|
2738
|
+
const registry = await buildCapabilityRegistry();
|
|
2739
|
+
const capabilities2 = registry.getAllCapabilities();
|
|
2740
|
+
const skills = registry.getAllSkills();
|
|
2741
|
+
const rules = registry.getAllRules();
|
|
2742
|
+
const docs = registry.getAllDocs();
|
|
2743
|
+
const commands = capabilities2.flatMap((c) => c.commands);
|
|
2744
|
+
const subagents = capabilities2.flatMap((c) => c.subagents);
|
|
2745
|
+
const instructionsContent = generateInstructionsContent(rules, docs);
|
|
2746
|
+
const bundle = {
|
|
2747
|
+
capabilities: capabilities2,
|
|
2748
|
+
skills,
|
|
2749
|
+
rules,
|
|
2750
|
+
docs,
|
|
2751
|
+
commands,
|
|
2752
|
+
subagents,
|
|
2753
|
+
instructionsPath: ".omni/instructions.md",
|
|
2754
|
+
instructionsContent
|
|
2755
|
+
};
|
|
2756
|
+
return { bundle };
|
|
2757
|
+
}
|
|
2758
|
+
async function syncAgentConfiguration(options) {
|
|
2759
|
+
const silent = options?.silent ?? false;
|
|
2760
|
+
const adapters = options?.adapters ?? [];
|
|
2761
|
+
if (!silent) {
|
|
2762
|
+
console.log("Syncing agent configuration...");
|
|
2763
|
+
}
|
|
2764
|
+
const { bundle } = await buildSyncBundle({ silent });
|
|
2765
|
+
const capabilities2 = bundle.capabilities;
|
|
2766
|
+
const previousManifest = await loadManifest();
|
|
2767
|
+
const currentCapabilityIds = new Set(capabilities2.map((c) => c.id));
|
|
2768
|
+
const cleanupResult = await cleanupStaleResources(previousManifest, currentCapabilityIds);
|
|
2769
|
+
if (!silent && (cleanupResult.deletedSkills.length > 0 || cleanupResult.deletedRules.length > 0)) {
|
|
2770
|
+
console.log("Cleaned up stale resources:");
|
|
2771
|
+
if (cleanupResult.deletedSkills.length > 0) {
|
|
2772
|
+
console.log(` - Removed ${cleanupResult.deletedSkills.length} skill(s): ${cleanupResult.deletedSkills.join(", ")}`);
|
|
2773
|
+
}
|
|
2774
|
+
if (cleanupResult.deletedRules.length > 0) {
|
|
2775
|
+
console.log(` - Removed ${cleanupResult.deletedRules.length} rule(s): ${cleanupResult.deletedRules.join(", ")}`);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
for (const capability of capabilities2) {
|
|
2779
|
+
const defaultExport = capability.exports.default;
|
|
2780
|
+
if (defaultExport && typeof defaultExport.sync === "function") {
|
|
2781
|
+
try {
|
|
2782
|
+
await defaultExport.sync();
|
|
2783
|
+
} catch (error) {
|
|
2784
|
+
console.error(`Error running sync hook for ${capability.id}:`, error);
|
|
2785
|
+
}
|
|
2786
|
+
} else if (capability.config.sync?.on_sync) {
|
|
2787
|
+
const syncFnName = capability.config.sync.on_sync;
|
|
2788
|
+
const syncFn = capability.exports[syncFnName];
|
|
2789
|
+
if (typeof syncFn === "function") {
|
|
2790
|
+
try {
|
|
2791
|
+
await syncFn();
|
|
2792
|
+
} catch (error) {
|
|
2793
|
+
console.error(`Error running sync hook for ${capability.id}:`, error);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
mkdirSync4(".omni", { recursive: true });
|
|
2799
|
+
await writeRules(bundle.rules, bundle.docs);
|
|
2800
|
+
await syncMcpJson(capabilities2, previousManifest, { silent });
|
|
2801
|
+
const newManifest = buildManifestFromCapabilities(capabilities2);
|
|
2802
|
+
await saveManifest(newManifest);
|
|
2803
|
+
if (adapters.length > 0) {
|
|
2804
|
+
const config = await loadConfig();
|
|
2805
|
+
const ctx = {
|
|
2806
|
+
projectRoot: process.cwd(),
|
|
2807
|
+
config
|
|
2808
|
+
};
|
|
2809
|
+
for (const adapter of adapters) {
|
|
2810
|
+
try {
|
|
2811
|
+
const result = await adapter.sync(bundle, ctx);
|
|
2812
|
+
if (!silent && result.filesWritten.length > 0) {
|
|
2813
|
+
console.log(` - ${adapter.displayName}: ${result.filesWritten.length} files`);
|
|
2814
|
+
}
|
|
2815
|
+
} catch (error) {
|
|
2816
|
+
console.error(`Error running ${adapter.displayName} adapter:`, error);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
if (!silent) {
|
|
2821
|
+
console.log("✓ Synced:");
|
|
2822
|
+
console.log(` - .omni/instructions.md (${bundle.docs.length} docs, ${bundle.rules.length} rules)`);
|
|
2823
|
+
if (adapters.length > 0) {
|
|
2824
|
+
console.log(` - Provider adapters: ${adapters.map((a) => a.displayName).join(", ")}`);
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
return {
|
|
2828
|
+
capabilities: capabilities2.map((c) => c.id),
|
|
2829
|
+
skillCount: bundle.skills.length,
|
|
2830
|
+
ruleCount: bundle.rules.length,
|
|
2831
|
+
docCount: bundle.docs.length
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
function generateInstructionsContent(rules, docs) {
|
|
2835
|
+
if (rules.length === 0 && docs.length === 0) {
|
|
2836
|
+
return `## Capabilities
|
|
2837
|
+
|
|
2838
|
+
No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.`;
|
|
2839
|
+
}
|
|
2840
|
+
let content = `## Capabilities
|
|
2841
|
+
|
|
2842
|
+
`;
|
|
2843
|
+
if (docs.length > 0) {
|
|
2844
|
+
content += `### Documentation
|
|
2845
|
+
|
|
2846
|
+
`;
|
|
2847
|
+
for (const doc of docs) {
|
|
2848
|
+
content += `#### ${doc.name} (from ${doc.capabilityId})
|
|
2849
|
+
|
|
2850
|
+
${doc.content}
|
|
2851
|
+
|
|
2852
|
+
`;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
if (rules.length > 0) {
|
|
2856
|
+
content += `### Rules
|
|
2857
|
+
|
|
2858
|
+
`;
|
|
2859
|
+
for (const rule of rules) {
|
|
2860
|
+
content += `#### ${rule.name} (from ${rule.capabilityId})
|
|
2861
|
+
|
|
2862
|
+
${rule.content}
|
|
2863
|
+
|
|
2864
|
+
`;
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
return content.trim();
|
|
2868
|
+
}
|
|
2869
|
+
function generateAgentsTemplate() {
|
|
2870
|
+
return `# Project Instructions
|
|
2871
|
+
|
|
2872
|
+
<!-- Add your project-specific instructions here -->
|
|
2873
|
+
|
|
2874
|
+
## OmniDev
|
|
2875
|
+
|
|
2876
|
+
@import .omni/instructions.md
|
|
2877
|
+
`;
|
|
2878
|
+
}
|
|
2879
|
+
function generateClaudeTemplate() {
|
|
2880
|
+
return `# Project Instructions
|
|
2881
|
+
|
|
2882
|
+
<!-- Add your project-specific instructions here -->
|
|
2883
|
+
|
|
2884
|
+
## OmniDev
|
|
2885
|
+
|
|
2886
|
+
@import .omni/instructions.md
|
|
2887
|
+
`;
|
|
2888
|
+
}
|
|
2889
|
+
function generateInstructionsTemplate() {
|
|
2890
|
+
return `# OmniDev Instructions
|
|
2891
|
+
|
|
2892
|
+
## Project Description
|
|
2893
|
+
<!-- TODO: Add 2-3 sentences describing your project -->
|
|
2894
|
+
[Describe what this project does and its main purpose]
|
|
2895
|
+
|
|
2896
|
+
## How OmniDev Works
|
|
2897
|
+
|
|
2898
|
+
OmniDev manages capability content for your project. Capabilities can provide:
|
|
2899
|
+
|
|
2900
|
+
- Skills (for agent workflows)
|
|
2901
|
+
- Rules (for guardrails and conventions)
|
|
2902
|
+
- Docs (reference material)
|
|
2903
|
+
- Commands and subagents (optional)
|
|
2904
|
+
|
|
2905
|
+
Enable capabilities with:
|
|
2906
|
+
|
|
2907
|
+
\`\`\`
|
|
2908
|
+
omnidev capability enable <capability-id>
|
|
2909
|
+
\`\`\`
|
|
2910
|
+
|
|
2911
|
+
OmniDev will automatically sync enabled capabilities into your workspace. If you want to force a refresh:
|
|
2912
|
+
|
|
2913
|
+
\`\`\`
|
|
2914
|
+
omnidev sync
|
|
2915
|
+
\`\`\`
|
|
2916
|
+
|
|
2917
|
+
<!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->
|
|
2918
|
+
<!-- This section is automatically updated by 'omnidev agents sync' -->
|
|
2919
|
+
|
|
2920
|
+
## Capabilities
|
|
2921
|
+
|
|
2922
|
+
No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.
|
|
2923
|
+
|
|
2924
|
+
<!-- END OMNIDEV GENERATED CONTENT -->
|
|
2925
|
+
`;
|
|
2926
|
+
}
|
|
2927
|
+
function getActiveProviders(config) {
|
|
2928
|
+
if (config.providers)
|
|
2929
|
+
return config.providers;
|
|
2930
|
+
if (config.provider)
|
|
2931
|
+
return [config.provider];
|
|
2932
|
+
return ["claude"];
|
|
2933
|
+
}
|
|
2934
|
+
function debug(message, data) {
|
|
2935
|
+
if (process.env["OMNIDEV_DEBUG"] !== "1") {
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
const timestamp = new Date().toISOString();
|
|
2939
|
+
let logLine;
|
|
2940
|
+
if (data !== undefined) {
|
|
2941
|
+
logLine = `[${timestamp}] [omnidev] ${message} ${JSON.stringify(data, null, 2)}`;
|
|
2942
|
+
} else {
|
|
2943
|
+
logLine = `[${timestamp}] [omnidev] ${message}`;
|
|
2944
|
+
}
|
|
2945
|
+
console.log(logLine);
|
|
2946
|
+
}
|
|
2947
|
+
var version = "0.1.0";
|
|
2948
|
+
function getVersion() {
|
|
2949
|
+
return version;
|
|
2950
|
+
}
|
|
2951
|
+
export {
|
|
2952
|
+
writeRules,
|
|
2953
|
+
writeProviderConfig,
|
|
2954
|
+
writeMcpJson,
|
|
2955
|
+
writeEnabledProviders,
|
|
2956
|
+
writeConfig,
|
|
2957
|
+
writeActiveProfileState,
|
|
2958
|
+
version,
|
|
2959
|
+
validateEnv,
|
|
2960
|
+
syncMcpJson,
|
|
2961
|
+
syncAgentConfiguration,
|
|
2962
|
+
sourceToGitUrl,
|
|
2963
|
+
setProfile,
|
|
2964
|
+
setActiveProfile,
|
|
2965
|
+
saveManifest,
|
|
2966
|
+
saveLockFile,
|
|
2967
|
+
resolveEnabledCapabilities,
|
|
2968
|
+
readMcpJson,
|
|
2969
|
+
readEnabledProviders,
|
|
2970
|
+
readActiveProfileState,
|
|
2971
|
+
parseSourceConfig,
|
|
2972
|
+
parseProviderFlag,
|
|
2973
|
+
parseOmniConfig,
|
|
2974
|
+
parseCapabilityConfig,
|
|
2975
|
+
loadSubagents,
|
|
2976
|
+
loadSkills,
|
|
2977
|
+
loadRules,
|
|
2978
|
+
loadProviderConfig,
|
|
2979
|
+
loadProfileConfig,
|
|
2980
|
+
loadManifest,
|
|
2981
|
+
loadLockFile,
|
|
2982
|
+
loadEnvironment,
|
|
2983
|
+
loadDocs,
|
|
2984
|
+
loadConfig,
|
|
2985
|
+
loadCommands,
|
|
2986
|
+
loadCapabilityConfig,
|
|
2987
|
+
loadCapability,
|
|
2988
|
+
isSecretEnvVar,
|
|
2989
|
+
isProviderEnabled,
|
|
2990
|
+
installCapabilityDependencies,
|
|
2991
|
+
getVersion,
|
|
2992
|
+
getSourceCapabilityPath,
|
|
2993
|
+
getLockFilePath,
|
|
2994
|
+
getEnabledCapabilities,
|
|
2995
|
+
getActiveProviders,
|
|
2996
|
+
getActiveProfile,
|
|
2997
|
+
generateInstructionsTemplate,
|
|
2998
|
+
generateClaudeTemplate,
|
|
2999
|
+
generateAgentsTemplate,
|
|
3000
|
+
fetchCapabilitySource,
|
|
3001
|
+
fetchAllCapabilitySources,
|
|
3002
|
+
enableProvider,
|
|
3003
|
+
enableCapability,
|
|
3004
|
+
discoverCapabilities,
|
|
3005
|
+
disableProvider,
|
|
3006
|
+
disableCapability,
|
|
3007
|
+
debug,
|
|
3008
|
+
clearActiveProfileState,
|
|
3009
|
+
cleanupStaleResources,
|
|
3010
|
+
checkForUpdates,
|
|
3011
|
+
buildSyncBundle,
|
|
3012
|
+
buildRouteMap,
|
|
3013
|
+
buildManifestFromCapabilities,
|
|
3014
|
+
buildCommand,
|
|
3015
|
+
buildCapabilityRegistry
|
|
3016
|
+
};
|
|
3017
|
+
export { __toESM, __require, discoverCapabilities, loadCapabilityConfig, loadConfig, writeConfig, getActiveProfile, setActiveProfile, resolveEnabledCapabilities, getEnabledCapabilities, enableCapability, disableCapability, readEnabledProviders, writeEnabledProviders, enableProvider, disableProvider, syncAgentConfiguration, generateInstructionsTemplate, debug };
|