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.
Files changed (155) hide show
  1. package/README.md +11 -5
  2. package/assets/opencode-plugin.mjs +6 -0
  3. package/assets/session-key.mjs +48 -0
  4. package/assets/tui-brand-plugin.tsx +73 -0
  5. package/dist/backup/cache.js +1 -5
  6. package/dist/backup/match.js +0 -6
  7. package/dist/backup/run.js +3 -35
  8. package/dist/captureExclusions.js +0 -12
  9. package/dist/captureLog.js +0 -28
  10. package/dist/captureReadiness.js +0 -58
  11. package/dist/captureSignals.js +184 -0
  12. package/dist/cli.js +135 -72
  13. package/dist/cliSession.js +456 -0
  14. package/dist/codexHookTrust.js +125 -6
  15. package/dist/codexLimitScan.js +359 -0
  16. package/dist/codie.js +77 -130
  17. package/dist/codieBrandInstall.js +80 -0
  18. package/dist/codieLauncher.js +76 -79
  19. package/dist/codieRuntime.js +19 -23
  20. package/dist/codieRuntimeInstall.js +108 -23
  21. package/dist/codieUsage.js +181 -0
  22. package/dist/commands/agentTemplates.generated.js +136 -0
  23. package/dist/commands/agents.js +1101 -0
  24. package/dist/commands/agentsSdk.generated.js +7 -0
  25. package/dist/commands/ask.js +0 -3
  26. package/dist/commands/capture.js +0 -2
  27. package/dist/commands/createTicket.js +0 -10
  28. package/dist/commands/doctor.js +53 -120
  29. package/dist/commands/evals.js +187 -0
  30. package/dist/commands/inbox.js +0 -2
  31. package/dist/commands/library.js +0 -3
  32. package/dist/commands/pack.js +573 -0
  33. package/dist/commands/ping.js +0 -2
  34. package/dist/commands/pull.js +6 -10
  35. package/dist/commands/push.js +2 -12
  36. package/dist/commands/search.js +0 -4
  37. package/dist/commands/send.js +0 -5
  38. package/dist/commands/share.js +0 -4
  39. package/dist/commands/shared.js +0 -35
  40. package/dist/commands/traces.js +344 -0
  41. package/dist/commands/tracesLabel.js +348 -0
  42. package/dist/configDir.js +18 -11
  43. package/dist/connect.js +55 -118
  44. package/dist/connectFlow.js +35 -9
  45. package/dist/copilotHooks.js +391 -0
  46. package/dist/copilotInstallation.js +64 -0
  47. package/dist/cursorHooks.js +38 -30
  48. package/dist/cursorLifecycle.js +109 -0
  49. package/dist/detection.js +0 -30
  50. package/dist/harnessDirs.js +77 -0
  51. package/dist/hook.js +256 -445
  52. package/dist/inboxFormat.js +0 -24
  53. package/dist/launchContext.js +150 -0
  54. package/dist/launcher.js +168 -0
  55. package/dist/limitEvent.js +44 -13
  56. package/dist/limitEventReporter.js +1 -1
  57. package/dist/managedPi.js +256 -0
  58. package/dist/mcp.js +5 -384
  59. package/dist/nativeSteering.js +24 -0
  60. package/dist/packs/codie.js +182 -0
  61. package/dist/packs/harnesses.js +411 -0
  62. package/dist/packs/hookGate.js +256 -0
  63. package/dist/packs/install.js +286 -0
  64. package/dist/packs/manifest.js +126 -0
  65. package/dist/packs/paths.js +81 -0
  66. package/dist/packs/shim.js +214 -0
  67. package/dist/packs/store.js +155 -0
  68. package/dist/packs/sync.js +170 -0
  69. package/dist/piHooks.js +216 -0
  70. package/dist/piRuntime.js +120 -0
  71. package/dist/repoResolver.js +0 -19
  72. package/dist/secret.js +0 -3
  73. package/dist/selfUpdate.js +376 -0
  74. package/dist/skillMarker.js +23 -0
  75. package/dist/subagents.js +135 -0
  76. package/dist/teamSkillContext.js +10 -0
  77. package/dist/transcriptModels.js +205 -0
  78. package/dist/upgradeNudge.js +0 -30
  79. package/dist/utils.js +502 -828
  80. package/dist/version.js +0 -10
  81. package/dist/versionCheck.js +0 -33
  82. package/package.json +8 -9
  83. package/dist/backup/cache.d.ts +0 -14
  84. package/dist/backup/match.d.ts +0 -2
  85. package/dist/backup/run.d.ts +0 -25
  86. package/dist/captureExclusions.d.ts +0 -30
  87. package/dist/captureLog.d.ts +0 -20
  88. package/dist/captureReadiness.d.ts +0 -65
  89. package/dist/cli.d.ts +0 -16
  90. package/dist/codexHookTrust.d.ts +0 -34
  91. package/dist/codie.d.ts +0 -2
  92. package/dist/codieLauncher.d.ts +0 -53
  93. package/dist/codieRuntime.d.ts +0 -29
  94. package/dist/codieRuntimeInstall.d.ts +0 -28
  95. package/dist/codieStartup.d.ts +0 -26
  96. package/dist/codieStartup.js +0 -30
  97. package/dist/commands/ask.d.ts +0 -1
  98. package/dist/commands/capture.d.ts +0 -14
  99. package/dist/commands/createTicket.d.ts +0 -27
  100. package/dist/commands/delete.d.ts +0 -1
  101. package/dist/commands/doctor.d.ts +0 -1
  102. package/dist/commands/inbox.d.ts +0 -3
  103. package/dist/commands/library.d.ts +0 -2
  104. package/dist/commands/note.d.ts +0 -1
  105. package/dist/commands/ping.d.ts +0 -1
  106. package/dist/commands/promote.d.ts +0 -1
  107. package/dist/commands/promote.js +0 -53
  108. package/dist/commands/pull.d.ts +0 -2
  109. package/dist/commands/push.d.ts +0 -33
  110. package/dist/commands/react.d.ts +0 -2
  111. package/dist/commands/react.js +0 -40
  112. package/dist/commands/reverseTicket.d.ts +0 -2
  113. package/dist/commands/reverseTicket.js +0 -477
  114. package/dist/commands/search.d.ts +0 -2
  115. package/dist/commands/security.d.ts +0 -1
  116. package/dist/commands/security.js +0 -156
  117. package/dist/commands/send.d.ts +0 -2
  118. package/dist/commands/share.d.ts +0 -2
  119. package/dist/commands/shared.d.ts +0 -64
  120. package/dist/configDir.d.ts +0 -1
  121. package/dist/connect.d.ts +0 -1
  122. package/dist/connectFlow.d.ts +0 -13
  123. package/dist/cursorHooks.d.ts +0 -23
  124. package/dist/detection.d.ts +0 -30
  125. package/dist/hook.d.ts +0 -6
  126. package/dist/inboxFormat.d.ts +0 -38
  127. package/dist/limitEvent.d.ts +0 -16
  128. package/dist/limitEventReporter.d.ts +0 -21
  129. package/dist/mcp.d.ts +0 -12
  130. package/dist/prUrl.d.ts +0 -9
  131. package/dist/prUrl.js +0 -33
  132. package/dist/repoResolver.d.ts +0 -17
  133. package/dist/secret.d.ts +0 -1
  134. package/dist/security.d.ts +0 -102
  135. package/dist/security.js +0 -437
  136. package/dist/securityDeepScan.d.ts +0 -41
  137. package/dist/securityDeepScan.js +0 -248
  138. package/dist/securityDoctor.d.ts +0 -32
  139. package/dist/securityDoctor.js +0 -63
  140. package/dist/securityFeature.d.ts +0 -5
  141. package/dist/securityFeature.js +0 -7
  142. package/dist/securityInstall.d.ts +0 -63
  143. package/dist/securityInstall.js +0 -239
  144. package/dist/securityInventory.d.ts +0 -54
  145. package/dist/securityInventory.js +0 -556
  146. package/dist/securityPilot.d.ts +0 -19
  147. package/dist/securityPilot.js +0 -61
  148. package/dist/securityPolicy.d.ts +0 -33
  149. package/dist/securityPolicy.js +0 -188
  150. package/dist/securitySession.d.ts +0 -6
  151. package/dist/securitySession.js +0 -72
  152. package/dist/upgradeNudge.d.ts +0 -25
  153. package/dist/utils.d.ts +0 -342
  154. package/dist/version.d.ts +0 -1
  155. package/dist/versionCheck.d.ts +0 -68
@@ -0,0 +1,573 @@
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
+ exports.DEFAULT_BACKEND_URL = "https://cli.codiedev.com";
63
+ function resolvePackConfig(env = process.env) {
64
+ const token = env.CODIEDEV_TOKEN?.trim();
65
+ if (token) {
66
+ return {
67
+ token,
68
+ backendUrl: env.CODIEDEV_URL?.trim() || exports.DEFAULT_BACKEND_URL,
69
+ companyId: "",
70
+ companyName: "",
71
+ repos: [],
72
+ lastSynced: "",
73
+ };
74
+ }
75
+ const config = (0, utils_1.readConfig)();
76
+ return config ? (0, shared_1.applyConfigEnvOverrides)(config) : null;
77
+ }
78
+ function ciHarnesses(args, env, home) {
79
+ if (args.harnesses?.length)
80
+ return args.harnesses;
81
+ if ((0, harnesses_2.onPath)("claude", env))
82
+ return ["claude"];
83
+ const root = (0, paths_1.packsRoot)(env, home);
84
+ const found = harnesses_2.ADAPTERS.filter((a) => a.name !== "codie" && a.detect({ root, env, home: home ?? os.homedir(), run: () => ({ status: 0, stdout: "", stderr: "" }) }))
85
+ .map((a) => a.name);
86
+ throw new Error(`CI mode needs --harness: claude isn't on PATH. Detected: ${found.length ? found.join(", ") : "none"}. ` +
87
+ `Install Claude Code (npm i -g @anthropic-ai/claude-code) or pass e.g. --harness ${found[0] ?? "codex"}.`);
88
+ }
89
+ const USAGE = `Usage:
90
+ codiedev pack list Packs assigned to your team
91
+ codiedev pack install [slug] [--harness claude,codex,copilot,cursor,codie,pi] [--dry-run] [--json] [--quiet]
92
+ Install one pack, or every pack that applies
93
+ codiedev pack status [--json] Installed packs and whether they're up to date
94
+ codiedev pack export <slug> --out <dir> [--git] [--manifest]
95
+ Write the pack as a Claude-format plugin folder plus
96
+ .claude-plugin/marketplace.json (commit it for CI).
97
+ --git inits the folder and commits; --manifest writes
98
+ an editable <slug>.pack.md instead
99
+ codiedev pack apply <pack.md> Create or update a pack from a pack.md
100
+
101
+ Cursor: packs are copied to ~/.cursor/plugins/local/codiedev-<slug>/. A Cursor
102
+ team admin can disable "user local plugin imports"; then Cursor ignores them.
103
+
104
+ Auto-update: session starts run \`codiedev pack install --quiet\` in the background
105
+ about once an hour. Opt out with CODIEDEV_PACK_AUTOSYNC=0.
106
+
107
+ Hooks for specific skills only: codiedev hook-gate --skills a,b --cmd-b64 <base64url of the command>
108
+ (the pack editor writes this; the command runs via /bin/sh, or cmd.exe on Windows)
109
+
110
+ CI: set CODIEDEV_TOKEN to a pack CI key (cdvci_…, from the pack's page in the portal).
111
+ No \`codiedev connect\` or config file is needed. CI mode (a cdvci_ key, or CI=true)
112
+ never auto-syncs, reports or prunes, installs the one pack the key can see, defaults
113
+ --harness to claude when it's on PATH, and exits non-zero unless every requested tool
114
+ got the pack. Skills from a pack are /<pack-slug>:<skill> in Claude Code.
115
+
116
+ GitLab CI (.gitlab-ci.yml; add CODIEDEV_TOKEN and ANTHROPIC_API_KEY as masked
117
+ CI/CD variables):
118
+ pack-scan:
119
+ image: node:22
120
+ script:
121
+ - npm i -g codiedev @anthropic-ai/claude-code
122
+ - codiedev pack install --harness claude
123
+ - claude -p "/platform-starter:healthcare-scan" --output-format text
124
+
125
+ GitHub Actions (repository secrets CODIEDEV_TOKEN and ANTHROPIC_API_KEY):
126
+ - uses: actions/setup-node@v4
127
+ with: { node-version: 22 }
128
+ - run: npm i -g codiedev @anthropic-ai/claude-code
129
+ - run: codiedev pack install --harness claude
130
+ env:
131
+ CODIEDEV_TOKEN: \${{ secrets.CODIEDEV_TOKEN }}
132
+ - run: claude -p "/platform-starter:healthcare-scan" --output-format text
133
+ env:
134
+ ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
135
+
136
+ Or vendor the plugin into the repo instead of installing at build time:
137
+ codiedev pack export <slug> --out .codiedev/packs`;
138
+ function parsePackArgs(args) {
139
+ const out = { dryRun: false, json: false };
140
+ const positional = [];
141
+ for (let i = 0; i < args.length; i++) {
142
+ const a = args[i];
143
+ if (a === "--dry-run")
144
+ out.dryRun = true;
145
+ else if (a === "--json")
146
+ out.json = true;
147
+ else if (a === "--quiet" || a === "-q")
148
+ out.quiet = true;
149
+ else if (a === "--no-prune")
150
+ out.noPrune = true;
151
+ else if (a === "--git")
152
+ out.git = true;
153
+ else if (a === "--manifest")
154
+ out.manifest = true;
155
+ else if (a === "--out" || a === "-o") {
156
+ if (i + 1 >= args.length)
157
+ throw new Error("--out needs a directory");
158
+ out.out = args[++i];
159
+ }
160
+ else if (a.startsWith("--out="))
161
+ out.out = a.slice("--out=".length);
162
+ else if (a === "--harness" || a === "--harnesses") {
163
+ if (i + 1 >= args.length)
164
+ throw new Error("--harness needs a value");
165
+ out.harnesses = (0, harnesses_1.parseHarnessList)(args[++i]);
166
+ }
167
+ else if (a.startsWith("--harness="))
168
+ out.harnesses = (0, harnesses_1.parseHarnessList)(a.slice("--harness=".length));
169
+ else if (a.startsWith("--"))
170
+ throw new Error(`Unknown flag: ${a}`);
171
+ else
172
+ positional.push(a);
173
+ }
174
+ out.sub = positional[0];
175
+ out.slug = positional[1];
176
+ return out;
177
+ }
178
+ async function listPacks(config) {
179
+ const res = await (0, shared_1.apiRequest)("GET", "/api/cli/packs", { config });
180
+ return Array.isArray(res.packs) ? res.packs : [];
181
+ }
182
+ async function fetchManifest(config, slug) {
183
+ const raw = await (0, shared_1.apiRequest)("GET", "/api/cli/packs/manifest", { config, query: { slug } });
184
+ return (0, manifest_1.parseManifest)(raw);
185
+ }
186
+ async function runList(config, json, overrides = {}) {
187
+ const packs = await listPacks(config);
188
+ const registry = (0, store_1.readRegistry)((0, paths_1.packsRoot)(overrides.env, overrides.home));
189
+ if (json) {
190
+ console.log(JSON.stringify({ packs: packs.map((p) => ({ ...p, installed: registry.packs[p.slug] ?? null })) }, null, 2));
191
+ return;
192
+ }
193
+ if (packs.length === 0) {
194
+ console.log("No starter packs are assigned to your team yet.");
195
+ return;
196
+ }
197
+ for (const p of packs) {
198
+ const local = registry.packs[p.slug];
199
+ const state = local ? (local.version === p.version ? "installed" : `installed v${local.version}, v${p.version} available`) : "not installed";
200
+ const counts = [
201
+ `${p.itemCount ?? 0} skill${p.itemCount === 1 ? "" : "s"}`,
202
+ `${p.agentCount ?? 0} agent${p.agentCount === 1 ? "" : "s"}`,
203
+ ...(p.hasHooks ? ["hooks"] : []),
204
+ ].join(", ");
205
+ const channel = (0, manifest_2.cleanLabel)(p.channel, 16);
206
+ console.log(`${p.slug} v${p.version}${channel ? ` [${channel}]` : ""} ${(0, manifest_2.displayName)(p)} (${counts}) — ${state}`);
207
+ if (p.description)
208
+ console.log(` ${p.description}`);
209
+ }
210
+ console.log("\nInstall with: codiedev pack install <slug> (no slug installs all)");
211
+ }
212
+ function isNotFound(error) {
213
+ return error?.status === 404;
214
+ }
215
+ async function runInstall(config, args, overrides = {}) {
216
+ const root = (0, paths_1.packsRoot)(overrides.env, overrides.home);
217
+ const registry = (0, store_1.readRegistry)(root);
218
+ const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
219
+ const harnesses = ci && !args.dryRun ? ciHarnesses(args, overrides.env ?? process.env, overrides.home) : args.harnesses;
220
+ const listed = args.slug ? [args.slug] : (await listPacks(config)).map((p) => p.slug);
221
+ const slugs = args.slug || ci ? listed : [...new Set([...listed, ...Object.keys(registry.packs)])];
222
+ const manifests = [];
223
+ const revoked = [];
224
+ for (const slug of slugs) {
225
+ try {
226
+ manifests.push(await fetchManifest(config, slug));
227
+ }
228
+ catch (error) {
229
+ if (error instanceof manifest_1.ManifestError)
230
+ throw new Error(`Refusing to install ${slug}: ${error.message}`);
231
+ if (!isNotFound(error))
232
+ throw error;
233
+ if (registry.packs[slug] && !ci)
234
+ revoked.push(slug);
235
+ else if (args.slug)
236
+ throw new Error(`Pack not found or not assigned to your team: ${slug}`);
237
+ }
238
+ }
239
+ let installed = manifests.length
240
+ ? (0, install_1.installPacks)({ ...overrides, manifests, harnesses, dryRun: args.dryRun })
241
+ : [];
242
+ let removed = [];
243
+ if (!args.dryRun && !ci) {
244
+ const lateRevoked = await reportInstalled(config, installed);
245
+ installed = installed.filter((p) => !lateRevoked.includes(p.slug));
246
+ removed = await removePacks(config, [...revoked, ...lateRevoked], overrides);
247
+ }
248
+ const run = { installed, removed, wouldRemove: args.dryRun ? revoked : [] };
249
+ if (!args.dryRun && !ci) {
250
+ const previous = (0, sync_1.readStatusCache)(root);
251
+ const done = new Set(installed.map((p) => p.slug));
252
+ (0, sync_1.writeStatusCache)(root, {
253
+ checkedAt: Date.now(),
254
+ available: args.slug ? Math.max(previous?.available ?? 0, installed.length) : installed.length,
255
+ outdated: args.slug ? (previous?.outdated ?? []).filter((o) => !done.has(o.slug) && registry.packs[o.slug]) : [],
256
+ });
257
+ }
258
+ if (args.json) {
259
+ console.log(JSON.stringify({ dryRun: args.dryRun, packs: installed, removed, wouldRemove: run.wouldRemove }, null, 2));
260
+ }
261
+ else {
262
+ if (args.dryRun)
263
+ console.log("Dry run — nothing was written.\n");
264
+ if (installed.length === 0 && removed.length === 0 && revoked.length === 0) {
265
+ console.log("No starter packs are assigned to your team yet.");
266
+ }
267
+ for (const line of (0, install_1.formatResultLines)(installed))
268
+ console.log(line);
269
+ if (args.dryRun) {
270
+ for (const pack of installed)
271
+ console.log(` files: ${pack.files.join(", ")}`);
272
+ for (const slug of revoked)
273
+ console.log(`${slug} would be removed (no longer assigned to your team)`);
274
+ }
275
+ for (const line of (0, install_1.formatUninstallLines)(removed))
276
+ console.log(line);
277
+ }
278
+ const failed = installed.some((p) => p.results.some((r) => r.status === "failed") && !p.results.some((r) => r.status === "ok"));
279
+ const ciFailed = ci && !args.dryRun && (installed.length === 0 || installed.some((p) => p.results.some((r) => r.status !== "ok")));
280
+ if (ci && !args.dryRun && installed.length === 0)
281
+ console.error("No pack is visible to this key.");
282
+ if (failed || ciFailed || (args.slug && removed.length + revoked.length > 0))
283
+ process.exitCode = 1;
284
+ return run;
285
+ }
286
+ async function reportInstalled(config, results) {
287
+ const revoked = [];
288
+ for (const pack of results) {
289
+ try {
290
+ await (0, shared_1.apiRequest)("POST", "/api/cli/packs/installed", {
291
+ config,
292
+ body: { slug: pack.slug, version: pack.version, contentHash: pack.contentHash, harnesses: pack.harnesses },
293
+ });
294
+ }
295
+ catch (error) {
296
+ if (isNotFound(error))
297
+ revoked.push(pack.slug);
298
+ else
299
+ console.error(`warning: couldn't report the ${pack.slug} install to Codiedev (${error.message})`);
300
+ }
301
+ }
302
+ return revoked;
303
+ }
304
+ async function removePacks(config, slugs, overrides) {
305
+ if (slugs.length === 0)
306
+ return [];
307
+ const removed = (0, install_1.uninstallPacks)({ slugs: [...new Set(slugs)], env: overrides.env, home: overrides.home, run: overrides.run });
308
+ for (const pack of removed) {
309
+ try {
310
+ await (0, shared_1.apiRequest)("POST", "/api/cli/packs/uninstalled", { config, body: { slug: pack.slug } });
311
+ }
312
+ catch (error) {
313
+ console.error(`warning: couldn't report the ${pack.slug} removal to Codiedev (${error.message})`);
314
+ }
315
+ }
316
+ return removed;
317
+ }
318
+ async function runStatus(config, json, overrides = {}, options = {}) {
319
+ const root = (0, paths_1.packsRoot)(overrides.env, overrides.home);
320
+ const registry = (0, store_1.readRegistry)(root);
321
+ let available = null;
322
+ try {
323
+ available = await listPacks(config);
324
+ }
325
+ catch {
326
+ available = null;
327
+ }
328
+ const rows = [];
329
+ const revoked = [];
330
+ for (const [slug, entry] of Object.entries(registry.packs)) {
331
+ let upToDate = null;
332
+ let latestVersion = null;
333
+ let latestChannel = null;
334
+ if (available) {
335
+ try {
336
+ const m = await fetchManifest(config, slug);
337
+ upToDate = m.contentHash === entry.contentHash;
338
+ latestVersion = m.version;
339
+ latestChannel = m.channel ?? null;
340
+ }
341
+ catch (error) {
342
+ if (isNotFound(error)) {
343
+ revoked.push(slug);
344
+ continue;
345
+ }
346
+ upToDate = null;
347
+ }
348
+ }
349
+ rows.push({ slug, ...entry, upToDate, latestVersion, latestChannel });
350
+ }
351
+ const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
352
+ const removed = options.prune === false || ci ? [] : await removePacks(config, revoked, overrides);
353
+ if (available && !ci) {
354
+ (0, sync_1.writeStatusCache)(root, {
355
+ checkedAt: Date.now(),
356
+ available: available.length,
357
+ outdated: rows
358
+ .filter((r) => r.upToDate === false)
359
+ .map((r) => ({ slug: String(r.slug), ...(typeof r.name === "string" ? { name: r.name } : {}) })),
360
+ });
361
+ }
362
+ const notInstalled = (available ?? []).filter((p) => !registry.packs[p.slug]).map((p) => p.slug);
363
+ const result = { installed: rows, removed, notInstalled, offline: available === null };
364
+ if (json) {
365
+ console.log(JSON.stringify(result, null, 2));
366
+ return result;
367
+ }
368
+ if (rows.length === 0 && removed.length === 0)
369
+ console.log("No starter packs installed.");
370
+ for (const row of rows) {
371
+ const state = row.upToDate === true ? "up to date" : row.upToDate === false ? `outdated (v${row.latestVersion} available)` : "unknown (offline)";
372
+ const channel = typeof row.latestChannel === "string" ? row.latestChannel : typeof row.channel === "string" ? row.channel : undefined;
373
+ 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)}`);
374
+ }
375
+ for (const line of (0, install_1.formatUninstallLines)(removed))
376
+ console.log(line);
377
+ if (notInstalled.length)
378
+ console.log(`\nAvailable, not installed: ${notInstalled.join(", ")}`);
379
+ if (rows.some((r) => r.upToDate === false) || notInstalled.length)
380
+ console.log("Run `codiedev pack install` to sync.");
381
+ return result;
382
+ }
383
+ async function runPack(args) {
384
+ let parsed;
385
+ try {
386
+ parsed = parsePackArgs(args);
387
+ }
388
+ catch (error) {
389
+ console.error(error.message);
390
+ console.error(USAGE);
391
+ process.exit(1);
392
+ }
393
+ if (!parsed.sub || !["list", "ls", "install", "status", "export", "apply"].includes(parsed.sub)) {
394
+ console.error(USAGE);
395
+ process.exit(parsed.sub ? 1 : 0);
396
+ }
397
+ const config = resolvePackConfig() ?? (0, shared_1.requireConfig)();
398
+ const ci = (0, sync_1.isPackCiMode)();
399
+ const restore = parsed.quiet ? silenceConsole() : () => { };
400
+ const track = ci ? (_c, _t, fn) => fn() : shared_1.withTelemetry;
401
+ try {
402
+ await track(config, `codiedev_pack_${parsed.sub === "ls" ? "list" : parsed.sub}`, async () => {
403
+ if (parsed.sub === "list" || parsed.sub === "ls")
404
+ await runList(config, parsed.json);
405
+ else if (parsed.sub === "install")
406
+ await runInstall(config, parsed);
407
+ else if (parsed.sub === "export")
408
+ await runExport(config, parsed);
409
+ else if (parsed.sub === "apply")
410
+ await runApply(config, parsed);
411
+ else
412
+ await runStatus(config, parsed.json, {}, { prune: !parsed.noPrune });
413
+ });
414
+ }
415
+ catch (error) {
416
+ console.error(`pack ${parsed.sub} failed: ${error.message}`);
417
+ restore();
418
+ process.exit(1);
419
+ }
420
+ restore();
421
+ }
422
+ function silenceConsole() {
423
+ const log = console.log;
424
+ const error = console.error;
425
+ console.log = () => { };
426
+ console.error = () => { };
427
+ return () => {
428
+ console.log = log;
429
+ console.error = error;
430
+ };
431
+ }
432
+ function gitIn(dir, args) {
433
+ const r = (0, child_process_1.spawnSync)("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
434
+ return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? (r.error?.message ?? "") };
435
+ }
436
+ function commitExport(dir, message) {
437
+ if (!fs.existsSync(path.join(dir, ".git"))) {
438
+ const init = gitIn(dir, ["init", "-q"]);
439
+ if (init.status !== 0)
440
+ throw new Error(`git init failed: ${init.stderr.trim()}`);
441
+ }
442
+ const identity = gitIn(dir, ["config", "user.email"]).stdout.trim()
443
+ ? []
444
+ : ["-c", "user.name=Codiedev", "-c", "user.email=packs@codiedev.local"];
445
+ const add = gitIn(dir, ["add", "-A"]);
446
+ if (add.status !== 0)
447
+ throw new Error(`git add failed: ${add.stderr.trim()}`);
448
+ if (gitIn(dir, ["diff", "--cached", "--quiet"]).status === 0)
449
+ return "nothing to commit";
450
+ const commit = gitIn(dir, [...identity, "commit", "-q", "-m", message]);
451
+ if (commit.status !== 0)
452
+ throw new Error(`git commit failed: ${commit.stderr.trim()}`);
453
+ return gitIn(dir, ["rev-parse", "--short", "HEAD"]).stdout.trim();
454
+ }
455
+ function readMarketplaceEntries(dir) {
456
+ try {
457
+ const doc = JSON.parse(fs.readFileSync((0, paths_1.marketplacePath)(dir), "utf8"));
458
+ if (!Array.isArray(doc?.plugins))
459
+ return [];
460
+ return doc.plugins.flatMap((p) => {
461
+ const version = Number(String(p?.version ?? "").split(".")[0]);
462
+ return (0, manifest_2.isSafeSegment)(p?.name) && Number.isInteger(version)
463
+ ? [{ slug: p.name, description: typeof p.description === "string" ? p.description : undefined, version }]
464
+ : [];
465
+ });
466
+ }
467
+ catch {
468
+ return [];
469
+ }
470
+ }
471
+ function exportPluginFolder(out, manifest) {
472
+ fs.mkdirSync(out, { recursive: true });
473
+ const dir = (0, store_1.writePluginDir)(out, manifest);
474
+ const entries = readMarketplaceEntries(out).filter((e) => e.slug !== manifest.slug);
475
+ entries.push({ slug: manifest.slug, description: manifest.description || manifest.name, version: manifest.version });
476
+ (0, store_1.writeMarketplace)(out, entries);
477
+ return dir;
478
+ }
479
+ async function runExport(config, args) {
480
+ if (!args.slug)
481
+ throw new Error("Usage: codiedev pack export <slug> --out <dir> [--git] [--manifest]");
482
+ if (!args.out)
483
+ throw new Error("--out <dir> is required");
484
+ if (!(0, manifest_2.isSafeSegment)(args.slug))
485
+ throw new Error(`Not a pack slug: ${args.slug}`);
486
+ const out = path.resolve(args.out);
487
+ let written;
488
+ let message;
489
+ if (args.manifest) {
490
+ const res = await (0, shared_1.apiRequest)("GET", "/api/cli/packs/export", { config, query: { slug: args.slug } })
491
+ .catch((error) => {
492
+ if (isNotFound(error))
493
+ throw new Error(`Pack not found or not assigned to your team: ${args.slug}`);
494
+ throw error;
495
+ });
496
+ if (typeof res.markdown !== "string")
497
+ throw new Error("Server returned no markdown");
498
+ fs.mkdirSync(out, { recursive: true });
499
+ written = path.join(out, `${args.slug}.pack.md`);
500
+ (0, store_1.writeFileAtomic)(written, res.markdown.endsWith("\n") ? res.markdown : `${res.markdown}\n`);
501
+ message = `codiedev pack export ${args.slug} (${args.slug}.pack.md)`;
502
+ }
503
+ else {
504
+ let manifest;
505
+ try {
506
+ manifest = await fetchManifest(config, args.slug);
507
+ }
508
+ catch (error) {
509
+ if (isNotFound(error))
510
+ throw new Error(`Pack not found or not assigned to your team: ${args.slug}`);
511
+ if (error instanceof manifest_1.ManifestError)
512
+ throw new Error(`Refusing to export ${args.slug}: ${error.message}`);
513
+ throw error;
514
+ }
515
+ written = exportPluginFolder(out, manifest);
516
+ message = `codiedev pack export ${manifest.slug} v${manifest.version}`;
517
+ }
518
+ const commit = args.git ? commitExport(out, message) : undefined;
519
+ if (args.json)
520
+ console.log(JSON.stringify({ written, ...(commit ? { commit } : {}) }));
521
+ else {
522
+ console.log(`✓ Exported ${args.slug} → ${written}`);
523
+ if (!args.manifest)
524
+ console.log(` marketplace: ${(0, paths_1.marketplacePath)(out)}`);
525
+ if (commit)
526
+ console.log(` git: ${commit}`);
527
+ }
528
+ return { written, ...(commit ? { commit } : {}) };
529
+ }
530
+ async function runApply(config, args) {
531
+ if (!args.slug)
532
+ throw new Error("Usage: codiedev pack apply <pack.md>");
533
+ const file = path.resolve(args.slug);
534
+ let markdown;
535
+ try {
536
+ markdown = fs.readFileSync(file, "utf8");
537
+ }
538
+ catch {
539
+ throw new Error(`Can't read ${file}`);
540
+ }
541
+ if (!markdown.trim())
542
+ throw new Error(`${file} is empty`);
543
+ if (args.dryRun) {
544
+ console.log(`Dry run — would apply ${file} (${Buffer.byteLength(markdown)} bytes).`);
545
+ return {};
546
+ }
547
+ let res;
548
+ try {
549
+ res = await (0, shared_1.apiRequest)("POST", "/api/cli/packs/apply", { config, body: { markdown } });
550
+ }
551
+ catch (error) {
552
+ const e = error;
553
+ if (e.status === 403)
554
+ throw new Error(`You can't edit this pack (${e.message})`);
555
+ throw new Error(e.message);
556
+ }
557
+ if (args.json)
558
+ console.log(JSON.stringify(res));
559
+ else {
560
+ const slug = typeof res.slug === "string" ? res.slug : "pack";
561
+ const verb = res.created === true ? "Created" : res.changed === false ? "No changes to" : "Updated";
562
+ const version = typeof res.version === "number" ? ` v${res.version}` : "";
563
+ console.log(`✓ ${verb} ${slug}${version} from ${path.basename(file)}`);
564
+ if (Array.isArray(res.warnings)) {
565
+ for (const w of res.warnings) {
566
+ const text = typeof w === "string" ? w : typeof w?.message === "string" ? `${w.agent ? `${w.agent}: ` : ""}${w.message}` : JSON.stringify(w);
567
+ console.log(` ⚠ ${text}`);
568
+ }
569
+ }
570
+ }
571
+ return res;
572
+ }
573
+ exports.PACK_HELP = USAGE;
@@ -23,8 +23,6 @@ function parseArgs(args) {
23
23
  process.exit(1);
24
24
  }
25
25
  to = positional[0];
26
- // Join remaining positionals in case the shell split the message on spaces
27
- // despite quoting (happens with some escape sequences).
28
26
  const body = (0, shared_1.stripQuotes)(positional.slice(1).join(" "));
29
27
  return { to, body, subjectKey };
30
28
  }
@@ -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;
@@ -70,10 +71,6 @@ function parseArgs(args) {
70
71
  }
71
72
  return { key, version, out };
72
73
  }
73
- // The backend stores skills under extension-bearing keys (e.g. "pr-push.md"),
74
- // and lookup is exact-match, so a bare "pr-push" 404s. Make pull forgiving: try
75
- // the key as given, then a ".md" fallback when it has no extension, so the
76
- // intuitive bare name (and the hook's suggested key) both resolve.
77
74
  function candidateKeys(key) {
78
75
  if (/\.[a-z0-9]+$/i.test(key))
79
76
  return [key];
@@ -96,14 +93,14 @@ async function runPull(args) {
96
93
  }
97
94
  catch (err) {
98
95
  const e = err;
99
- // A 404 on a non-final candidate is retryable (fall through to ".md");
100
- // anything else — or the last candidate — propagates to the handler.
101
96
  if (e.status === 404 && i < keys.length - 1)
102
97
  continue;
103
98
  throw err;
104
99
  }
105
100
  }
106
101
  const a = res.artifact;
102
+ const isSkill = a.kind ? a.kind === "skill" : (0, skillMarker_1.looksLikeSkill)(a.markdown);
103
+ const markdown = isSkill ? (0, skillMarker_1.stampSkillMarker)(a.markdown, a._id) : a.markdown;
107
104
  (0, shared_1.recordAgentEvent)(config, {
108
105
  tool: "codiedev_pull",
109
106
  ok: true,
@@ -111,16 +108,15 @@ async function runPull(args) {
111
108
  });
112
109
  if (out) {
113
110
  const absolute = path.isAbsolute(out) ? out : path.resolve(process.cwd(), out);
114
- fs.writeFileSync(absolute, a.markdown, "utf8");
111
+ fs.writeFileSync(absolute, markdown, "utf8");
115
112
  console.log(`✓ Pulled ${a.key} v${a.version ?? 1} (${a.type}) → ${absolute}`);
116
113
  console.log(` title: ${a.title}`);
117
114
  console.log(` lifecycle: ${a.lifecycle ?? "draft"}`);
118
115
  console.log(` updated: ${(0, shared_1.timeAgo)(a.updatedAt ?? a.createdAt)}`);
119
116
  }
120
117
  else {
121
- // Stream to stdout — preserves shell pipes like `codiedev pull spec-214.md | less`.
122
- process.stdout.write(a.markdown);
123
- if (!a.markdown.endsWith("\n"))
118
+ process.stdout.write(markdown);
119
+ if (!markdown.endsWith("\n"))
124
120
  process.stdout.write("\n");
125
121
  }
126
122
  }
@@ -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");
@@ -106,10 +107,6 @@ function parseArgs(args) {
106
107
  console.error(`Invalid --kind. Valid kinds: ${[...VALID_KINDS].join(", ")}`);
107
108
  process.exit(1);
108
109
  }
109
- // The override key is stored verbatim by the server (docs key off it, and a
110
- // path-y value like "specs/feature-a/plan.md" populates folderPath). Strip a
111
- // leading slash so it can't be confused with an absolute path, and reject an
112
- // empty value — but otherwise allow slashes so callers can namespace.
113
110
  if (as !== undefined) {
114
111
  as = as.replace(/^\/+/, "").trim();
115
112
  if (!as) {
@@ -120,7 +117,7 @@ function parseArgs(args) {
120
117
  return { file, type, kind, tags, force, as, private: priv };
121
118
  }
122
119
  async function pushFile(config, opts) {
123
- const markdown = fs.readFileSync(opts.absPath, "utf8");
120
+ const markdown = (0, skillMarker_1.stripSkillMarker)(fs.readFileSync(opts.absPath, "utf8"));
124
121
  const gitRemoteUrl = (0, utils_1.getGitRemoteUrl)(process.cwd()) ?? undefined;
125
122
  return shared.apiRequest("POST", "/api/cli/push", {
126
123
  config,
@@ -153,10 +150,6 @@ async function runPush(args) {
153
150
  console.error(`File is empty: ${absolute}`);
154
151
  process.exit(1);
155
152
  }
156
- // Stored key defaults to the basename (legacy behavior). `--as` overrides it
157
- // so callers can namespace generically-named files — e.g. Spec Kit emits a
158
- // `plan.md`/`spec.md` per feature, which would otherwise all collide on one
159
- // key and silently version over each other.
160
153
  const filename = as ?? path.basename(absolute);
161
154
  const start = Date.now();
162
155
  try {
@@ -177,9 +170,6 @@ async function runPush(args) {
177
170
  const tagSummary = res.tags && res.tags.length > 0 ? ` tags=[${res.tags.join(", ")}]` : "";
178
171
  console.log(`✓ Pushed ${filename} (kind=${res.kind}${tagSummary}).`);
179
172
  console.log(` Visible under: ${res.destination}`);
180
- // Echo the *resolved* key the server stored, not the local filename —
181
- // skills are keyed by their frontmatter name (so two `SKILL.md` files
182
- // don't collide), so the stored key can differ from what was pushed.
183
173
  if (res.key && res.key !== filename) {
184
174
  console.log(` Stored as: ${res.key}`);
185
175
  }