@hiveku-apps/sync 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,6 +78,45 @@ narrowed tool list. Available: `full`, `sales`, `marketing`, `helpdesk`,
78
78
  `pm`, `dev`. Change `HIVEKU_PROFILE` in `.env` and reload the MCP server
79
79
  in your editor to switch.
80
80
 
81
+ ## Codex (OpenAI) support
82
+
83
+ By default `init` scaffolds a Claude Code folder (`.mcp.json` + `CLAUDE.md`). Add
84
+ `--codex` to also write the Codex-native equivalents so the same folder works
85
+ from the **Codex CLI / IDE extension / ChatGPT desktop**:
86
+
87
+ ```bash
88
+ # Scaffold with the Codex lane too:
89
+ hiveku-sync init acme-corp --codex
90
+
91
+ # Or retrofit an existing account folder (run after `pull` to include the
92
+ # account's latest skills/commands):
93
+ cd acme-corp && hiveku-sync codex
94
+ ```
95
+
96
+ This writes, merge-safely (only Hiveku-managed regions are touched):
97
+
98
+ - **`AGENTS.md`** — Codex's `CLAUDE.md` equivalent: a lean pointer to `CLAUDE.md`
99
+ plus the load-bearing rules (Codex caps the instruction chain at ~32 KiB).
100
+ - **`.codex/config.toml`** — the same `core.hiveku.com/mcp` server via
101
+ `[mcp_servers.hiveku]`, git-ignored. Your `OLYMPUS_API_KEY` is **inlined** when
102
+ `.env` has it (self-contained, no env setup); otherwise it's read from the
103
+ `OLYMPUS_API_KEY` env var. Also wires `playwright` for visual checks.
104
+ - **`.agents/skills/<name>/SKILL.md`** — the account's pulled `skills/` and
105
+ `commands/` mirrored as repo-scoped Codex skills (Codex prompts are global-only
106
+ and deprecated).
107
+
108
+ Pass `--trust` to pre-trust the folder in `~/.codex/config.toml` (so Codex loads
109
+ the repo config without its first-run trust prompt). Codex ignores repo-level
110
+ approval/sandbox config, so the safe-work rules ship as instructions in
111
+ `AGENTS.md`, not enforced permissions.
112
+
113
+ > **This vs. the plugin.** For a single account, you can instead install the
114
+ > published [Hiveku Codex plugin](https://github.com/locus2012/hiveku-codex-plugin)
115
+ > once (`codex plugin marketplace add locus2012/hiveku-codex-plugin`) and set
116
+ > `HIVEKU_TOKEN`. `hiveku-sync --codex` is the self-contained, per-account route —
117
+ > better for multi-account / CI / power-user setups, and it also brings the
118
+ > account's own knowledge. Use one route per folder, not both.
119
+
81
120
  ## Coder projects — pull, ship, deploy
82
121
 
83
122
  Beyond memory/rules/skills, `hiveku-sync` can drive a Hiveku **website project**
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { runInit } from '../src/init.mjs';
12
+ import { runCodex } from '../src/init.mjs';
12
13
  import { runPull } from '../src/pull.mjs';
13
14
  import {
14
15
  runProjectsList,
@@ -69,10 +70,21 @@ const HELP = `hiveku-sync — sync a Hiveku account into a local Claude Code fol
69
70
 
70
71
  Memory/rules/skills:
71
72
  init <slug> [--profile=<name>] Scaffold ./<slug>/ with .env, .mcp.json,
72
- CLAUDE.md, and the memory/rules/skills tree.
73
+ [--codex] [--trust] CLAUDE.md, and the memory/rules/skills tree.
74
+ --codex also writes the Codex lane (AGENTS.md
75
+ + .codex/config.toml + skills); --trust
76
+ pre-trusts the folder in ~/.codex/config.toml.
73
77
  pull [--profile=<name>] Pull memory/rules/skills via MCP into the
74
78
  current account folder. Reads OLYMPUS_API_KEY
75
79
  and OLYMPUS_BASE_URL from ./.env.
80
+ codex [<folder>] [--trust] Retrofit an existing account folder with the
81
+ Codex lane (AGENTS.md + .codex/config.toml +
82
+ mirror the account's skills/commands to
83
+ .agents/skills/). Run after 'pull' to include
84
+ the latest knowledge. Complements the published
85
+ Hiveku Codex plugin (single-account users can
86
+ instead run: codex plugin marketplace add
87
+ locus2012/hiveku-codex-plugin).
76
88
 
77
89
  Coder projects:
78
90
  projects List the account's coder projects.
@@ -132,6 +144,8 @@ async function main() {
132
144
  slug,
133
145
  parentDir: flags['parent'],
134
146
  profile: flags['profile'] || 'full',
147
+ codex: !!flags['codex'],
148
+ trust: !!flags['trust'],
135
149
  });
136
150
  console.log(`Initialized ${result.folder}`);
137
151
  console.log(` profile: ${result.profile}`);
@@ -141,9 +155,33 @@ async function main() {
141
155
  if (result.workspaceAdded) {
142
156
  console.log(` workspace: added entry to hiveku.code-workspace`);
143
157
  }
158
+ if (result.codex) {
159
+ console.log(` codex: wrote AGENTS.md + .codex/config.toml (git-ignored)`);
160
+ console.log(
161
+ result.codex.inlinedKey
162
+ ? ` MCP auth: inlined your OLYMPUS_API_KEY`
163
+ : ` MCP auth: reads the OLYMPUS_API_KEY env var — after pasting your key into .env, run \`hiveku-sync codex\` to inline it, or \`export OLYMPUS_API_KEY=...\``,
164
+ );
165
+ if (result.codex.trust === 'added') console.log(` trust: pre-trusted this folder in ~/.codex/config.toml`);
166
+ }
144
167
  console.log(` then: cd ${result.folder} && hiveku-sync pull`);
145
168
  break;
146
169
  }
170
+ case 'codex': {
171
+ const result = await runCodex({ folder: positional[0], trust: !!flags['trust'] });
172
+ console.log(`Codex lane ready in ${result.folder} (profile=${result.profile})`);
173
+ console.log(` AGENTS.md + .codex/config.toml written (git-ignored).`);
174
+ console.log(
175
+ result.inlinedKey
176
+ ? ` MCP auth: inlined OLYMPUS_API_KEY from .env.`
177
+ : ` MCP auth: reads the OLYMPUS_API_KEY env var (no key found in .env to inline) — set it, or paste it into .env and re-run.`,
178
+ );
179
+ console.log(` skills: mirrored ${result.skillsMirrored} account skill/command entr${result.skillsMirrored === 1 ? 'y' : 'ies'} into .agents/skills/.`);
180
+ if (result.trust === 'added') console.log(` trust: pre-trusted this folder in ~/.codex/config.toml.`);
181
+ else if (result.trust === 'already') console.log(` trust: folder already trusted.`);
182
+ console.log(` verify: cd there, run \`codex mcp list\` (the hiveku row), then \`get_account_info\`.`);
183
+ break;
184
+ }
147
185
  case 'pull': {
148
186
  const summary = await runPull({ profile: flags['profile'] });
149
187
  console.log(`Pulled ${summary.total} entries (profile=${summary.profile}, base=${summary.baseUrl}):`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiveku-apps/sync",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Sync a Hiveku account's memory/rules/skills + coder projects into a local folder, and ship projects (push/deploy/pull-env) from the terminal. Pairs with the Hiveku MCP at core.hiveku.com/mcp so Claude Code can drive Hiveku tools per-folder.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@
8
8
  "homepage": "https://app.hiveku.com",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/locus2012/hiveku-sync.git"
11
+ "url": "git+https://github.com/locus2012/hiveku-sync.git"
12
12
  },
13
13
  "bugs": {
14
14
  "url": "https://github.com/locus2012/hiveku-sync/issues"
@@ -19,7 +19,8 @@
19
19
  "claude",
20
20
  "claude-code",
21
21
  "cli",
22
- "olympus"
22
+ "olympus",
23
+ "codex"
23
24
  ],
24
25
  "bin": {
25
26
  "hiveku-sync": "bin/hiveku-sync.mjs"
package/src/codex.mjs ADDED
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Codex (OpenAI) support for hiveku-sync — the Codex-native equivalents of the
3
+ * Claude Code artifacts, so a Hiveku account folder works identically from the
4
+ * Codex CLI, the Codex IDE extension, and the ChatGPT desktop app (all three
5
+ * read the same files). Opt in with `--codex` on `init`, or run
6
+ * `hiveku-sync codex` in an existing folder.
7
+ *
8
+ * Writes, merge-safely (only our marked regions are rewritten; user content is
9
+ * preserved):
10
+ * CLAUDE.md → AGENTS.md (lean pointer + load-bearing rules; Codex
11
+ * concatenates AGENTS.md with a ~32KiB cap, so the
12
+ * full guide stays in CLAUDE.md and AGENTS.md says to
13
+ * read it)
14
+ * .mcp.json → .codex/config.toml [mcp_servers.*] (the SAME
15
+ * core.hiveku.com/mcp server + the SAME OLYMPUS_API_KEY;
16
+ * inlined when we can resolve the key, else read from
17
+ * the OLYMPUS_API_KEY env var). git-ignored.
18
+ * skill/command knowledge → .agents/skills/<name>/SKILL.md (repo-scoped; Codex
19
+ * prompts are global-only + deprecated)
20
+ * permissions → NO per-repo equivalent (Codex ignores approval/
21
+ * sandbox keys in repo config) — stated in AGENTS.md.
22
+ *
23
+ * This complements the published Hiveku Codex plugin
24
+ * (github.com/locus2012/hiveku-codex-plugin): the plugin ships the generic
25
+ * `hiveku` MCP server + doctrine skills for single-account users who install it
26
+ * once and set HIVEKU_TOKEN; this writes a SELF-CONTAINED per-account folder
27
+ * (its own MCP config + that account's own knowledge) for multi-account / CI /
28
+ * power-user setups. Use one route or the other per folder, not both.
29
+ */
30
+
31
+ import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
32
+ import { existsSync } from 'node:fs';
33
+ import { homedir } from 'node:os';
34
+ import { join, resolve, dirname } from 'node:path';
35
+
36
+ import { loadDotenv } from './util.mjs';
37
+
38
+ const AGENTS_BEGIN = '<!-- hiveku:begin -->';
39
+ const AGENTS_END = '<!-- hiveku:end -->';
40
+ const TOML_BEGIN = '# hiveku:begin (managed by hiveku-sync — do not edit inside)';
41
+ const TOML_END = '# hiveku:end';
42
+ const SKILL_STAMP = '<!-- generated-by: hiveku-sync (mirrored from account knowledge) -->';
43
+
44
+ /** Replace (or append) the marked region inside existing content. */
45
+ function upsertMarkedRegion(existing, begin, end, region) {
46
+ const b = existing.indexOf(begin);
47
+ const e = existing.indexOf(end);
48
+ if (b !== -1 && e !== -1 && e > b) {
49
+ return existing.slice(0, b) + region + existing.slice(e + end.length);
50
+ }
51
+ const sep = existing.length && !existing.endsWith('\n\n') ? (existing.endsWith('\n') ? '\n' : '\n\n') : '';
52
+ return existing + sep + region + '\n';
53
+ }
54
+
55
+ /** AGENTS.md — lean on purpose (Codex caps the instruction chain at ~32KiB). */
56
+ function agentsMdRegion({ slug }) {
57
+ return `${AGENTS_BEGIN}
58
+ # Hiveku — ${slug}
59
+
60
+ This folder is ONE Hiveku account (${slug}). Its MCP server \`hiveku\` (configured in
61
+ \`.codex/config.toml\`) carries this account's API key — never operate a different account from here.
62
+
63
+ **FIRST ACTION every session: read \`CLAUDE.md\` in this folder.** It is the full operating guide
64
+ (written for Claude Code, applies to you identically): account tools, department data, project
65
+ push/deploy flows, and the work-tracking rules. Ignore only its Claude-specific file paths
66
+ (\`.claude/*\`) — your equivalents are \`.agents/skills/*\` (the account's mirrored skills/commands,
67
+ invoked by name) and this file.
68
+
69
+ Non-negotiables (also in CLAUDE.md, restated because they are load-bearing):
70
+ - **Verify identity before ANY write:** \`get_account_info\` must return THIS account.
71
+ - **You are NOT the only writer.** Others push to these same projects while you work. Check what is
72
+ current BEFORE you start (\`project_version_log\`) and AGAIN before you push (\`project_files_status\`
73
+ — \`changed\` = they edited it, \`only_remote\` = they added files you lack). Before any tree-replace
74
+ (\`delete_missing: true\`), \`dry_run\` first and READ the would-delete list — a file you did not send
75
+ may be someone else's NEW work. Never blind-overwrite.
76
+ - **Keep ALL scratch work in \`.hiveku/tmp/\`** — never \`/tmp\` (shared across accounts), your home
77
+ dir, or the repo root.
78
+ - **Never ingest local agent config into a project push.** \`.codex/config.toml\`, \`.mcp.json\`, and
79
+ \`.env*\` carry this account's key/secrets — exclude them from every tar/bulk-save. Secrets belong in
80
+ \`project_secrets_*\`, never in project code. Never read or print \`.env.local\`.
81
+ - **PM tasks are required** — create one when you start, comment as you go, complete it when done,
82
+ attributed to the authenticated user (\`crm_list_users\`).
83
+ - **Every completed task ends with an "Owner update"** — 2–4 calm, plain-language sentences a busy owner
84
+ can skim: benefit first, no alarm vocabulary, accurate.
85
+ - Video generation is paid + capped — \`marketing_generate_video\` with \`dry_run: true\` first.
86
+
87
+ Discover tools with \`hiveku_docs_search\` / \`hiveku_docs_get\` and \`hiveku_playbooks_list\` /
88
+ \`hiveku_playbook_get\`. Codex ignores repo-level approval/sandbox config, so the rules above are
89
+ instructions, not enforced permissions — follow them.
90
+ ${AGENTS_END}`;
91
+ }
92
+
93
+ /** The [mcp_servers.*] region for <folder>/.codex/config.toml. */
94
+ function codexTomlRegion({ baseUrl, profile, apiKey }) {
95
+ const base = baseUrl.replace(/\/+$/, '');
96
+ const url = profile === 'full' ? `${base}/mcp` : `${base}/mcp?profile=${encodeURIComponent(profile)}`;
97
+ // Prefer an inlined key (self-contained, no env setup needed; the file is
98
+ // git-ignored). Fall back to the OLYMPUS_API_KEY env var when we can't
99
+ // resolve a real key at scaffold time (e.g. `init` before the user pastes it).
100
+ const auth = apiKey
101
+ ? `http_headers = { "Authorization" = "Bearer ${apiKey}" }`
102
+ : `bearer_token_env_var = "OLYMPUS_API_KEY"`;
103
+ return `${TOML_BEGIN}
104
+ [mcp_servers.hiveku]
105
+ url = "${url}"
106
+ ${auth}
107
+
108
+ [mcp_servers.playwright]
109
+ command = "npx"
110
+ args = ["-y", "@playwright/mcp@latest"]
111
+ ${TOML_END}`;
112
+ }
113
+
114
+ /** Add lines to .gitignore when missing (non-destructive). */
115
+ async function appendGitignoreLines(folder, lines) {
116
+ const giPath = join(folder, '.gitignore');
117
+ let gi = '';
118
+ try {
119
+ gi = await readFile(giPath, 'utf8');
120
+ } catch {
121
+ /* new file */
122
+ }
123
+ const have = new Set(gi.split('\n').map((l) => l.trim()));
124
+ const missing = lines.filter((l) => !have.has(l));
125
+ if (missing.length === 0) return;
126
+ const sep = gi.length && !gi.endsWith('\n') ? '\n' : '';
127
+ await writeFile(giPath, gi + sep + missing.join('\n') + '\n', 'utf8');
128
+ }
129
+
130
+ /**
131
+ * Mirror the account's pulled skill + command knowledge into repo-scoped Codex
132
+ * skills at .agents/skills/<name>/SKILL.md. Reads the folders `hiveku-sync pull`
133
+ * writes (skills/, commands/). Idempotent: a skill dir without our stamp is
134
+ * left alone (user-authored). Returns the count written.
135
+ */
136
+ export async function mirrorKnowledgeToSkills(folder) {
137
+ let written = 0;
138
+ for (const srcDir of ['skills', 'commands']) {
139
+ const root = join(folder, srcDir);
140
+ if (!existsSync(root)) continue;
141
+ // pull writes <type>/<department>/<slug>.md — walk one level of subdirs + top-level files.
142
+ const files = [];
143
+ for (const entry of await readdir(root, { withFileTypes: true })) {
144
+ if (entry.isDirectory()) {
145
+ for (const f of await readdir(join(root, entry.name))) {
146
+ if (f.endsWith('.md')) files.push(join(root, entry.name, f));
147
+ }
148
+ } else if (entry.name.endsWith('.md')) {
149
+ files.push(join(root, entry.name));
150
+ }
151
+ }
152
+ for (const filePath of files) {
153
+ const src = await readFile(filePath, 'utf8');
154
+ const base = filePath.split('/').pop().replace(/\.md$/, '');
155
+ const name = `hiveku-${srcDir === 'commands' ? 'cmd' : 'skill'}-${base}`.slice(0, 100);
156
+ let description = `Hiveku ${srcDir === 'commands' ? 'command' : 'skill'}: ${base}`;
157
+ let body = src;
158
+ const fm = src.match(/^---\n([\s\S]*?)\n---\n?/);
159
+ if (fm) {
160
+ const d = fm[1].match(/^description:\s*(.+)$/m);
161
+ if (d) description = d[1].trim().replace(/^["']|["']$/g, '');
162
+ body = src.slice(fm[0].length);
163
+ }
164
+ body = body.replace(/\$ARGUMENTS/g, '(the arguments the user gave after the skill name)');
165
+ const skillDir = join(folder, '.agents', 'skills', name);
166
+ const skillPath = join(skillDir, 'SKILL.md');
167
+ try {
168
+ const existing = await readFile(skillPath, 'utf8');
169
+ if (!existing.includes(SKILL_STAMP)) continue; // user-authored — never clobber
170
+ } catch {
171
+ /* new */
172
+ }
173
+ await mkdir(skillDir, { recursive: true });
174
+ const skill = `---\nname: ${name}\ndescription: ${JSON.stringify(description)}\n---\n${SKILL_STAMP}\n\n${body.trimStart()}`;
175
+ await writeFile(skillPath, skill, 'utf8');
176
+ written += 1;
177
+ }
178
+ }
179
+ return written;
180
+ }
181
+
182
+ /**
183
+ * Write/refresh the Codex scaffold for one account folder: AGENTS.md +
184
+ * .codex/config.toml (git-ignored) + mirror the account's knowledge to skills.
185
+ * Resolves OLYMPUS_API_KEY from the folder's .env / process env to inline the
186
+ * MCP auth when possible.
187
+ */
188
+ export async function writeCodexScaffold({ folder, slug, baseUrl, profile }) {
189
+ const dotenv = await loadDotenv(join(folder, '.env'));
190
+ const apiKey =
191
+ process.env.OLYMPUS_API_KEY ||
192
+ (dotenv.OLYMPUS_API_KEY && dotenv.OLYMPUS_API_KEY !== 'olp_replace_me' ? dotenv.OLYMPUS_API_KEY : '');
193
+
194
+ // AGENTS.md (marker-managed)
195
+ const agentsPath = join(folder, 'AGENTS.md');
196
+ let agents = '';
197
+ try {
198
+ agents = await readFile(agentsPath, 'utf8');
199
+ } catch {
200
+ /* new */
201
+ }
202
+ await writeFile(agentsPath, upsertMarkedRegion(agents, AGENTS_BEGIN, AGENTS_END, agentsMdRegion({ slug })), 'utf8');
203
+
204
+ // .codex/config.toml (marker-managed region; rest of user's config untouched)
205
+ const tomlPath = join(folder, '.codex', 'config.toml');
206
+ let toml = '';
207
+ try {
208
+ toml = await readFile(tomlPath, 'utf8');
209
+ } catch {
210
+ /* new */
211
+ }
212
+ await mkdir(dirname(tomlPath), { recursive: true });
213
+ await writeFile(tomlPath, upsertMarkedRegion(toml, TOML_BEGIN, TOML_END, codexTomlRegion({ baseUrl, profile, apiKey })), 'utf8');
214
+
215
+ await appendGitignoreLines(folder, ['.codex/config.toml']);
216
+
217
+ const skills = await mirrorKnowledgeToSkills(folder);
218
+ return { inlinedKey: !!apiKey, skillsMirrored: skills };
219
+ }
220
+
221
+ /**
222
+ * Pre-trust a folder in the USER-GLOBAL ~/.codex/config.toml so its repo-level
223
+ * .codex/config.toml (which carries the MCP server + key) loads without Codex's
224
+ * first-run trust prompt. Crosses a security boundary — the caller must have
225
+ * explicit user intent (the `--trust` flag).
226
+ */
227
+ export async function preTrustFolder(folder) {
228
+ const cfgPath = join(homedir(), '.codex', 'config.toml');
229
+ let cfg = '';
230
+ try {
231
+ cfg = await readFile(cfgPath, 'utf8');
232
+ } catch {
233
+ /* none yet */
234
+ }
235
+ const abs = resolve(folder);
236
+ const header = `[projects."${abs}"]`;
237
+ if (cfg.includes(header)) return 'already';
238
+ await mkdir(dirname(cfgPath), { recursive: true });
239
+ const sep = cfg.length && !cfg.endsWith('\n') ? '\n' : '';
240
+ await writeFile(cfgPath, `${cfg}${sep}\n${header}\ntrust_level = "trusted"\n`, 'utf8');
241
+ return 'added';
242
+ }
package/src/init.mjs CHANGED
@@ -7,9 +7,10 @@
7
7
 
8
8
  import { existsSync } from 'node:fs';
9
9
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
10
- import { dirname, join, resolve } from 'node:path';
10
+ import { basename, dirname, join, resolve } from 'node:path';
11
11
 
12
- import { writeFileAtomic, safeSlug, TYPE_TO_FOLDER } from './util.mjs';
12
+ import { writeFileAtomic, safeSlug, TYPE_TO_FOLDER, loadDotenv } from './util.mjs';
13
+ import { writeCodexScaffold, preTrustFolder } from './codex.mjs';
13
14
 
14
15
  const VALID_PROFILES = ['full', 'sales', 'marketing', 'helpdesk', 'pm', 'dev'];
15
16
  const DEFAULT_BASE_URL = 'https://core.hiveku.com';
@@ -148,7 +149,7 @@ async function appendToWorkspaceFile(workspaceFilePath, folderRelPath) {
148
149
  return true;
149
150
  }
150
151
 
151
- export async function runInit({ slug, parentDir, profile = 'full' }) {
152
+ export async function runInit({ slug, parentDir, profile = 'full', codex = false, trust = false }) {
152
153
  if (!slug) throw new Error('slug is required, e.g. hiveku-sync init acme-corp');
153
154
  if (!VALID_PROFILES.includes(profile)) {
154
155
  throw new Error(
@@ -190,5 +191,36 @@ export async function runInit({ slug, parentDir, profile = 'full' }) {
190
191
  `./${safe}`,
191
192
  );
192
193
 
193
- return { folder, slug: safe, profile, workspaceAdded };
194
+ // Optional Codex lane: AGENTS.md + .codex/config.toml + knowledge→skills, so
195
+ // the same folder works from the Codex CLI / IDE / ChatGPT desktop too.
196
+ let codexResult = null;
197
+ if (codex) {
198
+ codexResult = await writeCodexScaffold({
199
+ folder,
200
+ slug: safe,
201
+ baseUrl: DEFAULT_BASE_URL,
202
+ profile,
203
+ });
204
+ if (trust) codexResult.trust = await preTrustFolder(folder);
205
+ }
206
+
207
+ return { folder, slug: safe, profile, workspaceAdded, codex: codexResult };
208
+ }
209
+
210
+ /**
211
+ * Retrofit an EXISTING account folder with the Codex lane (AGENTS.md +
212
+ * .codex/config.toml + knowledge→skills). Run from inside the folder, or pass
213
+ * one. Derives the account slug from the folder name and the profile from
214
+ * .env (HIVEKU_PROFILE), and inlines the real OLYMPUS_API_KEY when .env has it.
215
+ */
216
+ export async function runCodex({ folder, trust = false } = {}) {
217
+ const target = folder ? resolve(folder) : process.cwd();
218
+ if (!existsSync(target)) throw new Error(`Folder not found: ${target}`);
219
+ const dotenv = await loadDotenv(join(target, '.env'));
220
+ const profile = dotenv.HIVEKU_PROFILE || 'full';
221
+ const baseUrl = dotenv.OLYMPUS_BASE_URL || DEFAULT_BASE_URL;
222
+ const slug = safeSlug(basename(target));
223
+ const result = await writeCodexScaffold({ folder: target, slug, baseUrl, profile });
224
+ if (trust) result.trust = await preTrustFolder(target);
225
+ return { folder: target, slug, profile, ...result };
194
226
  }