@hiveku-apps/sync 0.3.3 → 0.3.6
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 +70 -3
- package/bin/hiveku-sync.mjs +33 -0
- package/package.json +1 -1
- package/src/account-memory.mjs +240 -0
- package/src/codex-prompt-tools.json +252 -0
- package/src/codex.mjs +255 -28
- package/src/init.mjs +21 -0
- package/src/projects.mjs +8 -0
- package/src/pull.mjs +55 -7
- package/src/toml.mjs +666 -0
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Local sync CLI for a Hiveku account. Pairs with the Hiveku MCP server at
|
|
|
8
8
|
2. **`hiveku-sync pull`** — fetch the account's memory/rules/skills via the
|
|
9
9
|
`memory_list` MCP tool and write each entry as a Markdown file with
|
|
10
10
|
YAML frontmatter (name, type, domain, department, version, updated_at).
|
|
11
|
+
It also writes the account memory (see "Account memory" below) to
|
|
12
|
+
`hiveku-data/account/ACCOUNT_MEMORY.md`, read-only.
|
|
11
13
|
|
|
12
14
|
The goal: open one folder per Hiveku tenant in VS Code with Claude Code,
|
|
13
15
|
and the local `.md` files become the source-of-truth Claude reads. Tools
|
|
@@ -106,9 +108,52 @@ This writes, merge-safely (only Hiveku-managed regions are touched):
|
|
|
106
108
|
and deprecated).
|
|
107
109
|
|
|
108
110
|
Pass `--trust` to pre-trust the folder in `~/.codex/config.toml` (so Codex loads
|
|
109
|
-
the repo config without its first-run trust prompt).
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
the repo config without its first-run trust prompt).
|
|
112
|
+
|
|
113
|
+
**Approvals.** Codex asks first only for a **fixed list** of high-risk Hiveku
|
|
114
|
+
tools: the 246 names in `src/codex-prompt-tools.json`, the same list as the
|
|
115
|
+
Hiveku Codex plugin 0.1.16. It includes `deploy_site`, `site_delete`,
|
|
116
|
+
`project_files_bulk_save` and every `hiveku_batch`, and it is written as
|
|
117
|
+
`[mcp_servers.hiveku.tools.<tool>] approval_mode = "prompt"`. **Every other tool
|
|
118
|
+
on the `hiveku` server runs without asking** (`default_tools_approval_mode =
|
|
119
|
+
"approve"`), and that includes many deletes and sends, for example
|
|
120
|
+
`crm_delete_contact`, `content_delete` and `survey_send`. So the safe-work rules
|
|
121
|
+
in `AGENTS.md` still apply to every tool: they are instructions, not enforced
|
|
122
|
+
permissions.
|
|
123
|
+
|
|
124
|
+
**Your own setting wins.** When `.codex/config.toml` already has your own
|
|
125
|
+
`[mcp_servers.hiveku.tools.<tool>]` table for a listed tool, outside the
|
|
126
|
+
hiveku-sync block, hiveku-sync leaves that tool out of its block (TOML does not
|
|
127
|
+
allow a table to be declared twice) and prints a line naming it. If your table
|
|
128
|
+
sets no `approval_mode`, Codex uses the server default, `approve`, so that tool
|
|
129
|
+
runs without asking, and the line says so. Add `approval_mode = "prompt"` to
|
|
130
|
+
your table to have it ask first.
|
|
131
|
+
|
|
132
|
+
hiveku-sync checks the updated file before it writes anything. It stops only
|
|
133
|
+
when its own block would declare a table or key that another line of the file
|
|
134
|
+
also declares, for example your own copy of `[mcp_servers.hiveku]`. Then it
|
|
135
|
+
writes nothing in the folder and names that line by its number. It reads the
|
|
136
|
+
file as Codex does, so TOML 1.1 syntax and a byte-order mark at the start are
|
|
137
|
+
fine, and it leaves the rest of the file, including any line it cannot read,
|
|
138
|
+
to you and Codex.
|
|
139
|
+
|
|
140
|
+
The folder has to carry the list itself: Codex uses the folder's
|
|
141
|
+
`[mcp_servers.hiveku]` instead of the plugin's, including for approvals, so the
|
|
142
|
+
plugin's prompts do not apply here.
|
|
143
|
+
|
|
144
|
+
Headless and bypass modes:
|
|
145
|
+
|
|
146
|
+
- **`codex exec` never stops to ask.** Under a `read-only` or `workspace-write`
|
|
147
|
+
sandbox it cancels the listed calls and runs the rest.
|
|
148
|
+
- **The bypass flag skips every prompt.** With
|
|
149
|
+
`--dangerously-bypass-approvals-and-sandbox` (`--yolo`), in `codex exec` or in
|
|
150
|
+
interactive `codex`, and with `danger-full-access` in `codex exec`
|
|
151
|
+
(`-s danger-full-access` or `sandbox_mode = "danger-full-access"`), the listed
|
|
152
|
+
tools run unattended too, like every other Hiveku tool.
|
|
153
|
+
|
|
154
|
+
Folders set up by a hiveku-sync without these prompts (0.3.5 and earlier)
|
|
155
|
+
approve every tool: run `hiveku-sync codex` in them once, with a release that has
|
|
156
|
+
them, to add the prompts.
|
|
112
157
|
|
|
113
158
|
> **This vs. the plugin.** For a single account, you can instead install the
|
|
114
159
|
> published [Hiveku Codex plugin](https://github.com/locus2012/hiveku-codex-plugin)
|
|
@@ -182,3 +227,25 @@ For **projects**, `hiveku-sync push` / `deploy` write straight back to Hiveku
|
|
|
182
227
|
(see "Coder projects" above). For **memory/rules/skills**, `pull` is read-only —
|
|
183
228
|
persist changes with the `memory_create` / `memory_update` / `memory_delete` MCP
|
|
184
229
|
tools through Claude, then `hiveku-sync pull` to refresh the local copies.
|
|
230
|
+
|
|
231
|
+
## Account memory
|
|
232
|
+
|
|
233
|
+
The account memory is the business facts every Hiveku department agent reads.
|
|
234
|
+
Owners and admins edit it on the Hiveku dashboard, under Account memory
|
|
235
|
+
(`https://app.hiveku.com/<account id>/dashboard/memory`); no tool sets it.
|
|
236
|
+
|
|
237
|
+
`pull` reads it with `account_memory_get` and writes one file,
|
|
238
|
+
`hiveku-data/account/ACCOUNT_MEMORY.md`, the same file, byte for byte, that the
|
|
239
|
+
Hiveku plugin and the VS Code extension write. The file is read-only on disk and
|
|
240
|
+
starts with a note saying where it is edited, with the link (the unscoped
|
|
241
|
+
`https://app.hiveku.com/dashboard/memory` when the account id cannot be read),
|
|
242
|
+
and that it is internal to the team. The owners' text comes first, then the suggestions
|
|
243
|
+
agents and team members made that an owner has not reviewed yet, each with who
|
|
244
|
+
suggested it and when. The two account-memory rows (`account` and
|
|
245
|
+
`account-suggestions`) are never written under `memory/`, even when an older
|
|
246
|
+
server still lists them.
|
|
247
|
+
|
|
248
|
+
Nothing uploads this file: `push`, `commit` and `status` leave any
|
|
249
|
+
`hiveku-data/account/` folder out, and the next `pull` replaces it. To add a
|
|
250
|
+
fact, suggest it with the `account_memory_append` tool; an owner keeps or
|
|
251
|
+
removes it on the dashboard.
|
package/bin/hiveku-sync.mjs
CHANGED
|
@@ -116,6 +116,30 @@ Ship a project (the agent-native deploy loop):
|
|
|
116
116
|
Profiles: full, sales, marketing, helpdesk, pm, dev
|
|
117
117
|
`;
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Plain lines naming the listed tools whose own config.toml table hiveku-sync
|
|
121
|
+
* left alone. A table that sets no approval_mode gets the server default
|
|
122
|
+
* ("approve"), so those tools are said to run without asking.
|
|
123
|
+
*/
|
|
124
|
+
function userToolTablesLines({ userToolTables = [], userTablesWithoutApprovalMode = [] }) {
|
|
125
|
+
const lines = [];
|
|
126
|
+
const kept = userToolTables.filter((tool) => !userTablesWithoutApprovalMode.includes(tool));
|
|
127
|
+
if (kept.length) {
|
|
128
|
+
const them = kept.length === 1 ? 'it' : 'them';
|
|
129
|
+
lines.push(
|
|
130
|
+
`your own setting kept for ${kept.join(', ')}: .codex/config.toml sets ${them} outside the hiveku-sync block, so hiveku-sync did not add its "ask first" setting for ${them}.`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const bare = userTablesWithoutApprovalMode;
|
|
134
|
+
if (bare.length) {
|
|
135
|
+
const one = bare.length === 1;
|
|
136
|
+
lines.push(
|
|
137
|
+
`${bare.join(', ')} ${one ? 'runs' : 'run'} without asking: your own ${one ? 'table' : 'tables'} for ${one ? 'it' : 'them'} in .codex/config.toml, outside the hiveku-sync block, ${one ? 'sets' : 'set'} no approval_mode, so Codex uses the server default ("approve"). To have ${one ? 'it' : 'them'} ask first, add approval_mode = "prompt" to ${one ? 'that table' : 'those tables'}.`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return lines;
|
|
141
|
+
}
|
|
142
|
+
|
|
119
143
|
function commitMessageFrom(flags) {
|
|
120
144
|
// --commit "msg" | --commit=msg | -m "msg" | --message "msg". A bare --commit
|
|
121
145
|
// (boolean true) means "commit with no message" → fall back to the default.
|
|
@@ -163,6 +187,7 @@ async function main() {
|
|
|
163
187
|
? ` MCP auth: inlined your OLYMPUS_API_KEY`
|
|
164
188
|
: ` 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=...\``,
|
|
165
189
|
);
|
|
190
|
+
for (const line of userToolTablesLines(result.codex)) console.log(` ${line}`);
|
|
166
191
|
if (result.codex.trust === 'added') console.log(` trust: pre-trusted this folder in ~/.codex/config.toml`);
|
|
167
192
|
}
|
|
168
193
|
console.log(` then: cd ${result.folder} && hiveku-sync pull`);
|
|
@@ -172,6 +197,7 @@ async function main() {
|
|
|
172
197
|
const result = await runCodex({ folder: positional[0], trust: !!flags['trust'] });
|
|
173
198
|
console.log(`Codex lane ready in ${result.folder} (profile=${result.profile})`);
|
|
174
199
|
console.log(` AGENTS.md + .codex/config.toml written (git-ignored).`);
|
|
200
|
+
for (const line of userToolTablesLines(result)) console.log(` ${line}`);
|
|
175
201
|
console.log(
|
|
176
202
|
result.inlinedKey
|
|
177
203
|
? ` MCP auth: inlined OLYMPUS_API_KEY from .env.`
|
|
@@ -189,6 +215,13 @@ async function main() {
|
|
|
189
215
|
for (const [type, count] of Object.entries(summary.by_type)) {
|
|
190
216
|
console.log(` ${type}: ${count}`);
|
|
191
217
|
}
|
|
218
|
+
if (summary.account_memory) {
|
|
219
|
+
const am = summary.account_memory;
|
|
220
|
+
console.log(
|
|
221
|
+
` account memory: ${am.file} (read-only; ${am.version > 0 ? `version ${am.version}` : 'not written yet'}, ` +
|
|
222
|
+
`${am.suggestions} suggestion${am.suggestions === 1 ? '' : 's'} waiting). Owners edit it on the dashboard.`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
192
225
|
if (summary.removed_remotely.length > 0) {
|
|
193
226
|
console.log('\nNote: these files were present in the last sync but are no longer in Olympus:');
|
|
194
227
|
for (const f of summary.removed_remotely) console.log(` ${f}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiveku-apps/sync",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
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",
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account memory, pulled read-only.
|
|
3
|
+
*
|
|
4
|
+
* The account memory is the business facts every Hiveku department agent reads.
|
|
5
|
+
* It lives in two rows of account_ai_memory, and neither is a department:
|
|
6
|
+
*
|
|
7
|
+
* domain `account` the owner's document (owners and admins edit
|
|
8
|
+
* it on the dashboard, nowhere else);
|
|
9
|
+
* domain `account-suggestions` one-line suggestions from agents, MCP clients
|
|
10
|
+
* and team members, waiting for an owner's
|
|
11
|
+
* Keep or Remove (JSON Lines).
|
|
12
|
+
*
|
|
13
|
+
* Neither row may land in the memory/ tree as if it were a department's
|
|
14
|
+
* document: `account-suggestions` would be raw JSON Lines, and a local edit to
|
|
15
|
+
* either would look like something `memory_update` could write back, which the
|
|
16
|
+
* server refuses. So `hiveku-sync pull` classifies both domains explicitly and
|
|
17
|
+
* writes one read-only file instead, in the same place the Hiveku plugin does:
|
|
18
|
+
*
|
|
19
|
+
* hiveku-data/account/ACCOUNT_MEMORY.md
|
|
20
|
+
*
|
|
21
|
+
* from `account_memory_get`. Nothing in hiveku-sync ever uploads that file:
|
|
22
|
+
* there is no tool that sets the account memory, and `push` leaves the folder
|
|
23
|
+
* out (see isAccountMemoryPath).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { chmod } from 'node:fs/promises';
|
|
27
|
+
import { existsSync } from 'node:fs';
|
|
28
|
+
|
|
29
|
+
import { writeFileAtomic } from './util.mjs';
|
|
30
|
+
|
|
31
|
+
export const ACCOUNT_MEMORY_DOMAIN = 'account';
|
|
32
|
+
export const ACCOUNT_SUGGESTIONS_DOMAIN = 'account-suggestions';
|
|
33
|
+
export const ACCOUNT_MEMORY_DOMAINS = new Set([ACCOUNT_MEMORY_DOMAIN, ACCOUNT_SUGGESTIONS_DOMAIN]);
|
|
34
|
+
|
|
35
|
+
/** Folder and file, relative to the account folder (same layout as the plugin). */
|
|
36
|
+
export const ACCOUNT_DATA_DIR = 'hiveku-data/account';
|
|
37
|
+
export const ACCOUNT_MEMORY_FILE = `${ACCOUNT_DATA_DIR}/ACCOUNT_MEMORY.md`;
|
|
38
|
+
|
|
39
|
+
export const APP_URL = 'https://app.hiveku.com';
|
|
40
|
+
|
|
41
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42
|
+
|
|
43
|
+
export function isAccountMemoryDomain(domain) {
|
|
44
|
+
return typeof domain === 'string' && ACCOUNT_MEMORY_DOMAINS.has(domain.trim());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Where a memory_list row belongs. The two account domains are named
|
|
49
|
+
* explicitly, before any folder or department tag is looked at: they carry no
|
|
50
|
+
* department tag, so a generic rule would file them under memory/ (or, in a
|
|
51
|
+
* by-department layout, invent an "account" department).
|
|
52
|
+
*
|
|
53
|
+
* { kind: 'account' } the owner document (written from account_memory_get)
|
|
54
|
+
* { kind: 'account-suggestions' } the suggestions row (listed under the document)
|
|
55
|
+
* { kind: 'knowledge', folder } everything else, in its type's folder
|
|
56
|
+
*/
|
|
57
|
+
export function classifyMemoryEntry(entry, folder) {
|
|
58
|
+
const domain = typeof entry?.domain === 'string' ? entry.domain.trim() : '';
|
|
59
|
+
if (domain === ACCOUNT_MEMORY_DOMAIN) return { kind: 'account' };
|
|
60
|
+
if (domain === ACCOUNT_SUGGESTIONS_DOMAIN) return { kind: 'account-suggestions' };
|
|
61
|
+
return { kind: 'knowledge', folder };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* True for a path (relative, forward or back slashes) inside the pulled
|
|
66
|
+
* account-memory folder, wherever that folder sits. `push` drops these paths,
|
|
67
|
+
* so the read-only copy can never be uploaded as site code.
|
|
68
|
+
*/
|
|
69
|
+
export function isAccountMemoryPath(relPath) {
|
|
70
|
+
const p = String(relPath || '').replace(/\\/g, '/').replace(/^\.?\//, '');
|
|
71
|
+
return p === ACCOUNT_DATA_DIR || p.startsWith(`${ACCOUNT_DATA_DIR}/`) || p.includes(`/${ACCOUNT_DATA_DIR}/`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const ACCOUNT_MEMORY_TOOL = 'account_memory_get';
|
|
75
|
+
|
|
76
|
+
/** The builder's dashboard page (src/app/dashboard/memory, scoped mirror under /[accountId]). */
|
|
77
|
+
const DASHBOARD_PATH = 'dashboard/memory';
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* ONE FILE, THREE WRITERS. The Claude Code plugin (lib/account-memory.mjs),
|
|
81
|
+
* the VS Code extension (src/accountMemory.ts) and hiveku-sync all write
|
|
82
|
+
* hiveku-data/account/ACCOUNT_MEMORY.md, and the plugin reads its front matter
|
|
83
|
+
* back (`fetched_at`, for `pull --list` and `--stale`). So everything from here
|
|
84
|
+
* to writeReadOnly is a port of the plugin's normalizeAccountMemory,
|
|
85
|
+
* accountMemoryDashboardUrl and renderAccountMemoryFile, and
|
|
86
|
+
* test/account-memory.test.mjs compares the two byte for byte whenever a plugin
|
|
87
|
+
* checkout sits beside this repo. Change them together.
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The page where owners and admins edit it. Account-scoped when the account id
|
|
92
|
+
* is a real UUID (the scoped route opens on the right account for a person in
|
|
93
|
+
* several); the unscoped page otherwise, never a half-built URL.
|
|
94
|
+
*/
|
|
95
|
+
export function accountMemoryDashboardUrl(accountId, appUrl = APP_URL) {
|
|
96
|
+
const base = String(appUrl || APP_URL).replace(/\/+$/, '');
|
|
97
|
+
return typeof accountId === 'string' && UUID_RE.test(accountId)
|
|
98
|
+
? `${base}/${accountId.toLowerCase()}/${DASHBOARD_PATH}`
|
|
99
|
+
: `${base}/${DASHBOARD_PATH}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The tool result, checked. account_memory_get returns the builder's
|
|
104
|
+
* `{ data: { content, version, updated_at, bytes, suggestions, suggestions_version,
|
|
105
|
+
* injected, truncated } }`. Anything else is an error, not an empty memory:
|
|
106
|
+
* writing "nothing written yet" over a real document because the shape moved
|
|
107
|
+
* would be a lie on disk.
|
|
108
|
+
*/
|
|
109
|
+
export function normalizeAccountMemory(payload) {
|
|
110
|
+
const inner = payload && typeof payload === 'object' && !Array.isArray(payload) && 'data' in payload ? payload.data : payload;
|
|
111
|
+
if (!inner || typeof inner !== 'object' || Array.isArray(inner)) {
|
|
112
|
+
throw new Error(`${ACCOUNT_MEMORY_TOOL} returned an unexpected shape (no object)`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof inner.content !== 'string' || !Number.isFinite(Number(inner.version))) {
|
|
115
|
+
throw new Error(`${ACCOUNT_MEMORY_TOOL} returned an unexpected shape (no content or version)`);
|
|
116
|
+
}
|
|
117
|
+
const suggestions = Array.isArray(inner.suggestions) ? inner.suggestions : [];
|
|
118
|
+
return {
|
|
119
|
+
content: inner.content,
|
|
120
|
+
version: Number(inner.version),
|
|
121
|
+
updated_at: typeof inner.updated_at === 'string' ? inner.updated_at : null,
|
|
122
|
+
suggestions: suggestions
|
|
123
|
+
.filter((s) => s && typeof s === 'object' && typeof s.text === 'string' && s.text.trim())
|
|
124
|
+
.map((s) => ({
|
|
125
|
+
id: typeof s.id === 'string' ? s.id : '',
|
|
126
|
+
at: typeof s.at === 'string' ? s.at : '',
|
|
127
|
+
source: typeof s.source === 'string' && s.source.trim() ? s.source : 'Unknown',
|
|
128
|
+
text: s.text,
|
|
129
|
+
})),
|
|
130
|
+
suggestions_version: Number.isFinite(Number(inner.suggestions_version)) ? Number(inner.suggestions_version) : 0,
|
|
131
|
+
truncated: inner.truncated === true,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** "2026-09-23 14:02 UTC", or the raw value when it is not a date. */
|
|
136
|
+
function readableTime(value) {
|
|
137
|
+
if (!value) return 'unknown time';
|
|
138
|
+
const d = new Date(value);
|
|
139
|
+
if (Number.isNaN(d.getTime())) return String(value);
|
|
140
|
+
return `${d.toISOString().slice(0, 16).replace('T', ' ')} UTC`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** One line, no line breaks: a suggestion is one line on the server too. */
|
|
144
|
+
function oneLine(value) {
|
|
145
|
+
return String(value).replace(/[\r\n\u2028\u2029]+/g, ' ').trim();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function yamlString(value) {
|
|
149
|
+
return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The file text. Plain language: the person reading it may be the owner.
|
|
154
|
+
* Held suggestions (from the agents that talk to customers, and onboarding)
|
|
155
|
+
* are never in the tool's answer, so they are never here either.
|
|
156
|
+
*/
|
|
157
|
+
export function renderAccountMemoryFile({ memory, accountId, fetchedAt, appUrl = APP_URL }) {
|
|
158
|
+
const editUrl = accountMemoryDashboardUrl(accountId, appUrl);
|
|
159
|
+
const fm = [
|
|
160
|
+
'---',
|
|
161
|
+
`read_only: true`,
|
|
162
|
+
`source_tool: ${ACCOUNT_MEMORY_TOOL}`,
|
|
163
|
+
`version: ${memory.version}`,
|
|
164
|
+
...(memory.updated_at ? [`updated_at: ${yamlString(memory.updated_at)}`] : []),
|
|
165
|
+
`suggestions: ${memory.suggestions.length}`,
|
|
166
|
+
`suggestions_version: ${memory.suggestions_version}`,
|
|
167
|
+
`fetched_at: ${yamlString(fetchedAt)}`,
|
|
168
|
+
`edit_url: ${yamlString(editUrl)}`,
|
|
169
|
+
'---',
|
|
170
|
+
'',
|
|
171
|
+
];
|
|
172
|
+
const lines = [
|
|
173
|
+
'# Account memory',
|
|
174
|
+
'',
|
|
175
|
+
'> This is a read-only copy of the account memory, the facts about the business that every',
|
|
176
|
+
'> Hiveku department agent reads. Owners and admins edit it on the Hiveku dashboard:',
|
|
177
|
+
`> ${editUrl}`,
|
|
178
|
+
'>',
|
|
179
|
+
'> Changes made to this file are not saved to Hiveku. The next pull replaces this file, and',
|
|
180
|
+
'> nothing uploads it. To add a fact, suggest it with account_memory_append (an owner or admin',
|
|
181
|
+
'> reviews it on the dashboard), or ask an owner or admin to change the document there.',
|
|
182
|
+
'> It is internal to the team: do not quote it to customers.',
|
|
183
|
+
'',
|
|
184
|
+
];
|
|
185
|
+
if (memory.version > 0 && memory.content.trim()) {
|
|
186
|
+
lines.push(`Version ${memory.version}, last changed ${readableTime(memory.updated_at)}.`);
|
|
187
|
+
if (memory.truncated) {
|
|
188
|
+
lines.push(
|
|
189
|
+
'Agents see a shortened version: with the suggestions, it is longer than the 8,000 characters they read.',
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
lines.push('', '---', '', memory.content.replace(/\s+$/, ''), '', '---', '');
|
|
193
|
+
} else {
|
|
194
|
+
lines.push('Nothing has been written yet. An owner or admin can start it on the dashboard.', '');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
lines.push('# Suggestions from agents, not reviewed yet', '');
|
|
198
|
+
if (memory.suggestions.length) {
|
|
199
|
+
lines.push(
|
|
200
|
+
'These lines are not part of the account memory until an owner or admin keeps them on the dashboard.',
|
|
201
|
+
'',
|
|
202
|
+
);
|
|
203
|
+
for (const s of memory.suggestions) {
|
|
204
|
+
lines.push(`- ${oneLine(s.text)} (suggested by ${oneLine(s.source)}, ${readableTime(s.at)})`);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
lines.push('None waiting.');
|
|
208
|
+
}
|
|
209
|
+
lines.push('');
|
|
210
|
+
return fm.join('\n') + lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Write the file read-only (0444), replacing any earlier read-only copy. */
|
|
214
|
+
export async function writeReadOnly(path, contents) {
|
|
215
|
+
if (existsSync(path)) {
|
|
216
|
+
// A 0444 file can be renamed over on POSIX, but not on Windows; open it up
|
|
217
|
+
// for the replace, then lock it again.
|
|
218
|
+
await chmod(path, 0o644).catch(() => {});
|
|
219
|
+
}
|
|
220
|
+
await writeFileAtomic(path, contents);
|
|
221
|
+
await chmod(path, 0o444);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The account id for the dashboard link, from get_account_info. The key is
|
|
226
|
+
* pinned to one account, so the first row is it. Any failure (a narrowed
|
|
227
|
+
* profile without the tool, an older server) gives null, and the link falls
|
|
228
|
+
* back to the unscoped dashboard page.
|
|
229
|
+
*/
|
|
230
|
+
export async function resolveAccountId(client) {
|
|
231
|
+
try {
|
|
232
|
+
const result = await client.callToolJson('get_account_info', {});
|
|
233
|
+
const data = result?.data ?? result;
|
|
234
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
235
|
+
const id = typeof row?.id === 'string' ? row.id.trim() : '';
|
|
236
|
+
return UUID_RE.test(id) ? id : null;
|
|
237
|
+
} catch {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "locus2012/hiveku-codex-plugin 0.1.16 (5b4e580), plugins/hiveku/.mcp.json: every hiveku.tools entry (all approval_mode \"prompt\")",
|
|
3
|
+
"why": "hiveku-sync writes its own [mcp_servers.hiveku] into a Codex folder. Codex then uses that entry, not the plugin's, for the server's connection and for every tool's approval mode, so these prompts must be written here too. Refresh this list whenever the Codex plugin's prompt list changes.",
|
|
4
|
+
"tools": [
|
|
5
|
+
"deploy_site",
|
|
6
|
+
"deploy_run",
|
|
7
|
+
"project_files_bulk_save",
|
|
8
|
+
"project_vcs_commit",
|
|
9
|
+
"project_vcs_merge",
|
|
10
|
+
"project_vcs_pr_merge",
|
|
11
|
+
"project_vcs_env_bind",
|
|
12
|
+
"project_vcs_branch_delete",
|
|
13
|
+
"project_vcs_stash",
|
|
14
|
+
"project_vcs_prune",
|
|
15
|
+
"project_vcs_branch_preview_teardown",
|
|
16
|
+
"project_vcs_revert",
|
|
17
|
+
"hiveku_batch",
|
|
18
|
+
"project_checkpoint_restore",
|
|
19
|
+
"checkpoint_restore",
|
|
20
|
+
"history_restore_to_time",
|
|
21
|
+
"checkpoint_delete",
|
|
22
|
+
"site_delete",
|
|
23
|
+
"site_firewall_allow",
|
|
24
|
+
"site_firewall_remove",
|
|
25
|
+
"project_indexing_set",
|
|
26
|
+
"account_memory_append",
|
|
27
|
+
"memory_update",
|
|
28
|
+
"memory_delete",
|
|
29
|
+
"memory_restore_version",
|
|
30
|
+
"memory_bulk_create",
|
|
31
|
+
"content_publish_to_site",
|
|
32
|
+
"content_schedule",
|
|
33
|
+
"email_campaign_send_now",
|
|
34
|
+
"email_campaign_schedule",
|
|
35
|
+
"email_campaign_resend_non_openers",
|
|
36
|
+
"email_campaign_test_send",
|
|
37
|
+
"crm_envelope_send",
|
|
38
|
+
"crm_estimate_send",
|
|
39
|
+
"helpdesk_ticket_send_reply",
|
|
40
|
+
"marketing_report_send",
|
|
41
|
+
"ppc_budget_update",
|
|
42
|
+
"ppc_platform_budget_update",
|
|
43
|
+
"ppc_bid_modifier_update",
|
|
44
|
+
"ppc_keyword_bid_update",
|
|
45
|
+
"ppc_platform_keyword_bid_update",
|
|
46
|
+
"ppc_bidding_strategy_update",
|
|
47
|
+
"ppc_platform_bidding_strategy_update",
|
|
48
|
+
"ppc_campaign_create",
|
|
49
|
+
"ppc_google_auto_apply_set",
|
|
50
|
+
"ppc_google_campaign_settings_set",
|
|
51
|
+
"ppc_google_ad_schedule_set",
|
|
52
|
+
"ppc_google_audience_exclusions_set",
|
|
53
|
+
"ppc_google_call_settings_set",
|
|
54
|
+
"ppc_google_campaign_ai_settings_set",
|
|
55
|
+
"ppc_bing_campaign_ai_settings_set",
|
|
56
|
+
"ppc_google_ad_text_update",
|
|
57
|
+
"ppc_google_ads_text_replace",
|
|
58
|
+
"ppc_negatives_remove",
|
|
59
|
+
"ppc_bing_url_tracking_set",
|
|
60
|
+
"ppc_bing_ad_extension_update",
|
|
61
|
+
"ppc_bing_ad_extension_remove",
|
|
62
|
+
"ppc_bing_experiment_create",
|
|
63
|
+
"ppc_bing_experiment_update",
|
|
64
|
+
"ppc_experiment_promote",
|
|
65
|
+
"ppc_experiment_treatment_set",
|
|
66
|
+
"ppc_conversion_adjustments_run",
|
|
67
|
+
"ppc_conversion_adjustments_set",
|
|
68
|
+
"ppc_meta_lead_quality_test",
|
|
69
|
+
"ppc_experiment_schedule",
|
|
70
|
+
"ppc_experiment_graduate",
|
|
71
|
+
"ppc_offline_conversion_upload",
|
|
72
|
+
"ppc_customer_match_upload",
|
|
73
|
+
"ppc_goals_set",
|
|
74
|
+
"ppc_budget_target_set",
|
|
75
|
+
"ppc_connection_update",
|
|
76
|
+
"agent_approval_approve",
|
|
77
|
+
"seo_ga4_key_event_update",
|
|
78
|
+
"seo_ga4_event_create_rule_update",
|
|
79
|
+
"accounting_bill_record_payment",
|
|
80
|
+
"accounting_invoice_record_payment",
|
|
81
|
+
"crm_contact_email_send",
|
|
82
|
+
"crm_enroll_sequence",
|
|
83
|
+
"crm_sequence_enroll_bulk",
|
|
84
|
+
"email_sequence_enroll",
|
|
85
|
+
"email_sequence_activate",
|
|
86
|
+
"voice_sms_send",
|
|
87
|
+
"voice_sms_send_to_contact",
|
|
88
|
+
"voice_sms_thread_reply",
|
|
89
|
+
"crm_remove_dnc",
|
|
90
|
+
"outbound_campaign_status_set",
|
|
91
|
+
"outbound_campaign_sequences_save",
|
|
92
|
+
"outbound_reply_draft_send",
|
|
93
|
+
"voice_number_purchase",
|
|
94
|
+
"voice_number_release",
|
|
95
|
+
"voice_e911_address_create",
|
|
96
|
+
"voice_extension_delete",
|
|
97
|
+
"voice_ivr_create",
|
|
98
|
+
"voice_ivr_update",
|
|
99
|
+
"voice_ivr_delete",
|
|
100
|
+
"voice_ring_group_delete",
|
|
101
|
+
"voice_queue_delete",
|
|
102
|
+
"voice_pool_delete",
|
|
103
|
+
"voice_call_tracking_setup",
|
|
104
|
+
"voice_call_originate",
|
|
105
|
+
"voice_sms_bulk_send",
|
|
106
|
+
"voice_sms_brand_submit",
|
|
107
|
+
"voice_sms_campaign_submit",
|
|
108
|
+
"voice_sms_campaign_resubmit",
|
|
109
|
+
"voice_sms_campaign_delete",
|
|
110
|
+
"voice_sms_number_assign_campaign",
|
|
111
|
+
"voice_sms_toll_free_verification_submit",
|
|
112
|
+
"voice_port_order_create",
|
|
113
|
+
"voice_port_order_action",
|
|
114
|
+
"voice_settings_update",
|
|
115
|
+
"voice_number_update",
|
|
116
|
+
"voice_blocked_numbers_remove",
|
|
117
|
+
"voice_tenant_repair",
|
|
118
|
+
"voice_swap_test",
|
|
119
|
+
"voice_call_tracking_live_probe",
|
|
120
|
+
"voice_phone_tracking_config_set",
|
|
121
|
+
"voice_phone_tracking_config_delete",
|
|
122
|
+
"voice_pool_update",
|
|
123
|
+
"voice_pool_create",
|
|
124
|
+
"voice_pool_numbers_add",
|
|
125
|
+
"voice_pool_numbers_remove",
|
|
126
|
+
"voice_pool_e911_apply",
|
|
127
|
+
"voice_webhook_create",
|
|
128
|
+
"voice_webhook_update",
|
|
129
|
+
"voice_webhook_delete",
|
|
130
|
+
"voice_sms_registration_share_link_create",
|
|
131
|
+
"voice_sms_registration_share_link_revoke",
|
|
132
|
+
"voice_port_order_share_link_create",
|
|
133
|
+
"voice_port_order_share_link_revoke",
|
|
134
|
+
"voice_ivrs_reprovision",
|
|
135
|
+
"voice_ai_agent_config_update",
|
|
136
|
+
"voice_number_cnam_set",
|
|
137
|
+
"voice_sms_campaign_appeal",
|
|
138
|
+
"voice_port_order_verification_codes_send",
|
|
139
|
+
"voice_sms_scheduled_cancel",
|
|
140
|
+
"voice_numbers_search",
|
|
141
|
+
"seo_gbp_review_reply",
|
|
142
|
+
"seo_gbp_review_reply_delete",
|
|
143
|
+
"seo_gbp_location_update",
|
|
144
|
+
"seo_gbp_attributes_update",
|
|
145
|
+
"seo_gsc_delete_sitemap",
|
|
146
|
+
"seo_report_clear",
|
|
147
|
+
"seo_deliverable_delete",
|
|
148
|
+
"seo_task_implement",
|
|
149
|
+
"seo_connection_delete",
|
|
150
|
+
"seo_gbp_services_update",
|
|
151
|
+
"seo_gbp_media_add",
|
|
152
|
+
"seo_gbp_media_delete",
|
|
153
|
+
"seo_gtm_publish",
|
|
154
|
+
"seo_gtm_install",
|
|
155
|
+
"seo_ga4_key_event_delete",
|
|
156
|
+
"seo_ga4_event_create_rule_delete",
|
|
157
|
+
"seo_tracking_project_delete",
|
|
158
|
+
"seo_rankings_platforms_set",
|
|
159
|
+
"seo_listings_scan",
|
|
160
|
+
"seo_connection_test",
|
|
161
|
+
"seo_competitor_delete",
|
|
162
|
+
"seo_keyword_cluster_delete",
|
|
163
|
+
"seo_topic_cluster_delete",
|
|
164
|
+
"seo_backlink_tracker_delete",
|
|
165
|
+
"seo_backlink_opportunity_delete",
|
|
166
|
+
"seo_automated_report_delete",
|
|
167
|
+
"seo_page_schema_delete",
|
|
168
|
+
"workflow_run",
|
|
169
|
+
"workflow_enable",
|
|
170
|
+
"workflow_trigger_update",
|
|
171
|
+
"workflow_delete",
|
|
172
|
+
"workflow_delete_schedule",
|
|
173
|
+
"workflow_stranded_replay",
|
|
174
|
+
"workflow_run_replay",
|
|
175
|
+
"workflow_run_retry",
|
|
176
|
+
"workflow_dead_letter_resolve",
|
|
177
|
+
"workflow_set_recipient",
|
|
178
|
+
"hiveboard_restore_version",
|
|
179
|
+
"hiveboard_delete",
|
|
180
|
+
"hiveboard_elements_prune",
|
|
181
|
+
"customer_avatar_version_restore",
|
|
182
|
+
"before_after_grid_version_restore",
|
|
183
|
+
"customer_journey_version_restore",
|
|
184
|
+
"customer_avatar_delete",
|
|
185
|
+
"customer_journey_delete",
|
|
186
|
+
"before_after_grid_delete",
|
|
187
|
+
"customer_journey_stage_delete",
|
|
188
|
+
"media_delete",
|
|
189
|
+
"media_folder_delete",
|
|
190
|
+
"brand_guide_delete",
|
|
191
|
+
"brand_guide_purge",
|
|
192
|
+
"marketing_storyboard_delete",
|
|
193
|
+
"marketing_generate_video",
|
|
194
|
+
"marketing_video_pipeline_retry_scene",
|
|
195
|
+
"design_voiceover_create",
|
|
196
|
+
"generate_image_set",
|
|
197
|
+
"media_upscale",
|
|
198
|
+
"marketing_testimonial_media_replace",
|
|
199
|
+
"marketing_form_upload_settings_update",
|
|
200
|
+
"marketing_form_capture_settings_update",
|
|
201
|
+
"marketing_form_capture_purge",
|
|
202
|
+
"social_publish_post",
|
|
203
|
+
"social_delete_post",
|
|
204
|
+
"social_comment_reply",
|
|
205
|
+
"social_linkedin_comment_add",
|
|
206
|
+
"social_linkedin_reaction_add",
|
|
207
|
+
"social_linkedin_comment_delete",
|
|
208
|
+
"social_post_reject",
|
|
209
|
+
"social_pillar_delete",
|
|
210
|
+
"social_hashtags_delete",
|
|
211
|
+
"social_calendar_delete",
|
|
212
|
+
"social_schedule_slot_delete",
|
|
213
|
+
"social_post_retry",
|
|
214
|
+
"webflow_project_cms_provider_set",
|
|
215
|
+
"webflow_site_publish",
|
|
216
|
+
"webflow_cms_item_publish",
|
|
217
|
+
"webflow_cms_item_unpublish",
|
|
218
|
+
"webflow_cms_item_delete",
|
|
219
|
+
"webflow_page_customcode_set",
|
|
220
|
+
"webflow_page_customcode_delete",
|
|
221
|
+
"webflow_cms_collection_delete",
|
|
222
|
+
"webflow_cms_field_delete",
|
|
223
|
+
"webflow_cms_item_unpublish_bulk",
|
|
224
|
+
"webflow_cms_item_delete_bulk",
|
|
225
|
+
"webflow_asset_delete",
|
|
226
|
+
"webflow_site_customcode_set",
|
|
227
|
+
"webflow_site_customcode_delete",
|
|
228
|
+
"webflow_hiveku_snippet_install",
|
|
229
|
+
"webflow_google_tag_upsert",
|
|
230
|
+
"webflow_google_tag_delete",
|
|
231
|
+
"webflow_form_submission_delete",
|
|
232
|
+
"webflow_webhook_create",
|
|
233
|
+
"webflow_webhook_update",
|
|
234
|
+
"webflow_webhook_delete",
|
|
235
|
+
"webflow_order_fulfill",
|
|
236
|
+
"webflow_order_refund",
|
|
237
|
+
"webflow_redirect_create",
|
|
238
|
+
"webflow_redirect_delete",
|
|
239
|
+
"webflow_robots_update",
|
|
240
|
+
"webflow_robots_replace",
|
|
241
|
+
"webflow_robots_delete",
|
|
242
|
+
"webflow_wellknown_create",
|
|
243
|
+
"webflow_wellknown_delete",
|
|
244
|
+
"webflow_llms_txt_set",
|
|
245
|
+
"webflow_llms_txt_delete",
|
|
246
|
+
"cms_publish",
|
|
247
|
+
"cms_delete_entry",
|
|
248
|
+
"cms_bulk_delete",
|
|
249
|
+
"cms_delete_collection",
|
|
250
|
+
"cms_remove_field"
|
|
251
|
+
]
|
|
252
|
+
}
|