codiedev 0.10.0-beta.1 → 0.10.0-beta.11
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/README.md +11 -5
- package/assets/opencode-plugin.mjs +6 -0
- package/assets/session-key.mjs +48 -0
- package/assets/tui-brand-plugin.tsx +73 -0
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +184 -0
- package/dist/cli.js +135 -72
- package/dist/cliSession.js +456 -0
- package/dist/codexHookTrust.js +125 -6
- package/dist/codexLimitScan.js +359 -0
- package/dist/codie.js +77 -130
- package/dist/codieBrandInstall.js +80 -0
- package/dist/codieLauncher.js +76 -79
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieUsage.js +181 -0
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1101 -0
- package/dist/commands/agentsSdk.generated.js +7 -0
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +53 -120
- package/dist/commands/evals.js +187 -0
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +573 -0
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +6 -10
- package/dist/commands/push.js +2 -12
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +344 -0
- package/dist/commands/tracesLabel.js +348 -0
- package/dist/configDir.js +18 -11
- package/dist/connect.js +55 -118
- package/dist/connectFlow.js +35 -9
- package/dist/copilotHooks.js +391 -0
- package/dist/copilotInstallation.js +64 -0
- package/dist/cursorHooks.js +38 -30
- package/dist/cursorLifecycle.js +109 -0
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +77 -0
- package/dist/hook.js +256 -445
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +168 -0
- package/dist/limitEvent.js +44 -13
- package/dist/limitEventReporter.js +1 -1
- package/dist/managedPi.js +256 -0
- package/dist/mcp.js +5 -384
- package/dist/nativeSteering.js +24 -0
- package/dist/packs/codie.js +182 -0
- package/dist/packs/harnesses.js +411 -0
- package/dist/packs/hookGate.js +256 -0
- package/dist/packs/install.js +286 -0
- package/dist/packs/manifest.js +126 -0
- package/dist/packs/paths.js +81 -0
- package/dist/packs/shim.js +214 -0
- package/dist/packs/store.js +155 -0
- package/dist/packs/sync.js +170 -0
- package/dist/piHooks.js +216 -0
- package/dist/piRuntime.js +120 -0
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/selfUpdate.js +376 -0
- package/dist/skillMarker.js +23 -0
- package/dist/subagents.js +135 -0
- package/dist/teamSkillContext.js +10 -0
- package/dist/transcriptModels.js +205 -0
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +502 -828
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +8 -9
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codie.d.ts +0 -2
- package/dist/codieLauncher.d.ts +0 -53
- package/dist/codieRuntime.d.ts +0 -29
- package/dist/codieRuntimeInstall.d.ts +0 -28
- package/dist/codieStartup.d.ts +0 -26
- package/dist/codieStartup.js +0 -30
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/promote.d.ts +0 -1
- package/dist/commands/promote.js +0 -53
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/react.d.ts +0 -2
- package/dist/commands/react.js +0 -40
- package/dist/commands/reverseTicket.d.ts +0 -2
- package/dist/commands/reverseTicket.js +0 -477
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/security.js +0 -156
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/configDir.d.ts +0 -1
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -13
- package/dist/cursorHooks.d.ts +0 -23
- package/dist/detection.d.ts +0 -30
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/limitEvent.d.ts +0 -16
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/mcp.d.ts +0 -12
- package/dist/prUrl.d.ts +0 -9
- package/dist/prUrl.js +0 -33
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/security.d.ts +0 -102
- package/dist/security.js +0 -437
- package/dist/securityDeepScan.d.ts +0 -41
- package/dist/securityDeepScan.js +0 -248
- package/dist/securityDoctor.d.ts +0 -32
- package/dist/securityDoctor.js +0 -63
- package/dist/securityFeature.d.ts +0 -5
- package/dist/securityFeature.js +0 -7
- package/dist/securityInstall.d.ts +0 -63
- package/dist/securityInstall.js +0 -239
- package/dist/securityInventory.d.ts +0 -54
- package/dist/securityInventory.js +0 -556
- package/dist/securityPilot.d.ts +0 -19
- package/dist/securityPilot.js +0 -61
- package/dist/securityPolicy.d.ts +0 -33
- package/dist/securityPolicy.js +0 -188
- package/dist/securitySession.d.ts +0 -6
- package/dist/securitySession.js +0 -72
- package/dist/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -342
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.levelToModel = levelToModel;
|
|
37
|
+
exports.mapAgentModel = mapAgentModel;
|
|
38
|
+
exports.codiePackConfig = codiePackConfig;
|
|
39
|
+
exports.mergeCodiePackConfig = mergeCodiePackConfig;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const url_1 = require("url");
|
|
43
|
+
const manifest_1 = require("./manifest");
|
|
44
|
+
const paths_1 = require("./paths");
|
|
45
|
+
const shim_1 = require("./shim");
|
|
46
|
+
const store_1 = require("./store");
|
|
47
|
+
const LEVEL_LABEL = { opus: "Strongest", sonnet: "Balanced", haiku: "Fastest" };
|
|
48
|
+
function newestFirst(a, b) {
|
|
49
|
+
return b.id.localeCompare(a.id, undefined, { numeric: true });
|
|
50
|
+
}
|
|
51
|
+
function levelToModel(level, catalog) {
|
|
52
|
+
const key = level.toLowerCase();
|
|
53
|
+
const tierNames = {
|
|
54
|
+
opus: ["strongest", "opus", "high", "max"],
|
|
55
|
+
sonnet: ["balanced", "sonnet", "medium", "default"],
|
|
56
|
+
haiku: ["fastest", "haiku", "low", "fast"],
|
|
57
|
+
};
|
|
58
|
+
const tiered = catalog.filter((m) => [m.tier, m.level].some((t) => typeof t === "string" && tierNames[key].includes(t.toLowerCase())));
|
|
59
|
+
if (tiered.length)
|
|
60
|
+
return { model: [...tiered].sort(newestFirst)[0].id, how: "tier" };
|
|
61
|
+
const family = catalog.filter((m) => new RegExp(`(^|[^a-z])${key}([^a-z]|$)`, "i").test(m.id));
|
|
62
|
+
if (family.length)
|
|
63
|
+
return { model: [...family].sort(newestFirst)[0].id, how: "family" };
|
|
64
|
+
const priced = catalog
|
|
65
|
+
.filter((m) => m.cost && Number.isFinite(m.cost.input) && Number.isFinite(m.cost.output))
|
|
66
|
+
.sort((a, b) => a.cost.input + a.cost.output - (b.cost.input + b.cost.output) || a.id.localeCompare(b.id));
|
|
67
|
+
if (priced.length) {
|
|
68
|
+
const index = key === "haiku" ? 0 : key === "opus" ? priced.length - 1 : Math.floor((priced.length - 1) / 2);
|
|
69
|
+
return { model: priced[index].id, how: "price" };
|
|
70
|
+
}
|
|
71
|
+
return { how: "none" };
|
|
72
|
+
}
|
|
73
|
+
function mapAgentModel(value, approved, catalog) {
|
|
74
|
+
const model = (value ?? "").trim();
|
|
75
|
+
if (!model || model === "inherit")
|
|
76
|
+
return {};
|
|
77
|
+
const key = model.toLowerCase();
|
|
78
|
+
if (LEVEL_LABEL[key]) {
|
|
79
|
+
const usable = (catalog ?? []).filter((m) => !approved || approved.has(m.id.replace(/^codiedev\//, "")));
|
|
80
|
+
const picked = levelToModel(key, usable);
|
|
81
|
+
if (!picked.model) {
|
|
82
|
+
return { note: `level ${LEVEL_LABEL[key]} (${model}): no matching approved model, using the governed default` };
|
|
83
|
+
}
|
|
84
|
+
return { model: picked.model, note: `level ${LEVEL_LABEL[key]} (${model}) → ${picked.model} (by ${picked.how})` };
|
|
85
|
+
}
|
|
86
|
+
if (!model.includes("/"))
|
|
87
|
+
return { note: `unknown level "${model}", using the governed default` };
|
|
88
|
+
if (approved) {
|
|
89
|
+
const bare = model.replace(/^codiedev\//, "");
|
|
90
|
+
if (!approved.has(bare))
|
|
91
|
+
return { note: `dropped model "${model}" (not approved for this Codiedev account)` };
|
|
92
|
+
}
|
|
93
|
+
return { model };
|
|
94
|
+
}
|
|
95
|
+
function codiePackConfig(root, options = {}) {
|
|
96
|
+
const registry = (0, store_1.readRegistry)(root);
|
|
97
|
+
const result = { skillPaths: [], agents: {}, plugins: [], notes: [] };
|
|
98
|
+
for (const [slug, entry] of Object.entries(registry.packs).sort(([a], [b]) => a.localeCompare(b))) {
|
|
99
|
+
if (!(0, manifest_1.isSafeSegment)(slug) || !Array.isArray(entry.harnesses) || !entry.harnesses.includes("codie"))
|
|
100
|
+
continue;
|
|
101
|
+
const dir = typeof entry.dir === "string" ? entry.dir : "";
|
|
102
|
+
if (!dir || !fs.existsSync(dir))
|
|
103
|
+
continue;
|
|
104
|
+
try {
|
|
105
|
+
const hooks = JSON.parse(fs.readFileSync(path.join(dir, "hooks", "hooks.json"), "utf8"))?.hooks;
|
|
106
|
+
const skipped = (0, shim_1.unsupportedShimEvents)([{ slug, root: dir, hooks: hooks ?? {} }]).get(slug);
|
|
107
|
+
if (skipped?.length)
|
|
108
|
+
result.notes.push(`pack ${slug}: hooks Codie can't run were skipped: ${skipped.join(", ")}`);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
}
|
|
112
|
+
const skills = path.join(dir, "skills");
|
|
113
|
+
if (fs.existsSync(skills))
|
|
114
|
+
result.skillPaths.push(skills);
|
|
115
|
+
const agentsDir = path.join(dir, "agents");
|
|
116
|
+
let agentFiles = [];
|
|
117
|
+
try {
|
|
118
|
+
agentFiles = fs.readdirSync(agentsDir).filter((f) => f.endsWith(".md")).sort();
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
agentFiles = [];
|
|
122
|
+
}
|
|
123
|
+
for (const file of agentFiles) {
|
|
124
|
+
const fallbackName = file.slice(0, -3);
|
|
125
|
+
let content;
|
|
126
|
+
try {
|
|
127
|
+
content = fs.readFileSync(path.join(agentsDir, file), "utf8");
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const { data, body } = (0, manifest_1.parseFrontmatter)(content);
|
|
133
|
+
const name = (0, manifest_1.isSafeSegment)(data.name) ? data.name : fallbackName;
|
|
134
|
+
if (result.agents[name]) {
|
|
135
|
+
result.notes.push(`agent "${name}" from pack ${slug} skipped (already defined by another pack)`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const mapped = mapAgentModel(data.model, options.approvedModels, options.catalog);
|
|
139
|
+
if (mapped.note)
|
|
140
|
+
result.notes.push(`agent "${name}" (${slug}): ${mapped.note}`);
|
|
141
|
+
result.agents[name] = {
|
|
142
|
+
description: data.description || `${name} (from starter pack ${slug})`,
|
|
143
|
+
prompt: body.trim(),
|
|
144
|
+
mode: "subagent",
|
|
145
|
+
...(mapped.model ? { model: mapped.model } : {}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const shim = (0, paths_1.shimPath)(root);
|
|
150
|
+
const anyCodie = Object.values(registry.packs).some((e) => Array.isArray(e.harnesses) && e.harnesses.includes("codie"));
|
|
151
|
+
if (anyCodie && fs.existsSync(shim))
|
|
152
|
+
result.plugins.push((0, url_1.pathToFileURL)(shim).href);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function mergeCodiePackConfig(existing, packs) {
|
|
156
|
+
const next = { ...existing };
|
|
157
|
+
if (packs.skillPaths.length) {
|
|
158
|
+
const current = existing.skills;
|
|
159
|
+
const base = Array.isArray(current)
|
|
160
|
+
? { paths: current.filter((p) => typeof p === "string") }
|
|
161
|
+
: current && typeof current === "object"
|
|
162
|
+
? { ...current }
|
|
163
|
+
: {};
|
|
164
|
+
const paths = Array.isArray(base.paths) ? [...base.paths] : [];
|
|
165
|
+
for (const p of packs.skillPaths)
|
|
166
|
+
if (!paths.includes(p))
|
|
167
|
+
paths.push(p);
|
|
168
|
+
next.skills = { ...base, paths };
|
|
169
|
+
}
|
|
170
|
+
if (Object.keys(packs.agents).length) {
|
|
171
|
+
const current = existing.agent && typeof existing.agent === "object" ? existing.agent : {};
|
|
172
|
+
next.agent = { ...packs.agents, ...current };
|
|
173
|
+
}
|
|
174
|
+
if (packs.plugins.length) {
|
|
175
|
+
const current = Array.isArray(existing.plugin) ? [...existing.plugin] : [];
|
|
176
|
+
for (const p of packs.plugins)
|
|
177
|
+
if (!current.includes(p))
|
|
178
|
+
current.push(p);
|
|
179
|
+
next.plugin = current;
|
|
180
|
+
}
|
|
181
|
+
return next;
|
|
182
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ADAPTERS = exports.piAdapter = exports.codieAdapter = exports.cursorAdapter = exports.CURSOR_MARKER = exports.codexAdapter = exports.copilotAdapter = exports.claudeAdapter = exports.HARNESSES = void 0;
|
|
37
|
+
exports.defaultRunner = defaultRunner;
|
|
38
|
+
exports.onPath = onPath;
|
|
39
|
+
exports.copyCursorSkills = copyCursorSkills;
|
|
40
|
+
exports.removeCursorSkills = removeCursorSkills;
|
|
41
|
+
exports.cursorLocalPluginsDir = cursorLocalPluginsDir;
|
|
42
|
+
exports.cursorPluginDir = cursorPluginDir;
|
|
43
|
+
exports.installCursorPlugin = installCursorPlugin;
|
|
44
|
+
exports.removeCursorPlugin = removeCursorPlugin;
|
|
45
|
+
exports.parseHarnessList = parseHarnessList;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
const harnessDirs_1 = require("../harnessDirs");
|
|
50
|
+
const paths_1 = require("./paths");
|
|
51
|
+
exports.HARNESSES = ["claude", "copilot", "codex", "cursor", "codie", "pi"];
|
|
52
|
+
function defaultRunner(env) {
|
|
53
|
+
return (command, args, options = {}) => {
|
|
54
|
+
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
55
|
+
encoding: "utf8",
|
|
56
|
+
env,
|
|
57
|
+
cwd: options.cwd,
|
|
58
|
+
timeout: 180_000,
|
|
59
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
status: result.status,
|
|
63
|
+
stdout: result.stdout ?? "",
|
|
64
|
+
stderr: result.stderr ?? "",
|
|
65
|
+
...(result.error ? { error: result.error.message } : {}),
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function onPath(bin, env) {
|
|
70
|
+
const dirs = (env.PATH ?? "").split(path.delimiter).filter(Boolean);
|
|
71
|
+
const exts = process.platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
72
|
+
for (const dir of dirs) {
|
|
73
|
+
for (const ext of exts) {
|
|
74
|
+
const candidate = path.join(dir, bin + ext);
|
|
75
|
+
try {
|
|
76
|
+
const stat = fs.statSync(candidate);
|
|
77
|
+
if (stat.isFile()) {
|
|
78
|
+
if (process.platform === "win32")
|
|
79
|
+
return true;
|
|
80
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
function uninstallRef(slug) {
|
|
91
|
+
return `${slug}@${paths_1.MARKETPLACE_NAME}`;
|
|
92
|
+
}
|
|
93
|
+
function uninstallWith(ctx, bin, verb) {
|
|
94
|
+
const result = ctx.run(bin, ["plugin", verb, uninstallRef(ctx.slug)]);
|
|
95
|
+
if (result.status === 0)
|
|
96
|
+
return "uninstalled";
|
|
97
|
+
if (/not (found|installed)/i.test(`${result.stdout}\n${result.stderr}`))
|
|
98
|
+
return "already removed";
|
|
99
|
+
throw new Error(`\`${bin} plugin ${verb} ${uninstallRef(ctx.slug)}\` failed (${describe(result)})`);
|
|
100
|
+
}
|
|
101
|
+
function describe(result) {
|
|
102
|
+
if (result.error)
|
|
103
|
+
return result.error;
|
|
104
|
+
const text = (result.stderr || result.stdout || "").trim().split("\n").filter(Boolean).pop();
|
|
105
|
+
return text ? `exit ${result.status}: ${text.slice(0, 200)}` : `exit ${result.status}`;
|
|
106
|
+
}
|
|
107
|
+
function must(ctx, bin, args, options) {
|
|
108
|
+
const result = ctx.run(bin, args, options);
|
|
109
|
+
if (result.status !== 0)
|
|
110
|
+
throw new Error(`\`${bin} ${args.join(" ")}\` failed (${describe(result)})`);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
function alreadyExists(result) {
|
|
114
|
+
return /already|exists/i.test(`${result.stdout}\n${result.stderr}`);
|
|
115
|
+
}
|
|
116
|
+
function wasInstalled(ctx, harness) {
|
|
117
|
+
return Boolean(ctx.previous?.harnesses?.includes(harness));
|
|
118
|
+
}
|
|
119
|
+
function contentChangedInPlace(ctx) {
|
|
120
|
+
const prev = ctx.previous;
|
|
121
|
+
return Boolean(prev && prev.version === ctx.manifest.version && prev.contentHash !== ctx.manifest.contentHash);
|
|
122
|
+
}
|
|
123
|
+
function pluginRef(ctx) {
|
|
124
|
+
return `${ctx.manifest.slug}@${paths_1.MARKETPLACE_NAME}`;
|
|
125
|
+
}
|
|
126
|
+
function claudeMarketplaceKnown(env, home) {
|
|
127
|
+
try {
|
|
128
|
+
const known = JSON.parse(fs.readFileSync(path.join((0, harnessDirs_1.claudeConfigDir)(env, home), "plugins", "known_marketplaces.json"), "utf8"));
|
|
129
|
+
return Boolean(known && typeof known === "object" && paths_1.MARKETPLACE_NAME in known);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function ensurePortableMarketplace(ctx, bin) {
|
|
136
|
+
const root = (0, paths_1.portableRoot)(ctx.root);
|
|
137
|
+
const stamp = path.join(root, `.registered-${bin}`);
|
|
138
|
+
if (!fs.existsSync(stamp))
|
|
139
|
+
ctx.run(bin, ["plugin", "marketplace", "remove", paths_1.MARKETPLACE_NAME]);
|
|
140
|
+
const added = ctx.run(bin, ["plugin", "marketplace", "add", root]);
|
|
141
|
+
if (added.status !== 0 && !alreadyExists(added)) {
|
|
142
|
+
throw new Error(`\`${bin} plugin marketplace add\` failed (${describe(added)})`);
|
|
143
|
+
}
|
|
144
|
+
fs.writeFileSync(stamp, `${root}\n`);
|
|
145
|
+
}
|
|
146
|
+
function marketplaceInstall(ctx, bin, harness, verbs, marketplaceKnown, portable = false) {
|
|
147
|
+
if (portable) {
|
|
148
|
+
ensurePortableMarketplace(ctx, bin);
|
|
149
|
+
}
|
|
150
|
+
else if (marketplaceKnown) {
|
|
151
|
+
ctx.run(bin, ["plugin", "marketplace", "update", paths_1.MARKETPLACE_NAME]);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const added = ctx.run(bin, ["plugin", "marketplace", "add", ctx.root]);
|
|
155
|
+
if (added.status !== 0 && !alreadyExists(added)) {
|
|
156
|
+
throw new Error(`\`${bin} plugin marketplace add\` failed (${describe(added)})`);
|
|
157
|
+
}
|
|
158
|
+
if (added.status !== 0)
|
|
159
|
+
ctx.run(bin, ["plugin", "marketplace", "update", paths_1.MARKETPLACE_NAME]);
|
|
160
|
+
}
|
|
161
|
+
const ref = pluginRef(ctx);
|
|
162
|
+
if (wasInstalled(ctx, harness)) {
|
|
163
|
+
if (contentChangedInPlace(ctx)) {
|
|
164
|
+
ctx.run(bin, ["plugin", verbs.uninstall, ref]);
|
|
165
|
+
must(ctx, bin, ["plugin", verbs.install, ref]);
|
|
166
|
+
return "reinstalled";
|
|
167
|
+
}
|
|
168
|
+
const updated = ctx.run(bin, ["plugin", verbs.update, ref]);
|
|
169
|
+
if (updated.status === 0)
|
|
170
|
+
return "updated";
|
|
171
|
+
}
|
|
172
|
+
const installed = ctx.run(bin, ["plugin", verbs.install, ref]);
|
|
173
|
+
if (installed.status === 0)
|
|
174
|
+
return "installed";
|
|
175
|
+
if (alreadyExists(installed)) {
|
|
176
|
+
must(ctx, bin, ["plugin", verbs.update, ref]);
|
|
177
|
+
return "updated";
|
|
178
|
+
}
|
|
179
|
+
throw new Error(`\`${bin} plugin ${verbs.install} ${ref}\` failed (${describe(installed)})`);
|
|
180
|
+
}
|
|
181
|
+
exports.claudeAdapter = {
|
|
182
|
+
name: "claude",
|
|
183
|
+
detect: (ctx) => onPath("claude", ctx.env),
|
|
184
|
+
install: (ctx) => marketplaceInstall(ctx, "claude", "claude", { install: "install", update: "update", uninstall: "uninstall" }, claudeMarketplaceKnown(ctx.env, ctx.home)),
|
|
185
|
+
plan: (ctx) => `claude plugin marketplace add ${ctx.root}; claude plugin install ${pluginRef(ctx)}`,
|
|
186
|
+
uninstall: (ctx) => uninstallWith(ctx, "claude", "uninstall"),
|
|
187
|
+
};
|
|
188
|
+
exports.copilotAdapter = {
|
|
189
|
+
name: "copilot",
|
|
190
|
+
detect: (ctx) => onPath("copilot", ctx.env),
|
|
191
|
+
install: (ctx) => marketplaceInstall(ctx, "copilot", "copilot", { install: "install", update: "update", uninstall: "uninstall" }, false, true),
|
|
192
|
+
plan: (ctx) => `copilot plugin marketplace add ${(0, paths_1.portableRoot)(ctx.root)}; copilot plugin install ${pluginRef(ctx)}`,
|
|
193
|
+
uninstall: (ctx) => uninstallWith(ctx, "copilot", "uninstall"),
|
|
194
|
+
};
|
|
195
|
+
exports.codexAdapter = {
|
|
196
|
+
name: "codex",
|
|
197
|
+
detect: (ctx) => onPath("codex", ctx.env),
|
|
198
|
+
install: (ctx) => {
|
|
199
|
+
ensurePortableMarketplace(ctx, "codex");
|
|
200
|
+
const ref = pluginRef(ctx);
|
|
201
|
+
const reinstall = wasInstalled(ctx, "codex");
|
|
202
|
+
if (reinstall)
|
|
203
|
+
ctx.run("codex", ["plugin", "remove", ref]);
|
|
204
|
+
must(ctx, "codex", ["plugin", "add", ref]);
|
|
205
|
+
return reinstall ? "reinstalled" : "installed";
|
|
206
|
+
},
|
|
207
|
+
plan: (ctx) => `codex plugin marketplace add ${(0, paths_1.portableRoot)(ctx.root)}; codex plugin add ${pluginRef(ctx)}; trust pack hooks in config.toml`,
|
|
208
|
+
uninstall: (ctx) => uninstallWith(ctx, "codex", "remove"),
|
|
209
|
+
};
|
|
210
|
+
exports.CURSOR_MARKER = ".codiedev-pack";
|
|
211
|
+
function cursorSkillsDir(home) {
|
|
212
|
+
return path.join(home, ".cursor", "skills");
|
|
213
|
+
}
|
|
214
|
+
function copyCursorSkills(home, manifest, pluginDirPath) {
|
|
215
|
+
const base = cursorSkillsDir(home);
|
|
216
|
+
fs.mkdirSync(base, { recursive: true });
|
|
217
|
+
const notes = [];
|
|
218
|
+
const wanted = new Set(manifest.skills.map((s) => s.name));
|
|
219
|
+
for (const entry of fs.readdirSync(base)) {
|
|
220
|
+
const marker = path.join(base, entry, exports.CURSOR_MARKER);
|
|
221
|
+
try {
|
|
222
|
+
if (fs.readFileSync(marker, "utf8").trim() === manifest.slug && !wanted.has(entry)) {
|
|
223
|
+
fs.rmSync(path.join(base, entry), { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
for (const skill of manifest.skills) {
|
|
230
|
+
const dest = path.join(base, skill.name);
|
|
231
|
+
if (fs.existsSync(dest)) {
|
|
232
|
+
let owner = "";
|
|
233
|
+
try {
|
|
234
|
+
owner = fs.readFileSync(path.join(dest, exports.CURSOR_MARKER), "utf8").trim();
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
owner = "";
|
|
238
|
+
}
|
|
239
|
+
if (owner !== manifest.slug) {
|
|
240
|
+
notes.push(`kept existing ~/.cursor/skills/${skill.name}`);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
246
|
+
fs.copyFileSync(path.join(pluginDirPath, "skills", skill.name, "SKILL.md"), path.join(dest, "SKILL.md"));
|
|
247
|
+
fs.writeFileSync(path.join(dest, exports.CURSOR_MARKER), `${manifest.slug}\n`);
|
|
248
|
+
}
|
|
249
|
+
return notes;
|
|
250
|
+
}
|
|
251
|
+
function removeCursorSkills(home, slug) {
|
|
252
|
+
const base = cursorSkillsDir(home);
|
|
253
|
+
let removed = 0;
|
|
254
|
+
let entries = [];
|
|
255
|
+
try {
|
|
256
|
+
entries = fs.readdirSync(base);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
for (const entry of entries) {
|
|
262
|
+
try {
|
|
263
|
+
if (fs.readFileSync(path.join(base, entry, exports.CURSOR_MARKER), "utf8").trim() === slug) {
|
|
264
|
+
fs.rmSync(path.join(base, entry), { recursive: true, force: true });
|
|
265
|
+
removed += 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return removed;
|
|
272
|
+
}
|
|
273
|
+
function cursorLocalPluginsDir(home) {
|
|
274
|
+
return path.join(home, ".cursor", "plugins", "local");
|
|
275
|
+
}
|
|
276
|
+
function cursorPluginDir(home, slug) {
|
|
277
|
+
return path.join(cursorLocalPluginsDir(home), `codiedev-${slug}`);
|
|
278
|
+
}
|
|
279
|
+
function markerOwner(dir) {
|
|
280
|
+
try {
|
|
281
|
+
return fs.readFileSync(path.join(dir, exports.CURSOR_MARKER), "utf8").trim();
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function copyTreeNoSymlinks(source, dest, depth = 0) {
|
|
288
|
+
if (depth > 32)
|
|
289
|
+
throw new Error(`plugin tree too deep at ${source}`);
|
|
290
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
291
|
+
for (const entry of fs.readdirSync(source)) {
|
|
292
|
+
const from = path.join(source, entry);
|
|
293
|
+
const to = path.join(dest, entry);
|
|
294
|
+
const stat = fs.statSync(from);
|
|
295
|
+
if (stat.isDirectory())
|
|
296
|
+
copyTreeNoSymlinks(from, to, depth + 1);
|
|
297
|
+
else if (stat.isFile())
|
|
298
|
+
fs.writeFileSync(to, fs.readFileSync(from));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function installCursorPlugin(home, slug, sourceDir) {
|
|
302
|
+
const parent = cursorLocalPluginsDir(home);
|
|
303
|
+
const target = cursorPluginDir(home, slug);
|
|
304
|
+
if (fs.existsSync(target) && markerOwner(target) !== slug) {
|
|
305
|
+
throw new Error(`${target} exists and wasn't written by Codiedev; leaving it alone`);
|
|
306
|
+
}
|
|
307
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
308
|
+
const stamp = `${process.pid}-${Date.now()}`;
|
|
309
|
+
const tmp = path.join(parent, `.codiedev-${slug}.tmp-${stamp}`);
|
|
310
|
+
const old = path.join(parent, `.codiedev-${slug}.old-${stamp}`);
|
|
311
|
+
try {
|
|
312
|
+
copyTreeNoSymlinks(sourceDir, tmp);
|
|
313
|
+
fs.writeFileSync(path.join(tmp, exports.CURSOR_MARKER), `${slug}\n`);
|
|
314
|
+
if (fs.existsSync(target))
|
|
315
|
+
fs.renameSync(target, old);
|
|
316
|
+
fs.renameSync(tmp, target);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
320
|
+
if (!fs.existsSync(target) && fs.existsSync(old))
|
|
321
|
+
fs.renameSync(old, target);
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
fs.rmSync(old, { recursive: true, force: true });
|
|
325
|
+
return target;
|
|
326
|
+
}
|
|
327
|
+
function removeCursorPlugin(home, slug) {
|
|
328
|
+
const target = cursorPluginDir(home, slug);
|
|
329
|
+
if (markerOwner(target) !== slug)
|
|
330
|
+
return false;
|
|
331
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
exports.cursorAdapter = {
|
|
335
|
+
name: "cursor",
|
|
336
|
+
detect: (ctx) => onPath("cursor-agent", ctx.env) || fs.existsSync(path.join(ctx.home, ".cursor")),
|
|
337
|
+
install: (ctx) => {
|
|
338
|
+
try {
|
|
339
|
+
installCursorPlugin(ctx.home, ctx.manifest.slug, ctx.portablePluginDir);
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
const notes = copyCursorSkills(ctx.home, ctx.manifest, ctx.portablePluginDir);
|
|
343
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
344
|
+
return [`skills only (couldn't write ~/.cursor/plugins/local: ${reason})`, ...notes].join("; ");
|
|
345
|
+
}
|
|
346
|
+
const migrated = removeCursorSkills(ctx.home, ctx.manifest.slug);
|
|
347
|
+
return migrated ? `installed (removed ${migrated} old ~/.cursor/skills cop${migrated === 1 ? "y" : "ies"})` : "installed";
|
|
348
|
+
},
|
|
349
|
+
plan: (ctx) => `copy plugin to ${cursorPluginDir(ctx.home, ctx.manifest.slug)}`,
|
|
350
|
+
uninstall: (ctx) => {
|
|
351
|
+
const plugin = removeCursorPlugin(ctx.home, ctx.slug);
|
|
352
|
+
const skills = removeCursorSkills(ctx.home, ctx.slug);
|
|
353
|
+
if (!plugin && !skills)
|
|
354
|
+
return "already removed";
|
|
355
|
+
return skills ? `uninstalled (${skills} copied skill${skills === 1 ? "" : "s"} removed)` : "uninstalled";
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
exports.codieAdapter = {
|
|
359
|
+
name: "codie",
|
|
360
|
+
detect: () => true,
|
|
361
|
+
install: () => "loads on next `codie` launch",
|
|
362
|
+
plan: () => "inject skills/agents/hook shim at next codie launch",
|
|
363
|
+
uninstall: () => "removed from next `codie` launch",
|
|
364
|
+
};
|
|
365
|
+
function piSettingsPath(env, home) {
|
|
366
|
+
return path.join((0, harnessDirs_1.piAgentDir)(env, home), "settings.json");
|
|
367
|
+
}
|
|
368
|
+
function updatePiSkillPaths(file, update) {
|
|
369
|
+
let settings = {};
|
|
370
|
+
if (fs.existsSync(file)) {
|
|
371
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
372
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
373
|
+
throw new Error(`${file} is not a JSON object; leaving it alone`);
|
|
374
|
+
settings = parsed;
|
|
375
|
+
}
|
|
376
|
+
const current = Array.isArray(settings.skills) ? settings.skills.filter((p) => typeof p === "string") : [];
|
|
377
|
+
const next = update(current);
|
|
378
|
+
if (next.length === current.length && next.every((p, i) => p === current[i]))
|
|
379
|
+
return false;
|
|
380
|
+
settings.skills = next;
|
|
381
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
382
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
383
|
+
fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n");
|
|
384
|
+
fs.renameSync(tmp, file);
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
exports.piAdapter = {
|
|
388
|
+
name: "pi",
|
|
389
|
+
detect: (ctx) => onPath("pi", ctx.env) || fs.existsSync((0, harnessDirs_1.piAgentDir)(ctx.env, ctx.home)),
|
|
390
|
+
install: (ctx) => {
|
|
391
|
+
const skills = path.join(ctx.portablePluginDir, "skills");
|
|
392
|
+
const changed = updatePiSkillPaths(piSettingsPath(ctx.env, ctx.home), (paths) => paths.includes(skills) ? paths : [...paths, skills]);
|
|
393
|
+
return changed ? "installed" : "already installed";
|
|
394
|
+
},
|
|
395
|
+
plan: (ctx) => `add ${path.join(ctx.portablePluginDir, "skills")} to ${piSettingsPath(ctx.env, ctx.home)} skills`,
|
|
396
|
+
uninstall: (ctx) => {
|
|
397
|
+
const dir = (0, paths_1.pluginDir)((0, paths_1.portableRoot)(ctx.root), ctx.slug);
|
|
398
|
+
const changed = updatePiSkillPaths(piSettingsPath(ctx.env, ctx.home), (paths) => paths.filter((p) => p !== dir && !p.startsWith(dir + path.sep)));
|
|
399
|
+
return changed ? "uninstalled" : "already removed";
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
exports.ADAPTERS = [exports.claudeAdapter, exports.copilotAdapter, exports.codexAdapter, exports.cursorAdapter, exports.codieAdapter, exports.piAdapter];
|
|
403
|
+
function parseHarnessList(value) {
|
|
404
|
+
if (value === undefined)
|
|
405
|
+
return undefined;
|
|
406
|
+
const names = value.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
407
|
+
const unknown = names.filter((n) => !exports.HARNESSES.includes(n));
|
|
408
|
+
if (unknown.length)
|
|
409
|
+
throw new Error(`Unknown harness: ${unknown.join(", ")} (valid: ${exports.HARNESSES.join(", ")})`);
|
|
410
|
+
return [...new Set(names)];
|
|
411
|
+
}
|