codiedev 0.9.66 → 0.9.68
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/captureSignals.d.ts +7 -0
- package/dist/captureSignals.js +23 -2
- package/dist/cli.js +32 -0
- package/dist/codexHookTrust.d.ts +6 -0
- package/dist/codexHookTrust.js +73 -0
- package/dist/codie.js +21 -1
- package/dist/commands/pack.d.ts +54 -0
- package/dist/commands/pack.js +602 -0
- package/dist/commands/pull.js +8 -3
- package/dist/commands/push.js +3 -1
- package/dist/hook.js +15 -1
- package/dist/mcp.js +5 -2
- package/dist/packs/codie.d.ts +45 -0
- package/dist/packs/codie.js +197 -0
- package/dist/packs/harnesses.d.ts +71 -0
- package/dist/packs/harnesses.js +407 -0
- package/dist/packs/hookGate.d.ts +53 -0
- package/dist/packs/hookGate.js +289 -0
- package/dist/packs/install.d.ts +46 -0
- package/dist/packs/install.js +304 -0
- package/dist/packs/manifest.d.ts +52 -0
- package/dist/packs/manifest.js +140 -0
- package/dist/packs/paths.d.ts +17 -0
- package/dist/packs/paths.js +89 -0
- package/dist/packs/shim.d.ts +26 -0
- package/dist/packs/shim.js +222 -0
- package/dist/packs/store.d.ts +42 -0
- package/dist/packs/store.js +167 -0
- package/dist/packs/sync.d.ts +40 -0
- package/dist/packs/sync.js +190 -0
- package/dist/skillMarker.d.ts +6 -0
- package/dist/skillMarker.js +37 -0
- package/dist/subagents.js +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,602 @@
|
|
|
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.PACK_HELP = exports.DEFAULT_BACKEND_URL = void 0;
|
|
37
|
+
exports.resolvePackConfig = resolvePackConfig;
|
|
38
|
+
exports.ciHarnesses = ciHarnesses;
|
|
39
|
+
exports.parsePackArgs = parsePackArgs;
|
|
40
|
+
exports.runList = runList;
|
|
41
|
+
exports.runInstall = runInstall;
|
|
42
|
+
exports.runStatus = runStatus;
|
|
43
|
+
exports.runPack = runPack;
|
|
44
|
+
exports.commitExport = commitExport;
|
|
45
|
+
exports.exportPluginFolder = exportPluginFolder;
|
|
46
|
+
exports.runExport = runExport;
|
|
47
|
+
exports.runApply = runApply;
|
|
48
|
+
const install_1 = require("../packs/install");
|
|
49
|
+
const harnesses_1 = require("../packs/harnesses");
|
|
50
|
+
const manifest_1 = require("../packs/manifest");
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const child_process_1 = require("child_process");
|
|
54
|
+
const manifest_2 = require("../packs/manifest");
|
|
55
|
+
const paths_1 = require("../packs/paths");
|
|
56
|
+
const store_1 = require("../packs/store");
|
|
57
|
+
const sync_1 = require("../packs/sync");
|
|
58
|
+
const harnesses_2 = require("../packs/harnesses");
|
|
59
|
+
const os = __importStar(require("os"));
|
|
60
|
+
const utils_1 = require("../utils");
|
|
61
|
+
const shared_1 = require("./shared");
|
|
62
|
+
// --- CI (pack-scoped `cdvci_` keys) ------------------------------------------
|
|
63
|
+
exports.DEFAULT_BACKEND_URL = "https://cli.codiedev.com";
|
|
64
|
+
/** CODIEDEV_TOKEN alone is enough: no ~/.codiedev/config.json needed (CI).
|
|
65
|
+
* CODIEDEV_URL defaults to production. Otherwise the connected config. */
|
|
66
|
+
function resolvePackConfig(env = process.env) {
|
|
67
|
+
const token = env.CODIEDEV_TOKEN?.trim();
|
|
68
|
+
if (token) {
|
|
69
|
+
return {
|
|
70
|
+
token,
|
|
71
|
+
backendUrl: env.CODIEDEV_URL?.trim() || exports.DEFAULT_BACKEND_URL,
|
|
72
|
+
companyId: "",
|
|
73
|
+
companyName: "",
|
|
74
|
+
repos: [],
|
|
75
|
+
lastSynced: "",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const config = (0, utils_1.readConfig)();
|
|
79
|
+
return config ? (0, shared_1.applyConfigEnvOverrides)(config) : null;
|
|
80
|
+
}
|
|
81
|
+
/** In CI, `--harness` defaults to claude when it's on PATH; anything else is
|
|
82
|
+
* ambiguous on a build box, so say what was found. */
|
|
83
|
+
function ciHarnesses(args, env, home) {
|
|
84
|
+
if (args.harnesses?.length)
|
|
85
|
+
return args.harnesses;
|
|
86
|
+
if ((0, harnesses_2.onPath)("claude", env))
|
|
87
|
+
return ["claude"];
|
|
88
|
+
const root = (0, paths_1.packsRoot)(env, home);
|
|
89
|
+
const found = harnesses_2.ADAPTERS.filter((a) => a.name !== "codie" && a.detect({ root, env, home: home ?? os.homedir(), run: () => ({ status: 0, stdout: "", stderr: "" }) }))
|
|
90
|
+
.map((a) => a.name);
|
|
91
|
+
throw new Error(`CI mode needs --harness: claude isn't on PATH. Detected: ${found.length ? found.join(", ") : "none"}. ` +
|
|
92
|
+
`Install Claude Code (npm i -g @anthropic-ai/claude-code) or pass e.g. --harness ${found[0] ?? "codex"}.`);
|
|
93
|
+
}
|
|
94
|
+
const USAGE = `Usage:
|
|
95
|
+
codiedev pack list Packs assigned to your team
|
|
96
|
+
codiedev pack install [slug] [--harness claude,codex,copilot,cursor,codie] [--dry-run] [--json] [--quiet]
|
|
97
|
+
Install one pack, or every pack that applies
|
|
98
|
+
codiedev pack status [--json] Installed packs and whether they're up to date
|
|
99
|
+
codiedev pack export <slug> --out <dir> [--git] [--manifest]
|
|
100
|
+
Write the pack as a Claude-format plugin folder plus
|
|
101
|
+
.claude-plugin/marketplace.json (commit it for CI).
|
|
102
|
+
--git inits the folder and commits; --manifest writes
|
|
103
|
+
an editable <slug>.pack.md instead
|
|
104
|
+
codiedev pack apply <pack.md> Create or update a pack from a pack.md
|
|
105
|
+
|
|
106
|
+
Cursor: packs are copied to ~/.cursor/plugins/local/codiedev-<slug>/. A Cursor
|
|
107
|
+
team admin can disable "user local plugin imports"; then Cursor ignores them.
|
|
108
|
+
|
|
109
|
+
Auto-update: session starts run \`codiedev pack install --quiet\` in the background
|
|
110
|
+
about once an hour. Opt out with CODIEDEV_PACK_AUTOSYNC=0.
|
|
111
|
+
|
|
112
|
+
Hooks for specific skills only: codiedev hook-gate --skills a,b --cmd-b64 <base64url of the command>
|
|
113
|
+
(the pack editor writes this; the command runs via /bin/sh, or cmd.exe on Windows)
|
|
114
|
+
|
|
115
|
+
CI: set CODIEDEV_TOKEN to a pack CI key (cdvci_…, from the pack's page in the portal).
|
|
116
|
+
No \`codiedev connect\` or config file is needed. CI mode (a cdvci_ key, or CI=true)
|
|
117
|
+
never auto-syncs, reports or prunes, installs the one pack the key can see, defaults
|
|
118
|
+
--harness to claude when it's on PATH, and exits non-zero unless every requested tool
|
|
119
|
+
got the pack. Skills from a pack are /<pack-slug>:<skill> in Claude Code.
|
|
120
|
+
|
|
121
|
+
GitLab CI (.gitlab-ci.yml; add CODIEDEV_TOKEN and ANTHROPIC_API_KEY as masked
|
|
122
|
+
CI/CD variables):
|
|
123
|
+
pack-scan:
|
|
124
|
+
image: node:22
|
|
125
|
+
script:
|
|
126
|
+
- npm i -g codiedev @anthropic-ai/claude-code
|
|
127
|
+
- codiedev pack install --harness claude
|
|
128
|
+
- claude -p "/platform-starter:healthcare-scan" --output-format text
|
|
129
|
+
|
|
130
|
+
GitHub Actions (repository secrets CODIEDEV_TOKEN and ANTHROPIC_API_KEY):
|
|
131
|
+
- uses: actions/setup-node@v4
|
|
132
|
+
with: { node-version: 22 }
|
|
133
|
+
- run: npm i -g codiedev @anthropic-ai/claude-code
|
|
134
|
+
- run: codiedev pack install --harness claude
|
|
135
|
+
env:
|
|
136
|
+
CODIEDEV_TOKEN: \${{ secrets.CODIEDEV_TOKEN }}
|
|
137
|
+
- run: claude -p "/platform-starter:healthcare-scan" --output-format text
|
|
138
|
+
env:
|
|
139
|
+
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
140
|
+
|
|
141
|
+
Or vendor the plugin into the repo instead of installing at build time:
|
|
142
|
+
codiedev pack export <slug> --out .codiedev/packs`;
|
|
143
|
+
function parsePackArgs(args) {
|
|
144
|
+
const out = { dryRun: false, json: false };
|
|
145
|
+
const positional = [];
|
|
146
|
+
for (let i = 0; i < args.length; i++) {
|
|
147
|
+
const a = args[i];
|
|
148
|
+
if (a === "--dry-run")
|
|
149
|
+
out.dryRun = true;
|
|
150
|
+
else if (a === "--json")
|
|
151
|
+
out.json = true;
|
|
152
|
+
else if (a === "--quiet" || a === "-q")
|
|
153
|
+
out.quiet = true;
|
|
154
|
+
else if (a === "--no-prune")
|
|
155
|
+
out.noPrune = true;
|
|
156
|
+
else if (a === "--git")
|
|
157
|
+
out.git = true;
|
|
158
|
+
else if (a === "--manifest")
|
|
159
|
+
out.manifest = true;
|
|
160
|
+
else if (a === "--out" || a === "-o") {
|
|
161
|
+
if (i + 1 >= args.length)
|
|
162
|
+
throw new Error("--out needs a directory");
|
|
163
|
+
out.out = args[++i];
|
|
164
|
+
}
|
|
165
|
+
else if (a.startsWith("--out="))
|
|
166
|
+
out.out = a.slice("--out=".length);
|
|
167
|
+
else if (a === "--harness" || a === "--harnesses") {
|
|
168
|
+
if (i + 1 >= args.length)
|
|
169
|
+
throw new Error("--harness needs a value");
|
|
170
|
+
out.harnesses = (0, harnesses_1.parseHarnessList)(args[++i]);
|
|
171
|
+
}
|
|
172
|
+
else if (a.startsWith("--harness="))
|
|
173
|
+
out.harnesses = (0, harnesses_1.parseHarnessList)(a.slice("--harness=".length));
|
|
174
|
+
else if (a.startsWith("--"))
|
|
175
|
+
throw new Error(`Unknown flag: ${a}`);
|
|
176
|
+
else
|
|
177
|
+
positional.push(a);
|
|
178
|
+
}
|
|
179
|
+
out.sub = positional[0];
|
|
180
|
+
out.slug = positional[1];
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
async function listPacks(config) {
|
|
184
|
+
const res = await (0, shared_1.apiRequest)("GET", "/api/cli/packs", { config });
|
|
185
|
+
return Array.isArray(res.packs) ? res.packs : [];
|
|
186
|
+
}
|
|
187
|
+
async function fetchManifest(config, slug) {
|
|
188
|
+
const raw = await (0, shared_1.apiRequest)("GET", "/api/cli/packs/manifest", { config, query: { slug } });
|
|
189
|
+
return (0, manifest_1.parseManifest)(raw);
|
|
190
|
+
}
|
|
191
|
+
async function runList(config, json, overrides = {}) {
|
|
192
|
+
const packs = await listPacks(config);
|
|
193
|
+
const registry = (0, store_1.readRegistry)((0, paths_1.packsRoot)(overrides.env, overrides.home));
|
|
194
|
+
if (json) {
|
|
195
|
+
console.log(JSON.stringify({ packs: packs.map((p) => ({ ...p, installed: registry.packs[p.slug] ?? null })) }, null, 2));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (packs.length === 0) {
|
|
199
|
+
console.log("No starter packs are assigned to your team yet.");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
for (const p of packs) {
|
|
203
|
+
const local = registry.packs[p.slug];
|
|
204
|
+
const state = local ? (local.version === p.version ? "installed" : `installed v${local.version}, v${p.version} available`) : "not installed";
|
|
205
|
+
const counts = [
|
|
206
|
+
`${p.itemCount ?? 0} skill${p.itemCount === 1 ? "" : "s"}`,
|
|
207
|
+
`${p.agentCount ?? 0} agent${p.agentCount === 1 ? "" : "s"}`,
|
|
208
|
+
...(p.hasHooks ? ["hooks"] : []),
|
|
209
|
+
].join(", ");
|
|
210
|
+
const channel = (0, manifest_2.cleanLabel)(p.channel, 16);
|
|
211
|
+
console.log(`${p.slug} v${p.version}${channel ? ` [${channel}]` : ""} ${(0, manifest_2.displayName)(p)} (${counts}) — ${state}`);
|
|
212
|
+
if (p.description)
|
|
213
|
+
console.log(` ${p.description}`);
|
|
214
|
+
}
|
|
215
|
+
console.log("\nInstall with: codiedev pack install <slug> (no slug installs all)");
|
|
216
|
+
}
|
|
217
|
+
function isNotFound(error) {
|
|
218
|
+
return error?.status === 404;
|
|
219
|
+
}
|
|
220
|
+
async function runInstall(config, args, overrides = {}) {
|
|
221
|
+
const root = (0, paths_1.packsRoot)(overrides.env, overrides.home);
|
|
222
|
+
const registry = (0, store_1.readRegistry)(root);
|
|
223
|
+
const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
|
|
224
|
+
const harnesses = ci && !args.dryRun ? ciHarnesses(args, overrides.env ?? process.env, overrides.home) : args.harnesses;
|
|
225
|
+
const listed = args.slug ? [args.slug] : (await listPacks(config)).map((p) => p.slug);
|
|
226
|
+
// With no slug, also re-check every locally installed pack: one that was
|
|
227
|
+
// unassigned or archived 404s and must be removed locally. Not in CI: no pruning.
|
|
228
|
+
const slugs = args.slug || ci ? listed : [...new Set([...listed, ...Object.keys(registry.packs)])];
|
|
229
|
+
const manifests = [];
|
|
230
|
+
const revoked = [];
|
|
231
|
+
for (const slug of slugs) {
|
|
232
|
+
try {
|
|
233
|
+
manifests.push(await fetchManifest(config, slug));
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error instanceof manifest_1.ManifestError)
|
|
237
|
+
throw new Error(`Refusing to install ${slug}: ${error.message}`);
|
|
238
|
+
if (!isNotFound(error))
|
|
239
|
+
throw error;
|
|
240
|
+
if (registry.packs[slug] && !ci)
|
|
241
|
+
revoked.push(slug);
|
|
242
|
+
else if (args.slug)
|
|
243
|
+
throw new Error(`Pack not found or not assigned to your team: ${slug}`);
|
|
244
|
+
// A listed pack that 404s a moment later (race with an unassign) is skipped.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
let installed = manifests.length
|
|
248
|
+
? (0, install_1.installPacks)({ ...overrides, manifests, harnesses, dryRun: args.dryRun })
|
|
249
|
+
: [];
|
|
250
|
+
let removed = [];
|
|
251
|
+
if (!args.dryRun && !ci) {
|
|
252
|
+
// A 404 on the installed report means the pack stopped applying between
|
|
253
|
+
// the manifest fetch and now: treat it exactly like a revoked pack.
|
|
254
|
+
const lateRevoked = await reportInstalled(config, installed);
|
|
255
|
+
installed = installed.filter((p) => !lateRevoked.includes(p.slug));
|
|
256
|
+
removed = await removePacks(config, [...revoked, ...lateRevoked], overrides);
|
|
257
|
+
}
|
|
258
|
+
const run = { installed, removed, wouldRemove: args.dryRun ? revoked : [] };
|
|
259
|
+
if (!args.dryRun && !ci) {
|
|
260
|
+
const previous = (0, sync_1.readStatusCache)(root);
|
|
261
|
+
const done = new Set(installed.map((p) => p.slug));
|
|
262
|
+
(0, sync_1.writeStatusCache)(root, {
|
|
263
|
+
checkedAt: Date.now(),
|
|
264
|
+
available: args.slug ? Math.max(previous?.available ?? 0, installed.length) : installed.length,
|
|
265
|
+
outdated: args.slug ? (previous?.outdated ?? []).filter((o) => !done.has(o.slug) && registry.packs[o.slug]) : [],
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (args.json) {
|
|
269
|
+
console.log(JSON.stringify({ dryRun: args.dryRun, packs: installed, removed, wouldRemove: run.wouldRemove }, null, 2));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
if (args.dryRun)
|
|
273
|
+
console.log("Dry run — nothing was written.\n");
|
|
274
|
+
if (installed.length === 0 && removed.length === 0 && revoked.length === 0) {
|
|
275
|
+
console.log("No starter packs are assigned to your team yet.");
|
|
276
|
+
}
|
|
277
|
+
for (const line of (0, install_1.formatResultLines)(installed))
|
|
278
|
+
console.log(line);
|
|
279
|
+
if (args.dryRun) {
|
|
280
|
+
for (const pack of installed)
|
|
281
|
+
console.log(` files: ${pack.files.join(", ")}`);
|
|
282
|
+
for (const slug of revoked)
|
|
283
|
+
console.log(`${slug} would be removed (no longer assigned to your team)`);
|
|
284
|
+
}
|
|
285
|
+
for (const line of (0, install_1.formatUninstallLines)(removed))
|
|
286
|
+
console.log(line);
|
|
287
|
+
}
|
|
288
|
+
// Exit non-zero when a pack reached no tool because of a failure, or the
|
|
289
|
+
// pack explicitly asked for is no longer available.
|
|
290
|
+
const failed = installed.some((p) => p.results.some((r) => r.status === "failed") && !p.results.some((r) => r.status === "ok"));
|
|
291
|
+
// CI is strict: every requested tool must have taken every pack, and there
|
|
292
|
+
// must have been a pack to install at all.
|
|
293
|
+
const ciFailed = ci && !args.dryRun && (installed.length === 0 || installed.some((p) => p.results.some((r) => r.status !== "ok")));
|
|
294
|
+
if (ci && !args.dryRun && installed.length === 0)
|
|
295
|
+
console.error("No pack is visible to this key.");
|
|
296
|
+
if (failed || ciFailed || (args.slug && removed.length + revoked.length > 0))
|
|
297
|
+
process.exitCode = 1;
|
|
298
|
+
return run;
|
|
299
|
+
}
|
|
300
|
+
/** POST each install. Returns slugs the server answered 404 for (revoked);
|
|
301
|
+
* any other failure is only a warning. */
|
|
302
|
+
async function reportInstalled(config, results) {
|
|
303
|
+
const revoked = [];
|
|
304
|
+
for (const pack of results) {
|
|
305
|
+
try {
|
|
306
|
+
await (0, shared_1.apiRequest)("POST", "/api/cli/packs/installed", {
|
|
307
|
+
config,
|
|
308
|
+
body: { slug: pack.slug, version: pack.version, contentHash: pack.contentHash, harnesses: pack.harnesses },
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
if (isNotFound(error))
|
|
313
|
+
revoked.push(pack.slug);
|
|
314
|
+
else
|
|
315
|
+
console.error(`warning: couldn't report the ${pack.slug} install to Codiedev (${error.message})`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return revoked;
|
|
319
|
+
}
|
|
320
|
+
/** Uninstall packs locally, then tell the server (best-effort, idempotent). */
|
|
321
|
+
async function removePacks(config, slugs, overrides) {
|
|
322
|
+
if (slugs.length === 0)
|
|
323
|
+
return [];
|
|
324
|
+
const removed = (0, install_1.uninstallPacks)({ slugs: [...new Set(slugs)], env: overrides.env, home: overrides.home, run: overrides.run });
|
|
325
|
+
for (const pack of removed) {
|
|
326
|
+
try {
|
|
327
|
+
await (0, shared_1.apiRequest)("POST", "/api/cli/packs/uninstalled", { config, body: { slug: pack.slug } });
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
console.error(`warning: couldn't report the ${pack.slug} removal to Codiedev (${error.message})`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return removed;
|
|
334
|
+
}
|
|
335
|
+
async function runStatus(config, json, overrides = {}, options = {}) {
|
|
336
|
+
const root = (0, paths_1.packsRoot)(overrides.env, overrides.home);
|
|
337
|
+
const registry = (0, store_1.readRegistry)(root);
|
|
338
|
+
let available = null;
|
|
339
|
+
try {
|
|
340
|
+
available = await listPacks(config);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
available = null;
|
|
344
|
+
}
|
|
345
|
+
const rows = [];
|
|
346
|
+
const revoked = [];
|
|
347
|
+
for (const [slug, entry] of Object.entries(registry.packs)) {
|
|
348
|
+
let upToDate = null;
|
|
349
|
+
let latestVersion = null;
|
|
350
|
+
let latestChannel = null;
|
|
351
|
+
if (available) {
|
|
352
|
+
try {
|
|
353
|
+
const m = await fetchManifest(config, slug);
|
|
354
|
+
upToDate = m.contentHash === entry.contentHash;
|
|
355
|
+
latestVersion = m.version;
|
|
356
|
+
latestChannel = m.channel ?? null;
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
if (isNotFound(error)) {
|
|
360
|
+
revoked.push(slug);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
upToDate = null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
rows.push({ slug, ...entry, upToDate, latestVersion, latestChannel });
|
|
367
|
+
}
|
|
368
|
+
// --no-prune (background check when auto-update is off) and CI: report only.
|
|
369
|
+
const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
|
|
370
|
+
const removed = options.prune === false || ci ? [] : await removePacks(config, revoked, overrides);
|
|
371
|
+
if (available && !ci) {
|
|
372
|
+
(0, sync_1.writeStatusCache)(root, {
|
|
373
|
+
checkedAt: Date.now(),
|
|
374
|
+
available: available.length,
|
|
375
|
+
outdated: rows
|
|
376
|
+
.filter((r) => r.upToDate === false)
|
|
377
|
+
.map((r) => ({ slug: String(r.slug), ...(typeof r.name === "string" ? { name: r.name } : {}) })),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const notInstalled = (available ?? []).filter((p) => !registry.packs[p.slug]).map((p) => p.slug);
|
|
381
|
+
const result = { installed: rows, removed, notInstalled, offline: available === null };
|
|
382
|
+
if (json) {
|
|
383
|
+
console.log(JSON.stringify(result, null, 2));
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
if (rows.length === 0 && removed.length === 0)
|
|
387
|
+
console.log("No starter packs installed.");
|
|
388
|
+
for (const row of rows) {
|
|
389
|
+
const state = row.upToDate === true ? "up to date" : row.upToDate === false ? `outdated (v${row.latestVersion} available)` : "unknown (offline)";
|
|
390
|
+
const channel = typeof row.latestChannel === "string" ? row.latestChannel : typeof row.channel === "string" ? row.channel : undefined;
|
|
391
|
+
console.log(`${(0, manifest_2.displayName)({ slug: String(row.slug), name: row.name, icon: row.icon })} v${row.version}${channel ? ` [${channel}]` : ""} ${state} [${row.harnesses.join(", ") || "no tools"}] installed ${(0, shared_1.timeAgo)(row.installedAt)}`);
|
|
392
|
+
}
|
|
393
|
+
for (const line of (0, install_1.formatUninstallLines)(removed))
|
|
394
|
+
console.log(line);
|
|
395
|
+
if (notInstalled.length)
|
|
396
|
+
console.log(`\nAvailable, not installed: ${notInstalled.join(", ")}`);
|
|
397
|
+
if (rows.some((r) => r.upToDate === false) || notInstalled.length)
|
|
398
|
+
console.log("Run `codiedev pack install` to sync.");
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
async function runPack(args) {
|
|
402
|
+
let parsed;
|
|
403
|
+
try {
|
|
404
|
+
parsed = parsePackArgs(args);
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
console.error(error.message);
|
|
408
|
+
console.error(USAGE);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
if (!parsed.sub || !["list", "ls", "install", "status", "export", "apply"].includes(parsed.sub)) {
|
|
412
|
+
console.error(USAGE);
|
|
413
|
+
process.exit(parsed.sub ? 1 : 0);
|
|
414
|
+
}
|
|
415
|
+
const config = resolvePackConfig() ?? (0, shared_1.requireConfig)();
|
|
416
|
+
const ci = (0, sync_1.isPackCiMode)();
|
|
417
|
+
// --quiet (the background auto-update): no output at all.
|
|
418
|
+
const restore = parsed.quiet ? silenceConsole() : () => { };
|
|
419
|
+
// CI keys can only read packs: no telemetry POST either.
|
|
420
|
+
const track = ci ? (_c, _t, fn) => fn() : shared_1.withTelemetry;
|
|
421
|
+
try {
|
|
422
|
+
await track(config, `codiedev_pack_${parsed.sub === "ls" ? "list" : parsed.sub}`, async () => {
|
|
423
|
+
if (parsed.sub === "list" || parsed.sub === "ls")
|
|
424
|
+
await runList(config, parsed.json);
|
|
425
|
+
else if (parsed.sub === "install")
|
|
426
|
+
await runInstall(config, parsed);
|
|
427
|
+
else if (parsed.sub === "export")
|
|
428
|
+
await runExport(config, parsed);
|
|
429
|
+
else if (parsed.sub === "apply")
|
|
430
|
+
await runApply(config, parsed);
|
|
431
|
+
else
|
|
432
|
+
await runStatus(config, parsed.json, {}, { prune: !parsed.noPrune });
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
console.error(`pack ${parsed.sub} failed: ${error.message}`);
|
|
437
|
+
restore();
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
restore();
|
|
441
|
+
}
|
|
442
|
+
function silenceConsole() {
|
|
443
|
+
const log = console.log;
|
|
444
|
+
const error = console.error;
|
|
445
|
+
console.log = () => { };
|
|
446
|
+
console.error = () => { };
|
|
447
|
+
return () => {
|
|
448
|
+
console.log = log;
|
|
449
|
+
console.error = error;
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
// --- pack export / apply ---------------------------------------------------
|
|
453
|
+
function gitIn(dir, args) {
|
|
454
|
+
const r = (0, child_process_1.spawnSync)("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
455
|
+
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? (r.error?.message ?? "") };
|
|
456
|
+
}
|
|
457
|
+
/** Init (if needed) and commit everything under `dir`. Uses the user's git
|
|
458
|
+
* identity when configured, a neutral Codiedev one otherwise (fresh CI). */
|
|
459
|
+
function commitExport(dir, message) {
|
|
460
|
+
if (!fs.existsSync(path.join(dir, ".git"))) {
|
|
461
|
+
const init = gitIn(dir, ["init", "-q"]);
|
|
462
|
+
if (init.status !== 0)
|
|
463
|
+
throw new Error(`git init failed: ${init.stderr.trim()}`);
|
|
464
|
+
}
|
|
465
|
+
const identity = gitIn(dir, ["config", "user.email"]).stdout.trim()
|
|
466
|
+
? []
|
|
467
|
+
: ["-c", "user.name=Codiedev", "-c", "user.email=packs@codiedev.local"];
|
|
468
|
+
const add = gitIn(dir, ["add", "-A"]);
|
|
469
|
+
if (add.status !== 0)
|
|
470
|
+
throw new Error(`git add failed: ${add.stderr.trim()}`);
|
|
471
|
+
if (gitIn(dir, ["diff", "--cached", "--quiet"]).status === 0)
|
|
472
|
+
return "nothing to commit";
|
|
473
|
+
const commit = gitIn(dir, [...identity, "commit", "-q", "-m", message]);
|
|
474
|
+
if (commit.status !== 0)
|
|
475
|
+
throw new Error(`git commit failed: ${commit.stderr.trim()}`);
|
|
476
|
+
return gitIn(dir, ["rev-parse", "--short", "HEAD"]).stdout.trim();
|
|
477
|
+
}
|
|
478
|
+
function readMarketplaceEntries(dir) {
|
|
479
|
+
try {
|
|
480
|
+
const doc = JSON.parse(fs.readFileSync((0, paths_1.marketplacePath)(dir), "utf8"));
|
|
481
|
+
if (!Array.isArray(doc?.plugins))
|
|
482
|
+
return [];
|
|
483
|
+
return doc.plugins.flatMap((p) => {
|
|
484
|
+
const version = Number(String(p?.version ?? "").split(".")[0]);
|
|
485
|
+
return (0, manifest_2.isSafeSegment)(p?.name) && Number.isInteger(version)
|
|
486
|
+
? [{ slug: p.name, description: typeof p.description === "string" ? p.description : undefined, version }]
|
|
487
|
+
: [];
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/** Write a CI-committable copy of the pack: `<out>/plugins/<slug>/…` plus
|
|
495
|
+
* `<out>/.claude-plugin/marketplace.json` (existing entries for other packs
|
|
496
|
+
* in the same folder are kept). */
|
|
497
|
+
function exportPluginFolder(out, manifest) {
|
|
498
|
+
fs.mkdirSync(out, { recursive: true });
|
|
499
|
+
const dir = (0, store_1.writePluginDir)(out, manifest);
|
|
500
|
+
const entries = readMarketplaceEntries(out).filter((e) => e.slug !== manifest.slug);
|
|
501
|
+
entries.push({ slug: manifest.slug, description: manifest.description || manifest.name, version: manifest.version });
|
|
502
|
+
(0, store_1.writeMarketplace)(out, entries);
|
|
503
|
+
return dir;
|
|
504
|
+
}
|
|
505
|
+
async function runExport(config, args) {
|
|
506
|
+
if (!args.slug)
|
|
507
|
+
throw new Error("Usage: codiedev pack export <slug> --out <dir> [--git] [--manifest]");
|
|
508
|
+
if (!args.out)
|
|
509
|
+
throw new Error("--out <dir> is required");
|
|
510
|
+
if (!(0, manifest_2.isSafeSegment)(args.slug))
|
|
511
|
+
throw new Error(`Not a pack slug: ${args.slug}`);
|
|
512
|
+
const out = path.resolve(args.out);
|
|
513
|
+
let written;
|
|
514
|
+
let message;
|
|
515
|
+
if (args.manifest) {
|
|
516
|
+
const res = await (0, shared_1.apiRequest)("GET", "/api/cli/packs/export", { config, query: { slug: args.slug } })
|
|
517
|
+
.catch((error) => {
|
|
518
|
+
if (isNotFound(error))
|
|
519
|
+
throw new Error(`Pack not found or not assigned to your team: ${args.slug}`);
|
|
520
|
+
throw error;
|
|
521
|
+
});
|
|
522
|
+
if (typeof res.markdown !== "string")
|
|
523
|
+
throw new Error("Server returned no markdown");
|
|
524
|
+
fs.mkdirSync(out, { recursive: true });
|
|
525
|
+
// <slug>.pack.md so several packs can share one folder.
|
|
526
|
+
written = path.join(out, `${args.slug}.pack.md`);
|
|
527
|
+
(0, store_1.writeFileAtomic)(written, res.markdown.endsWith("\n") ? res.markdown : `${res.markdown}\n`);
|
|
528
|
+
message = `codiedev pack export ${args.slug} (${args.slug}.pack.md)`;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
let manifest;
|
|
532
|
+
try {
|
|
533
|
+
manifest = await fetchManifest(config, args.slug);
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
if (isNotFound(error))
|
|
537
|
+
throw new Error(`Pack not found or not assigned to your team: ${args.slug}`);
|
|
538
|
+
if (error instanceof manifest_1.ManifestError)
|
|
539
|
+
throw new Error(`Refusing to export ${args.slug}: ${error.message}`);
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
written = exportPluginFolder(out, manifest);
|
|
543
|
+
message = `codiedev pack export ${manifest.slug} v${manifest.version}`;
|
|
544
|
+
}
|
|
545
|
+
const commit = args.git ? commitExport(out, message) : undefined;
|
|
546
|
+
if (args.json)
|
|
547
|
+
console.log(JSON.stringify({ written, ...(commit ? { commit } : {}) }));
|
|
548
|
+
else {
|
|
549
|
+
console.log(`✓ Exported ${args.slug} → ${written}`);
|
|
550
|
+
if (!args.manifest)
|
|
551
|
+
console.log(` marketplace: ${(0, paths_1.marketplacePath)(out)}`);
|
|
552
|
+
if (commit)
|
|
553
|
+
console.log(` git: ${commit}`);
|
|
554
|
+
}
|
|
555
|
+
return { written, ...(commit ? { commit } : {}) };
|
|
556
|
+
}
|
|
557
|
+
async function runApply(config, args) {
|
|
558
|
+
if (!args.slug)
|
|
559
|
+
throw new Error("Usage: codiedev pack apply <pack.md>");
|
|
560
|
+
const file = path.resolve(args.slug);
|
|
561
|
+
let markdown;
|
|
562
|
+
try {
|
|
563
|
+
markdown = fs.readFileSync(file, "utf8");
|
|
564
|
+
}
|
|
565
|
+
catch {
|
|
566
|
+
throw new Error(`Can't read ${file}`);
|
|
567
|
+
}
|
|
568
|
+
if (!markdown.trim())
|
|
569
|
+
throw new Error(`${file} is empty`);
|
|
570
|
+
if (args.dryRun) {
|
|
571
|
+
console.log(`Dry run — would apply ${file} (${Buffer.byteLength(markdown)} bytes).`);
|
|
572
|
+
return {};
|
|
573
|
+
}
|
|
574
|
+
let res;
|
|
575
|
+
try {
|
|
576
|
+
res = await (0, shared_1.apiRequest)("POST", "/api/cli/packs/apply", { config, body: { markdown } });
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
// The server reports pack.md problems with line numbers; show them as-is.
|
|
580
|
+
// The server's 400 is already "pack.md line N: …" — print it verbatim.
|
|
581
|
+
const e = error;
|
|
582
|
+
if (e.status === 403)
|
|
583
|
+
throw new Error(`You can't edit this pack (${e.message})`);
|
|
584
|
+
throw new Error(e.message);
|
|
585
|
+
}
|
|
586
|
+
if (args.json)
|
|
587
|
+
console.log(JSON.stringify(res));
|
|
588
|
+
else {
|
|
589
|
+
const slug = typeof res.slug === "string" ? res.slug : "pack";
|
|
590
|
+
const verb = res.created === true ? "Created" : res.changed === false ? "No changes to" : "Updated";
|
|
591
|
+
const version = typeof res.version === "number" ? ` v${res.version}` : "";
|
|
592
|
+
console.log(`✓ ${verb} ${slug}${version} from ${path.basename(file)}`);
|
|
593
|
+
if (Array.isArray(res.warnings)) {
|
|
594
|
+
for (const w of res.warnings) {
|
|
595
|
+
const text = typeof w === "string" ? w : typeof w?.message === "string" ? `${w.agent ? `${w.agent}: ` : ""}${w.message}` : JSON.stringify(w);
|
|
596
|
+
console.log(` ⚠ ${text}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return res;
|
|
601
|
+
}
|
|
602
|
+
exports.PACK_HELP = USAGE;
|
package/dist/commands/pull.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.runPull = runPull;
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const shared_1 = require("./shared");
|
|
41
|
+
const skillMarker_1 = require("../skillMarker");
|
|
41
42
|
function parseArgs(args) {
|
|
42
43
|
let key;
|
|
43
44
|
let version;
|
|
@@ -104,6 +105,10 @@ async function runPull(args) {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
const a = res.artifact;
|
|
108
|
+
// A skill carries its identity onto disk so its runs attribute exactly
|
|
109
|
+
// (see skillMarker.ts). Docs are written verbatim.
|
|
110
|
+
const isSkill = a.kind ? a.kind === "skill" : (0, skillMarker_1.looksLikeSkill)(a.markdown);
|
|
111
|
+
const markdown = isSkill ? (0, skillMarker_1.stampSkillMarker)(a.markdown, a._id) : a.markdown;
|
|
107
112
|
(0, shared_1.recordAgentEvent)(config, {
|
|
108
113
|
tool: "codiedev_pull",
|
|
109
114
|
ok: true,
|
|
@@ -111,7 +116,7 @@ async function runPull(args) {
|
|
|
111
116
|
});
|
|
112
117
|
if (out) {
|
|
113
118
|
const absolute = path.isAbsolute(out) ? out : path.resolve(process.cwd(), out);
|
|
114
|
-
fs.writeFileSync(absolute,
|
|
119
|
+
fs.writeFileSync(absolute, markdown, "utf8");
|
|
115
120
|
console.log(`✓ Pulled ${a.key} v${a.version ?? 1} (${a.type}) → ${absolute}`);
|
|
116
121
|
console.log(` title: ${a.title}`);
|
|
117
122
|
console.log(` lifecycle: ${a.lifecycle ?? "draft"}`);
|
|
@@ -119,8 +124,8 @@ async function runPull(args) {
|
|
|
119
124
|
}
|
|
120
125
|
else {
|
|
121
126
|
// Stream to stdout — preserves shell pipes like `codiedev pull spec-214.md | less`.
|
|
122
|
-
process.stdout.write(
|
|
123
|
-
if (!
|
|
127
|
+
process.stdout.write(markdown);
|
|
128
|
+
if (!markdown.endsWith("\n"))
|
|
124
129
|
process.stdout.write("\n");
|
|
125
130
|
}
|
|
126
131
|
}
|
package/dist/commands/push.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.parseArgs = parseArgs;
|
|
|
37
37
|
exports.pushFile = pushFile;
|
|
38
38
|
exports.runPush = runPush;
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
|
+
const skillMarker_1 = require("../skillMarker");
|
|
40
41
|
const path = __importStar(require("path"));
|
|
41
42
|
const shared = __importStar(require("./shared"));
|
|
42
43
|
const shared_1 = require("./shared");
|
|
@@ -120,7 +121,8 @@ function parseArgs(args) {
|
|
|
120
121
|
return { file, type, kind, tags, force, as, private: priv };
|
|
121
122
|
}
|
|
122
123
|
async function pushFile(config, opts) {
|
|
123
|
-
|
|
124
|
+
// Never upload another artifact's identity marker (skillMarker.ts).
|
|
125
|
+
const markdown = (0, skillMarker_1.stripSkillMarker)(fs.readFileSync(opts.absPath, "utf8"));
|
|
124
126
|
const gitRemoteUrl = (0, utils_1.getGitRemoteUrl)(process.cwd()) ?? undefined;
|
|
125
127
|
return shared.apiRequest("POST", "/api/cli/push", {
|
|
126
128
|
config,
|