@askexenow/exe-os 0.9.101 → 0.9.102

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.
@@ -0,0 +1,458 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin/pre-publish.ts
4
+ import { readFileSync as readFileSync2 } from "fs";
5
+ import path2 from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ // src/lib/release-notes.ts
9
+ import { spawnSync } from "child_process";
10
+ import { existsSync, readFileSync, writeFileSync } from "fs";
11
+ import path from "path";
12
+ function git(args, cwd) {
13
+ const result = spawnSync("git", args, { cwd, encoding: "utf8", timeout: 1e4 });
14
+ return result.status === 0 ? result.stdout.trim() : "";
15
+ }
16
+ function categorizeCommit(message) {
17
+ const firstLine = message.split("\n")[0].trim();
18
+ const summary = firstLine.replace(/^(feat|fix|security|chore|refactor|test|docs|perf|ci|build)(\(.+?\))?:\s*/, "");
19
+ if (/^feat/i.test(firstLine)) return { category: "features", summary };
20
+ if (/^fix/i.test(firstLine)) return { category: "fixes", summary };
21
+ if (/^security/i.test(firstLine) || /security/i.test(summary)) return { category: "security", summary };
22
+ if (/^(chore|refactor|test|docs|perf|ci|build)/i.test(firstLine)) return { category: "other", summary };
23
+ return { category: "other", summary };
24
+ }
25
+ function generateReleaseNotes(repoRoot2, currentVersion) {
26
+ const tags = git(["tag", "--sort=-v:refname", "--list", "v*"], repoRoot2).split("\n").filter(Boolean);
27
+ const currentTag = `v${currentVersion}`;
28
+ const prevTag = tags.find((t) => t !== currentTag) ?? "";
29
+ const range = prevTag ? `${prevTag}..HEAD` : "HEAD~50..HEAD";
30
+ const log = git(["log", range, "--oneline", "--no-merges", "--format=%s"], repoRoot2);
31
+ const commits = log.split("\n").filter(Boolean);
32
+ const note = {
33
+ version: currentVersion,
34
+ date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
35
+ features: [],
36
+ fixes: [],
37
+ security: [],
38
+ other: [],
39
+ migration_notes: []
40
+ };
41
+ for (const msg of commits) {
42
+ const { category, summary } = categorizeCommit(msg);
43
+ if (summary && !note[category].includes(summary)) {
44
+ note[category].push(summary);
45
+ }
46
+ }
47
+ const fullLog = git(["log", range, "--no-merges", "--format=%B---END---"], repoRoot2);
48
+ const fullMessages = fullLog.split("---END---").filter(Boolean);
49
+ for (const body of fullMessages) {
50
+ const lines = body.split("\n");
51
+ for (const line of lines) {
52
+ const trimmed = line.trim();
53
+ const migMatch = trimmed.match(/^(?:MIGRATION|BREAKING|ACTION):\s*(.+)/i);
54
+ if (migMatch?.[1] && !note.migration_notes.includes(migMatch[1])) {
55
+ note.migration_notes.push(migMatch[1]);
56
+ }
57
+ }
58
+ }
59
+ return note;
60
+ }
61
+ function loadReleaseNotes(repoRoot2) {
62
+ const filePath = path.join(repoRoot2, "release-notes.json");
63
+ if (existsSync(filePath)) {
64
+ try {
65
+ return JSON.parse(readFileSync(filePath, "utf8"));
66
+ } catch {
67
+ return { current: "", notes: {} };
68
+ }
69
+ }
70
+ return { current: "", notes: {} };
71
+ }
72
+ function saveReleaseNotes(repoRoot2, data) {
73
+ const filePath = path.join(repoRoot2, "release-notes.json");
74
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
75
+ }
76
+ function updateReleaseNotes(repoRoot2, currentVersion) {
77
+ const data = loadReleaseNotes(repoRoot2);
78
+ const note = generateReleaseNotes(repoRoot2, currentVersion);
79
+ data.current = currentVersion;
80
+ data.notes[currentVersion] = note;
81
+ const versions = Object.keys(data.notes).sort().reverse();
82
+ if (versions.length > 20) {
83
+ for (const old of versions.slice(20)) delete data.notes[old];
84
+ }
85
+ saveReleaseNotes(repoRoot2, data);
86
+ return note;
87
+ }
88
+
89
+ // src/lib/platform-procedures.ts
90
+ var PLATFORM_PROCEDURES = [
91
+ // --- Foundation: what is exe-os ---
92
+ {
93
+ title: "What is exe-os \u2014 the operating model every agent must understand",
94
+ domain: "architecture",
95
+ priority: "p0",
96
+ content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO, CTO, CMO, engineers, and content production specialists. Each agent has identity, expertise, and experience layers \u2014 persistent memory that makes them better over time. All data is local-first, E2EE, owned by the user. The MCP server is the ONLY data interface \u2014 never access the DB directly."
97
+ },
98
+ {
99
+ title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
100
+ domain: "architecture",
101
+ priority: "p0",
102
+ content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code, Codex, or OpenCode. The founder picks their default tool at setup. The COO manages employees in tmux sessions. Each coordinator session is a separate window/project. Employees run in their own tmux panes via create_task auto-spawn. The founder talks to the COO; the COO orchestrates the team. The tool is the shell, exe-os is the brain."
103
+ },
104
+ {
105
+ title: "Sessions explained \u2014 coordinator session names and projects",
106
+ domain: "architecture",
107
+ priority: "p0",
108
+ content: "Each coordinator session is an isolated project session. One might be exe-os development, another might be exe-wiki. Each session spawns its own employees using {employee}-{coordinatorSession}. Sessions share the same memory DB but tasks are scoped to the session that created them. A founder can run multiple projects simultaneously. Sessions never interfere with each other."
109
+ },
110
+ {
111
+ title: "Runtime settings \u2014 COO can view and change tools per agent",
112
+ domain: "workflow",
113
+ priority: "p1",
114
+ content: "exe-os supports three tools: Claude Code (Anthropic), Codex (OpenAI), and OpenCode (open source, 75+ providers). Each agent can use a different tool and model. COO uses set_agent_config MCP tool to view or change settings. Call with no args to show all agents. Call with agent_id + runtime + model to change. Users can also run `exe-os settings` from terminal for interactive arrow-key selection."
115
+ },
116
+ // --- Updates and deployment ---
117
+ {
118
+ title: "How to update exe-os \u2014 CLI update + stack update (two steps)",
119
+ domain: "operations",
120
+ priority: "p0",
121
+ content: 'Updating exe-os is a two-step process. Step 1 \u2014 update the CLI: `npm install -g @askexenow/exe-os@latest` (gets bug fixes, new platform procedures, new tools). Step 2 \u2014 update the VPS stack (if applicable): `exe-os stack-update --target <version> --yes` (pulls new Docker images for exed, wiki, CRM, gateway). Step 1 MUST happen before Step 2 \u2014 the stack-update command itself may have bug fixes. After updating, restart the daemon: the deploy script handles this automatically. Check update availability: `exe-os update --check` or config(action="check_update"). Agents should NEVER run npm install or stack-update autonomously \u2014 always confirm with the user first.'
122
+ },
123
+ {
124
+ title: "First install \u2014 setup wizard and license activation",
125
+ domain: "operations",
126
+ priority: "p1",
127
+ content: "Fresh install: `npm install -g @askexenow/exe-os` then run `exe` to start the setup wizard. The wizard prompts for: encryption passphrase (creates master key), license key (exe_sk_* from AskExe team), COO name, and optional team members. No license key = free tier (1 employee, 5K memories). After setup: hooks install automatically, MCP server registers in ~/.claude.json, daemon starts. Verify health: run `exe-os healthcheck` or use mcp_ping() tool."
128
+ },
129
+ // --- Hierarchy and dispatch ---
130
+ {
131
+ title: "Chain of command \u2014 who talks to whom",
132
+ domain: "workflow",
133
+ priority: "p0",
134
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
135
+ },
136
+ {
137
+ title: "Customer orchestration maturity \u2014 recommend, never trap",
138
+ domain: "workflow",
139
+ priority: "p1",
140
+ content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
141
+ },
142
+ {
143
+ title: "Single dispatch path \u2014 create_task only",
144
+ domain: "workflow",
145
+ priority: "p0",
146
+ content: "create_task is the ONLY way to dispatch work to another agent. No direct ensureEmployee calls, no manual tmux spawns, no send_message for actionable work. create_task \u2192 system auto-spawns \u2192 session correctly named. ONE PATH. No backdoors. No exceptions."
147
+ },
148
+ // --- Session isolation ---
149
+ {
150
+ title: "Session scoping \u2014 stay in your coordinator boundary",
151
+ domain: "security",
152
+ priority: "p0",
153
+ content: "Session scoping is mandatory. Managers dispatch to workers within their own coordinator session ONLY. Employee sessions use {employee}-{coordinatorSession}. Cross-session dispatch is blocked by the system. Verify session names before dispatch. Tasks are scoped to the creating coordinator session."
154
+ },
155
+ {
156
+ title: "Session isolation \u2014 never touch another session's work",
157
+ domain: "workflow",
158
+ priority: "p0",
159
+ content: "Sessions are isolated. A coordinator session owns ONLY tasks it dispatched. (1) Never close/update/cancel tasks from another coordinator session. (2) Never review work from a different session \u2014 report that it belongs to another session and skip. (3) Ignore other sessions' items in list_tasks results. (4) Employees inherit session: employee sessions work ONLY on their parent coordinator session's tasks. Cross-session work is a system violation."
160
+ },
161
+ // --- Engineering: session scoping in code ---
162
+ {
163
+ title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
164
+ domain: "architecture",
165
+ priority: "p0",
166
+ content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching the current coordinator session. (2) Project \u2014 filter by project_name. (3) Role \u2014 agents only see data at their hierarchy level. When writing ANY function that touches tasks, reviews, messages, or notifications: always accept a sessionScope parameter and pass it to the SQL WHERE clause. Unscoped queries are bugs. Test by running 2+ coordinator sessions simultaneously."
167
+ },
168
+ // --- Hard constraints ---
169
+ {
170
+ title: "What you CANNOT do in exe-os \u2014 hard constraints",
171
+ domain: "security",
172
+ priority: "p0",
173
+ content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
174
+ },
175
+ {
176
+ title: "Customer patch triage \u2014 upstream bug vs customization",
177
+ domain: "support",
178
+ priority: "p0",
179
+ content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
180
+ },
181
+ {
182
+ title: "Bug report status check \u2014 surface available fixes on boot",
183
+ domain: "support",
184
+ priority: "p1",
185
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
186
+ },
187
+ {
188
+ title: "Feature request triage \u2014 upstream feature vs local customization",
189
+ domain: "support",
190
+ priority: "p0",
191
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
192
+ },
193
+ {
194
+ title: "Feature request status check \u2014 surface shipped features on boot",
195
+ domain: "support",
196
+ priority: "p1",
197
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
198
+ },
199
+ // --- Tool guidance ---
200
+ {
201
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
202
+ domain: "tools",
203
+ priority: "p2",
204
+ content: "The company_actions tool executes business actions through gateway connectors (e.g. send WhatsApp, trigger workflows, update CRM). It routes through the exe-gateway on the VPS. Actions are defined by the customer's gateway configuration \u2014 each connector (WhatsApp, Shopify, email, etc.) exposes specific actions. Use query_company_brain to find available data first, then company_actions to act on it. Requires gateway auth token. Read-only founders should NOT use this \u2014 it mutates external state."
205
+ },
206
+ // --- Release awareness ---
207
+ {
208
+ title: "What's New check \u2014 surface new features after update",
209
+ domain: "support",
210
+ priority: "p1",
211
+ content: "Once per session (COO boot only, never repeat), check if the installed exe-os version is newer than the last session. If it is, read the bundled release-notes.json (at the package root) and surface a brief summary to the founder: 'Updated to exe-os vX.Y.Z \u2014 N new features, M fixes.' List the top 3 features by name. This helps the founder know what they got from the update. If release-notes.json doesn't exist or the version hasn't changed, skip silently. Never repeat this check in the same session."
212
+ },
213
+ // --- Platform vs Customer ownership ---
214
+ {
215
+ title: "What the platform provides vs what you customize",
216
+ domain: "architecture",
217
+ priority: "p0",
218
+ content: "Exe OS has two layers. PLATFORM layer (shipped in code, updated via npm): platform procedures, default identity templates, MCP tools, tool gating, schema migrations, daemon behavior. You get improvements automatically on update. CUSTOMER layer (yours, stored locally): agent identities (exe.md files), behaviors, company procedures, config.json, wiki content, CRM data, memory. These are NEVER overwritten by updates. Identity templates are stamped once at /exe-new-employee \u2014 after that the file is yours. If the platform ships a better template, you can compare yours against the default with getTemplate() and merge what you want. Company procedures (company_procedure tool) layer ON TOP of platform procedures \u2014 both are injected, platform first. Behaviors are always yours. Config is always yours. The platform will never modify your local data."
219
+ },
220
+ // --- Updates ---
221
+ {
222
+ title: "How to update exe-os \u2014 CLI first, then stack",
223
+ domain: "operations",
224
+ priority: "p0",
225
+ content: "When bug fixes or features are available (surfaced at boot via list_my_bug_reports/list_my_feature_requests), update in two steps: (1) CLI: `npm install -g @askexenow/exe-os@latest` \u2014 this updates the local tools, MCP server, and bundled stack manifest. Must happen first so the stack-update tool itself has the latest fixes. (2) Stack (VPS only): `exe-os stack-update --target <version> --yes` \u2014 pulls new Docker images and restarts services. The target version comes from the stack manifest bundled with the CLI. Always update CLI before stack. Use `exe-os update --check` to see if a CLI update is available. Use `exe-os stack-update --check` to see stack status. For non-interactive/SSH: use `--yes` or `-y` flag. Never run `npm install -g` inside a tmux agent session \u2014 have the founder or COO do it from the host shell. If stack-update fails with EACCES on /opt/exe-stack, run: `sudo mkdir -p /opt/exe-stack && sudo chown $(whoami) /opt/exe-stack` then retry."
226
+ },
227
+ {
228
+ title: "CLI version vs stack version \u2014 two tracks, both normal",
229
+ domain: "operations",
230
+ priority: "p0",
231
+ content: "exe-os has TWO version numbers that move independently. This is normal \u2014 do not treat a mismatch as an error. (1) CLI version (e.g. 0.9.89, 0.9.90) \u2014 the npm package installed locally. Updates frequently: bug fixes, new MCP tools, platform procedures, search improvements, client-side changes. Update with `npm install -g @askexenow/exe-os@latest`. (2) Stack version (e.g. 0.9.7, 0.9.8) \u2014 the Docker images on the VPS. Updates less frequently: only when server-side daemon, gateway, wiki, or CRM images need rebuilding. Update with `exe-os stack-update --target <version> --yes`. The CLI version will almost always be higher than the stack version. A CLI at 0.9.90 with a stack at 0.9.8 is perfectly normal \u2014 it means the CLI got 12 patches since the last Docker image rebuild. Only update the stack when: (a) the boot brief surfaces a fix that mentions 'stack update required', (b) a new stack manifest version is bundled in the CLI (`exe-os stack-update --check` shows pending changes), or (c) AskExe support explicitly tells you to. Do NOT attempt to make the numbers match \u2014 they are separate tracks."
232
+ },
233
+ // --- Operations ---
234
+ {
235
+ title: "Managers must supervise deployed workers",
236
+ domain: "workflow",
237
+ priority: "p0",
238
+ content: `Every manager (COO/CTO/CMO) who dispatches work to a worker MUST actively monitor them. Check tmux capture-pane every 10 minutes. Verify they're working, not stuck. If idle at prompt with in_progress task \u2192 send intercom. If stuck \u2192 unblock or escalate. "Standing by" without checking is negligence.`
239
+ },
240
+ {
241
+ title: "COO boot health check \u2014 memory, cloud sync, daemon on every launch",
242
+ domain: "workflow",
243
+ priority: "p0",
244
+ content: "On every /exe boot, COO MUST check system health BEFORE other work: (1) daemon \u2014 is exed PID alive, (2) cloud sync \u2014 grep workers.log for recent cloud-sync errors, (3) memory count \u2014 total in DB, (4) sync delta \u2014 local vs cloud storage_bytes. Report as 4-line status table. If ANY check fails, surface to founder immediately. Do not proceed to tasks until health confirmed."
245
+ },
246
+ {
247
+ title: "exe-build-adv mandatory for 3+ files",
248
+ domain: "workflow",
249
+ priority: "p0",
250
+ content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
251
+ },
252
+ {
253
+ title: "Code context first for repository orientation",
254
+ domain: "workflow",
255
+ priority: "p1",
256
+ content: "Before broad repo exploration, symbol tracing, blast-radius review, or codebase Q&A, agents should use the consolidated code_context MCP tool instead of manual grep/read loops. Use action=index or stats to refresh/check the index; action=search with query, limit, offset, languages, paths, refresh_index for fresh multi-language code/doc search; action=trace for symbol imports/dependents; action=blast_radius for impact analysis before edits. CLI parity exists via exe-os code-context init|index|status|stats|search|doctor. Keep code_context separate from durable employee memory: promote only validated decisions, procedures, or lessons into store_memory/commit_memory."
257
+ },
258
+ {
259
+ title: "Commit discipline \u2014 never leave verified work floating",
260
+ domain: "workflow",
261
+ priority: "p1",
262
+ content: "After any code-change batch passes typecheck/tests/build, run git status, summarize changed files, and commit with a clear message before ending the session. If work must remain uncommitted for review/dogfood, explicitly say so, list the files, and state the blocker. Never imply work is complete while verified changes are still floating locally."
263
+ },
264
+ {
265
+ title: "Desktop and TUI are the same product",
266
+ domain: "architecture",
267
+ priority: "p0",
268
+ content: "Desktop and TUI are the SAME product in different renderers. Same data contracts, same interactions, same acceptance criteria. Desktop tab specs in ARCHITECTURE.md ARE the TUI specs. When building TUI, cross-reference Desktop spec. Different tab names, identical behavior. Never treat them as separate products."
269
+ },
270
+ // --- Orchestration golden path ---
271
+ {
272
+ title: "Task lifecycle \u2014 the golden path every agent follows",
273
+ domain: "workflow",
274
+ priority: "p0",
275
+ content: "create_task is dispatch + delivery. Task lifecycle: open \u2192 in_progress (you start) \u2192 done (update_task when finished) \u2192 needs_review (reviewer nudged) \u2192 closed (COO only via close_task). DB is the reliable delivery \u2014 intercom is just a speedup nudge. If you finish a task, self-chain: check for next task immediately (step 7). Never wait for a nudge. Never say 'standing by.'"
276
+ },
277
+ {
278
+ title: "Intercom is a speedup, not delivery \u2014 DB is the source of truth",
279
+ domain: "architecture",
280
+ priority: "p0",
281
+ content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
282
+ },
283
+ // --- Encryption key + cloud sync ---
284
+ {
285
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
286
+ domain: "security",
287
+ priority: "p0",
288
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
289
+ },
290
+ {
291
+ title: "Cloud endpoint is cloud.askexe.com \u2014 not askexe.com/cloud",
292
+ domain: "architecture",
293
+ priority: "p1",
294
+ content: "All cloud API calls (auth, sync, licensing, device registry, WebSocket) go to https://cloud.askexe.com, NOT https://askexe.com/cloud. This is a Cloudflare Workers Custom Domain that bypasses the zone-level managed challenge on askexe.com. Datacenter IPs (Hetzner, AWS, etc.) get HTTP 403 on askexe.com due to Bot Fight Mode, but cloud.askexe.com routes directly to the Worker before WAF rules evaluate. If a customer reports 403/challenge errors on cloud sync: verify they are on the latest exe-os version (cloud.askexe.com endpoint). Fix: `npm install -g @askexenow/exe-os@latest`. The EXE_CLOUD_ENDPOINT env var can override the endpoint if needed."
295
+ },
296
+ // --- MCP is the ONLY data interface ---
297
+ {
298
+ title: "MCP disconnect \u2014 ask the user, never work around it",
299
+ domain: "workflow",
300
+ priority: "p0",
301
+ content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
302
+ },
303
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
304
+ {
305
+ title: "MCP tool dispatch \u2014 all tools use action parameter",
306
+ domain: "tool-use",
307
+ priority: "p0",
308
+ content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
309
+ },
310
+ {
311
+ title: "MCP tools \u2014 memory, decision, and search",
312
+ domain: "tool-use",
313
+ priority: "p1",
314
+ content: `memory(action="recall") / recall_my_memory: search memories (semantic + FTS). Params: as_of (bi-temporal \u2014 what did I know at time X?), kind (decision|procedure|observation|raw|conversation|behavior), retrieval_mode (all|decisions_only|procedures_only|operational|recent_high_value). memory(action="ask_team") / ask_team_memory: search a colleague's memories. memory(action="store") / store_memory: persist a memory. Params: kind, procedure_for (domain tag for procedures). memory(action="commit") / commit_memory: high-importance, survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal window. Requires session_id + target_timestamp. memory(action="get_by_id"): fetch one memory by UUID with full untruncated text. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. memory(action="supersede"): replace an old memory with a new version (old_id + new text). decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
315
+ },
316
+ {
317
+ title: "MCP tools \u2014 task orchestration",
318
+ domain: "tool-use",
319
+ priority: "p1",
320
+ content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Params: blocked_by (task ID for dependency), parent_task_id (subtask hierarchy), reviewer, complexity (routine|standard|complex|critical), budget_tokens (max token cap), budget_fallback_model, spawn_runtime (override runtime: claude|codex|opencode), spawn_model (override model). task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
321
+ },
322
+ {
323
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
324
+ domain: "tool-use",
325
+ priority: "p1",
326
+ content: 'graph(action="query_relationships") / query_relationships: find connections between entities. graph(action="entity_neighbors") / get_entity_neighbors: explore direct connections. graph(action="hot_entities") / get_hot_entities: most-referenced entities. graph(action="stats") / get_graph_stats: entity/relationship counts. graph(action="export") / export_graph: export for visualization (output_path, format). graph(action="merge_entities") / merge_entities: deduplicate entities (source_id, target_id). graph(action="similar_trajectories") / find_similar_trajectories: match patterns to past solutions.'
327
+ },
328
+ {
329
+ title: "MCP tools \u2014 identity, behavior, and support",
330
+ domain: "tool-use",
331
+ priority: "p1",
332
+ content: `identity(action="get") / get_identity: read an agent's exe.md (Layer 1 identity). identity(action="update") / update_identity: write an agent's exe.md. Identity > behavior. behavior(action="store") / store_behavior: record a correction or pattern (Layer 2 expertise). behavior(action="list") / list_behaviors: view active behaviors. behavior(action="deactivate") / deactivate_behavior: soft-delete a stale behavior. support(action="create_bug") / create_bug_report: file a bug report to AskExe (title, description, severity). support(action="create_feature") / create_feature_request: file a feature request. support(action="health") / support_health: check support server readiness. support(action="triage_bug"): triage a bug (id, triage_notes). AskExe-internal only. CRITICAL: triage uses triage_notes NOT notes.`
333
+ },
334
+ {
335
+ title: "MCP tools \u2014 communication and messaging",
336
+ domain: "tool-use",
337
+ priority: "p1",
338
+ content: 'message(action="send") / send_message: send context to another agent (NOT for actionable work \u2014 use task). message(action="acknowledge") / acknowledge_messages: mark messages as read. reminder(action="create") / create_reminder: set a reminder (text, due_date). Shown in boot brief. reminder(action="list") / list_reminders: view pending reminders. reminder(action="complete") / complete_reminder: mark done (reminder_id). session(action="events") / get_session_events: view session event log (session_id). session(action="last_response") / get_last_assistant_response: get most recent response.'
339
+ },
340
+ {
341
+ title: "MCP tools \u2014 wiki, documents, CRM, and data",
342
+ domain: "tool-use",
343
+ priority: "p1",
344
+ content: 'wiki(action="list") / list_wiki_pages: list wiki pages (workspace). wiki(action="get") / get_wiki_page: read a wiki page (workspace, title or document_id). document(action="ingest") / ingest_document: import a file as memory chunks (workspace_id, filename, chunks). document(action="list") / list_documents: browse documents (workspace_id). document(action="purge") / purge_document: remove a document (document_id). document(action="set_importance") / set_document_importance: adjust chunk scores. document(action="rerank") / rerank_documents: re-score relevance (query, candidates). crm(action="list_people|get_person|list_tables|describe_table"): CRM records from exe-db. raw_data(action="list_sources|query|get"): read raw landing-pad events. gateway(action="send_whatsapp") / send_whatsapp: send WhatsApp message (recipients, message). gateway(action="query_conversations") / query_conversations: search conversations across channels. gateway(action="query_company_brain") / query_company_brain: unified RAG across company knowledge.'
345
+ },
346
+ {
347
+ title: "MCP tools \u2014 admin, config, and operations",
348
+ domain: "tool-use",
349
+ priority: "p1",
350
+ content: 'config(action="list_employees"): view roster. config(action="set_agent_config"): view or change per-agent runtime + model. Call with no args to show all agents. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. Supports cloud_action param: status|sync|reupload. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="worker_gate"): check spawn slot availability \u2014 alive/stale/reserved counts vs max. Use before dispatching. config(action="auto_wake_status"): orphaned tasks, blocked tasks, auto-wake retry status. config(action="orchestration_phase"): view/change org phase (phase_1_coo|phase_2_executives|phase_3_parallel_org). config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. diagnostics(action="pending_work_summary"): pending reviews + messages + notifications in one call. diagnostics(action="rename_employee"): rename an agent across all systems (roster, identity, DB, symlinks). diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role. mcp_ping(): daemon health + license status + tool usage stats.'
351
+ }
352
+ ];
353
+ var PLATFORM_PROCEDURE_TITLES = new Set(
354
+ PLATFORM_PROCEDURES.map((p) => p.title)
355
+ );
356
+
357
+ // src/lib/platform-audit.ts
358
+ function extractToolMeta() {
359
+ return [
360
+ { name: "memory", description: "Recall, store, commit, search, consolidate memories", actions: ["recall", "ask_team", "store", "commit", "search", "session_context", "consolidate", "cardinality", "supersede"] },
361
+ { name: "task", description: "Create, list, get, update, close, checkpoint tasks", actions: ["create", "list", "get", "update", "close", "checkpoint", "resume"] },
362
+ { name: "behavior", description: "Store, list, deactivate behavioral patterns", actions: ["store", "list", "deactivate"] },
363
+ { name: "identity", description: "Get, update agent identities", actions: ["get", "update", "list"] },
364
+ { name: "message", description: "Send messages between agents", actions: ["send", "acknowledge"] },
365
+ { name: "reminder", description: "Create, list, complete reminders", actions: ["create", "list", "complete"] },
366
+ { name: "document", description: "Ingest, list, purge, rerank documents", actions: ["ingest", "list", "purge", "rerank", "set_importance"] },
367
+ { name: "graph", description: "Query relationships, entity neighbors, hot entities, export graph", actions: ["query_relationships", "entity_neighbors", "hot_entities", "stats", "export", "merge_entities"] },
368
+ { name: "wiki", description: "List and get wiki pages from exe-wiki", actions: ["list", "get"] },
369
+ { name: "gateway", description: "Send WhatsApp messages, query conversations", actions: ["send_whatsapp", "query_conversations"] },
370
+ { name: "config", description: "Manage triggers, starter packs, orchestration, skills", actions: ["create_trigger", "list_triggers", "apply_starter_pack", "load_skill", "deploy_client", "export_orchestration", "import_orchestration"] },
371
+ { name: "crm", description: "Read-only CRM access from exe-db", actions: ["list_people", "get_person", "list_tables", "describe_table"] },
372
+ { name: "raw_data", description: "Read-only access to exe-db raw events landing pad", actions: ["list_sources", "query", "get"] },
373
+ { name: "code_context", description: "Codebase search, trace, blast_radius with semantic vector search", actions: ["index", "index_embed", "search", "trace", "blast_radius", "stats"] },
374
+ { name: "decision", description: "Store and retrieve authoritative decisions by domain", actions: ["store", "get"] },
375
+ { name: "support", description: "File bug reports and feature requests to AskExe", actions: ["create_bug", "create_feature", "list_my_bugs", "list_my_features"] },
376
+ { name: "diagnostics", description: "Health checks, update status, cloud status, key management", actions: ["healthcheck", "doctor", "status_brief", "check_update", "stack_update_check", "cloud_status", "tool_search"] },
377
+ { name: "query_company_brain", description: "Search across VPS Company Brain: raw events, CRM, wiki, gateway conversations", actions: ["search", "list_sources", "sql"] },
378
+ { name: "company_actions", description: "Execute company actions through gateway connectors" },
379
+ { name: "company_procedure", description: "Store and manage organization-wide procedures", actions: ["store", "list", "deactivate"] }
380
+ ];
381
+ }
382
+ function isCovered(toolName, action, procedures) {
383
+ const searchTerms = [toolName.toLowerCase()];
384
+ if (action) searchTerms.push(action.toLowerCase());
385
+ return procedures.some((proc) => {
386
+ const text = (proc.title + " " + proc.content).toLowerCase();
387
+ return searchTerms.every((term) => text.includes(term));
388
+ });
389
+ }
390
+ function generateProcedure(tool, action) {
391
+ const toolLabel = action ? `${tool.name}:${action}` : tool.name;
392
+ const actionList = tool.actions?.join(", ") ?? "";
393
+ let content;
394
+ if (action) {
395
+ content = `The ${tool.name} tool supports action="${action}". ${tool.description}. Use this when you need to ${action.replace(/_/g, " ")} within the ${tool.name} domain. Available actions: ${actionList}. Call via MCP: ${tool.name}(action="${action}", ...).`;
396
+ } else {
397
+ content = `${tool.description}. ` + (actionList ? `Available actions: ${actionList}. ` : "") + `This tool is available via MCP. Use it when the task requires ${tool.name.replace(/_/g, " ")} operations.`;
398
+ }
399
+ if (content.length > 500) content = content.slice(0, 497) + "...";
400
+ return {
401
+ title: `How to use ${toolLabel}`,
402
+ content,
403
+ priority: "p2",
404
+ domain: "tools"
405
+ };
406
+ }
407
+ function auditPlatformProcedures() {
408
+ const tools = extractToolMeta();
409
+ const gaps2 = [];
410
+ for (const tool of tools) {
411
+ if (!isCovered(tool.name, void 0, PLATFORM_PROCEDURES)) {
412
+ gaps2.push({
413
+ toolName: tool.name,
414
+ description: tool.description,
415
+ generatedProcedure: generateProcedure(tool)
416
+ });
417
+ }
418
+ }
419
+ return gaps2;
420
+ }
421
+
422
+ // src/bin/pre-publish.ts
423
+ var __dirname = path2.dirname(fileURLToPath(import.meta.url));
424
+ var repoRoot = path2.resolve(__dirname, "../..");
425
+ var pkg = JSON.parse(readFileSync2(path2.join(repoRoot, "package.json"), "utf8"));
426
+ var version = pkg.version;
427
+ console.log(`
428
+ [pre-publish] exe-os v${version}
429
+ `);
430
+ console.log("[pre-publish] Generating release notes...");
431
+ var notes = updateReleaseNotes(repoRoot, version);
432
+ console.log(` Features: ${notes.features.length}`);
433
+ console.log(` Fixes: ${notes.fixes.length}`);
434
+ console.log(` Security: ${notes.security.length}`);
435
+ if (notes.features.length > 0) {
436
+ for (const f of notes.features.slice(0, 5)) console.log(` + ${f}`);
437
+ if (notes.features.length > 5) console.log(` ... and ${notes.features.length - 5} more`);
438
+ }
439
+ console.log("\n[pre-publish] Auditing platform procedures...");
440
+ var gaps = auditPlatformProcedures();
441
+ if (gaps.length === 0) {
442
+ console.log(" \u2713 All MCP tools have matching platform procedures.");
443
+ } else {
444
+ console.log(` \u2717 ${gaps.length} tool(s) without platform procedures:`);
445
+ for (const gap of gaps) {
446
+ console.log(` - ${gap.toolName}: ${gap.description}`);
447
+ console.log(` Suggested: "${gap.generatedProcedure.title}"`);
448
+ }
449
+ console.log("\n These tools ship without COO instructions.");
450
+ console.log(" Add procedures to platform-procedures.ts before publish.");
451
+ }
452
+ console.log("\n[pre-publish] Release summary:");
453
+ console.log(` Features: ${notes.features.length}, Fixes: ${notes.fixes.length}, Security: ${notes.security.length}`);
454
+ if (notes.migration_notes.length > 0) {
455
+ console.log(` \u26A0 Migration notes: ${notes.migration_notes.length}`);
456
+ for (const m of notes.migration_notes) console.log(` \u2192 ${m}`);
457
+ }
458
+ console.log("\n[pre-publish] Done.\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.101",
3
+ "version": "0.9.102",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -85,7 +85,7 @@
85
85
  "build": "tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/assets/statusline-command.sh dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh",
86
86
  "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/assets/statusline-command.sh dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. Run /mcp in active sessions to reconnect.'",
87
87
  "postinstall": "node dist/bin/install.js --commands-only 2>/dev/null || true",
88
- "prepublishOnly": "npm run typecheck && npm run build && node dist/bin/customer-readiness.js",
88
+ "prepublishOnly": "npm run typecheck && npm run build && node dist/bin/customer-readiness.js && node dist/bin/pre-publish.js",
89
89
  "test:publish": "npx vitest run --maxWorkers=4 --exclude 'tests/tui/**' --exclude 'tests/lib/tmux-routing.test.ts' --exclude 'tests/lib/intercom-routing.test.ts' --exclude 'tests/gateway/**' --exclude 'tests/installer/setup-wizard.test.ts' --exclude 'tests/mcp/ingest-document.test.ts' --exclude 'tests/lib/hybrid-search.test.ts' --exclude 'tests/lib/worker-gate.test.ts'",
90
90
  "benchmark:longmemeval": "npx tsx tests/benchmarks/longmemeval.ts"
91
91
  },