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/hook.js CHANGED
@@ -39,6 +39,7 @@ const captureSignals_1 = require("./captureSignals");
39
39
  const transcriptModels_1 = require("./transcriptModels");
40
40
  const fs = __importStar(require("fs"));
41
41
  const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
42
43
  const https = __importStar(require("https"));
43
44
  const http = __importStar(require("http"));
44
45
  const child_process_1 = require("child_process");
@@ -50,6 +51,9 @@ const codexLimitScan_1 = require("./codexLimitScan");
50
51
  const launchContext_1 = require("./launchContext");
51
52
  const selfUpdate_1 = require("./selfUpdate");
52
53
  const version_1 = require("./version");
54
+ // --- starter packs (SIG-113 phase 2): autosync, nudge ---
55
+ const sync_1 = require("./packs/sync");
56
+ // --- end starter packs ---
53
57
  const captureLog_1 = require("./captureLog");
54
58
  const subagents_1 = require("./subagents");
55
59
  const captureExclusions_1 = require("./captureExclusions");
@@ -594,6 +598,9 @@ async function main() {
594
598
  // chance to report it. Runs before runDiscover, which exits the process.
595
599
  if (process.argv[2] === "discover-codex") {
596
600
  await runCodexLimitSweep(hookInput.session_id);
601
+ // --- starter packs: Codex has no SessionStart hook; same hourly throttle ---
602
+ (0, sync_1.maybeSpawnPackSync)({ connected: Boolean((0, utils_1.readConfig)()), cli: path.join(__dirname, "cli.js") });
603
+ // --- end starter packs ---
597
604
  }
598
605
  if (process.argv[2] !== "discover-copilot")
599
606
  await runDiscover(hookInput.prompt);
@@ -613,6 +620,13 @@ async function main() {
613
620
  process.argv[2] === "session-start-cursor" ||
614
621
  process.argv[2] === "session-start-copilot" ||
615
622
  process.argv[2] === "session-start-opencode") {
623
+ // --- starter packs: hourly detached `pack install --quiet` + daily nudge ---
624
+ (0, sync_1.maybeSpawnPackSync)({ connected: Boolean((0, utils_1.readConfig)()), cli: path.join(__dirname, "cli.js") });
625
+ const packNag = process.argv[2] === "session-start-opencode" ? null : (0, sync_1.packNagLine)();
626
+ // Claude Code adds SessionStart stdout to the session context.
627
+ if (packNag && process.argv[2] === "session-start")
628
+ process.stdout.write(`${packNag}\n`);
629
+ // --- end starter packs ---
616
630
  await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
617
631
  ? "opencode"
618
632
  : process.argv[2] === "session-start-cursor"
@@ -626,7 +640,7 @@ async function main() {
626
640
  const prompt = hookInput.initialPrompt
627
641
  ?? hookInput.initial_prompt;
628
642
  const discovered = typeof prompt === "string" ? await discoverContext(prompt) : null;
629
- process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered].filter(Boolean).join("\n\n"))));
643
+ process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered, packNag].filter(Boolean).join("\n\n"))));
630
644
  }
631
645
  return;
632
646
  }
package/dist/mcp.js CHANGED
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  })();
46
46
  Object.defineProperty(exports, "__esModule", { value: true });
47
47
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
48
+ const skillMarker_1 = require("./skillMarker");
48
49
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
49
50
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
50
51
  const fs = __importStar(require("fs"));
@@ -492,7 +493,7 @@ async function handlePush(args, config) {
492
493
  }
493
494
  const res = await (0, shared_1.apiRequest)("POST", "/api/cli/push", {
494
495
  config,
495
- body: { filename, markdown, type, kind, tags, force },
496
+ body: { filename, markdown: (0, skillMarker_1.stripSkillMarker)(markdown), type, kind, tags, force },
496
497
  });
497
498
  const tagSummary = res.tags && res.tags.length > 0 ? `, tags=[${res.tags.join(", ")}]` : "";
498
499
  return {
@@ -524,7 +525,9 @@ async function handlePull(args, config) {
524
525
  `**${kindLabel}** · v${a.version ?? 1} · ${a.lifecycle ?? "draft"}${tagSuffix} · ` +
525
526
  `updated ${(0, shared_1.timeAgo)(a.updatedAt ?? a.createdAt)}\n\n---\n\n`;
526
527
  return {
527
- content: [{ type: "text", text: header + a.markdown }],
528
+ // A skill's text carries its identity marker, so a copy the agent writes
529
+ // to disk still attributes its runs exactly (skillMarker.ts).
530
+ content: [{ type: "text", text: header + (a.kind === "skill" || (!a.kind && (0, skillMarker_1.looksLikeSkill)(a.markdown)) ? (0, skillMarker_1.stampSkillMarker)(a.markdown, a._id) : a.markdown) }],
528
531
  };
529
532
  }
530
533
  async function handleDelete(args, config) {
@@ -0,0 +1,45 @@
1
+ export interface CodiePackConfig {
2
+ skillPaths: string[];
3
+ agents: Record<string, {
4
+ description: string;
5
+ prompt: string;
6
+ mode: "subagent";
7
+ model?: string;
8
+ }>;
9
+ plugins: string[];
10
+ notes: string[];
11
+ }
12
+ /** A model the account approves, as Codie's catalog lists it. */
13
+ export interface LevelCatalogModel {
14
+ id: string;
15
+ name?: string;
16
+ cost?: {
17
+ input: number;
18
+ output: number;
19
+ };
20
+ tier?: string;
21
+ level?: string;
22
+ }
23
+ /** Resolve an agent level (opus = Strongest, sonnet = Balanced, haiku =
24
+ * Fastest) to one of the account's approved models:
25
+ * 1. explicit tier/level metadata on the catalog entry, when the gateway sends it
26
+ * 2. the same model family by name (e.g. a "claude-opus-…" model), newest first
27
+ * 3. by price: Strongest = priciest, Fastest = cheapest, Balanced = in between
28
+ * Otherwise undefined → Codie's governed default. Only catalog ids are returned. */
29
+ export declare function levelToModel(level: string, catalog: LevelCatalogModel[]): {
30
+ model?: string;
31
+ how: string;
32
+ };
33
+ /** Map a Claude agent `model:` to an OpenCode model id, or undefined to let
34
+ * Codie's governed default apply. */
35
+ export declare function mapAgentModel(value: string | undefined, approved?: Set<string>, catalog?: LevelCatalogModel[]): {
36
+ model?: string;
37
+ note?: string;
38
+ };
39
+ export declare function codiePackConfig(root: string, options?: {
40
+ approvedModels?: Set<string>;
41
+ catalog?: LevelCatalogModel[];
42
+ }): CodiePackConfig;
43
+ /** Merge pack config into an OpenCode config without clobbering anything the
44
+ * user or Codie already set: existing skill paths, agents and plugins win. */
45
+ export declare function mergeCodiePackConfig(existing: Record<string, any>, packs: CodiePackConfig): Record<string, any>;
@@ -0,0 +1,197 @@
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
+ // Codie (our OpenCode launcher) installs nothing: at launch it reads
41
+ // installed.json and injects the packs into OPENCODE_CONFIG_CONTENT.
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const url_1 = require("url");
45
+ const manifest_1 = require("./manifest");
46
+ const paths_1 = require("./paths");
47
+ const shim_1 = require("./shim");
48
+ const store_1 = require("./store");
49
+ const LEVEL_LABEL = { opus: "Strongest", sonnet: "Balanced", haiku: "Fastest" };
50
+ function newestFirst(a, b) {
51
+ return b.id.localeCompare(a.id, undefined, { numeric: true });
52
+ }
53
+ /** Resolve an agent level (opus = Strongest, sonnet = Balanced, haiku =
54
+ * Fastest) to one of the account's approved models:
55
+ * 1. explicit tier/level metadata on the catalog entry, when the gateway sends it
56
+ * 2. the same model family by name (e.g. a "claude-opus-…" model), newest first
57
+ * 3. by price: Strongest = priciest, Fastest = cheapest, Balanced = in between
58
+ * Otherwise undefined → Codie's governed default. Only catalog ids are returned. */
59
+ function levelToModel(level, catalog) {
60
+ const key = level.toLowerCase();
61
+ const tierNames = {
62
+ opus: ["strongest", "opus", "high", "max"],
63
+ sonnet: ["balanced", "sonnet", "medium", "default"],
64
+ haiku: ["fastest", "haiku", "low", "fast"],
65
+ };
66
+ const tiered = catalog.filter((m) => [m.tier, m.level].some((t) => typeof t === "string" && tierNames[key].includes(t.toLowerCase())));
67
+ if (tiered.length)
68
+ return { model: [...tiered].sort(newestFirst)[0].id, how: "tier" };
69
+ const family = catalog.filter((m) => new RegExp(`(^|[^a-z])${key}([^a-z]|$)`, "i").test(m.id));
70
+ if (family.length)
71
+ return { model: [...family].sort(newestFirst)[0].id, how: "family" };
72
+ const priced = catalog
73
+ .filter((m) => m.cost && Number.isFinite(m.cost.input) && Number.isFinite(m.cost.output))
74
+ .sort((a, b) => a.cost.input + a.cost.output - (b.cost.input + b.cost.output) || a.id.localeCompare(b.id));
75
+ if (priced.length) {
76
+ const index = key === "haiku" ? 0 : key === "opus" ? priced.length - 1 : Math.floor((priced.length - 1) / 2);
77
+ return { model: priced[index].id, how: "price" };
78
+ }
79
+ return { how: "none" };
80
+ }
81
+ /** Map a Claude agent `model:` to an OpenCode model id, or undefined to let
82
+ * Codie's governed default apply. */
83
+ function mapAgentModel(value, approved, catalog) {
84
+ const model = (value ?? "").trim();
85
+ if (!model || model === "inherit")
86
+ return {};
87
+ const key = model.toLowerCase();
88
+ if (LEVEL_LABEL[key]) {
89
+ const usable = (catalog ?? []).filter((m) => !approved || approved.has(m.id.replace(/^codiedev\//, "")));
90
+ const picked = levelToModel(key, usable);
91
+ if (!picked.model) {
92
+ return { note: `level ${LEVEL_LABEL[key]} (${model}): no matching approved model, using the governed default` };
93
+ }
94
+ return { model: picked.model, note: `level ${LEVEL_LABEL[key]} (${model}) → ${picked.model} (by ${picked.how})` };
95
+ }
96
+ if (!model.includes("/"))
97
+ return { note: `unknown level "${model}", using the governed default` };
98
+ if (approved) {
99
+ const bare = model.replace(/^codiedev\//, "");
100
+ if (!approved.has(bare))
101
+ return { note: `dropped model "${model}" (not approved for this Codiedev account)` };
102
+ }
103
+ return { model };
104
+ }
105
+ function codiePackConfig(root, options = {}) {
106
+ const registry = (0, store_1.readRegistry)(root);
107
+ const result = { skillPaths: [], agents: {}, plugins: [], notes: [] };
108
+ for (const [slug, entry] of Object.entries(registry.packs).sort(([a], [b]) => a.localeCompare(b))) {
109
+ if (!(0, manifest_1.isSafeSegment)(slug) || !Array.isArray(entry.harnesses) || !entry.harnesses.includes("codie"))
110
+ continue;
111
+ const dir = typeof entry.dir === "string" ? entry.dir : "";
112
+ if (!dir || !fs.existsSync(dir))
113
+ continue;
114
+ try {
115
+ const hooks = JSON.parse(fs.readFileSync(path.join(dir, "hooks", "hooks.json"), "utf8"))?.hooks;
116
+ const skipped = (0, shim_1.unsupportedShimEvents)([{ slug, root: dir, hooks: hooks ?? {} }]).get(slug);
117
+ if (skipped?.length)
118
+ result.notes.push(`pack ${slug}: hooks Codie can't run were skipped: ${skipped.join(", ")}`);
119
+ }
120
+ catch {
121
+ // no hooks
122
+ }
123
+ const skills = path.join(dir, "skills");
124
+ if (fs.existsSync(skills))
125
+ result.skillPaths.push(skills);
126
+ const agentsDir = path.join(dir, "agents");
127
+ let agentFiles = [];
128
+ try {
129
+ agentFiles = fs.readdirSync(agentsDir).filter((f) => f.endsWith(".md")).sort();
130
+ }
131
+ catch {
132
+ agentFiles = [];
133
+ }
134
+ for (const file of agentFiles) {
135
+ const fallbackName = file.slice(0, -3);
136
+ let content;
137
+ try {
138
+ content = fs.readFileSync(path.join(agentsDir, file), "utf8");
139
+ }
140
+ catch {
141
+ continue;
142
+ }
143
+ const { data, body } = (0, manifest_1.parseFrontmatter)(content);
144
+ const name = (0, manifest_1.isSafeSegment)(data.name) ? data.name : fallbackName;
145
+ if (result.agents[name]) {
146
+ result.notes.push(`agent "${name}" from pack ${slug} skipped (already defined by another pack)`);
147
+ continue;
148
+ }
149
+ const mapped = mapAgentModel(data.model, options.approvedModels, options.catalog);
150
+ if (mapped.note)
151
+ result.notes.push(`agent "${name}" (${slug}): ${mapped.note}`);
152
+ result.agents[name] = {
153
+ description: data.description || `${name} (from starter pack ${slug})`,
154
+ prompt: body.trim(),
155
+ mode: "subagent",
156
+ ...(mapped.model ? { model: mapped.model } : {}),
157
+ };
158
+ }
159
+ }
160
+ // The shim only exists while some installed pack has hooks.
161
+ const shim = (0, paths_1.shimPath)(root);
162
+ const anyCodie = Object.values(registry.packs).some((e) => Array.isArray(e.harnesses) && e.harnesses.includes("codie"));
163
+ if (anyCodie && fs.existsSync(shim))
164
+ result.plugins.push((0, url_1.pathToFileURL)(shim).href);
165
+ return result;
166
+ }
167
+ /** Merge pack config into an OpenCode config without clobbering anything the
168
+ * user or Codie already set: existing skill paths, agents and plugins win. */
169
+ function mergeCodiePackConfig(existing, packs) {
170
+ const next = { ...existing };
171
+ if (packs.skillPaths.length) {
172
+ // `skills` must be the object form; the array form is rejected by the engine.
173
+ const current = existing.skills;
174
+ const base = Array.isArray(current)
175
+ ? { paths: current.filter((p) => typeof p === "string") }
176
+ : current && typeof current === "object"
177
+ ? { ...current }
178
+ : {};
179
+ const paths = Array.isArray(base.paths) ? [...base.paths] : [];
180
+ for (const p of packs.skillPaths)
181
+ if (!paths.includes(p))
182
+ paths.push(p);
183
+ next.skills = { ...base, paths };
184
+ }
185
+ if (Object.keys(packs.agents).length) {
186
+ const current = existing.agent && typeof existing.agent === "object" ? existing.agent : {};
187
+ next.agent = { ...packs.agents, ...current };
188
+ }
189
+ if (packs.plugins.length) {
190
+ const current = Array.isArray(existing.plugin) ? [...existing.plugin] : [];
191
+ for (const p of packs.plugins)
192
+ if (!current.includes(p))
193
+ current.push(p);
194
+ next.plugin = current;
195
+ }
196
+ return next;
197
+ }
@@ -0,0 +1,71 @@
1
+ import { PackManifest } from "./manifest";
2
+ import { RegistryEntry } from "./store";
3
+ export declare const HARNESSES: readonly ["claude", "copilot", "codex", "cursor", "codie"];
4
+ export type HarnessName = (typeof HARNESSES)[number];
5
+ export interface RunResult {
6
+ status: number | null;
7
+ stdout: string;
8
+ stderr: string;
9
+ error?: string;
10
+ }
11
+ export type Runner = (command: string, args: string[], options?: {
12
+ cwd?: string;
13
+ }) => RunResult;
14
+ export declare function defaultRunner(env: NodeJS.ProcessEnv): Runner;
15
+ /** True when `bin` is an executable file on `env.PATH`. */
16
+ export declare function onPath(bin: string, env: NodeJS.ProcessEnv): boolean;
17
+ export interface AdapterContext {
18
+ root: string;
19
+ env: NodeJS.ProcessEnv;
20
+ home: string;
21
+ run: Runner;
22
+ manifest: PackManifest;
23
+ pluginDir: string;
24
+ /** Same plugin without agent model levels (Codex, Copilot, Cursor). */
25
+ portablePluginDir: string;
26
+ previous?: RegistryEntry;
27
+ }
28
+ export interface AdapterResult {
29
+ harness: HarnessName;
30
+ status: "ok" | "skipped" | "failed" | "dry-run";
31
+ detail: string;
32
+ /** Debug-level notes (printed with CODIEDEV_DEBUG; always in --json). */
33
+ notes?: string[];
34
+ }
35
+ export interface Adapter {
36
+ name: HarnessName;
37
+ detect(ctx: Omit<AdapterContext, "manifest" | "pluginDir" | "portablePluginDir" | "previous">): boolean;
38
+ /** Returns a short success detail; throws on failure. */
39
+ install(ctx: AdapterContext): string;
40
+ /** Human-readable description of what install would do (for --dry-run). */
41
+ plan(ctx: AdapterContext): string;
42
+ /** Remove the pack from this tool. Returns a detail; throws on failure. */
43
+ uninstall(ctx: UninstallContext): string;
44
+ }
45
+ export interface UninstallContext {
46
+ root: string;
47
+ env: NodeJS.ProcessEnv;
48
+ home: string;
49
+ run: Runner;
50
+ slug: string;
51
+ }
52
+ export declare const claudeAdapter: Adapter;
53
+ export declare const copilotAdapter: Adapter;
54
+ export declare const codexAdapter: Adapter;
55
+ export declare const CURSOR_MARKER = ".codiedev-pack";
56
+ /** Fallback: copy skills to ~/.cursor/skills/<name>/, never overwriting a
57
+ * skill dir Codiedev didn't write, and pruning ones this pack no longer has. */
58
+ export declare function copyCursorSkills(home: string, manifest: PackManifest, pluginDirPath: string): string[];
59
+ /** Remove every ~/.cursor/skills dir this pack's fallback copy wrote. */
60
+ export declare function removeCursorSkills(home: string, slug: string): number;
61
+ export declare function cursorLocalPluginsDir(home: string): string;
62
+ export declare function cursorPluginDir(home: string, slug: string): string;
63
+ /** Copy the plugin dir into place via a sibling temp dir + rename. Refuses to
64
+ * replace a dir that doesn't carry our marker for this pack. */
65
+ export declare function installCursorPlugin(home: string, slug: string, sourceDir: string): string;
66
+ /** Remove our copy (only when it carries our marker). */
67
+ export declare function removeCursorPlugin(home: string, slug: string): boolean;
68
+ export declare const cursorAdapter: Adapter;
69
+ export declare const codieAdapter: Adapter;
70
+ export declare const ADAPTERS: Adapter[];
71
+ export declare function parseHarnessList(value: string | undefined): HarnessName[] | undefined;