agentpacks 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +32 -0
- package/dist/api.js +2852 -0
- package/dist/cli/export-cmd.d.ts +12 -0
- package/dist/cli/export-cmd.js +786 -0
- package/dist/cli/generate.d.ts +13 -0
- package/dist/cli/generate.js +2018 -0
- package/dist/cli/import-cmd.d.ts +9 -0
- package/dist/cli/import-cmd.js +691 -0
- package/dist/cli/init.d.ts +5 -0
- package/dist/cli/init.js +214 -0
- package/dist/cli/install.d.ts +11 -0
- package/dist/cli/install.js +610 -0
- package/dist/cli/pack/create.d.ts +4 -0
- package/dist/cli/pack/create.js +175 -0
- package/dist/cli/pack/enable.d.ts +8 -0
- package/dist/cli/pack/enable.js +96 -0
- package/dist/cli/pack/list.d.ts +4 -0
- package/dist/cli/pack/list.js +699 -0
- package/dist/cli/pack/validate.d.ts +4 -0
- package/dist/cli/pack/validate.js +364 -0
- package/dist/core/config.d.ts +77 -0
- package/dist/core/config.js +181 -0
- package/dist/core/dependency-resolver.d.ts +38 -0
- package/dist/core/dependency-resolver.js +151 -0
- package/dist/core/feature-merger.d.ts +61 -0
- package/dist/core/feature-merger.js +139 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +924 -0
- package/dist/core/lockfile.d.ts +50 -0
- package/dist/core/lockfile.js +72 -0
- package/dist/core/metarepo.d.ts +26 -0
- package/dist/core/metarepo.js +218 -0
- package/dist/core/pack-loader.d.ts +61 -0
- package/dist/core/pack-loader.js +646 -0
- package/dist/exporters/cursor-plugin.d.ts +29 -0
- package/dist/exporters/cursor-plugin.js +258 -0
- package/dist/exporters/index.d.ts +1 -0
- package/dist/exporters/index.js +258 -0
- package/dist/features/agents.d.ts +48 -0
- package/dist/features/agents.js +190 -0
- package/dist/features/commands.d.ts +33 -0
- package/dist/features/commands.js +190 -0
- package/dist/features/hooks.d.ts +33 -0
- package/dist/features/hooks.js +182 -0
- package/dist/features/ignore.d.ts +21 -0
- package/dist/features/ignore.js +72 -0
- package/dist/features/index.d.ts +8 -0
- package/dist/features/index.js +426 -0
- package/dist/features/mcp.d.ts +34 -0
- package/dist/features/mcp.js +172 -0
- package/dist/features/plugins.d.ts +23 -0
- package/dist/features/plugins.js +168 -0
- package/dist/features/rules.d.ts +53 -0
- package/dist/features/rules.js +198 -0
- package/dist/features/skills.d.ts +42 -0
- package/dist/features/skills.js +198 -0
- package/dist/importers/claude-code.d.ts +5 -0
- package/dist/importers/claude-code.js +224 -0
- package/dist/importers/cursor.d.ts +5 -0
- package/dist/importers/cursor.js +288 -0
- package/dist/importers/opencode.d.ts +5 -0
- package/dist/importers/opencode.js +261 -0
- package/dist/importers/rulesync.d.ts +14 -0
- package/dist/importers/rulesync.js +280 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3464 -0
- package/dist/node/api.js +2852 -0
- package/dist/node/cli/export-cmd.js +786 -0
- package/dist/node/cli/generate.js +2018 -0
- package/dist/node/cli/import-cmd.js +691 -0
- package/dist/node/cli/init.js +214 -0
- package/dist/node/cli/install.js +610 -0
- package/dist/node/cli/pack/create.js +175 -0
- package/dist/node/cli/pack/enable.js +96 -0
- package/dist/node/cli/pack/list.js +699 -0
- package/dist/node/cli/pack/validate.js +364 -0
- package/dist/node/core/config.js +181 -0
- package/dist/node/core/dependency-resolver.js +151 -0
- package/dist/node/core/feature-merger.js +139 -0
- package/dist/node/core/index.js +924 -0
- package/dist/node/core/lockfile.js +72 -0
- package/dist/node/core/metarepo.js +218 -0
- package/dist/node/core/pack-loader.js +646 -0
- package/dist/node/exporters/cursor-plugin.js +258 -0
- package/dist/node/exporters/index.js +258 -0
- package/dist/node/features/agents.js +190 -0
- package/dist/node/features/commands.js +190 -0
- package/dist/node/features/hooks.js +182 -0
- package/dist/node/features/ignore.js +72 -0
- package/dist/node/features/index.js +426 -0
- package/dist/node/features/mcp.js +172 -0
- package/dist/node/features/plugins.js +168 -0
- package/dist/node/features/rules.js +198 -0
- package/dist/node/features/skills.js +198 -0
- package/dist/node/importers/claude-code.js +224 -0
- package/dist/node/importers/cursor.js +288 -0
- package/dist/node/importers/opencode.js +261 -0
- package/dist/node/importers/rulesync.js +280 -0
- package/dist/node/index.js +3464 -0
- package/dist/node/sources/git-ref.js +79 -0
- package/dist/node/sources/git.js +245 -0
- package/dist/node/sources/index.js +416 -0
- package/dist/node/sources/local.js +52 -0
- package/dist/node/sources/npm-ref.js +84 -0
- package/dist/node/sources/npm.js +211 -0
- package/dist/node/targets/additional-targets.js +428 -0
- package/dist/node/targets/agents-md.js +239 -0
- package/dist/node/targets/base-target.js +51 -0
- package/dist/node/targets/claude-code.js +490 -0
- package/dist/node/targets/codex-cli.js +297 -0
- package/dist/node/targets/copilot.js +390 -0
- package/dist/node/targets/cursor.js +441 -0
- package/dist/node/targets/gemini-cli.js +352 -0
- package/dist/node/targets/generic-md-target.js +319 -0
- package/dist/node/targets/index.js +1351 -0
- package/dist/node/targets/opencode.js +556 -0
- package/dist/node/targets/registry.js +1343 -0
- package/dist/node/utils/diff.js +144 -0
- package/dist/node/utils/filesystem.js +152 -0
- package/dist/node/utils/frontmatter.js +52 -0
- package/dist/node/utils/global.js +81 -0
- package/dist/node/utils/markdown.js +62 -0
- package/dist/sources/git-ref.d.ts +27 -0
- package/dist/sources/git-ref.js +79 -0
- package/dist/sources/git.d.ts +29 -0
- package/dist/sources/git.js +245 -0
- package/dist/sources/index.d.ts +5 -0
- package/dist/sources/index.js +416 -0
- package/dist/sources/local.d.ts +8 -0
- package/dist/sources/local.js +52 -0
- package/dist/sources/npm-ref.d.ts +30 -0
- package/dist/sources/npm-ref.js +84 -0
- package/dist/sources/npm.d.ts +20 -0
- package/dist/sources/npm.js +211 -0
- package/dist/targets/additional-targets.d.ts +65 -0
- package/dist/targets/additional-targets.js +428 -0
- package/dist/targets/agents-md.d.ts +13 -0
- package/dist/targets/agents-md.js +239 -0
- package/dist/targets/base-target.d.ts +61 -0
- package/dist/targets/base-target.js +51 -0
- package/dist/targets/claude-code.d.ts +13 -0
- package/dist/targets/claude-code.js +490 -0
- package/dist/targets/codex-cli.d.ts +12 -0
- package/dist/targets/codex-cli.js +297 -0
- package/dist/targets/copilot.d.ts +12 -0
- package/dist/targets/copilot.js +390 -0
- package/dist/targets/cursor.d.ts +13 -0
- package/dist/targets/cursor.js +441 -0
- package/dist/targets/gemini-cli.d.ts +12 -0
- package/dist/targets/gemini-cli.js +352 -0
- package/dist/targets/generic-md-target.d.ts +25 -0
- package/dist/targets/generic-md-target.js +319 -0
- package/dist/targets/index.d.ts +9 -0
- package/dist/targets/index.js +1351 -0
- package/dist/targets/opencode.d.ts +13 -0
- package/dist/targets/opencode.js +556 -0
- package/dist/targets/registry.d.ts +17 -0
- package/dist/targets/registry.js +1343 -0
- package/dist/utils/diff.d.ts +13 -0
- package/dist/utils/diff.js +144 -0
- package/dist/utils/filesystem.d.ts +49 -0
- package/dist/utils/filesystem.js +152 -0
- package/dist/utils/frontmatter.d.ts +19 -0
- package/dist/utils/frontmatter.js +52 -0
- package/dist/utils/global.d.ts +17 -0
- package/dist/utils/global.js +81 -0
- package/dist/utils/markdown.d.ts +20 -0
- package/dist/utils/markdown.js +62 -0
- package/package.json +808 -0
- package/templates/pack/pack.json +10 -0
- package/templates/pack/rules/overview.md +22 -0
- package/templates/workspace/agentpacks.jsonc +34 -0
package/dist/api.js
ADDED
|
@@ -0,0 +1,2852 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
|
+
var __require = import.meta.require;
|
|
31
|
+
|
|
32
|
+
// src/core/config.ts
|
|
33
|
+
var exports_config = {};
|
|
34
|
+
__export(exports_config, {
|
|
35
|
+
resolveTargets: () => resolveTargets,
|
|
36
|
+
resolveFeatures: () => resolveFeatures,
|
|
37
|
+
loadWorkspaceConfig: () => loadWorkspaceConfig,
|
|
38
|
+
loadPackManifest: () => loadPackManifest,
|
|
39
|
+
WorkspaceConfigSchema: () => WorkspaceConfigSchema,
|
|
40
|
+
TARGET_IDS: () => TARGET_IDS,
|
|
41
|
+
REPO_MODES: () => REPO_MODES,
|
|
42
|
+
PackManifestSchema: () => PackManifestSchema,
|
|
43
|
+
FEATURE_IDS: () => FEATURE_IDS
|
|
44
|
+
});
|
|
45
|
+
import { z } from "zod";
|
|
46
|
+
import { readFileSync, existsSync } from "fs";
|
|
47
|
+
import { resolve } from "path";
|
|
48
|
+
import { parse as parseJsonc } from "jsonc-parser";
|
|
49
|
+
function loadWorkspaceConfig(projectRoot) {
|
|
50
|
+
for (const filename of CONFIG_FILES) {
|
|
51
|
+
const filepath = resolve(projectRoot, filename);
|
|
52
|
+
if (existsSync(filepath)) {
|
|
53
|
+
const raw = readFileSync(filepath, "utf-8");
|
|
54
|
+
const parsed = parseJsonc(raw);
|
|
55
|
+
return WorkspaceConfigSchema.parse(parsed);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return WorkspaceConfigSchema.parse({});
|
|
59
|
+
}
|
|
60
|
+
function loadPackManifest(packDir) {
|
|
61
|
+
const filepath = resolve(packDir, "pack.json");
|
|
62
|
+
if (!existsSync(filepath)) {
|
|
63
|
+
const dirName = packDir.split("/").pop() ?? "unknown";
|
|
64
|
+
return PackManifestSchema.parse({ name: dirName });
|
|
65
|
+
}
|
|
66
|
+
const raw = readFileSync(filepath, "utf-8");
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
return PackManifestSchema.parse(parsed);
|
|
69
|
+
}
|
|
70
|
+
function resolveFeatures(config, targetId) {
|
|
71
|
+
const { features } = config;
|
|
72
|
+
if (features === "*") {
|
|
73
|
+
return [...FEATURE_IDS];
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(features)) {
|
|
76
|
+
if (features.includes("*"))
|
|
77
|
+
return [...FEATURE_IDS];
|
|
78
|
+
return features.filter((f) => FEATURE_IDS.includes(f));
|
|
79
|
+
}
|
|
80
|
+
const targetFeatures = features[targetId];
|
|
81
|
+
if (!targetFeatures)
|
|
82
|
+
return [];
|
|
83
|
+
if (targetFeatures === "*")
|
|
84
|
+
return [...FEATURE_IDS];
|
|
85
|
+
if (Array.isArray(targetFeatures) && targetFeatures.includes("*"))
|
|
86
|
+
return [...FEATURE_IDS];
|
|
87
|
+
return targetFeatures.filter((f) => FEATURE_IDS.includes(f));
|
|
88
|
+
}
|
|
89
|
+
function resolveTargets(config) {
|
|
90
|
+
if (config.targets === "*")
|
|
91
|
+
return [...TARGET_IDS];
|
|
92
|
+
return config.targets;
|
|
93
|
+
}
|
|
94
|
+
var TARGET_IDS, FEATURE_IDS, REPO_MODES, PackManifestSchema, FeaturesSchema, WorkspaceConfigSchema, CONFIG_FILES;
|
|
95
|
+
var init_config = __esm(() => {
|
|
96
|
+
TARGET_IDS = [
|
|
97
|
+
"opencode",
|
|
98
|
+
"cursor",
|
|
99
|
+
"claudecode",
|
|
100
|
+
"codexcli",
|
|
101
|
+
"geminicli",
|
|
102
|
+
"copilot",
|
|
103
|
+
"agentsmd",
|
|
104
|
+
"cline",
|
|
105
|
+
"kilo",
|
|
106
|
+
"roo",
|
|
107
|
+
"qwencode",
|
|
108
|
+
"kiro",
|
|
109
|
+
"factorydroid",
|
|
110
|
+
"antigravity",
|
|
111
|
+
"junie",
|
|
112
|
+
"augmentcode",
|
|
113
|
+
"windsurf",
|
|
114
|
+
"warp",
|
|
115
|
+
"replit",
|
|
116
|
+
"zed"
|
|
117
|
+
];
|
|
118
|
+
FEATURE_IDS = [
|
|
119
|
+
"rules",
|
|
120
|
+
"commands",
|
|
121
|
+
"agents",
|
|
122
|
+
"skills",
|
|
123
|
+
"hooks",
|
|
124
|
+
"plugins",
|
|
125
|
+
"mcp",
|
|
126
|
+
"ignore"
|
|
127
|
+
];
|
|
128
|
+
REPO_MODES = ["repo", "monorepo", "metarepo"];
|
|
129
|
+
PackManifestSchema = z.object({
|
|
130
|
+
name: z.string().min(1),
|
|
131
|
+
version: z.string().default("1.0.0"),
|
|
132
|
+
description: z.string().default(""),
|
|
133
|
+
author: z.union([
|
|
134
|
+
z.string(),
|
|
135
|
+
z.object({ name: z.string(), email: z.string().optional() })
|
|
136
|
+
]).optional(),
|
|
137
|
+
tags: z.array(z.string()).default([]),
|
|
138
|
+
dependencies: z.array(z.string()).default([]),
|
|
139
|
+
conflicts: z.array(z.string()).default([]),
|
|
140
|
+
targets: z.union([z.literal("*"), z.array(z.string())]).default("*"),
|
|
141
|
+
features: z.union([z.literal("*"), z.array(z.string())]).default("*")
|
|
142
|
+
});
|
|
143
|
+
FeaturesSchema = z.union([
|
|
144
|
+
z.literal("*"),
|
|
145
|
+
z.array(z.string()),
|
|
146
|
+
z.record(z.string(), z.union([z.literal("*"), z.array(z.string())]))
|
|
147
|
+
]);
|
|
148
|
+
WorkspaceConfigSchema = z.object({
|
|
149
|
+
$schema: z.string().optional(),
|
|
150
|
+
packs: z.array(z.string()).default(["./packs/default"]),
|
|
151
|
+
disabled: z.array(z.string()).default([]),
|
|
152
|
+
targets: z.union([z.literal("*"), z.array(z.string())]).default("*"),
|
|
153
|
+
features: FeaturesSchema.default("*"),
|
|
154
|
+
mode: z.enum(REPO_MODES).default("repo"),
|
|
155
|
+
baseDirs: z.array(z.string()).default(["."]),
|
|
156
|
+
global: z.boolean().default(false),
|
|
157
|
+
delete: z.boolean().default(true),
|
|
158
|
+
verbose: z.boolean().default(false),
|
|
159
|
+
silent: z.boolean().default(false),
|
|
160
|
+
overrides: z.record(z.string(), z.record(z.string(), z.string())).default({}),
|
|
161
|
+
sources: z.array(z.object({
|
|
162
|
+
source: z.string(),
|
|
163
|
+
packs: z.array(z.string()).optional(),
|
|
164
|
+
skills: z.array(z.string()).optional()
|
|
165
|
+
})).default([])
|
|
166
|
+
});
|
|
167
|
+
CONFIG_FILES = ["agentpacks.local.jsonc", "agentpacks.jsonc"];
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// src/utils/filesystem.ts
|
|
171
|
+
import {
|
|
172
|
+
existsSync as existsSync2,
|
|
173
|
+
mkdirSync,
|
|
174
|
+
readFileSync as readFileSync2,
|
|
175
|
+
writeFileSync,
|
|
176
|
+
readdirSync,
|
|
177
|
+
statSync
|
|
178
|
+
} from "fs";
|
|
179
|
+
import { dirname, relative, join } from "path";
|
|
180
|
+
import { removeSync } from "fs-extra";
|
|
181
|
+
var GENERATED_HEADER_MD = "<!-- Generated by agentpacks. DO NOT EDIT. -->";
|
|
182
|
+
var GENERATED_HEADER_JSON = "// Generated by agentpacks. DO NOT EDIT.";
|
|
183
|
+
var GENERATED_HEADER_JS = "// Generated by agentpacks. DO NOT EDIT.";
|
|
184
|
+
function writeGeneratedFile(filepath, content, options = {}) {
|
|
185
|
+
const { header = true, type } = options;
|
|
186
|
+
const ext = type ?? inferFileType(filepath);
|
|
187
|
+
ensureDir(dirname(filepath));
|
|
188
|
+
let output = content;
|
|
189
|
+
if (header) {
|
|
190
|
+
const headerComment = getHeader(ext);
|
|
191
|
+
if (headerComment) {
|
|
192
|
+
output = `${headerComment}
|
|
193
|
+
${content}`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
writeFileSync(filepath, output, "utf-8");
|
|
197
|
+
}
|
|
198
|
+
function writeGeneratedJson(filepath, data, options = {}) {
|
|
199
|
+
const json = JSON.stringify(data, null, 2);
|
|
200
|
+
writeGeneratedFile(filepath, json + `
|
|
201
|
+
`, { ...options, type: "json" });
|
|
202
|
+
}
|
|
203
|
+
function readFileOrNull(filepath) {
|
|
204
|
+
if (!existsSync2(filepath))
|
|
205
|
+
return null;
|
|
206
|
+
return readFileSync2(filepath, "utf-8");
|
|
207
|
+
}
|
|
208
|
+
function readJsonOrNull(filepath) {
|
|
209
|
+
const content = readFileOrNull(filepath);
|
|
210
|
+
if (content === null)
|
|
211
|
+
return null;
|
|
212
|
+
return JSON.parse(content);
|
|
213
|
+
}
|
|
214
|
+
function ensureDir(dirPath) {
|
|
215
|
+
if (!existsSync2(dirPath)) {
|
|
216
|
+
mkdirSync(dirPath, { recursive: true });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function removeIfExists(targetPath) {
|
|
220
|
+
if (existsSync2(targetPath)) {
|
|
221
|
+
removeSync(targetPath);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function listFiles(dirPath, options = {}) {
|
|
225
|
+
const { extension, recursive = false } = options;
|
|
226
|
+
if (!existsSync2(dirPath))
|
|
227
|
+
return [];
|
|
228
|
+
const results = [];
|
|
229
|
+
const entries = readdirSync(dirPath);
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
const fullPath = join(dirPath, entry);
|
|
232
|
+
const stat = statSync(fullPath);
|
|
233
|
+
if (stat.isDirectory() && recursive) {
|
|
234
|
+
results.push(...listFiles(fullPath, options));
|
|
235
|
+
} else if (stat.isFile()) {
|
|
236
|
+
if (!extension || entry.endsWith(extension)) {
|
|
237
|
+
results.push(fullPath);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
function listDirs(dirPath) {
|
|
244
|
+
if (!existsSync2(dirPath))
|
|
245
|
+
return [];
|
|
246
|
+
return readdirSync(dirPath).map((entry) => join(dirPath, entry)).filter((fullPath) => statSync(fullPath).isDirectory());
|
|
247
|
+
}
|
|
248
|
+
function relPath(projectRoot, filepath) {
|
|
249
|
+
return relative(projectRoot, filepath);
|
|
250
|
+
}
|
|
251
|
+
function isGeneratedFile(filepath) {
|
|
252
|
+
const content = readFileOrNull(filepath);
|
|
253
|
+
if (!content)
|
|
254
|
+
return false;
|
|
255
|
+
return content.startsWith(GENERATED_HEADER_MD) || content.startsWith(GENERATED_HEADER_JSON) || content.startsWith(GENERATED_HEADER_JS);
|
|
256
|
+
}
|
|
257
|
+
function inferFileType(filepath) {
|
|
258
|
+
if (filepath.endsWith(".json") || filepath.endsWith(".jsonc"))
|
|
259
|
+
return "json";
|
|
260
|
+
if (filepath.endsWith(".ts") || filepath.endsWith(".mts"))
|
|
261
|
+
return "ts";
|
|
262
|
+
if (filepath.endsWith(".js") || filepath.endsWith(".mjs"))
|
|
263
|
+
return "js";
|
|
264
|
+
return "md";
|
|
265
|
+
}
|
|
266
|
+
function getHeader(type) {
|
|
267
|
+
switch (type) {
|
|
268
|
+
case "md":
|
|
269
|
+
return GENERATED_HEADER_MD;
|
|
270
|
+
case "json":
|
|
271
|
+
return GENERATED_HEADER_JSON;
|
|
272
|
+
case "js":
|
|
273
|
+
case "ts":
|
|
274
|
+
return GENERATED_HEADER_JS;
|
|
275
|
+
default:
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/utils/frontmatter.ts
|
|
281
|
+
import matter from "gray-matter";
|
|
282
|
+
function parseFrontmatter(source) {
|
|
283
|
+
const { data, content } = matter(source);
|
|
284
|
+
return {
|
|
285
|
+
data,
|
|
286
|
+
content: content.trim(),
|
|
287
|
+
raw: source
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function serializeFrontmatter(data, content) {
|
|
291
|
+
const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
|
|
292
|
+
if (Object.keys(filtered).length === 0) {
|
|
293
|
+
return content;
|
|
294
|
+
}
|
|
295
|
+
return matter.stringify(content, filtered);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/features/rules.ts
|
|
299
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
300
|
+
import { basename } from "path";
|
|
301
|
+
function parseRules(rulesDir, packName) {
|
|
302
|
+
const files = listFiles(rulesDir, { extension: ".md" });
|
|
303
|
+
return files.map((filepath) => parseRuleFile(filepath, packName));
|
|
304
|
+
}
|
|
305
|
+
function parseRuleFile(filepath, packName) {
|
|
306
|
+
const raw = readFileSync3(filepath, "utf-8");
|
|
307
|
+
const { data, content } = parseFrontmatter(raw);
|
|
308
|
+
return {
|
|
309
|
+
name: basename(filepath, ".md"),
|
|
310
|
+
sourcePath: filepath,
|
|
311
|
+
packName,
|
|
312
|
+
meta: data,
|
|
313
|
+
content
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function ruleMatchesTarget(rule, targetId) {
|
|
317
|
+
const { targets } = rule.meta;
|
|
318
|
+
if (!targets || targets === "*")
|
|
319
|
+
return true;
|
|
320
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
321
|
+
return true;
|
|
322
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
323
|
+
}
|
|
324
|
+
function getRootRules(rules) {
|
|
325
|
+
return rules.filter((r) => r.meta.root === true);
|
|
326
|
+
}
|
|
327
|
+
function getDetailRules(rules) {
|
|
328
|
+
return rules.filter((r) => r.meta.root !== true);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/features/commands.ts
|
|
332
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
333
|
+
import { basename as basename2 } from "path";
|
|
334
|
+
function parseCommands(commandsDir, packName) {
|
|
335
|
+
const files = listFiles(commandsDir, { extension: ".md" });
|
|
336
|
+
return files.map((filepath) => parseCommandFile(filepath, packName));
|
|
337
|
+
}
|
|
338
|
+
function parseCommandFile(filepath, packName) {
|
|
339
|
+
const raw = readFileSync4(filepath, "utf-8");
|
|
340
|
+
const { data, content } = parseFrontmatter(raw);
|
|
341
|
+
return {
|
|
342
|
+
name: basename2(filepath, ".md"),
|
|
343
|
+
sourcePath: filepath,
|
|
344
|
+
packName,
|
|
345
|
+
meta: data,
|
|
346
|
+
content
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function commandMatchesTarget(cmd, targetId) {
|
|
350
|
+
const { targets } = cmd.meta;
|
|
351
|
+
if (!targets || targets === "*")
|
|
352
|
+
return true;
|
|
353
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
354
|
+
return true;
|
|
355
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/features/agents.ts
|
|
359
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
360
|
+
import { basename as basename3 } from "path";
|
|
361
|
+
function parseAgents(agentsDir, packName) {
|
|
362
|
+
const files = listFiles(agentsDir, { extension: ".md" });
|
|
363
|
+
return files.map((filepath) => parseAgentFile(filepath, packName));
|
|
364
|
+
}
|
|
365
|
+
function parseAgentFile(filepath, packName) {
|
|
366
|
+
const raw = readFileSync5(filepath, "utf-8");
|
|
367
|
+
const { data, content } = parseFrontmatter(raw);
|
|
368
|
+
return {
|
|
369
|
+
name: data.name ?? basename3(filepath, ".md"),
|
|
370
|
+
sourcePath: filepath,
|
|
371
|
+
packName,
|
|
372
|
+
meta: data,
|
|
373
|
+
content
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function agentMatchesTarget(agent, targetId) {
|
|
377
|
+
const { targets } = agent.meta;
|
|
378
|
+
if (!targets || targets === "*")
|
|
379
|
+
return true;
|
|
380
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
381
|
+
return true;
|
|
382
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/features/skills.ts
|
|
386
|
+
import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
|
|
387
|
+
import { basename as basename4, join as join2 } from "path";
|
|
388
|
+
function parseSkills(skillsDir, packName) {
|
|
389
|
+
const dirs = listDirs(skillsDir);
|
|
390
|
+
const skills = [];
|
|
391
|
+
for (const dir of dirs) {
|
|
392
|
+
const skillMd = join2(dir, "SKILL.md");
|
|
393
|
+
if (existsSync3(skillMd)) {
|
|
394
|
+
skills.push(parseSkillFile(skillMd, dir, packName));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return skills;
|
|
398
|
+
}
|
|
399
|
+
function parseSkillFile(filepath, skillDir, packName) {
|
|
400
|
+
const raw = readFileSync6(filepath, "utf-8");
|
|
401
|
+
const { data, content } = parseFrontmatter(raw);
|
|
402
|
+
return {
|
|
403
|
+
name: data.name ?? basename4(skillDir),
|
|
404
|
+
sourcePath: filepath,
|
|
405
|
+
sourceDir: skillDir,
|
|
406
|
+
packName,
|
|
407
|
+
meta: data,
|
|
408
|
+
content
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function skillMatchesTarget(skill, targetId) {
|
|
412
|
+
const { targets } = skill.meta;
|
|
413
|
+
if (!targets || targets === "*")
|
|
414
|
+
return true;
|
|
415
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
416
|
+
return true;
|
|
417
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/features/hooks.ts
|
|
421
|
+
import { join as join3 } from "path";
|
|
422
|
+
var TARGET_OVERRIDE_KEYS = ["cursor", "claudecode", "opencode"];
|
|
423
|
+
function parseHooks(packDir, packName) {
|
|
424
|
+
const hooksPath = join3(packDir, "hooks", "hooks.json");
|
|
425
|
+
const raw = readJsonOrNull(hooksPath);
|
|
426
|
+
if (!raw)
|
|
427
|
+
return null;
|
|
428
|
+
const shared = raw.hooks ?? {};
|
|
429
|
+
const targetOverrides = {};
|
|
430
|
+
for (const key of TARGET_OVERRIDE_KEYS) {
|
|
431
|
+
const override = raw[key];
|
|
432
|
+
if (override && typeof override === "object" && "hooks" in override && override.hooks) {
|
|
433
|
+
targetOverrides[key] = override.hooks;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
packName,
|
|
438
|
+
sourcePath: hooksPath,
|
|
439
|
+
version: raw.version,
|
|
440
|
+
shared,
|
|
441
|
+
targetOverrides
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function resolveHooksForTarget(hooks, targetId) {
|
|
445
|
+
const merged = {};
|
|
446
|
+
for (const [event, entries] of Object.entries(hooks.shared)) {
|
|
447
|
+
merged[event] = [...entries];
|
|
448
|
+
}
|
|
449
|
+
const overrides = hooks.targetOverrides[targetId];
|
|
450
|
+
if (overrides) {
|
|
451
|
+
for (const [event, entries] of Object.entries(overrides)) {
|
|
452
|
+
merged[event] = [...merged[event] ?? [], ...entries];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return merged;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/features/plugins.ts
|
|
459
|
+
import { readFileSync as readFileSync7, existsSync as existsSync4 } from "fs";
|
|
460
|
+
import { basename as basename5, join as join4 } from "path";
|
|
461
|
+
function parsePlugins(packDir, packName) {
|
|
462
|
+
const pluginsDir = join4(packDir, "plugins");
|
|
463
|
+
if (!existsSync4(pluginsDir))
|
|
464
|
+
return [];
|
|
465
|
+
const tsFiles = listFiles(pluginsDir, { extension: ".ts" });
|
|
466
|
+
const jsFiles = listFiles(pluginsDir, { extension: ".js" });
|
|
467
|
+
const allFiles = [...tsFiles, ...jsFiles];
|
|
468
|
+
return allFiles.map((filepath) => parsePluginFile(filepath, packName));
|
|
469
|
+
}
|
|
470
|
+
function parsePluginFile(filepath, packName) {
|
|
471
|
+
const content = readFileSync7(filepath, "utf-8");
|
|
472
|
+
const ext = filepath.endsWith(".ts") ? "ts" : "js";
|
|
473
|
+
return {
|
|
474
|
+
name: basename5(filepath, `.${ext}`),
|
|
475
|
+
sourcePath: filepath,
|
|
476
|
+
packName,
|
|
477
|
+
content,
|
|
478
|
+
extension: ext
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/features/mcp.ts
|
|
483
|
+
import { join as join5 } from "path";
|
|
484
|
+
function parseMcp(packDir, packName) {
|
|
485
|
+
const mcpPath = join5(packDir, "mcp.json");
|
|
486
|
+
const raw = readJsonOrNull(mcpPath);
|
|
487
|
+
if (!raw?.mcpServers)
|
|
488
|
+
return null;
|
|
489
|
+
return {
|
|
490
|
+
packName,
|
|
491
|
+
sourcePath: mcpPath,
|
|
492
|
+
servers: raw.mcpServers
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function mergeMcpConfigs(configs) {
|
|
496
|
+
const servers = {};
|
|
497
|
+
const warnings = [];
|
|
498
|
+
for (const config of configs) {
|
|
499
|
+
for (const [name, entry] of Object.entries(config.servers)) {
|
|
500
|
+
if (name in servers) {
|
|
501
|
+
warnings.push(`MCP server "${name}" from pack "${config.packName}" skipped (already defined).`);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
servers[name] = entry;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { servers, warnings };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/features/ignore.ts
|
|
511
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
|
|
512
|
+
import { join as join6 } from "path";
|
|
513
|
+
var IGNORE_FILES = ["ignore", ".aiignore"];
|
|
514
|
+
function parseIgnore(packDir, packName) {
|
|
515
|
+
for (const filename of IGNORE_FILES) {
|
|
516
|
+
const filepath = join6(packDir, filename);
|
|
517
|
+
if (existsSync5(filepath)) {
|
|
518
|
+
const raw = readFileSync8(filepath, "utf-8");
|
|
519
|
+
const patterns = parseIgnoreContent(raw);
|
|
520
|
+
return {
|
|
521
|
+
packName,
|
|
522
|
+
sourcePath: filepath,
|
|
523
|
+
patterns
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
function parseIgnoreContent(content) {
|
|
530
|
+
return content.split(`
|
|
531
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
532
|
+
}
|
|
533
|
+
function mergeIgnorePatterns(configs) {
|
|
534
|
+
const seen = new Set;
|
|
535
|
+
const result = [];
|
|
536
|
+
for (const config of configs) {
|
|
537
|
+
for (const pattern of config.patterns) {
|
|
538
|
+
if (!seen.has(pattern)) {
|
|
539
|
+
seen.add(pattern);
|
|
540
|
+
result.push(pattern);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/core/pack-loader.ts
|
|
548
|
+
init_config();
|
|
549
|
+
import { existsSync as existsSync6 } from "fs";
|
|
550
|
+
import { resolve as resolve3, isAbsolute } from "path";
|
|
551
|
+
class PackLoader {
|
|
552
|
+
projectRoot;
|
|
553
|
+
config;
|
|
554
|
+
constructor(projectRoot, config) {
|
|
555
|
+
this.projectRoot = projectRoot;
|
|
556
|
+
this.config = config;
|
|
557
|
+
}
|
|
558
|
+
loadAll() {
|
|
559
|
+
const warnings = [];
|
|
560
|
+
const packs = [];
|
|
561
|
+
const disabledSet = new Set(this.config.disabled);
|
|
562
|
+
for (const packRef of this.config.packs) {
|
|
563
|
+
const packDir = this.resolvePackPath(packRef);
|
|
564
|
+
if (!packDir) {
|
|
565
|
+
warnings.push(`Pack "${packRef}" could not be resolved. Skipping.`);
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (!existsSync6(packDir)) {
|
|
569
|
+
warnings.push(`Pack directory "${packDir}" does not exist. Skipping.`);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const manifest = loadPackManifest(packDir);
|
|
573
|
+
if (disabledSet.has(manifest.name) || disabledSet.has(packRef)) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
const loaded = this.loadPack(packDir, manifest);
|
|
577
|
+
packs.push(loaded);
|
|
578
|
+
}
|
|
579
|
+
return { packs, warnings };
|
|
580
|
+
}
|
|
581
|
+
loadPack(packDir, manifest) {
|
|
582
|
+
const name = manifest.name;
|
|
583
|
+
const rulesDir = resolve3(packDir, "rules");
|
|
584
|
+
const commandsDir = resolve3(packDir, "commands");
|
|
585
|
+
const agentsDir = resolve3(packDir, "agents");
|
|
586
|
+
const skillsDir = resolve3(packDir, "skills");
|
|
587
|
+
return {
|
|
588
|
+
manifest,
|
|
589
|
+
directory: packDir,
|
|
590
|
+
rules: existsSync6(rulesDir) ? parseRules(rulesDir, name) : [],
|
|
591
|
+
commands: existsSync6(commandsDir) ? parseCommands(commandsDir, name) : [],
|
|
592
|
+
agents: existsSync6(agentsDir) ? parseAgents(agentsDir, name) : [],
|
|
593
|
+
skills: existsSync6(skillsDir) ? parseSkills(skillsDir, name) : [],
|
|
594
|
+
hooks: parseHooks(packDir, name),
|
|
595
|
+
plugins: parsePlugins(packDir, name),
|
|
596
|
+
mcp: parseMcp(packDir, name),
|
|
597
|
+
ignore: parseIgnore(packDir, name)
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
loadForBaseDir(baseDir) {
|
|
601
|
+
const baseDirRoot = resolve3(this.projectRoot, baseDir);
|
|
602
|
+
const localConfigPath = resolve3(baseDirRoot, "agentpacks.jsonc");
|
|
603
|
+
if (!existsSync6(localConfigPath)) {
|
|
604
|
+
return { packs: [], warnings: [] };
|
|
605
|
+
}
|
|
606
|
+
const { loadWorkspaceConfig: loadWorkspaceConfig2 } = (init_config(), __toCommonJS(exports_config));
|
|
607
|
+
const localConfig = loadWorkspaceConfig2(baseDirRoot);
|
|
608
|
+
const loader = new PackLoader(baseDirRoot, localConfig);
|
|
609
|
+
return loader.loadAll();
|
|
610
|
+
}
|
|
611
|
+
resolveCuratedPack(packRef) {
|
|
612
|
+
const curatedDir = resolve3(this.projectRoot, ".agentpacks", ".curated");
|
|
613
|
+
let packName = packRef;
|
|
614
|
+
if (packName.startsWith("npm:"))
|
|
615
|
+
packName = packName.slice(4);
|
|
616
|
+
if (packName.startsWith("github:"))
|
|
617
|
+
packName = packName.slice(7);
|
|
618
|
+
if (packName.startsWith("@"))
|
|
619
|
+
packName = packName.slice(1);
|
|
620
|
+
if (packName.includes("/")) {
|
|
621
|
+
const parts = packName.split("/");
|
|
622
|
+
packName = packName.includes("@") ? parts.join("-") : parts[parts.length - 1] ?? packName;
|
|
623
|
+
}
|
|
624
|
+
packName = packName.split("@")[0].split(":")[0];
|
|
625
|
+
const resolved = resolve3(curatedDir, packName);
|
|
626
|
+
return existsSync6(resolved) ? resolved : null;
|
|
627
|
+
}
|
|
628
|
+
resolvePackPath(packRef) {
|
|
629
|
+
if (packRef.startsWith("./") || packRef.startsWith("../")) {
|
|
630
|
+
return resolve3(this.projectRoot, packRef);
|
|
631
|
+
}
|
|
632
|
+
if (isAbsolute(packRef)) {
|
|
633
|
+
return packRef;
|
|
634
|
+
}
|
|
635
|
+
if (packRef.startsWith("@") || packRef.startsWith("npm:") || !packRef.includes("/")) {
|
|
636
|
+
return this.resolveCuratedPack(packRef);
|
|
637
|
+
}
|
|
638
|
+
if (packRef.startsWith("github:") || packRef.includes("/")) {
|
|
639
|
+
return this.resolveCuratedPack(packRef);
|
|
640
|
+
}
|
|
641
|
+
return resolve3(this.projectRoot, packRef);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/core/feature-merger.ts
|
|
646
|
+
class FeatureMerger {
|
|
647
|
+
packs;
|
|
648
|
+
warnings = [];
|
|
649
|
+
constructor(packs) {
|
|
650
|
+
this.packs = packs;
|
|
651
|
+
}
|
|
652
|
+
merge() {
|
|
653
|
+
this.warnings = [];
|
|
654
|
+
const features = {
|
|
655
|
+
rules: this.mergeRules(),
|
|
656
|
+
commands: this.mergeByName("commands"),
|
|
657
|
+
agents: this.mergeByName("agents"),
|
|
658
|
+
skills: this.mergeByName("skills"),
|
|
659
|
+
hooks: this.mergeHooks(),
|
|
660
|
+
plugins: this.mergePlugins(),
|
|
661
|
+
mcpServers: this.mergeMcp(),
|
|
662
|
+
ignorePatterns: this.mergeIgnore()
|
|
663
|
+
};
|
|
664
|
+
return { features, warnings: this.warnings };
|
|
665
|
+
}
|
|
666
|
+
mergeRules() {
|
|
667
|
+
const seen = new Map;
|
|
668
|
+
const result = [];
|
|
669
|
+
for (const pack of this.packs) {
|
|
670
|
+
for (const rule of pack.rules) {
|
|
671
|
+
const existing = seen.get(rule.name);
|
|
672
|
+
if (existing) {
|
|
673
|
+
this.warnings.push(`Rule "${rule.name}" from pack "${rule.packName}" skipped (already defined by "${existing}").`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
seen.set(rule.name, rule.packName);
|
|
677
|
+
result.push(rule);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
682
|
+
mergeByName(featureKey) {
|
|
683
|
+
const seen = new Map;
|
|
684
|
+
const result = [];
|
|
685
|
+
for (const pack of this.packs) {
|
|
686
|
+
const items = pack[featureKey];
|
|
687
|
+
for (const item of items) {
|
|
688
|
+
const existing = seen.get(item.name);
|
|
689
|
+
if (existing) {
|
|
690
|
+
this.warnings.push(`${featureKey.slice(0, -1)} "${item.name}" from pack "${item.packName}" skipped (already defined by "${existing}").`);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
seen.set(item.name, item.packName);
|
|
694
|
+
result.push(item);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
mergeHooks() {
|
|
700
|
+
return this.packs.map((p) => p.hooks).filter((h) => h !== null);
|
|
701
|
+
}
|
|
702
|
+
mergePlugins() {
|
|
703
|
+
const seen = new Map;
|
|
704
|
+
const result = [];
|
|
705
|
+
for (const pack of this.packs) {
|
|
706
|
+
for (const plugin of pack.plugins) {
|
|
707
|
+
const key = `${plugin.name}.${plugin.extension}`;
|
|
708
|
+
const existing = seen.get(key);
|
|
709
|
+
if (existing) {
|
|
710
|
+
this.warnings.push(`Plugin "${key}" from pack "${plugin.packName}" skipped (already defined by "${existing}").`);
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
seen.set(key, plugin.packName);
|
|
714
|
+
result.push(plugin);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return result;
|
|
718
|
+
}
|
|
719
|
+
mergeMcp() {
|
|
720
|
+
const servers = {};
|
|
721
|
+
for (const pack of this.packs) {
|
|
722
|
+
if (!pack.mcp)
|
|
723
|
+
continue;
|
|
724
|
+
for (const [name, entry] of Object.entries(pack.mcp.servers)) {
|
|
725
|
+
if (name in servers) {
|
|
726
|
+
this.warnings.push(`MCP server "${name}" from pack "${pack.manifest.name}" skipped (already defined).`);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
servers[name] = entry;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return servers;
|
|
733
|
+
}
|
|
734
|
+
mergeIgnore() {
|
|
735
|
+
const seen = new Set;
|
|
736
|
+
const result = [];
|
|
737
|
+
for (const pack of this.packs) {
|
|
738
|
+
if (!pack.ignore)
|
|
739
|
+
continue;
|
|
740
|
+
for (const pattern of pack.ignore.patterns) {
|
|
741
|
+
if (!seen.has(pattern)) {
|
|
742
|
+
seen.add(pattern);
|
|
743
|
+
result.push(pattern);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/core/lockfile.ts
|
|
752
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "fs";
|
|
753
|
+
import { resolve as resolve4 } from "path";
|
|
754
|
+
import { createHash } from "crypto";
|
|
755
|
+
var LOCKFILE_VERSION = 1;
|
|
756
|
+
var LOCKFILE_NAME = "agentpacks.lock";
|
|
757
|
+
function loadLockfile(projectRoot) {
|
|
758
|
+
const filepath = resolve4(projectRoot, LOCKFILE_NAME);
|
|
759
|
+
if (!existsSync7(filepath)) {
|
|
760
|
+
return { lockfileVersion: LOCKFILE_VERSION, sources: {} };
|
|
761
|
+
}
|
|
762
|
+
const raw = readFileSync9(filepath, "utf-8");
|
|
763
|
+
return JSON.parse(raw);
|
|
764
|
+
}
|
|
765
|
+
function saveLockfile(projectRoot, lockfile) {
|
|
766
|
+
const filepath = resolve4(projectRoot, LOCKFILE_NAME);
|
|
767
|
+
writeFileSync2(filepath, JSON.stringify(lockfile, null, 2) + `
|
|
768
|
+
`);
|
|
769
|
+
}
|
|
770
|
+
function getLockedSource(lockfile, sourceKey) {
|
|
771
|
+
return lockfile.sources[sourceKey];
|
|
772
|
+
}
|
|
773
|
+
function setLockedSource(lockfile, sourceKey, entry) {
|
|
774
|
+
lockfile.sources[sourceKey] = entry;
|
|
775
|
+
}
|
|
776
|
+
function computeIntegrity(content) {
|
|
777
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
778
|
+
return `sha256-${hash}`;
|
|
779
|
+
}
|
|
780
|
+
function isLockfileFrozenValid(lockfile, sourceKeys) {
|
|
781
|
+
const missing = sourceKeys.filter((key) => !(key in lockfile.sources));
|
|
782
|
+
return { valid: missing.length === 0, missing };
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// src/core/dependency-resolver.ts
|
|
786
|
+
function buildDependencyGraph(manifests) {
|
|
787
|
+
const graph = new Map;
|
|
788
|
+
for (const m of manifests) {
|
|
789
|
+
graph.set(m.name, {
|
|
790
|
+
name: m.name,
|
|
791
|
+
manifest: m,
|
|
792
|
+
dependencies: m.dependencies,
|
|
793
|
+
conflicts: m.conflicts
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
return graph;
|
|
797
|
+
}
|
|
798
|
+
function resolveDependencies(manifests) {
|
|
799
|
+
const graph = buildDependencyGraph(manifests);
|
|
800
|
+
const allNames = new Set(graph.keys());
|
|
801
|
+
const missingDeps = [];
|
|
802
|
+
for (const [name, node] of graph) {
|
|
803
|
+
for (const dep of node.dependencies) {
|
|
804
|
+
if (!allNames.has(dep)) {
|
|
805
|
+
missingDeps.push([name, dep]);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const { sorted, cycles } = topologicalSort(graph);
|
|
810
|
+
const conflictPairs = detectConflicts(graph);
|
|
811
|
+
const ok = cycles.length === 0 && conflictPairs.length === 0 && missingDeps.length === 0;
|
|
812
|
+
return { sorted, cycles, conflictPairs, missingDeps, ok };
|
|
813
|
+
}
|
|
814
|
+
function topologicalSort(graph) {
|
|
815
|
+
const inDegree = new Map;
|
|
816
|
+
for (const name of graph.keys()) {
|
|
817
|
+
inDegree.set(name, 0);
|
|
818
|
+
}
|
|
819
|
+
for (const [, node] of graph) {
|
|
820
|
+
for (const dep of node.dependencies) {
|
|
821
|
+
if (graph.has(dep)) {
|
|
822
|
+
inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
const queue = [];
|
|
827
|
+
for (const [name, degree] of inDegree) {
|
|
828
|
+
if (degree === 0)
|
|
829
|
+
queue.push(name);
|
|
830
|
+
}
|
|
831
|
+
const sorted = [];
|
|
832
|
+
while (queue.length > 0) {
|
|
833
|
+
const current = queue.shift();
|
|
834
|
+
sorted.push(current);
|
|
835
|
+
const node = graph.get(current);
|
|
836
|
+
for (const dep of node.dependencies) {
|
|
837
|
+
if (!graph.has(dep))
|
|
838
|
+
continue;
|
|
839
|
+
const newDegree = (inDegree.get(dep) ?? 1) - 1;
|
|
840
|
+
inDegree.set(dep, newDegree);
|
|
841
|
+
if (newDegree === 0) {
|
|
842
|
+
queue.push(dep);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const cycles = [];
|
|
847
|
+
if (sorted.length < graph.size) {
|
|
848
|
+
const remaining = new Set;
|
|
849
|
+
for (const name of graph.keys()) {
|
|
850
|
+
if (!sorted.includes(name))
|
|
851
|
+
remaining.add(name);
|
|
852
|
+
}
|
|
853
|
+
const visited = new Set;
|
|
854
|
+
for (const start of remaining) {
|
|
855
|
+
if (visited.has(start))
|
|
856
|
+
continue;
|
|
857
|
+
const cycle = traceCycle(start, graph, remaining);
|
|
858
|
+
if (cycle.length > 0) {
|
|
859
|
+
cycles.push(cycle);
|
|
860
|
+
for (const n of cycle)
|
|
861
|
+
visited.add(n);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return { sorted: sorted.reverse(), cycles };
|
|
866
|
+
}
|
|
867
|
+
function traceCycle(start, graph, remaining) {
|
|
868
|
+
const path = [];
|
|
869
|
+
const pathSet = new Set;
|
|
870
|
+
let current = start;
|
|
871
|
+
while (current && !pathSet.has(current)) {
|
|
872
|
+
path.push(current);
|
|
873
|
+
pathSet.add(current);
|
|
874
|
+
const node = graph.get(current);
|
|
875
|
+
if (!node)
|
|
876
|
+
break;
|
|
877
|
+
current = node.dependencies.find((d) => remaining.has(d) && graph.has(d));
|
|
878
|
+
}
|
|
879
|
+
if (current && pathSet.has(current)) {
|
|
880
|
+
const cycleStart = path.indexOf(current);
|
|
881
|
+
return path.slice(cycleStart);
|
|
882
|
+
}
|
|
883
|
+
return path;
|
|
884
|
+
}
|
|
885
|
+
function detectConflicts(graph) {
|
|
886
|
+
const conflicts = [];
|
|
887
|
+
const seen = new Set;
|
|
888
|
+
for (const [name, node] of graph) {
|
|
889
|
+
for (const conflict of node.conflicts) {
|
|
890
|
+
if (graph.has(conflict)) {
|
|
891
|
+
const key = [name, conflict].sort().join(":");
|
|
892
|
+
if (!seen.has(key)) {
|
|
893
|
+
seen.add(key);
|
|
894
|
+
conflicts.push([name, conflict]);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return conflicts;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// src/sources/local.ts
|
|
903
|
+
import { existsSync as existsSync8 } from "fs";
|
|
904
|
+
import { resolve as resolve5, isAbsolute as isAbsolute2 } from "path";
|
|
905
|
+
function resolveLocalPack(packRef, projectRoot) {
|
|
906
|
+
let resolved;
|
|
907
|
+
if (isAbsolute2(packRef)) {
|
|
908
|
+
resolved = packRef;
|
|
909
|
+
} else {
|
|
910
|
+
resolved = resolve5(projectRoot, packRef);
|
|
911
|
+
}
|
|
912
|
+
if (!existsSync8(resolved))
|
|
913
|
+
return null;
|
|
914
|
+
return resolved;
|
|
915
|
+
}
|
|
916
|
+
function isLocalPackRef(packRef) {
|
|
917
|
+
return packRef.startsWith("./") || packRef.startsWith("../") || isAbsolute2(packRef);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/sources/git-ref.ts
|
|
921
|
+
function parseGitSourceRef(source) {
|
|
922
|
+
let s = source;
|
|
923
|
+
if (s.startsWith("github:")) {
|
|
924
|
+
s = s.slice(7);
|
|
925
|
+
}
|
|
926
|
+
let path = "";
|
|
927
|
+
const atIdx = s.indexOf("@");
|
|
928
|
+
const colonIdx = s.indexOf(":", atIdx > -1 ? atIdx : 0);
|
|
929
|
+
if (colonIdx > -1) {
|
|
930
|
+
path = s.slice(colonIdx + 1);
|
|
931
|
+
s = s.slice(0, colonIdx);
|
|
932
|
+
}
|
|
933
|
+
let ref = "main";
|
|
934
|
+
if (atIdx > -1) {
|
|
935
|
+
ref = s.slice(atIdx + 1);
|
|
936
|
+
s = s.slice(0, atIdx);
|
|
937
|
+
}
|
|
938
|
+
const parts = s.split("/");
|
|
939
|
+
if (parts.length < 2) {
|
|
940
|
+
throw new Error(`Invalid git source reference: "${source}". Expected owner/repo format.`);
|
|
941
|
+
}
|
|
942
|
+
return {
|
|
943
|
+
owner: parts[0],
|
|
944
|
+
repo: parts[1],
|
|
945
|
+
ref,
|
|
946
|
+
path: path || ""
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function isGitPackRef(packRef) {
|
|
950
|
+
if (packRef.startsWith("github:"))
|
|
951
|
+
return true;
|
|
952
|
+
if (packRef.startsWith("./") || packRef.startsWith("../") || packRef.startsWith("/")) {
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
if (packRef.startsWith("@") || packRef.startsWith("npm:"))
|
|
956
|
+
return false;
|
|
957
|
+
const parts = packRef.split("/");
|
|
958
|
+
return parts.length === 2 && !packRef.includes("node_modules");
|
|
959
|
+
}
|
|
960
|
+
function gitSourceKey(parsed) {
|
|
961
|
+
return `${parsed.owner}/${parsed.repo}`;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/sources/npm-ref.ts
|
|
965
|
+
function parseNpmSourceRef(source) {
|
|
966
|
+
let s = source;
|
|
967
|
+
if (s.startsWith("npm:")) {
|
|
968
|
+
s = s.slice(4);
|
|
969
|
+
}
|
|
970
|
+
let path = "";
|
|
971
|
+
const pathColonIdx = findPathColon(s);
|
|
972
|
+
if (pathColonIdx > -1) {
|
|
973
|
+
path = s.slice(pathColonIdx + 1);
|
|
974
|
+
s = s.slice(0, pathColonIdx);
|
|
975
|
+
}
|
|
976
|
+
let version = "latest";
|
|
977
|
+
const versionAtIdx = findVersionAt(s);
|
|
978
|
+
if (versionAtIdx > -1) {
|
|
979
|
+
version = s.slice(versionAtIdx + 1);
|
|
980
|
+
s = s.slice(0, versionAtIdx);
|
|
981
|
+
}
|
|
982
|
+
if (!s) {
|
|
983
|
+
throw new Error(`Invalid npm source reference: "${source}". Expected package name.`);
|
|
984
|
+
}
|
|
985
|
+
return { packageName: s, version, path };
|
|
986
|
+
}
|
|
987
|
+
function findPathColon(s) {
|
|
988
|
+
const startAfter = s.startsWith("@") ? s.indexOf("/") + 1 : 0;
|
|
989
|
+
const vAt = findVersionAt(s);
|
|
990
|
+
const searchFrom = vAt > -1 ? vAt : startAfter;
|
|
991
|
+
return s.indexOf(":", searchFrom);
|
|
992
|
+
}
|
|
993
|
+
function findVersionAt(s) {
|
|
994
|
+
if (s.startsWith("@")) {
|
|
995
|
+
const slashIdx = s.indexOf("/");
|
|
996
|
+
if (slashIdx === -1)
|
|
997
|
+
return -1;
|
|
998
|
+
return s.indexOf("@", slashIdx + 1);
|
|
999
|
+
}
|
|
1000
|
+
return s.indexOf("@");
|
|
1001
|
+
}
|
|
1002
|
+
function isNpmPackRef(packRef) {
|
|
1003
|
+
if (packRef.startsWith("npm:"))
|
|
1004
|
+
return true;
|
|
1005
|
+
if (packRef.startsWith("@") && packRef.includes("/"))
|
|
1006
|
+
return true;
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
function npmSourceKey(parsed) {
|
|
1010
|
+
return `npm:${parsed.packageName}`;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/sources/git.ts
|
|
1014
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1015
|
+
import { resolve as resolve6, join as join7 } from "path";
|
|
1016
|
+
var GITHUB_API = "https://api.github.com";
|
|
1017
|
+
function githubHeaders(token) {
|
|
1018
|
+
const h = {
|
|
1019
|
+
Accept: "application/vnd.github.v3+json",
|
|
1020
|
+
"User-Agent": "agentpacks"
|
|
1021
|
+
};
|
|
1022
|
+
if (token)
|
|
1023
|
+
h.Authorization = `token ${token}`;
|
|
1024
|
+
return h;
|
|
1025
|
+
}
|
|
1026
|
+
async function resolveGitRef(parsed, token) {
|
|
1027
|
+
const base = `${GITHUB_API}/repos/${parsed.owner}/${parsed.repo}/git/ref`;
|
|
1028
|
+
const res = await fetch(`${base}/heads/${parsed.ref}`, {
|
|
1029
|
+
headers: githubHeaders(token)
|
|
1030
|
+
});
|
|
1031
|
+
if (!res.ok) {
|
|
1032
|
+
const tagRes = await fetch(`${base}/tags/${parsed.ref}`, {
|
|
1033
|
+
headers: githubHeaders(token)
|
|
1034
|
+
});
|
|
1035
|
+
if (!tagRes.ok) {
|
|
1036
|
+
throw new Error(`Could not resolve ref "${parsed.ref}" for ${parsed.owner}/${parsed.repo}: ${res.status}`);
|
|
1037
|
+
}
|
|
1038
|
+
const tagData = await tagRes.json();
|
|
1039
|
+
return tagData.object.sha;
|
|
1040
|
+
}
|
|
1041
|
+
const data = await res.json();
|
|
1042
|
+
return data.object.sha;
|
|
1043
|
+
}
|
|
1044
|
+
async function fetchGitDirectory(parsed, sha, subpath, token) {
|
|
1045
|
+
const path = parsed.path ? `${parsed.path}/${subpath}` : subpath;
|
|
1046
|
+
const url = `${GITHUB_API}/repos/${parsed.owner}/${parsed.repo}/contents/${path}?ref=${sha}`;
|
|
1047
|
+
const res = await fetch(url, { headers: githubHeaders(token) });
|
|
1048
|
+
if (!res.ok)
|
|
1049
|
+
return [];
|
|
1050
|
+
return await res.json();
|
|
1051
|
+
}
|
|
1052
|
+
async function fetchGitFile(downloadUrl, token) {
|
|
1053
|
+
const headers = { "User-Agent": "agentpacks" };
|
|
1054
|
+
if (token)
|
|
1055
|
+
headers.Authorization = `token ${token}`;
|
|
1056
|
+
const res = await fetch(downloadUrl, { headers });
|
|
1057
|
+
if (!res.ok) {
|
|
1058
|
+
throw new Error(`Failed to fetch ${downloadUrl}: ${res.status}`);
|
|
1059
|
+
}
|
|
1060
|
+
return res.text();
|
|
1061
|
+
}
|
|
1062
|
+
async function fetchAndWriteSubdir(parsed, sha, remotePath, localDir, installed, token) {
|
|
1063
|
+
mkdirSync2(localDir, { recursive: true });
|
|
1064
|
+
const entries = await fetchGitDirectory(parsed, sha, remotePath, token);
|
|
1065
|
+
for (const entry of entries) {
|
|
1066
|
+
if (entry.type === "file" && entry.download_url) {
|
|
1067
|
+
const content = await fetchGitFile(entry.download_url, token);
|
|
1068
|
+
const filepath = join7(localDir, entry.name);
|
|
1069
|
+
writeFileSync3(filepath, content);
|
|
1070
|
+
installed.push(filepath);
|
|
1071
|
+
} else if (entry.type === "dir") {
|
|
1072
|
+
await fetchAndWriteSubdir(parsed, sha, `${remotePath}/${entry.name}`, join7(localDir, entry.name), installed, token);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async function installGitSource(projectRoot, source, lockfile, options = {}) {
|
|
1077
|
+
const parsed = parseGitSourceRef(source);
|
|
1078
|
+
const sourceKey = gitSourceKey(parsed);
|
|
1079
|
+
const installed = [];
|
|
1080
|
+
const warnings = [];
|
|
1081
|
+
let resolvedSha;
|
|
1082
|
+
const locked = getLockedSource(lockfile, sourceKey);
|
|
1083
|
+
if (options.frozen && !locked) {
|
|
1084
|
+
throw new Error(`Frozen mode: no lockfile entry for source "${sourceKey}".`);
|
|
1085
|
+
}
|
|
1086
|
+
if (locked && !options.update) {
|
|
1087
|
+
resolvedSha = locked.resolvedRef;
|
|
1088
|
+
} else {
|
|
1089
|
+
resolvedSha = await resolveGitRef(parsed, options.token);
|
|
1090
|
+
}
|
|
1091
|
+
const basePath = parsed.path || "packs";
|
|
1092
|
+
const entries = await fetchGitDirectory(parsed, resolvedSha, basePath, options.token);
|
|
1093
|
+
const curatedDir = resolve6(projectRoot, ".agentpacks", ".curated");
|
|
1094
|
+
mkdirSync2(curatedDir, { recursive: true });
|
|
1095
|
+
const newLockEntry = {
|
|
1096
|
+
requestedRef: parsed.ref,
|
|
1097
|
+
resolvedRef: resolvedSha,
|
|
1098
|
+
resolvedAt: new Date().toISOString(),
|
|
1099
|
+
skills: {},
|
|
1100
|
+
packs: {}
|
|
1101
|
+
};
|
|
1102
|
+
const packDirs = entries.filter((e) => e.type === "dir");
|
|
1103
|
+
for (const packEntry of packDirs) {
|
|
1104
|
+
const packName = packEntry.name;
|
|
1105
|
+
const packOutDir = resolve6(curatedDir, packName);
|
|
1106
|
+
const packFiles = await fetchGitDirectory(parsed, resolvedSha, `${basePath}/${packName}`, options.token);
|
|
1107
|
+
mkdirSync2(packOutDir, { recursive: true });
|
|
1108
|
+
for (const file of packFiles) {
|
|
1109
|
+
if (file.type === "file" && file.download_url) {
|
|
1110
|
+
const content = await fetchGitFile(file.download_url, options.token);
|
|
1111
|
+
writeFileSync3(join7(packOutDir, file.name), content);
|
|
1112
|
+
installed.push(join7(packOutDir, file.name));
|
|
1113
|
+
if (newLockEntry.packs) {
|
|
1114
|
+
newLockEntry.packs[packName] = {
|
|
1115
|
+
integrity: computeIntegrity(content)
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
} else if (file.type === "dir") {
|
|
1119
|
+
await fetchAndWriteSubdir(parsed, resolvedSha, `${basePath}/${packName}/${file.name}`, join7(packOutDir, file.name), installed, options.token);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (packDirs.length === 0) {
|
|
1124
|
+
const fileEntries = entries.filter((e) => e.type === "file");
|
|
1125
|
+
if (fileEntries.length > 0) {
|
|
1126
|
+
const packName = parsed.path.split("/").pop() ?? parsed.repo;
|
|
1127
|
+
const packOutDir = resolve6(curatedDir, packName);
|
|
1128
|
+
mkdirSync2(packOutDir, { recursive: true });
|
|
1129
|
+
for (const file of entries) {
|
|
1130
|
+
if (file.type === "file" && file.download_url) {
|
|
1131
|
+
const content = await fetchGitFile(file.download_url, options.token);
|
|
1132
|
+
writeFileSync3(join7(packOutDir, file.name), content);
|
|
1133
|
+
installed.push(join7(packOutDir, file.name));
|
|
1134
|
+
} else if (file.type === "dir") {
|
|
1135
|
+
await fetchAndWriteSubdir(parsed, resolvedSha, `${basePath}/${file.name}`, join7(packOutDir, file.name), installed, options.token);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
setLockedSource(lockfile, sourceKey, newLockEntry);
|
|
1141
|
+
return { installed, warnings };
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// src/sources/npm.ts
|
|
1145
|
+
import { mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
|
|
1146
|
+
import { resolve as resolve7, join as join8 } from "path";
|
|
1147
|
+
import { execSync } from "child_process";
|
|
1148
|
+
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
1149
|
+
async function resolveNpmVersion(parsed) {
|
|
1150
|
+
const url = `${NPM_REGISTRY}/${encodeURIComponent(parsed.packageName)}/${parsed.version}`;
|
|
1151
|
+
const res = await fetch(url, {
|
|
1152
|
+
headers: { Accept: "application/json" }
|
|
1153
|
+
});
|
|
1154
|
+
if (!res.ok) {
|
|
1155
|
+
throw new Error(`Could not resolve npm package "${parsed.packageName}@${parsed.version}": ${res.status}`);
|
|
1156
|
+
}
|
|
1157
|
+
const data = await res.json();
|
|
1158
|
+
return { version: data.version, tarball: data.dist.tarball };
|
|
1159
|
+
}
|
|
1160
|
+
async function installNpmSource(projectRoot, source, lockfile, options = {}) {
|
|
1161
|
+
const parsed = parseNpmSourceRef(source);
|
|
1162
|
+
const sourceKey = npmSourceKey(parsed);
|
|
1163
|
+
const installed = [];
|
|
1164
|
+
const warnings = [];
|
|
1165
|
+
const locked = getLockedSource(lockfile, sourceKey);
|
|
1166
|
+
if (options.frozen && !locked) {
|
|
1167
|
+
throw new Error(`Frozen mode: no lockfile entry for source "${sourceKey}".`);
|
|
1168
|
+
}
|
|
1169
|
+
let resolvedVersion;
|
|
1170
|
+
let tarballUrl;
|
|
1171
|
+
if (locked && !options.update) {
|
|
1172
|
+
resolvedVersion = locked.resolvedRef;
|
|
1173
|
+
tarballUrl = "";
|
|
1174
|
+
} else {
|
|
1175
|
+
const resolved = await resolveNpmVersion(parsed);
|
|
1176
|
+
resolvedVersion = resolved.version;
|
|
1177
|
+
tarballUrl = resolved.tarball;
|
|
1178
|
+
}
|
|
1179
|
+
const curatedDir = resolve7(projectRoot, ".agentpacks", ".curated");
|
|
1180
|
+
mkdirSync3(curatedDir, { recursive: true });
|
|
1181
|
+
const packDir = extractNpmPack(parsed, resolvedVersion, curatedDir, installed, warnings);
|
|
1182
|
+
const newEntry = {
|
|
1183
|
+
requestedRef: parsed.version,
|
|
1184
|
+
resolvedRef: resolvedVersion,
|
|
1185
|
+
resolvedAt: new Date().toISOString(),
|
|
1186
|
+
skills: {},
|
|
1187
|
+
packs: {}
|
|
1188
|
+
};
|
|
1189
|
+
setLockedSource(lockfile, sourceKey, newEntry);
|
|
1190
|
+
return { installed, warnings };
|
|
1191
|
+
}
|
|
1192
|
+
function extractNpmPack(parsed, version, curatedDir, installed, warnings) {
|
|
1193
|
+
const pkgSpec = `${parsed.packageName}@${version}`;
|
|
1194
|
+
const packName = parsed.packageName.replace(/^@/, "").replace(/\//g, "-");
|
|
1195
|
+
const packOutDir = resolve7(curatedDir, packName);
|
|
1196
|
+
try {
|
|
1197
|
+
const tmpDir = resolve7(curatedDir, ".tmp-npm");
|
|
1198
|
+
mkdirSync3(tmpDir, { recursive: true });
|
|
1199
|
+
execSync(`npm pack ${pkgSpec} --pack-destination "${tmpDir}"`, {
|
|
1200
|
+
stdio: "pipe",
|
|
1201
|
+
timeout: 30000
|
|
1202
|
+
});
|
|
1203
|
+
const tgzFiles = __require("fs").readdirSync(tmpDir).filter((f) => f.endsWith(".tgz"));
|
|
1204
|
+
if (tgzFiles.length === 0) {
|
|
1205
|
+
warnings.push(`No tarball found for ${pkgSpec}`);
|
|
1206
|
+
return packOutDir;
|
|
1207
|
+
}
|
|
1208
|
+
const tgzPath = join8(tmpDir, tgzFiles[0]);
|
|
1209
|
+
mkdirSync3(packOutDir, { recursive: true });
|
|
1210
|
+
execSync(`tar xzf "${tgzPath}" -C "${packOutDir}" --strip-components=1`, {
|
|
1211
|
+
stdio: "pipe",
|
|
1212
|
+
timeout: 15000
|
|
1213
|
+
});
|
|
1214
|
+
execSync(`rm -rf "${tmpDir}"`, { stdio: "pipe" });
|
|
1215
|
+
const subpath = parsed.path || "";
|
|
1216
|
+
const targetDir = subpath ? join8(packOutDir, subpath) : packOutDir;
|
|
1217
|
+
if (existsSync9(targetDir)) {
|
|
1218
|
+
collectFiles(targetDir, installed);
|
|
1219
|
+
}
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
warnings.push(`Failed to extract npm pack ${pkgSpec}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1222
|
+
}
|
|
1223
|
+
return packOutDir;
|
|
1224
|
+
}
|
|
1225
|
+
function collectFiles(dir, out) {
|
|
1226
|
+
const fs = __require("fs");
|
|
1227
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1228
|
+
for (const entry of entries) {
|
|
1229
|
+
const full = join8(dir, entry.name);
|
|
1230
|
+
if (entry.isDirectory()) {
|
|
1231
|
+
collectFiles(full, out);
|
|
1232
|
+
} else {
|
|
1233
|
+
out.push(full);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// src/targets/base-target.ts
|
|
1239
|
+
class BaseTarget {
|
|
1240
|
+
supportsFeature(feature) {
|
|
1241
|
+
return this.supportedFeatures.includes(feature);
|
|
1242
|
+
}
|
|
1243
|
+
getEffectiveFeatures(enabledFeatures) {
|
|
1244
|
+
return enabledFeatures.filter((f) => this.supportsFeature(f));
|
|
1245
|
+
}
|
|
1246
|
+
createResult(filesWritten = [], filesDeleted = [], warnings = []) {
|
|
1247
|
+
return {
|
|
1248
|
+
targetId: this.id,
|
|
1249
|
+
filesWritten,
|
|
1250
|
+
filesDeleted,
|
|
1251
|
+
warnings
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/utils/markdown.ts
|
|
1257
|
+
function stripGeneratedHeader(content) {
|
|
1258
|
+
return content.replace(/^<!-- Generated by agentpacks.*-->\n/, "").replace(/^\/\/ Generated by agentpacks.*\n/, "");
|
|
1259
|
+
}
|
|
1260
|
+
function packNameToIdentifier(packName) {
|
|
1261
|
+
return packName.split(/[-_.]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1262
|
+
}
|
|
1263
|
+
function extractFirstHeading(content) {
|
|
1264
|
+
const match = content.match(/^#{1,6}\s+(.+)$/m);
|
|
1265
|
+
return match?.[1]?.trim() ?? null;
|
|
1266
|
+
}
|
|
1267
|
+
function combineMarkdown(sections, separator = `
|
|
1268
|
+
|
|
1269
|
+
---
|
|
1270
|
+
|
|
1271
|
+
`) {
|
|
1272
|
+
return sections.filter(Boolean).join(separator);
|
|
1273
|
+
}
|
|
1274
|
+
function wrapSection(heading, content, level = 2) {
|
|
1275
|
+
const prefix = "#".repeat(level);
|
|
1276
|
+
return `${prefix} ${heading}
|
|
1277
|
+
|
|
1278
|
+
${content}`;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// src/targets/opencode.ts
|
|
1282
|
+
import { resolve as resolve8, join as join9 } from "path";
|
|
1283
|
+
var TARGET_ID = "opencode";
|
|
1284
|
+
|
|
1285
|
+
class OpenCodeTarget extends BaseTarget {
|
|
1286
|
+
id = TARGET_ID;
|
|
1287
|
+
name = "OpenCode";
|
|
1288
|
+
supportedFeatures = [
|
|
1289
|
+
"rules",
|
|
1290
|
+
"commands",
|
|
1291
|
+
"agents",
|
|
1292
|
+
"skills",
|
|
1293
|
+
"hooks",
|
|
1294
|
+
"plugins",
|
|
1295
|
+
"mcp",
|
|
1296
|
+
"ignore"
|
|
1297
|
+
];
|
|
1298
|
+
generate(options) {
|
|
1299
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1300
|
+
const root = resolve8(projectRoot, baseDir);
|
|
1301
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1302
|
+
const filesWritten = [];
|
|
1303
|
+
const filesDeleted = [];
|
|
1304
|
+
const warnings = [];
|
|
1305
|
+
const opencodeDir = resolve8(root, ".opencode");
|
|
1306
|
+
if (effective.includes("agents")) {
|
|
1307
|
+
const agentDir = resolve8(opencodeDir, "agent");
|
|
1308
|
+
if (deleteExisting) {
|
|
1309
|
+
removeIfExists(agentDir);
|
|
1310
|
+
filesDeleted.push(agentDir);
|
|
1311
|
+
}
|
|
1312
|
+
ensureDir(agentDir);
|
|
1313
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID));
|
|
1314
|
+
for (const agent of agents) {
|
|
1315
|
+
const filepath = join9(agentDir, `${agent.name}.md`);
|
|
1316
|
+
writeGeneratedFile(filepath, agent.content);
|
|
1317
|
+
filesWritten.push(filepath);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (effective.includes("skills")) {
|
|
1321
|
+
const skillDir = resolve8(opencodeDir, "skill");
|
|
1322
|
+
if (deleteExisting) {
|
|
1323
|
+
removeIfExists(skillDir);
|
|
1324
|
+
filesDeleted.push(skillDir);
|
|
1325
|
+
}
|
|
1326
|
+
ensureDir(skillDir);
|
|
1327
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
|
|
1328
|
+
for (const skill of skills) {
|
|
1329
|
+
const skillSubDir = join9(skillDir, skill.name);
|
|
1330
|
+
ensureDir(skillSubDir);
|
|
1331
|
+
const filepath = join9(skillSubDir, "SKILL.md");
|
|
1332
|
+
writeGeneratedFile(filepath, skill.content);
|
|
1333
|
+
filesWritten.push(filepath);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
if (effective.includes("commands")) {
|
|
1337
|
+
const cmdDir = resolve8(opencodeDir, "command");
|
|
1338
|
+
if (deleteExisting) {
|
|
1339
|
+
removeIfExists(cmdDir);
|
|
1340
|
+
filesDeleted.push(cmdDir);
|
|
1341
|
+
}
|
|
1342
|
+
ensureDir(cmdDir);
|
|
1343
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
|
|
1344
|
+
for (const cmd of commands) {
|
|
1345
|
+
const filepath = join9(cmdDir, `${cmd.name}.md`);
|
|
1346
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1347
|
+
filesWritten.push(filepath);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
if (effective.includes("hooks") || effective.includes("plugins")) {
|
|
1351
|
+
const pluginsDir = resolve8(opencodeDir, "plugins");
|
|
1352
|
+
ensureDir(pluginsDir);
|
|
1353
|
+
if (effective.includes("hooks")) {
|
|
1354
|
+
for (const hookSet of features.hooks) {
|
|
1355
|
+
const events = resolveHooksForTarget(hookSet, TARGET_ID);
|
|
1356
|
+
if (Object.keys(events).length > 0) {
|
|
1357
|
+
const filepath = join9(pluginsDir, `agentpacks-${hookSet.packName}.ts`);
|
|
1358
|
+
const content = generateOpenCodeHookPlugin(hookSet.packName, events);
|
|
1359
|
+
writeGeneratedFile(filepath, content, { type: "ts" });
|
|
1360
|
+
filesWritten.push(filepath);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
if (effective.includes("plugins")) {
|
|
1365
|
+
for (const plugin of features.plugins) {
|
|
1366
|
+
const filepath = join9(pluginsDir, `agentpacks-${plugin.packName}-${plugin.name}.${plugin.extension}`);
|
|
1367
|
+
writeGeneratedFile(filepath, plugin.content, {
|
|
1368
|
+
type: plugin.extension
|
|
1369
|
+
});
|
|
1370
|
+
filesWritten.push(filepath);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (effective.includes("mcp")) {
|
|
1375
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
1376
|
+
if (mcpEntries.length > 0) {
|
|
1377
|
+
const opencodeConfig = buildOpenCodeMcp(features.mcpServers);
|
|
1378
|
+
const filepath = resolve8(root, "opencode.json");
|
|
1379
|
+
writeGeneratedJson(filepath, opencodeConfig, { header: false });
|
|
1380
|
+
filesWritten.push(filepath);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
if (effective.includes("rules")) {
|
|
1384
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID));
|
|
1385
|
+
const rootRules = getRootRules(rules);
|
|
1386
|
+
const detailRules = getDetailRules(rules);
|
|
1387
|
+
if (detailRules.length > 0) {
|
|
1388
|
+
const memoriesDir = resolve8(opencodeDir, "memories");
|
|
1389
|
+
if (deleteExisting) {
|
|
1390
|
+
removeIfExists(memoriesDir);
|
|
1391
|
+
filesDeleted.push(memoriesDir);
|
|
1392
|
+
}
|
|
1393
|
+
ensureDir(memoriesDir);
|
|
1394
|
+
for (const rule of detailRules) {
|
|
1395
|
+
const filepath = join9(memoriesDir, `${rule.name}.md`);
|
|
1396
|
+
writeGeneratedFile(filepath, rule.content);
|
|
1397
|
+
filesWritten.push(filepath);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
function buildOpenCodeMcp(servers) {
|
|
1405
|
+
const mcp = {};
|
|
1406
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
1407
|
+
if (entry.url) {
|
|
1408
|
+
mcp[name] = {
|
|
1409
|
+
type: "remote",
|
|
1410
|
+
url: entry.url,
|
|
1411
|
+
enabled: true,
|
|
1412
|
+
...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
|
|
1413
|
+
};
|
|
1414
|
+
} else if (entry.command) {
|
|
1415
|
+
const cmd = entry.args ? [entry.command, ...entry.args] : [entry.command];
|
|
1416
|
+
mcp[name] = {
|
|
1417
|
+
type: "local",
|
|
1418
|
+
command: cmd,
|
|
1419
|
+
enabled: true,
|
|
1420
|
+
...entry.env && Object.keys(entry.env).length > 0 ? { environment: entry.env } : {}
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return {
|
|
1425
|
+
$schema: "https://opencode.ai/config.json",
|
|
1426
|
+
mcp
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function generateOpenCodeHookPlugin(packName, events) {
|
|
1430
|
+
const identifier = packNameToIdentifier(packName);
|
|
1431
|
+
const hookMap = mapHookEvents(events);
|
|
1432
|
+
const hookEntries = Object.entries(hookMap).map(([event, handlers]) => {
|
|
1433
|
+
const body = handlers.map((h) => {
|
|
1434
|
+
const matcherGuard = h.matcher ? `if (!/(?:${h.matcher})/.test(String(input?.tool ?? ""))) return;
|
|
1435
|
+
` : "";
|
|
1436
|
+
return ` ${matcherGuard}await $\`${h.command}\`;`;
|
|
1437
|
+
}).join(`
|
|
1438
|
+
`);
|
|
1439
|
+
return ` "${event}": async (input, output) => {
|
|
1440
|
+
${body}
|
|
1441
|
+
},`;
|
|
1442
|
+
}).join(`
|
|
1443
|
+
`);
|
|
1444
|
+
return `import type { Plugin } from "@opencode-ai/plugin";
|
|
1445
|
+
|
|
1446
|
+
export const ${identifier}Plugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|
1447
|
+
return {
|
|
1448
|
+
${hookEntries}
|
|
1449
|
+
};
|
|
1450
|
+
};
|
|
1451
|
+
`;
|
|
1452
|
+
}
|
|
1453
|
+
function mapHookEvents(events) {
|
|
1454
|
+
const mapped = {};
|
|
1455
|
+
const eventMapping = {
|
|
1456
|
+
sessionStart: "session.created",
|
|
1457
|
+
postToolUse: "tool.execute.after",
|
|
1458
|
+
preToolUse: "tool.execute.before",
|
|
1459
|
+
stop: "session.idle",
|
|
1460
|
+
afterFileEdit: "file.edited",
|
|
1461
|
+
afterShellExecution: "command.executed"
|
|
1462
|
+
};
|
|
1463
|
+
for (const [event, handlers] of Object.entries(events)) {
|
|
1464
|
+
const opencodeEvent = eventMapping[event] ?? event;
|
|
1465
|
+
if (!mapped[opencodeEvent]) {
|
|
1466
|
+
mapped[opencodeEvent] = [];
|
|
1467
|
+
}
|
|
1468
|
+
mapped[opencodeEvent].push(...handlers.filter((h) => h.command));
|
|
1469
|
+
}
|
|
1470
|
+
return mapped;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// src/targets/cursor.ts
|
|
1474
|
+
import { resolve as resolve9, join as join10 } from "path";
|
|
1475
|
+
var TARGET_ID2 = "cursor";
|
|
1476
|
+
|
|
1477
|
+
class CursorTarget extends BaseTarget {
|
|
1478
|
+
id = TARGET_ID2;
|
|
1479
|
+
name = "Cursor";
|
|
1480
|
+
supportedFeatures = [
|
|
1481
|
+
"rules",
|
|
1482
|
+
"commands",
|
|
1483
|
+
"agents",
|
|
1484
|
+
"skills",
|
|
1485
|
+
"hooks",
|
|
1486
|
+
"mcp",
|
|
1487
|
+
"ignore"
|
|
1488
|
+
];
|
|
1489
|
+
generate(options) {
|
|
1490
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1491
|
+
const root = resolve9(projectRoot, baseDir);
|
|
1492
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1493
|
+
const filesWritten = [];
|
|
1494
|
+
const filesDeleted = [];
|
|
1495
|
+
const warnings = [];
|
|
1496
|
+
const cursorDir = resolve9(root, ".cursor");
|
|
1497
|
+
if (effective.includes("rules")) {
|
|
1498
|
+
const rulesDir = resolve9(cursorDir, "rules");
|
|
1499
|
+
if (deleteExisting) {
|
|
1500
|
+
removeIfExists(rulesDir);
|
|
1501
|
+
filesDeleted.push(rulesDir);
|
|
1502
|
+
}
|
|
1503
|
+
ensureDir(rulesDir);
|
|
1504
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
|
|
1505
|
+
for (const rule of rules) {
|
|
1506
|
+
const cursorMeta = rule.meta.cursor ?? {};
|
|
1507
|
+
const frontmatter = {
|
|
1508
|
+
description: cursorMeta.description ?? rule.meta.description ?? "",
|
|
1509
|
+
alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
|
|
1510
|
+
};
|
|
1511
|
+
const globs = cursorMeta.globs ?? rule.meta.globs;
|
|
1512
|
+
if (globs) {
|
|
1513
|
+
frontmatter.globs = globs;
|
|
1514
|
+
}
|
|
1515
|
+
const filepath = join10(rulesDir, `${rule.name}.mdc`);
|
|
1516
|
+
const content = serializeFrontmatter(frontmatter, rule.content);
|
|
1517
|
+
writeGeneratedFile(filepath, content);
|
|
1518
|
+
filesWritten.push(filepath);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
if (effective.includes("agents")) {
|
|
1522
|
+
const agentsDir = resolve9(cursorDir, "agents");
|
|
1523
|
+
if (deleteExisting) {
|
|
1524
|
+
removeIfExists(agentsDir);
|
|
1525
|
+
filesDeleted.push(agentsDir);
|
|
1526
|
+
}
|
|
1527
|
+
ensureDir(agentsDir);
|
|
1528
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID2));
|
|
1529
|
+
for (const agent of agents) {
|
|
1530
|
+
const frontmatter = {
|
|
1531
|
+
name: agent.name,
|
|
1532
|
+
description: agent.meta.description ?? ""
|
|
1533
|
+
};
|
|
1534
|
+
const filepath = join10(agentsDir, `${agent.name}.md`);
|
|
1535
|
+
const content = serializeFrontmatter(frontmatter, agent.content);
|
|
1536
|
+
writeGeneratedFile(filepath, content);
|
|
1537
|
+
filesWritten.push(filepath);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (effective.includes("skills")) {
|
|
1541
|
+
const skillsDir = resolve9(cursorDir, "skills");
|
|
1542
|
+
if (deleteExisting) {
|
|
1543
|
+
removeIfExists(skillsDir);
|
|
1544
|
+
filesDeleted.push(skillsDir);
|
|
1545
|
+
}
|
|
1546
|
+
ensureDir(skillsDir);
|
|
1547
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID2));
|
|
1548
|
+
for (const skill of skills) {
|
|
1549
|
+
const skillSubDir = join10(skillsDir, skill.name);
|
|
1550
|
+
ensureDir(skillSubDir);
|
|
1551
|
+
const frontmatter = {
|
|
1552
|
+
name: skill.name,
|
|
1553
|
+
description: skill.meta.description ?? ""
|
|
1554
|
+
};
|
|
1555
|
+
const filepath = join10(skillSubDir, "SKILL.md");
|
|
1556
|
+
const content = serializeFrontmatter(frontmatter, skill.content);
|
|
1557
|
+
writeGeneratedFile(filepath, content);
|
|
1558
|
+
filesWritten.push(filepath);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (effective.includes("commands")) {
|
|
1562
|
+
const commandsDir = resolve9(cursorDir, "commands");
|
|
1563
|
+
if (deleteExisting) {
|
|
1564
|
+
removeIfExists(commandsDir);
|
|
1565
|
+
filesDeleted.push(commandsDir);
|
|
1566
|
+
}
|
|
1567
|
+
ensureDir(commandsDir);
|
|
1568
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID2));
|
|
1569
|
+
for (const cmd of commands) {
|
|
1570
|
+
const filepath = join10(commandsDir, `${cmd.name}.md`);
|
|
1571
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1572
|
+
filesWritten.push(filepath);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
if (effective.includes("mcp")) {
|
|
1576
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
1577
|
+
if (mcpEntries.length > 0) {
|
|
1578
|
+
const mcpConfig = buildCursorMcp(features.mcpServers);
|
|
1579
|
+
const filepath = resolve9(cursorDir, "mcp.json");
|
|
1580
|
+
writeGeneratedJson(filepath, mcpConfig, { header: false });
|
|
1581
|
+
filesWritten.push(filepath);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (effective.includes("ignore")) {
|
|
1585
|
+
if (features.ignorePatterns.length > 0) {
|
|
1586
|
+
const filepath = resolve9(root, ".cursorignore");
|
|
1587
|
+
const content = features.ignorePatterns.join(`
|
|
1588
|
+
`) + `
|
|
1589
|
+
`;
|
|
1590
|
+
writeGeneratedFile(filepath, content);
|
|
1591
|
+
filesWritten.push(filepath);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
function buildCursorMcp(servers) {
|
|
1598
|
+
const mcpServers = {};
|
|
1599
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
1600
|
+
if (entry.url) {
|
|
1601
|
+
mcpServers[name] = { url: entry.url };
|
|
1602
|
+
} else if (entry.command) {
|
|
1603
|
+
mcpServers[name] = {
|
|
1604
|
+
command: entry.command,
|
|
1605
|
+
...entry.args ? { args: entry.args } : {},
|
|
1606
|
+
...entry.env ? { env: entry.env } : {}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return { mcpServers };
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/targets/claude-code.ts
|
|
1614
|
+
import { resolve as resolve10, join as join11 } from "path";
|
|
1615
|
+
var TARGET_ID3 = "claudecode";
|
|
1616
|
+
|
|
1617
|
+
class ClaudeCodeTarget extends BaseTarget {
|
|
1618
|
+
id = TARGET_ID3;
|
|
1619
|
+
name = "Claude Code";
|
|
1620
|
+
supportedFeatures = [
|
|
1621
|
+
"rules",
|
|
1622
|
+
"commands",
|
|
1623
|
+
"agents",
|
|
1624
|
+
"skills",
|
|
1625
|
+
"hooks",
|
|
1626
|
+
"mcp",
|
|
1627
|
+
"ignore"
|
|
1628
|
+
];
|
|
1629
|
+
generate(options) {
|
|
1630
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1631
|
+
const root = resolve10(projectRoot, baseDir);
|
|
1632
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1633
|
+
const filesWritten = [];
|
|
1634
|
+
const filesDeleted = [];
|
|
1635
|
+
const warnings = [];
|
|
1636
|
+
const claudeDir = resolve10(root, ".claude");
|
|
1637
|
+
if (effective.includes("rules")) {
|
|
1638
|
+
const rulesDir = resolve10(claudeDir, "rules");
|
|
1639
|
+
if (deleteExisting) {
|
|
1640
|
+
removeIfExists(rulesDir);
|
|
1641
|
+
filesDeleted.push(rulesDir);
|
|
1642
|
+
}
|
|
1643
|
+
ensureDir(rulesDir);
|
|
1644
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID3));
|
|
1645
|
+
const rootRules = getRootRules(rules);
|
|
1646
|
+
const detailRules = getDetailRules(rules);
|
|
1647
|
+
if (rootRules.length > 0) {
|
|
1648
|
+
const claudeMd = rootRules.map((r) => r.content).join(`
|
|
1649
|
+
|
|
1650
|
+
`);
|
|
1651
|
+
const filepath = resolve10(claudeDir, "CLAUDE.md");
|
|
1652
|
+
writeGeneratedFile(filepath, claudeMd);
|
|
1653
|
+
filesWritten.push(filepath);
|
|
1654
|
+
}
|
|
1655
|
+
for (const rule of detailRules) {
|
|
1656
|
+
const filepath = join11(rulesDir, `${rule.name}.md`);
|
|
1657
|
+
writeGeneratedFile(filepath, rule.content);
|
|
1658
|
+
filesWritten.push(filepath);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (effective.includes("agents")) {
|
|
1662
|
+
const agentsDir = resolve10(claudeDir, "agents");
|
|
1663
|
+
if (deleteExisting) {
|
|
1664
|
+
removeIfExists(agentsDir);
|
|
1665
|
+
filesDeleted.push(agentsDir);
|
|
1666
|
+
}
|
|
1667
|
+
ensureDir(agentsDir);
|
|
1668
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID3));
|
|
1669
|
+
for (const agent of agents) {
|
|
1670
|
+
const filepath = join11(agentsDir, `${agent.name}.md`);
|
|
1671
|
+
writeGeneratedFile(filepath, agent.content);
|
|
1672
|
+
filesWritten.push(filepath);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
if (effective.includes("skills")) {
|
|
1676
|
+
const skillsDir = resolve10(claudeDir, "skills");
|
|
1677
|
+
if (deleteExisting) {
|
|
1678
|
+
removeIfExists(skillsDir);
|
|
1679
|
+
filesDeleted.push(skillsDir);
|
|
1680
|
+
}
|
|
1681
|
+
ensureDir(skillsDir);
|
|
1682
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID3));
|
|
1683
|
+
for (const skill of skills) {
|
|
1684
|
+
const skillSubDir = join11(skillsDir, skill.name);
|
|
1685
|
+
ensureDir(skillSubDir);
|
|
1686
|
+
const filepath = join11(skillSubDir, "SKILL.md");
|
|
1687
|
+
writeGeneratedFile(filepath, skill.content);
|
|
1688
|
+
filesWritten.push(filepath);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
if (effective.includes("commands")) {
|
|
1692
|
+
const commandsDir = resolve10(claudeDir, "commands");
|
|
1693
|
+
if (deleteExisting) {
|
|
1694
|
+
removeIfExists(commandsDir);
|
|
1695
|
+
filesDeleted.push(commandsDir);
|
|
1696
|
+
}
|
|
1697
|
+
ensureDir(commandsDir);
|
|
1698
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID3));
|
|
1699
|
+
for (const cmd of commands) {
|
|
1700
|
+
const filepath = join11(commandsDir, `${cmd.name}.md`);
|
|
1701
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1702
|
+
filesWritten.push(filepath);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
if (effective.includes("hooks") || effective.includes("mcp") || effective.includes("ignore")) {
|
|
1706
|
+
const settings = buildClaudeSettings(options, effective);
|
|
1707
|
+
if (Object.keys(settings).length > 0) {
|
|
1708
|
+
const filepath = resolve10(claudeDir, "settings.json");
|
|
1709
|
+
const existing = readJsonOrNull(filepath) ?? {};
|
|
1710
|
+
const merged = { ...existing, ...settings };
|
|
1711
|
+
writeGeneratedJson(filepath, merged, { header: false });
|
|
1712
|
+
filesWritten.push(filepath);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
function buildClaudeSettings(options, effective) {
|
|
1719
|
+
const settings = {};
|
|
1720
|
+
if (effective.includes("hooks")) {
|
|
1721
|
+
const allHookEntries = {};
|
|
1722
|
+
for (const hookSet of options.features.hooks) {
|
|
1723
|
+
const events = resolveHooksForTarget(hookSet, TARGET_ID3);
|
|
1724
|
+
for (const [event, entries] of Object.entries(events)) {
|
|
1725
|
+
const pascalEvent = toPascalCase(event);
|
|
1726
|
+
if (!allHookEntries[pascalEvent]) {
|
|
1727
|
+
allHookEntries[pascalEvent] = [];
|
|
1728
|
+
}
|
|
1729
|
+
allHookEntries[pascalEvent].push(...entries.map((e) => ({
|
|
1730
|
+
type: e.type ?? "command",
|
|
1731
|
+
...e.matcher ? { matcher: e.matcher } : {},
|
|
1732
|
+
command: e.command
|
|
1733
|
+
})));
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
if (Object.keys(allHookEntries).length > 0) {
|
|
1737
|
+
settings.hooks = allHookEntries;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
if (effective.includes("mcp")) {
|
|
1741
|
+
const mcpEntries = Object.entries(options.features.mcpServers);
|
|
1742
|
+
if (mcpEntries.length > 0) {
|
|
1743
|
+
const mcpServers = {};
|
|
1744
|
+
for (const [name, entry] of mcpEntries) {
|
|
1745
|
+
if (entry.url) {
|
|
1746
|
+
mcpServers[name] = { url: entry.url };
|
|
1747
|
+
} else if (entry.command) {
|
|
1748
|
+
mcpServers[name] = {
|
|
1749
|
+
command: entry.command,
|
|
1750
|
+
...entry.args ? { args: entry.args } : {},
|
|
1751
|
+
...entry.env ? { env: entry.env } : {}
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
settings.mcpServers = mcpServers;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return settings;
|
|
1759
|
+
}
|
|
1760
|
+
function toPascalCase(str) {
|
|
1761
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
// src/targets/codex-cli.ts
|
|
1765
|
+
import { resolve as resolve11, join as join12 } from "path";
|
|
1766
|
+
var TARGET_ID4 = "codexcli";
|
|
1767
|
+
|
|
1768
|
+
class CodexCliTarget extends BaseTarget {
|
|
1769
|
+
id = TARGET_ID4;
|
|
1770
|
+
name = "Codex CLI";
|
|
1771
|
+
supportedFeatures = ["rules", "skills", "mcp", "hooks"];
|
|
1772
|
+
generate(options) {
|
|
1773
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1774
|
+
const root = resolve11(projectRoot, baseDir);
|
|
1775
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1776
|
+
const filesWritten = [];
|
|
1777
|
+
const filesDeleted = [];
|
|
1778
|
+
const warnings = [];
|
|
1779
|
+
const codexDir = resolve11(root, ".codex");
|
|
1780
|
+
if (effective.includes("rules")) {
|
|
1781
|
+
const memoriesDir = resolve11(codexDir, "memories");
|
|
1782
|
+
if (deleteExisting) {
|
|
1783
|
+
removeIfExists(memoriesDir);
|
|
1784
|
+
filesDeleted.push(memoriesDir);
|
|
1785
|
+
}
|
|
1786
|
+
ensureDir(memoriesDir);
|
|
1787
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
|
|
1788
|
+
for (const rule of rules) {
|
|
1789
|
+
const filepath = join12(memoriesDir, `${rule.name}.md`);
|
|
1790
|
+
writeGeneratedFile(filepath, rule.content);
|
|
1791
|
+
filesWritten.push(filepath);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (effective.includes("skills")) {
|
|
1795
|
+
const skillsDir = resolve11(codexDir, "skills");
|
|
1796
|
+
if (deleteExisting) {
|
|
1797
|
+
removeIfExists(skillsDir);
|
|
1798
|
+
filesDeleted.push(skillsDir);
|
|
1799
|
+
}
|
|
1800
|
+
ensureDir(skillsDir);
|
|
1801
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID4));
|
|
1802
|
+
for (const skill of skills) {
|
|
1803
|
+
const skillSubDir = join12(skillsDir, skill.name);
|
|
1804
|
+
ensureDir(skillSubDir);
|
|
1805
|
+
const filepath = join12(skillSubDir, "SKILL.md");
|
|
1806
|
+
writeGeneratedFile(filepath, skill.content);
|
|
1807
|
+
filesWritten.push(filepath);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// src/targets/gemini-cli.ts
|
|
1815
|
+
import { resolve as resolve12, join as join13 } from "path";
|
|
1816
|
+
var TARGET_ID5 = "geminicli";
|
|
1817
|
+
|
|
1818
|
+
class GeminiCliTarget extends BaseTarget {
|
|
1819
|
+
id = TARGET_ID5;
|
|
1820
|
+
name = "Gemini CLI";
|
|
1821
|
+
supportedFeatures = [
|
|
1822
|
+
"rules",
|
|
1823
|
+
"commands",
|
|
1824
|
+
"mcp",
|
|
1825
|
+
"ignore",
|
|
1826
|
+
"skills",
|
|
1827
|
+
"hooks"
|
|
1828
|
+
];
|
|
1829
|
+
generate(options) {
|
|
1830
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1831
|
+
const root = resolve12(projectRoot, baseDir);
|
|
1832
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1833
|
+
const filesWritten = [];
|
|
1834
|
+
const filesDeleted = [];
|
|
1835
|
+
const warnings = [];
|
|
1836
|
+
const geminiDir = resolve12(root, ".gemini");
|
|
1837
|
+
if (effective.includes("rules")) {
|
|
1838
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID5));
|
|
1839
|
+
const rootRules = getRootRules(rules);
|
|
1840
|
+
const detailRules = getDetailRules(rules);
|
|
1841
|
+
if (rootRules.length > 0) {
|
|
1842
|
+
const geminiMd = rootRules.map((r) => r.content).join(`
|
|
1843
|
+
|
|
1844
|
+
`);
|
|
1845
|
+
const filepath = resolve12(root, "GEMINI.md");
|
|
1846
|
+
writeGeneratedFile(filepath, geminiMd);
|
|
1847
|
+
filesWritten.push(filepath);
|
|
1848
|
+
}
|
|
1849
|
+
if (detailRules.length > 0) {
|
|
1850
|
+
const memoriesDir = resolve12(geminiDir, "memories");
|
|
1851
|
+
if (deleteExisting) {
|
|
1852
|
+
removeIfExists(memoriesDir);
|
|
1853
|
+
filesDeleted.push(memoriesDir);
|
|
1854
|
+
}
|
|
1855
|
+
ensureDir(memoriesDir);
|
|
1856
|
+
for (const rule of detailRules) {
|
|
1857
|
+
const filepath = join13(memoriesDir, `${rule.name}.md`);
|
|
1858
|
+
writeGeneratedFile(filepath, rule.content);
|
|
1859
|
+
filesWritten.push(filepath);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
if (effective.includes("commands")) {
|
|
1864
|
+
const commandsDir = resolve12(geminiDir, "commands");
|
|
1865
|
+
if (deleteExisting) {
|
|
1866
|
+
removeIfExists(commandsDir);
|
|
1867
|
+
filesDeleted.push(commandsDir);
|
|
1868
|
+
}
|
|
1869
|
+
ensureDir(commandsDir);
|
|
1870
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID5));
|
|
1871
|
+
for (const cmd of commands) {
|
|
1872
|
+
const toml = buildGeminiCommand(cmd.name, cmd.meta.description ?? "", cmd.content);
|
|
1873
|
+
const filepath = join13(commandsDir, `${cmd.name}.toml`);
|
|
1874
|
+
writeGeneratedFile(filepath, toml, { type: "md" });
|
|
1875
|
+
filesWritten.push(filepath);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (effective.includes("mcp")) {
|
|
1879
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
1880
|
+
if (mcpEntries.length > 0) {
|
|
1881
|
+
const settings = buildGeminiSettings(features.mcpServers);
|
|
1882
|
+
const filepath = resolve12(geminiDir, "settings.json");
|
|
1883
|
+
writeGeneratedJson(filepath, settings, { header: false });
|
|
1884
|
+
filesWritten.push(filepath);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
if (effective.includes("ignore")) {
|
|
1888
|
+
if (features.ignorePatterns.length > 0) {
|
|
1889
|
+
const filepath = resolve12(root, ".geminiignore");
|
|
1890
|
+
const content = features.ignorePatterns.join(`
|
|
1891
|
+
`) + `
|
|
1892
|
+
`;
|
|
1893
|
+
writeGeneratedFile(filepath, content);
|
|
1894
|
+
filesWritten.push(filepath);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
function buildGeminiCommand(name, description, content) {
|
|
1901
|
+
return `[command]
|
|
1902
|
+
name = "${name}"
|
|
1903
|
+
description = "${description}"
|
|
1904
|
+
|
|
1905
|
+
[prompt]
|
|
1906
|
+
content = """
|
|
1907
|
+
${content}
|
|
1908
|
+
"""
|
|
1909
|
+
`;
|
|
1910
|
+
}
|
|
1911
|
+
function buildGeminiSettings(servers) {
|
|
1912
|
+
const mcpServers = {};
|
|
1913
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
1914
|
+
if (entry.command) {
|
|
1915
|
+
mcpServers[name] = {
|
|
1916
|
+
command: entry.command,
|
|
1917
|
+
...entry.args ? { args: entry.args } : {},
|
|
1918
|
+
...entry.env ? { env: entry.env } : {}
|
|
1919
|
+
};
|
|
1920
|
+
} else if (entry.url) {
|
|
1921
|
+
mcpServers[name] = { url: entry.url };
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
return { mcpServers };
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// src/targets/copilot.ts
|
|
1928
|
+
import { resolve as resolve13, join as join14 } from "path";
|
|
1929
|
+
var TARGET_ID6 = "copilot";
|
|
1930
|
+
|
|
1931
|
+
class CopilotTarget extends BaseTarget {
|
|
1932
|
+
id = TARGET_ID6;
|
|
1933
|
+
name = "GitHub Copilot";
|
|
1934
|
+
supportedFeatures = [
|
|
1935
|
+
"rules",
|
|
1936
|
+
"commands",
|
|
1937
|
+
"agents",
|
|
1938
|
+
"skills",
|
|
1939
|
+
"mcp",
|
|
1940
|
+
"ignore"
|
|
1941
|
+
];
|
|
1942
|
+
generate(options) {
|
|
1943
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1944
|
+
const root = resolve13(projectRoot, baseDir);
|
|
1945
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1946
|
+
const filesWritten = [];
|
|
1947
|
+
const filesDeleted = [];
|
|
1948
|
+
const warnings = [];
|
|
1949
|
+
const githubDir = resolve13(root, ".github");
|
|
1950
|
+
if (effective.includes("rules")) {
|
|
1951
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID6));
|
|
1952
|
+
if (rules.length > 0) {
|
|
1953
|
+
const combinedContent = rules.map((r) => r.content).join(`
|
|
1954
|
+
|
|
1955
|
+
---
|
|
1956
|
+
|
|
1957
|
+
`);
|
|
1958
|
+
const filepath = resolve13(githubDir, "copilot-instructions.md");
|
|
1959
|
+
ensureDir(githubDir);
|
|
1960
|
+
writeGeneratedFile(filepath, combinedContent);
|
|
1961
|
+
filesWritten.push(filepath);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
if (effective.includes("agents")) {
|
|
1965
|
+
const copilotDir = resolve13(githubDir, "copilot");
|
|
1966
|
+
const agentsDir = resolve13(copilotDir, "agents");
|
|
1967
|
+
if (deleteExisting) {
|
|
1968
|
+
removeIfExists(agentsDir);
|
|
1969
|
+
filesDeleted.push(agentsDir);
|
|
1970
|
+
}
|
|
1971
|
+
ensureDir(agentsDir);
|
|
1972
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID6));
|
|
1973
|
+
for (const agent of agents) {
|
|
1974
|
+
const filepath = join14(agentsDir, `${agent.name}.md`);
|
|
1975
|
+
writeGeneratedFile(filepath, agent.content);
|
|
1976
|
+
filesWritten.push(filepath);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
if (effective.includes("skills")) {
|
|
1980
|
+
const copilotDir = resolve13(githubDir, "copilot");
|
|
1981
|
+
const skillsDir = resolve13(copilotDir, "skills");
|
|
1982
|
+
if (deleteExisting) {
|
|
1983
|
+
removeIfExists(skillsDir);
|
|
1984
|
+
filesDeleted.push(skillsDir);
|
|
1985
|
+
}
|
|
1986
|
+
ensureDir(skillsDir);
|
|
1987
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID6));
|
|
1988
|
+
for (const skill of skills) {
|
|
1989
|
+
const skillSubDir = join14(skillsDir, skill.name);
|
|
1990
|
+
ensureDir(skillSubDir);
|
|
1991
|
+
const filepath = join14(skillSubDir, "SKILL.md");
|
|
1992
|
+
writeGeneratedFile(filepath, skill.content);
|
|
1993
|
+
filesWritten.push(filepath);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
if (effective.includes("commands")) {
|
|
1997
|
+
const copilotDir = resolve13(githubDir, "copilot");
|
|
1998
|
+
const commandsDir = resolve13(copilotDir, "commands");
|
|
1999
|
+
if (deleteExisting) {
|
|
2000
|
+
removeIfExists(commandsDir);
|
|
2001
|
+
filesDeleted.push(commandsDir);
|
|
2002
|
+
}
|
|
2003
|
+
ensureDir(commandsDir);
|
|
2004
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID6));
|
|
2005
|
+
for (const cmd of commands) {
|
|
2006
|
+
const filepath = join14(commandsDir, `${cmd.name}.md`);
|
|
2007
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
2008
|
+
filesWritten.push(filepath);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
if (effective.includes("ignore")) {}
|
|
2012
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
// src/targets/agents-md.ts
|
|
2017
|
+
import { resolve as resolve14 } from "path";
|
|
2018
|
+
class AgentsMdTarget extends BaseTarget {
|
|
2019
|
+
id = "agentsmd";
|
|
2020
|
+
name = "AGENTS.md";
|
|
2021
|
+
supportedFeatures = ["rules"];
|
|
2022
|
+
generate(options) {
|
|
2023
|
+
const { projectRoot, baseDir, features } = options;
|
|
2024
|
+
const root = resolve14(projectRoot, baseDir);
|
|
2025
|
+
const filesWritten = [];
|
|
2026
|
+
const warnings = [];
|
|
2027
|
+
const rootRules = getRootRules(features.rules);
|
|
2028
|
+
if (rootRules.length === 0) {
|
|
2029
|
+
warnings.push("No root rules found. AGENTS.md will not be generated.");
|
|
2030
|
+
return this.createResult(filesWritten, [], warnings);
|
|
2031
|
+
}
|
|
2032
|
+
const sections = rootRules.map((r) => r.content);
|
|
2033
|
+
const content = sections.join(`
|
|
2034
|
+
|
|
2035
|
+
`);
|
|
2036
|
+
const filepath = resolve14(root, "AGENTS.md");
|
|
2037
|
+
writeGeneratedFile(filepath, content);
|
|
2038
|
+
filesWritten.push(filepath);
|
|
2039
|
+
return this.createResult(filesWritten, [], warnings);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/targets/generic-md-target.ts
|
|
2044
|
+
import { resolve as resolve15, join as join15 } from "path";
|
|
2045
|
+
function createGenericMdTarget(config) {
|
|
2046
|
+
return new GenericMdTarget(config);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
class GenericMdTarget extends BaseTarget {
|
|
2050
|
+
id;
|
|
2051
|
+
name;
|
|
2052
|
+
supportedFeatures;
|
|
2053
|
+
config;
|
|
2054
|
+
constructor(config) {
|
|
2055
|
+
super();
|
|
2056
|
+
this.id = config.id;
|
|
2057
|
+
this.name = config.name;
|
|
2058
|
+
this.supportedFeatures = config.supportedFeatures;
|
|
2059
|
+
this.config = config;
|
|
2060
|
+
}
|
|
2061
|
+
generate(options) {
|
|
2062
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
2063
|
+
const root = resolve15(projectRoot, baseDir);
|
|
2064
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
2065
|
+
const filesWritten = [];
|
|
2066
|
+
const filesDeleted = [];
|
|
2067
|
+
const warnings = [];
|
|
2068
|
+
const configDir = resolve15(root, this.config.configDir);
|
|
2069
|
+
const rulesSubDir = this.config.rulesDir ?? "rules";
|
|
2070
|
+
const ext = this.config.ruleExtension ?? ".md";
|
|
2071
|
+
if (effective.includes("rules")) {
|
|
2072
|
+
const rulesDir = resolve15(configDir, rulesSubDir);
|
|
2073
|
+
if (deleteExisting) {
|
|
2074
|
+
removeIfExists(rulesDir);
|
|
2075
|
+
filesDeleted.push(rulesDir);
|
|
2076
|
+
}
|
|
2077
|
+
ensureDir(rulesDir);
|
|
2078
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, this.id));
|
|
2079
|
+
for (const rule of rules) {
|
|
2080
|
+
const filepath = join15(rulesDir, `${rule.name}${ext}`);
|
|
2081
|
+
writeGeneratedFile(filepath, rule.content);
|
|
2082
|
+
filesWritten.push(filepath);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if (effective.includes("commands")) {
|
|
2086
|
+
const commandsDir = resolve15(configDir, "commands");
|
|
2087
|
+
if (deleteExisting) {
|
|
2088
|
+
removeIfExists(commandsDir);
|
|
2089
|
+
filesDeleted.push(commandsDir);
|
|
2090
|
+
}
|
|
2091
|
+
ensureDir(commandsDir);
|
|
2092
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, this.id));
|
|
2093
|
+
for (const cmd of commands) {
|
|
2094
|
+
const filepath = join15(commandsDir, `${cmd.name}.md`);
|
|
2095
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
2096
|
+
filesWritten.push(filepath);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
if (effective.includes("mcp")) {
|
|
2100
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
2101
|
+
if (mcpEntries.length > 0) {
|
|
2102
|
+
const mcpDir = this.config.mcpInConfigDir ? configDir : root;
|
|
2103
|
+
const filepath = resolve15(mcpDir, "mcp.json");
|
|
2104
|
+
writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
|
|
2105
|
+
header: false
|
|
2106
|
+
});
|
|
2107
|
+
filesWritten.push(filepath);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
if (effective.includes("ignore") && this.config.ignoreFile) {
|
|
2111
|
+
if (features.ignorePatterns.length > 0) {
|
|
2112
|
+
const filepath = resolve15(root, this.config.ignoreFile);
|
|
2113
|
+
writeGeneratedFile(filepath, features.ignorePatterns.join(`
|
|
2114
|
+
`) + `
|
|
2115
|
+
`);
|
|
2116
|
+
filesWritten.push(filepath);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// src/targets/additional-targets.ts
|
|
2124
|
+
var ClineTarget = createGenericMdTarget({
|
|
2125
|
+
id: "cline",
|
|
2126
|
+
name: "Cline",
|
|
2127
|
+
configDir: ".cline",
|
|
2128
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
2129
|
+
ignoreFile: ".clineignore",
|
|
2130
|
+
mcpInConfigDir: true
|
|
2131
|
+
});
|
|
2132
|
+
var KiloTarget = createGenericMdTarget({
|
|
2133
|
+
id: "kilo",
|
|
2134
|
+
name: "Kilo Code",
|
|
2135
|
+
configDir: ".kilo",
|
|
2136
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
2137
|
+
ignoreFile: ".kiloignore",
|
|
2138
|
+
mcpInConfigDir: true
|
|
2139
|
+
});
|
|
2140
|
+
var RooTarget = createGenericMdTarget({
|
|
2141
|
+
id: "roo",
|
|
2142
|
+
name: "Roo Code",
|
|
2143
|
+
configDir: ".roo",
|
|
2144
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
2145
|
+
ignoreFile: ".rooignore",
|
|
2146
|
+
mcpInConfigDir: true
|
|
2147
|
+
});
|
|
2148
|
+
var QwenCodeTarget = createGenericMdTarget({
|
|
2149
|
+
id: "qwencode",
|
|
2150
|
+
name: "Qwen Code",
|
|
2151
|
+
configDir: ".qwencode",
|
|
2152
|
+
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
2153
|
+
ignoreFile: ".qwencodeignore",
|
|
2154
|
+
mcpInConfigDir: true
|
|
2155
|
+
});
|
|
2156
|
+
var KiroTarget = createGenericMdTarget({
|
|
2157
|
+
id: "kiro",
|
|
2158
|
+
name: "Kiro",
|
|
2159
|
+
configDir: ".kiro",
|
|
2160
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2161
|
+
mcpInConfigDir: true
|
|
2162
|
+
});
|
|
2163
|
+
var FactoryDroidTarget = createGenericMdTarget({
|
|
2164
|
+
id: "factorydroid",
|
|
2165
|
+
name: "Factory Droid",
|
|
2166
|
+
configDir: ".factorydroid",
|
|
2167
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2168
|
+
mcpInConfigDir: true
|
|
2169
|
+
});
|
|
2170
|
+
var AntiGravityTarget = createGenericMdTarget({
|
|
2171
|
+
id: "antigravity",
|
|
2172
|
+
name: "AntiGravity",
|
|
2173
|
+
configDir: ".antigravity",
|
|
2174
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2175
|
+
mcpInConfigDir: true
|
|
2176
|
+
});
|
|
2177
|
+
var JunieTarget = createGenericMdTarget({
|
|
2178
|
+
id: "junie",
|
|
2179
|
+
name: "Junie",
|
|
2180
|
+
configDir: ".junie",
|
|
2181
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2182
|
+
mcpInConfigDir: true
|
|
2183
|
+
});
|
|
2184
|
+
var AugmentCodeTarget = createGenericMdTarget({
|
|
2185
|
+
id: "augmentcode",
|
|
2186
|
+
name: "Augment Code",
|
|
2187
|
+
configDir: ".augmentcode",
|
|
2188
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2189
|
+
mcpInConfigDir: true
|
|
2190
|
+
});
|
|
2191
|
+
var WindsurfTarget = createGenericMdTarget({
|
|
2192
|
+
id: "windsurf",
|
|
2193
|
+
name: "Windsurf",
|
|
2194
|
+
configDir: ".windsurf",
|
|
2195
|
+
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
2196
|
+
ignoreFile: ".windsurfignore",
|
|
2197
|
+
mcpInConfigDir: true
|
|
2198
|
+
});
|
|
2199
|
+
var WarpTarget = createGenericMdTarget({
|
|
2200
|
+
id: "warp",
|
|
2201
|
+
name: "Warp",
|
|
2202
|
+
configDir: ".warp",
|
|
2203
|
+
supportedFeatures: ["rules"]
|
|
2204
|
+
});
|
|
2205
|
+
var ReplitTarget = createGenericMdTarget({
|
|
2206
|
+
id: "replit",
|
|
2207
|
+
name: "Replit Agent",
|
|
2208
|
+
configDir: ".replit",
|
|
2209
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2210
|
+
mcpInConfigDir: true
|
|
2211
|
+
});
|
|
2212
|
+
var ZedTarget = createGenericMdTarget({
|
|
2213
|
+
id: "zed",
|
|
2214
|
+
name: "Zed",
|
|
2215
|
+
configDir: ".zed",
|
|
2216
|
+
supportedFeatures: ["rules", "mcp"],
|
|
2217
|
+
mcpInConfigDir: true
|
|
2218
|
+
});
|
|
2219
|
+
|
|
2220
|
+
// src/targets/registry.ts
|
|
2221
|
+
var TARGETS = [
|
|
2222
|
+
new OpenCodeTarget,
|
|
2223
|
+
new CursorTarget,
|
|
2224
|
+
new ClaudeCodeTarget,
|
|
2225
|
+
new CodexCliTarget,
|
|
2226
|
+
new GeminiCliTarget,
|
|
2227
|
+
new CopilotTarget,
|
|
2228
|
+
new AgentsMdTarget,
|
|
2229
|
+
ClineTarget,
|
|
2230
|
+
KiloTarget,
|
|
2231
|
+
RooTarget,
|
|
2232
|
+
QwenCodeTarget,
|
|
2233
|
+
KiroTarget,
|
|
2234
|
+
FactoryDroidTarget,
|
|
2235
|
+
AntiGravityTarget,
|
|
2236
|
+
JunieTarget,
|
|
2237
|
+
AugmentCodeTarget,
|
|
2238
|
+
WindsurfTarget,
|
|
2239
|
+
WarpTarget,
|
|
2240
|
+
ReplitTarget,
|
|
2241
|
+
ZedTarget
|
|
2242
|
+
];
|
|
2243
|
+
var targetMap = new Map(TARGETS.map((t) => [t.id, t]));
|
|
2244
|
+
function getTarget(id) {
|
|
2245
|
+
return targetMap.get(id);
|
|
2246
|
+
}
|
|
2247
|
+
function getAllTargets() {
|
|
2248
|
+
return [...TARGETS];
|
|
2249
|
+
}
|
|
2250
|
+
function getTargets(ids) {
|
|
2251
|
+
return ids.map((id) => targetMap.get(id)).filter((t) => t !== undefined);
|
|
2252
|
+
}
|
|
2253
|
+
function listTargetIds() {
|
|
2254
|
+
return TARGETS.map((t) => t.id);
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/exporters/cursor-plugin.ts
|
|
2258
|
+
import { resolve as resolve16, join as join16 } from "path";
|
|
2259
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
2260
|
+
function exportCursorPlugin(pack, outputDir) {
|
|
2261
|
+
const filesWritten = [];
|
|
2262
|
+
const pluginDir = resolve16(outputDir, pack.manifest.name);
|
|
2263
|
+
mkdirSync4(pluginDir, { recursive: true });
|
|
2264
|
+
const manifest = {
|
|
2265
|
+
name: pack.manifest.name,
|
|
2266
|
+
version: pack.manifest.version,
|
|
2267
|
+
description: pack.manifest.description
|
|
2268
|
+
};
|
|
2269
|
+
if (pack.manifest.author) {
|
|
2270
|
+
manifest.author = typeof pack.manifest.author === "string" ? pack.manifest.author : pack.manifest.author.name;
|
|
2271
|
+
}
|
|
2272
|
+
if (pack.manifest.tags.length > 0) {
|
|
2273
|
+
manifest.tags = pack.manifest.tags;
|
|
2274
|
+
}
|
|
2275
|
+
if (pack.rules.length > 0) {
|
|
2276
|
+
const rulesDir = join16(pluginDir, "rules");
|
|
2277
|
+
ensureDir(rulesDir);
|
|
2278
|
+
manifest.rules = [];
|
|
2279
|
+
for (const rule of pack.rules) {
|
|
2280
|
+
const cursorMeta = rule.meta.cursor ?? {};
|
|
2281
|
+
const fm = {
|
|
2282
|
+
description: cursorMeta.description ?? rule.meta.description ?? "",
|
|
2283
|
+
alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
|
|
2284
|
+
};
|
|
2285
|
+
const globs = cursorMeta.globs ?? rule.meta.globs;
|
|
2286
|
+
if (globs)
|
|
2287
|
+
fm.globs = globs;
|
|
2288
|
+
const filename = `${rule.name}.mdc`;
|
|
2289
|
+
const filepath = join16(rulesDir, filename);
|
|
2290
|
+
writeFileSync5(filepath, serializeFrontmatter(fm, rule.content));
|
|
2291
|
+
filesWritten.push(filepath);
|
|
2292
|
+
manifest.rules.push(filename);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
if (pack.agents.length > 0) {
|
|
2296
|
+
const agentsDir = join16(pluginDir, "agents");
|
|
2297
|
+
ensureDir(agentsDir);
|
|
2298
|
+
manifest.agents = [];
|
|
2299
|
+
for (const agent of pack.agents) {
|
|
2300
|
+
const fm = {
|
|
2301
|
+
name: agent.name,
|
|
2302
|
+
description: agent.meta.description ?? ""
|
|
2303
|
+
};
|
|
2304
|
+
const filename = `${agent.name}.md`;
|
|
2305
|
+
const filepath = join16(agentsDir, filename);
|
|
2306
|
+
writeFileSync5(filepath, serializeFrontmatter(fm, agent.content));
|
|
2307
|
+
filesWritten.push(filepath);
|
|
2308
|
+
manifest.agents.push(filename);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (pack.skills.length > 0) {
|
|
2312
|
+
const skillsDir = join16(pluginDir, "skills");
|
|
2313
|
+
ensureDir(skillsDir);
|
|
2314
|
+
manifest.skills = [];
|
|
2315
|
+
for (const skill of pack.skills) {
|
|
2316
|
+
const skillSubDir = join16(skillsDir, skill.name);
|
|
2317
|
+
ensureDir(skillSubDir);
|
|
2318
|
+
const fm = {
|
|
2319
|
+
name: skill.name,
|
|
2320
|
+
description: skill.meta.description ?? ""
|
|
2321
|
+
};
|
|
2322
|
+
const filepath = join16(skillSubDir, "SKILL.md");
|
|
2323
|
+
writeFileSync5(filepath, serializeFrontmatter(fm, skill.content));
|
|
2324
|
+
filesWritten.push(filepath);
|
|
2325
|
+
manifest.skills.push(skill.name);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
if (pack.commands.length > 0) {
|
|
2329
|
+
const commandsDir = join16(pluginDir, "commands");
|
|
2330
|
+
ensureDir(commandsDir);
|
|
2331
|
+
manifest.commands = [];
|
|
2332
|
+
for (const cmd of pack.commands) {
|
|
2333
|
+
const filename = `${cmd.name}.md`;
|
|
2334
|
+
const filepath = join16(commandsDir, filename);
|
|
2335
|
+
writeFileSync5(filepath, cmd.content);
|
|
2336
|
+
filesWritten.push(filepath);
|
|
2337
|
+
manifest.commands.push(filename);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
if (pack.mcp && Object.keys(pack.mcp.servers).length > 0) {
|
|
2341
|
+
manifest.mcp = true;
|
|
2342
|
+
const filepath = join16(pluginDir, "mcp.json");
|
|
2343
|
+
writeFileSync5(filepath, JSON.stringify({ mcpServers: pack.mcp.servers }, null, 2) + `
|
|
2344
|
+
`);
|
|
2345
|
+
filesWritten.push(filepath);
|
|
2346
|
+
}
|
|
2347
|
+
const manifestPath = join16(pluginDir, "manifest.json");
|
|
2348
|
+
writeFileSync5(manifestPath, JSON.stringify(manifest, null, 2) + `
|
|
2349
|
+
`);
|
|
2350
|
+
filesWritten.push(manifestPath);
|
|
2351
|
+
return { outputDir: pluginDir, filesWritten, manifest };
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// src/importers/rulesync.ts
|
|
2355
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, copyFileSync, writeFileSync as writeFileSync6 } from "fs";
|
|
2356
|
+
import { resolve as resolve17, join as join17, basename as basename6 } from "path";
|
|
2357
|
+
import { parse as parseJsonc2 } from "jsonc-parser";
|
|
2358
|
+
function importFromRulesync(projectRoot, outputPackDir) {
|
|
2359
|
+
const rulesyncDir = resolve17(projectRoot, ".rulesync");
|
|
2360
|
+
const warnings = [];
|
|
2361
|
+
const filesImported = [];
|
|
2362
|
+
if (!existsSync10(rulesyncDir)) {
|
|
2363
|
+
return {
|
|
2364
|
+
packDir: "",
|
|
2365
|
+
filesImported: [],
|
|
2366
|
+
warnings: ["No .rulesync/ directory found."],
|
|
2367
|
+
configGenerated: false
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
const packDir = outputPackDir ?? resolve17(projectRoot, "packs", "default");
|
|
2371
|
+
ensureDir(packDir);
|
|
2372
|
+
const rulesDir = resolve17(rulesyncDir, "rules");
|
|
2373
|
+
if (existsSync10(rulesDir)) {
|
|
2374
|
+
const outRulesDir = resolve17(packDir, "rules");
|
|
2375
|
+
ensureDir(outRulesDir);
|
|
2376
|
+
const files = listFiles(rulesDir, { extension: ".md" });
|
|
2377
|
+
for (const file of files) {
|
|
2378
|
+
const dest = join17(outRulesDir, basename6(file));
|
|
2379
|
+
copyFileSync(file, dest);
|
|
2380
|
+
filesImported.push(dest);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
const commandsDir = resolve17(rulesyncDir, "commands");
|
|
2384
|
+
if (existsSync10(commandsDir)) {
|
|
2385
|
+
const outCommandsDir = resolve17(packDir, "commands");
|
|
2386
|
+
ensureDir(outCommandsDir);
|
|
2387
|
+
const files = listFiles(commandsDir, { extension: ".md" });
|
|
2388
|
+
for (const file of files) {
|
|
2389
|
+
const dest = join17(outCommandsDir, basename6(file));
|
|
2390
|
+
copyFileSync(file, dest);
|
|
2391
|
+
filesImported.push(dest);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
const subagentsDir = resolve17(rulesyncDir, "subagents");
|
|
2395
|
+
if (existsSync10(subagentsDir)) {
|
|
2396
|
+
const outAgentsDir = resolve17(packDir, "agents");
|
|
2397
|
+
ensureDir(outAgentsDir);
|
|
2398
|
+
const files = listFiles(subagentsDir, { extension: ".md" });
|
|
2399
|
+
for (const file of files) {
|
|
2400
|
+
const dest = join17(outAgentsDir, basename6(file));
|
|
2401
|
+
copyFileSync(file, dest);
|
|
2402
|
+
filesImported.push(dest);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
const skillsDir = resolve17(rulesyncDir, "skills");
|
|
2406
|
+
if (existsSync10(skillsDir)) {
|
|
2407
|
+
const outSkillsDir = resolve17(packDir, "skills");
|
|
2408
|
+
ensureDir(outSkillsDir);
|
|
2409
|
+
const skillDirs = listDirs(skillsDir);
|
|
2410
|
+
for (const skillDir of skillDirs) {
|
|
2411
|
+
const skillName = basename6(skillDir);
|
|
2412
|
+
if (skillName.startsWith("."))
|
|
2413
|
+
continue;
|
|
2414
|
+
const skillMd = join17(skillDir, "SKILL.md");
|
|
2415
|
+
if (existsSync10(skillMd)) {
|
|
2416
|
+
const outSkillDir = join17(outSkillsDir, skillName);
|
|
2417
|
+
ensureDir(outSkillDir);
|
|
2418
|
+
copyFileSync(skillMd, join17(outSkillDir, "SKILL.md"));
|
|
2419
|
+
filesImported.push(join17(outSkillDir, "SKILL.md"));
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
const hooksJson = resolve17(rulesyncDir, "hooks.json");
|
|
2424
|
+
if (existsSync10(hooksJson)) {
|
|
2425
|
+
const outHooksDir = resolve17(packDir, "hooks");
|
|
2426
|
+
ensureDir(outHooksDir);
|
|
2427
|
+
copyFileSync(hooksJson, join17(outHooksDir, "hooks.json"));
|
|
2428
|
+
filesImported.push(join17(outHooksDir, "hooks.json"));
|
|
2429
|
+
}
|
|
2430
|
+
const mcpJson = resolve17(rulesyncDir, "mcp.json");
|
|
2431
|
+
if (existsSync10(mcpJson)) {
|
|
2432
|
+
copyFileSync(mcpJson, join17(packDir, "mcp.json"));
|
|
2433
|
+
filesImported.push(join17(packDir, "mcp.json"));
|
|
2434
|
+
}
|
|
2435
|
+
const aiIgnore = resolve17(rulesyncDir, ".aiignore");
|
|
2436
|
+
const rulesyncIgnore = resolve17(projectRoot, ".rulesyncignore");
|
|
2437
|
+
if (existsSync10(aiIgnore)) {
|
|
2438
|
+
copyFileSync(aiIgnore, join17(packDir, "ignore"));
|
|
2439
|
+
filesImported.push(join17(packDir, "ignore"));
|
|
2440
|
+
} else if (existsSync10(rulesyncIgnore)) {
|
|
2441
|
+
copyFileSync(rulesyncIgnore, join17(packDir, "ignore"));
|
|
2442
|
+
filesImported.push(join17(packDir, "ignore"));
|
|
2443
|
+
}
|
|
2444
|
+
const packJson = {
|
|
2445
|
+
name: "default",
|
|
2446
|
+
version: "1.0.0",
|
|
2447
|
+
description: "Imported from rulesync",
|
|
2448
|
+
tags: ["imported", "rulesync"],
|
|
2449
|
+
dependencies: [],
|
|
2450
|
+
conflicts: [],
|
|
2451
|
+
targets: "*",
|
|
2452
|
+
features: "*"
|
|
2453
|
+
};
|
|
2454
|
+
writeFileSync6(join17(packDir, "pack.json"), JSON.stringify(packJson, null, 2) + `
|
|
2455
|
+
`);
|
|
2456
|
+
filesImported.push(join17(packDir, "pack.json"));
|
|
2457
|
+
let configGenerated = false;
|
|
2458
|
+
const rulesyncConfig = resolve17(projectRoot, "rulesync.jsonc");
|
|
2459
|
+
if (existsSync10(rulesyncConfig)) {
|
|
2460
|
+
const agentpacksConfig = convertRulesyncConfig(rulesyncConfig, packDir, projectRoot);
|
|
2461
|
+
const configPath = resolve17(projectRoot, "agentpacks.jsonc");
|
|
2462
|
+
writeFileSync6(configPath, agentpacksConfig);
|
|
2463
|
+
configGenerated = true;
|
|
2464
|
+
}
|
|
2465
|
+
return { packDir, filesImported, warnings, configGenerated };
|
|
2466
|
+
}
|
|
2467
|
+
function convertRulesyncConfig(rulesyncPath, packDir, projectRoot) {
|
|
2468
|
+
const raw = readFileSync10(rulesyncPath, "utf-8");
|
|
2469
|
+
const parsed = parseJsonc2(raw);
|
|
2470
|
+
const targets = parsed.targets ?? ["opencode", "cursor", "claudecode"];
|
|
2471
|
+
const features = parsed.features ?? ["*"];
|
|
2472
|
+
const baseDirs = parsed.baseDirs ?? ["."];
|
|
2473
|
+
const global = parsed.global ?? false;
|
|
2474
|
+
const deleteVal = parsed.delete ?? true;
|
|
2475
|
+
const relPackDir = "./" + join17("packs", "default");
|
|
2476
|
+
const config = {
|
|
2477
|
+
$schema: "https://unpkg.com/agentpacks/schema.json",
|
|
2478
|
+
packs: [relPackDir],
|
|
2479
|
+
disabled: [],
|
|
2480
|
+
targets,
|
|
2481
|
+
features,
|
|
2482
|
+
mode: baseDirs.length > 1 ? "monorepo" : "repo",
|
|
2483
|
+
baseDirs,
|
|
2484
|
+
global,
|
|
2485
|
+
delete: deleteVal
|
|
2486
|
+
};
|
|
2487
|
+
return JSON.stringify(config, null, 2) + `
|
|
2488
|
+
`;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
// src/importers/cursor.ts
|
|
2492
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync7, copyFileSync as copyFileSync2 } from "fs";
|
|
2493
|
+
import { resolve as resolve18, join as join18, basename as basename7 } from "path";
|
|
2494
|
+
function importFromCursor(projectRoot, outputPackDir) {
|
|
2495
|
+
const cursorDir = resolve18(projectRoot, ".cursor");
|
|
2496
|
+
const warnings = [];
|
|
2497
|
+
const filesImported = [];
|
|
2498
|
+
if (!existsSync11(cursorDir)) {
|
|
2499
|
+
return {
|
|
2500
|
+
packDir: "",
|
|
2501
|
+
filesImported: [],
|
|
2502
|
+
warnings: ["No .cursor/ directory found."],
|
|
2503
|
+
configGenerated: false
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
const packDir = outputPackDir ?? resolve18(projectRoot, "packs", "cursor-import");
|
|
2507
|
+
ensureDir(packDir);
|
|
2508
|
+
const rulesDir = resolve18(cursorDir, "rules");
|
|
2509
|
+
if (existsSync11(rulesDir)) {
|
|
2510
|
+
const outRulesDir = resolve18(packDir, "rules");
|
|
2511
|
+
ensureDir(outRulesDir);
|
|
2512
|
+
const files = listFiles(rulesDir, { extension: ".mdc" });
|
|
2513
|
+
for (const file of files) {
|
|
2514
|
+
const raw = readFileSync11(file, "utf-8");
|
|
2515
|
+
const { data, content } = parseFrontmatter(raw);
|
|
2516
|
+
const meta = {};
|
|
2517
|
+
if (data.description)
|
|
2518
|
+
meta.description = data.description;
|
|
2519
|
+
if (data.alwaysApply)
|
|
2520
|
+
meta.root = true;
|
|
2521
|
+
if (data.globs)
|
|
2522
|
+
meta.globs = data.globs;
|
|
2523
|
+
meta.cursor = { ...data };
|
|
2524
|
+
const mdContent = buildAgentpacksRule(meta, content);
|
|
2525
|
+
const name = basename7(file, ".mdc");
|
|
2526
|
+
const dest = join18(outRulesDir, `${name}.md`);
|
|
2527
|
+
writeFileSync7(dest, mdContent);
|
|
2528
|
+
filesImported.push(dest);
|
|
2529
|
+
}
|
|
2530
|
+
const mdFiles = listFiles(rulesDir, { extension: ".md" });
|
|
2531
|
+
for (const file of mdFiles) {
|
|
2532
|
+
const dest = join18(outRulesDir, basename7(file));
|
|
2533
|
+
copyFileSync2(file, dest);
|
|
2534
|
+
filesImported.push(dest);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
const agentsDir = resolve18(cursorDir, "agents");
|
|
2538
|
+
if (existsSync11(agentsDir)) {
|
|
2539
|
+
const outDir = resolve18(packDir, "agents");
|
|
2540
|
+
ensureDir(outDir);
|
|
2541
|
+
const files = listFiles(agentsDir, { extension: ".md" });
|
|
2542
|
+
for (const file of files) {
|
|
2543
|
+
const dest = join18(outDir, basename7(file));
|
|
2544
|
+
copyFileSync2(file, dest);
|
|
2545
|
+
filesImported.push(dest);
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
const skillsDir = resolve18(cursorDir, "skills");
|
|
2549
|
+
if (existsSync11(skillsDir)) {
|
|
2550
|
+
const outDir = resolve18(packDir, "skills");
|
|
2551
|
+
ensureDir(outDir);
|
|
2552
|
+
const dirs = listDirs(skillsDir);
|
|
2553
|
+
for (const dir of dirs) {
|
|
2554
|
+
const name = basename7(dir);
|
|
2555
|
+
const skillMd = join18(dir, "SKILL.md");
|
|
2556
|
+
if (existsSync11(skillMd)) {
|
|
2557
|
+
const outSkillDir = join18(outDir, name);
|
|
2558
|
+
ensureDir(outSkillDir);
|
|
2559
|
+
copyFileSync2(skillMd, join18(outSkillDir, "SKILL.md"));
|
|
2560
|
+
filesImported.push(join18(outSkillDir, "SKILL.md"));
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
const commandsDir = resolve18(cursorDir, "commands");
|
|
2565
|
+
if (existsSync11(commandsDir)) {
|
|
2566
|
+
const outDir = resolve18(packDir, "commands");
|
|
2567
|
+
ensureDir(outDir);
|
|
2568
|
+
const files = listFiles(commandsDir, { extension: ".md" });
|
|
2569
|
+
for (const file of files) {
|
|
2570
|
+
const dest = join18(outDir, basename7(file));
|
|
2571
|
+
copyFileSync2(file, dest);
|
|
2572
|
+
filesImported.push(dest);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
const mcpJson = resolve18(cursorDir, "mcp.json");
|
|
2576
|
+
if (existsSync11(mcpJson)) {
|
|
2577
|
+
copyFileSync2(mcpJson, join18(packDir, "mcp.json"));
|
|
2578
|
+
filesImported.push(join18(packDir, "mcp.json"));
|
|
2579
|
+
}
|
|
2580
|
+
const cursorIgnore = resolve18(projectRoot, ".cursorignore");
|
|
2581
|
+
if (existsSync11(cursorIgnore)) {
|
|
2582
|
+
copyFileSync2(cursorIgnore, join18(packDir, "ignore"));
|
|
2583
|
+
filesImported.push(join18(packDir, "ignore"));
|
|
2584
|
+
}
|
|
2585
|
+
writePackJson(packDir, "cursor-import", filesImported);
|
|
2586
|
+
return { packDir, filesImported, warnings, configGenerated: false };
|
|
2587
|
+
}
|
|
2588
|
+
function buildAgentpacksRule(meta, content) {
|
|
2589
|
+
const lines = ["---"];
|
|
2590
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
2591
|
+
if (typeof v === "object") {
|
|
2592
|
+
lines.push(`${k}: ${JSON.stringify(v)}`);
|
|
2593
|
+
} else {
|
|
2594
|
+
lines.push(`${k}: ${v}`);
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
lines.push("---", "", content);
|
|
2598
|
+
return lines.join(`
|
|
2599
|
+
`);
|
|
2600
|
+
}
|
|
2601
|
+
function writePackJson(packDir, name, filesImported) {
|
|
2602
|
+
const packJson = {
|
|
2603
|
+
name,
|
|
2604
|
+
version: "1.0.0",
|
|
2605
|
+
description: `Imported from Cursor`,
|
|
2606
|
+
tags: ["imported", "cursor"],
|
|
2607
|
+
dependencies: [],
|
|
2608
|
+
conflicts: [],
|
|
2609
|
+
targets: "*",
|
|
2610
|
+
features: "*"
|
|
2611
|
+
};
|
|
2612
|
+
const dest = join18(packDir, "pack.json");
|
|
2613
|
+
writeFileSync7(dest, JSON.stringify(packJson, null, 2) + `
|
|
2614
|
+
`);
|
|
2615
|
+
filesImported.push(dest);
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
// src/importers/claude-code.ts
|
|
2619
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync8, copyFileSync as copyFileSync3 } from "fs";
|
|
2620
|
+
import { resolve as resolve19, join as join19, basename as basename8 } from "path";
|
|
2621
|
+
function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
2622
|
+
const warnings = [];
|
|
2623
|
+
const filesImported = [];
|
|
2624
|
+
const claudeDir = resolve19(projectRoot, ".claude");
|
|
2625
|
+
const hasClaudeMd = existsSync12(resolve19(projectRoot, "CLAUDE.md"));
|
|
2626
|
+
const hasClaudeDir = existsSync12(claudeDir);
|
|
2627
|
+
if (!hasClaudeMd && !hasClaudeDir) {
|
|
2628
|
+
return {
|
|
2629
|
+
packDir: "",
|
|
2630
|
+
filesImported: [],
|
|
2631
|
+
warnings: ["No CLAUDE.md or .claude/ directory found."],
|
|
2632
|
+
configGenerated: false
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
const packDir = outputPackDir ?? resolve19(projectRoot, "packs", "claude-import");
|
|
2636
|
+
ensureDir(packDir);
|
|
2637
|
+
const rulesDir = resolve19(packDir, "rules");
|
|
2638
|
+
ensureDir(rulesDir);
|
|
2639
|
+
if (hasClaudeMd) {
|
|
2640
|
+
const raw = readFileSync12(resolve19(projectRoot, "CLAUDE.md"), "utf-8");
|
|
2641
|
+
const ruleContent = [
|
|
2642
|
+
"---",
|
|
2643
|
+
"root: true",
|
|
2644
|
+
'description: "Root Claude Code rules"',
|
|
2645
|
+
"---",
|
|
2646
|
+
"",
|
|
2647
|
+
raw
|
|
2648
|
+
].join(`
|
|
2649
|
+
`);
|
|
2650
|
+
const dest = join19(rulesDir, "claude-root.md");
|
|
2651
|
+
writeFileSync8(dest, ruleContent);
|
|
2652
|
+
filesImported.push(dest);
|
|
2653
|
+
}
|
|
2654
|
+
if (hasClaudeDir) {
|
|
2655
|
+
const claudeRulesDir = resolve19(claudeDir, "rules");
|
|
2656
|
+
if (existsSync12(claudeRulesDir)) {
|
|
2657
|
+
const files = listFiles(claudeRulesDir, { extension: ".md" });
|
|
2658
|
+
for (const file of files) {
|
|
2659
|
+
const dest = join19(rulesDir, basename8(file));
|
|
2660
|
+
copyFileSync3(file, dest);
|
|
2661
|
+
filesImported.push(dest);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
const settingsPath = resolve19(claudeDir, "settings.json");
|
|
2665
|
+
if (existsSync12(settingsPath)) {
|
|
2666
|
+
try {
|
|
2667
|
+
const raw = readFileSync12(settingsPath, "utf-8");
|
|
2668
|
+
const settings = JSON.parse(raw);
|
|
2669
|
+
const mcpServers = settings.mcpServers ?? settings.mcp_servers;
|
|
2670
|
+
if (mcpServers && typeof mcpServers === "object") {
|
|
2671
|
+
const mcpConfig = { servers: mcpServers };
|
|
2672
|
+
const dest = join19(packDir, "mcp.json");
|
|
2673
|
+
writeFileSync8(dest, JSON.stringify(mcpConfig, null, 2) + `
|
|
2674
|
+
`);
|
|
2675
|
+
filesImported.push(dest);
|
|
2676
|
+
}
|
|
2677
|
+
} catch {
|
|
2678
|
+
warnings.push("Failed to parse .claude/settings.json");
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
const packJson = {
|
|
2683
|
+
name: "claude-import",
|
|
2684
|
+
version: "1.0.0",
|
|
2685
|
+
description: "Imported from Claude Code",
|
|
2686
|
+
tags: ["imported", "claude-code"],
|
|
2687
|
+
dependencies: [],
|
|
2688
|
+
conflicts: [],
|
|
2689
|
+
targets: "*",
|
|
2690
|
+
features: "*"
|
|
2691
|
+
};
|
|
2692
|
+
const packJsonPath = join19(packDir, "pack.json");
|
|
2693
|
+
writeFileSync8(packJsonPath, JSON.stringify(packJson, null, 2) + `
|
|
2694
|
+
`);
|
|
2695
|
+
filesImported.push(packJsonPath);
|
|
2696
|
+
return { packDir, filesImported, warnings, configGenerated: false };
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
// src/importers/opencode.ts
|
|
2700
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync9, copyFileSync as copyFileSync4 } from "fs";
|
|
2701
|
+
import { resolve as resolve20, join as join20, basename as basename9 } from "path";
|
|
2702
|
+
function importFromOpenCode(projectRoot, outputPackDir) {
|
|
2703
|
+
const warnings = [];
|
|
2704
|
+
const filesImported = [];
|
|
2705
|
+
const ocDir = resolve20(projectRoot, ".opencode");
|
|
2706
|
+
if (!existsSync13(ocDir)) {
|
|
2707
|
+
return {
|
|
2708
|
+
packDir: "",
|
|
2709
|
+
filesImported: [],
|
|
2710
|
+
warnings: ["No .opencode/ directory found."],
|
|
2711
|
+
configGenerated: false
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
const packDir = outputPackDir ?? resolve20(projectRoot, "packs", "opencode-import");
|
|
2715
|
+
ensureDir(packDir);
|
|
2716
|
+
importDirMd(resolve20(ocDir, "rules"), resolve20(packDir, "rules"), filesImported);
|
|
2717
|
+
importDirMd(resolve20(ocDir, "commands"), resolve20(packDir, "commands"), filesImported);
|
|
2718
|
+
importDirMd(resolve20(ocDir, "agents"), resolve20(packDir, "agents"), filesImported);
|
|
2719
|
+
const skillDir = resolve20(ocDir, "skill");
|
|
2720
|
+
if (existsSync13(skillDir)) {
|
|
2721
|
+
const outSkillDir = resolve20(packDir, "skills");
|
|
2722
|
+
ensureDir(outSkillDir);
|
|
2723
|
+
const dirs = listDirs(skillDir);
|
|
2724
|
+
for (const dir of dirs) {
|
|
2725
|
+
const name = basename9(dir);
|
|
2726
|
+
if (name.startsWith("."))
|
|
2727
|
+
continue;
|
|
2728
|
+
const skillMd = join20(dir, "SKILL.md");
|
|
2729
|
+
if (existsSync13(skillMd)) {
|
|
2730
|
+
const outDir = join20(outSkillDir, name);
|
|
2731
|
+
ensureDir(outDir);
|
|
2732
|
+
copyFileSync4(skillMd, join20(outDir, "SKILL.md"));
|
|
2733
|
+
filesImported.push(join20(outDir, "SKILL.md"));
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
const pluginsDir = resolve20(ocDir, "plugins");
|
|
2738
|
+
if (existsSync13(pluginsDir)) {
|
|
2739
|
+
const outPluginsDir = resolve20(packDir, "plugins");
|
|
2740
|
+
ensureDir(outPluginsDir);
|
|
2741
|
+
const files = listFiles(pluginsDir);
|
|
2742
|
+
for (const file of files) {
|
|
2743
|
+
if (file.endsWith(".ts") || file.endsWith(".js")) {
|
|
2744
|
+
const dest2 = join20(outPluginsDir, basename9(file));
|
|
2745
|
+
copyFileSync4(file, dest2);
|
|
2746
|
+
filesImported.push(dest2);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
const agentsMd = resolve20(projectRoot, "AGENTS.md");
|
|
2751
|
+
if (existsSync13(agentsMd)) {
|
|
2752
|
+
const outRulesDir = resolve20(packDir, "rules");
|
|
2753
|
+
ensureDir(outRulesDir);
|
|
2754
|
+
const raw = readFileSync13(agentsMd, "utf-8");
|
|
2755
|
+
const ruleContent = [
|
|
2756
|
+
"---",
|
|
2757
|
+
"root: true",
|
|
2758
|
+
'description: "AGENTS.md root rules"',
|
|
2759
|
+
"---",
|
|
2760
|
+
"",
|
|
2761
|
+
raw
|
|
2762
|
+
].join(`
|
|
2763
|
+
`);
|
|
2764
|
+
const dest2 = join20(outRulesDir, "agents-md-root.md");
|
|
2765
|
+
writeFileSync9(dest2, ruleContent);
|
|
2766
|
+
filesImported.push(dest2);
|
|
2767
|
+
}
|
|
2768
|
+
const ocJson = resolve20(projectRoot, "opencode.json");
|
|
2769
|
+
if (existsSync13(ocJson)) {
|
|
2770
|
+
try {
|
|
2771
|
+
const raw = readFileSync13(ocJson, "utf-8");
|
|
2772
|
+
const config = JSON.parse(raw);
|
|
2773
|
+
const mcpObj = config.mcp;
|
|
2774
|
+
if (mcpObj) {
|
|
2775
|
+
const dest2 = join20(packDir, "mcp.json");
|
|
2776
|
+
writeFileSync9(dest2, JSON.stringify({ servers: mcpObj }, null, 2) + `
|
|
2777
|
+
`);
|
|
2778
|
+
filesImported.push(dest2);
|
|
2779
|
+
}
|
|
2780
|
+
} catch {
|
|
2781
|
+
warnings.push("Failed to parse opencode.json");
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
const ocIgnore = resolve20(projectRoot, ".opencodeignore");
|
|
2785
|
+
if (existsSync13(ocIgnore)) {
|
|
2786
|
+
copyFileSync4(ocIgnore, join20(packDir, "ignore"));
|
|
2787
|
+
filesImported.push(join20(packDir, "ignore"));
|
|
2788
|
+
}
|
|
2789
|
+
const packJson = {
|
|
2790
|
+
name: "opencode-import",
|
|
2791
|
+
version: "1.0.0",
|
|
2792
|
+
description: "Imported from OpenCode",
|
|
2793
|
+
tags: ["imported", "opencode"],
|
|
2794
|
+
dependencies: [],
|
|
2795
|
+
conflicts: [],
|
|
2796
|
+
targets: "*",
|
|
2797
|
+
features: "*"
|
|
2798
|
+
};
|
|
2799
|
+
const dest = join20(packDir, "pack.json");
|
|
2800
|
+
writeFileSync9(dest, JSON.stringify(packJson, null, 2) + `
|
|
2801
|
+
`);
|
|
2802
|
+
filesImported.push(dest);
|
|
2803
|
+
return { packDir, filesImported, warnings, configGenerated: false };
|
|
2804
|
+
}
|
|
2805
|
+
function importDirMd(srcDir, outDir, filesImported) {
|
|
2806
|
+
if (!existsSync13(srcDir))
|
|
2807
|
+
return;
|
|
2808
|
+
ensureDir(outDir);
|
|
2809
|
+
const files = listFiles(srcDir, { extension: ".md" });
|
|
2810
|
+
for (const file of files) {
|
|
2811
|
+
const dest = join20(outDir, basename9(file));
|
|
2812
|
+
copyFileSync4(file, dest);
|
|
2813
|
+
filesImported.push(dest);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
// src/api.ts
|
|
2818
|
+
init_config();
|
|
2819
|
+
export {
|
|
2820
|
+
saveLockfile,
|
|
2821
|
+
resolveTargets,
|
|
2822
|
+
resolveLocalPack,
|
|
2823
|
+
resolveFeatures,
|
|
2824
|
+
resolveDependencies,
|
|
2825
|
+
parseNpmSourceRef,
|
|
2826
|
+
parseGitSourceRef,
|
|
2827
|
+
loadWorkspaceConfig,
|
|
2828
|
+
loadPackManifest,
|
|
2829
|
+
loadLockfile,
|
|
2830
|
+
listTargetIds,
|
|
2831
|
+
isNpmPackRef,
|
|
2832
|
+
isLocalPackRef,
|
|
2833
|
+
isGitPackRef,
|
|
2834
|
+
installNpmSource,
|
|
2835
|
+
installGitSource,
|
|
2836
|
+
importFromRulesync,
|
|
2837
|
+
importFromOpenCode,
|
|
2838
|
+
importFromCursor,
|
|
2839
|
+
importFromClaudeCode,
|
|
2840
|
+
getTargets,
|
|
2841
|
+
getTarget,
|
|
2842
|
+
getAllTargets,
|
|
2843
|
+
exportCursorPlugin,
|
|
2844
|
+
computeIntegrity,
|
|
2845
|
+
WorkspaceConfigSchema,
|
|
2846
|
+
TARGET_IDS,
|
|
2847
|
+
REPO_MODES,
|
|
2848
|
+
PackManifestSchema,
|
|
2849
|
+
PackLoader,
|
|
2850
|
+
FeatureMerger,
|
|
2851
|
+
FEATURE_IDS
|
|
2852
|
+
};
|