@hiveku-apps/sync 0.3.6 → 0.5.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 +183 -9
- package/bin/hiveku-sync.mjs +178 -28
- package/package.json +1 -1
- package/src/claude-md.mjs +193 -0
- package/src/codex-prompt-tools.json +2 -1
- package/src/codex.mjs +234 -45
- package/src/init.mjs +60 -53
- package/src/knowledge-files.mjs +306 -0
- package/src/memory-owner.mjs +464 -0
- package/src/projects.mjs +126 -69
- package/src/pull.mjs +182 -78
- package/src/pullRecord.mjs +98 -0
- package/src/pullTree.mjs +211 -0
- package/src/push.mjs +230 -94
- package/src/pushGuards.mjs +100 -0
- package/src/saveFiles.mjs +111 -0
- package/src/util.mjs +0 -10
- package/src/versionName.mjs +569 -0
- package/src/versions.mjs +811 -0
package/README.md
CHANGED
|
@@ -6,10 +6,13 @@ Local sync CLI for a Hiveku account. Pairs with the Hiveku MCP server at
|
|
|
6
6
|
1. **`hiveku-sync init <slug>`** — scaffold a per-account folder with `.env`,
|
|
7
7
|
`.mcp.json`, `CLAUDE.md`, and the empty `memory/ rules/ skills/` tree.
|
|
8
8
|
2. **`hiveku-sync pull`** — fetch the account's memory/rules/skills via the
|
|
9
|
-
`memory_list` MCP tool and write each entry as a Markdown file
|
|
10
|
-
|
|
9
|
+
`memory_list` MCP tool and write each entry as a Markdown file, filed under
|
|
10
|
+
the agent that owns it (see "Filed by owner" below), with YAML frontmatter
|
|
11
|
+
(id, name, type, domain, owner, version, updated_at).
|
|
11
12
|
It also writes the account memory (see "Account memory" below) to
|
|
12
|
-
`hiveku-data/account/ACCOUNT_MEMORY.md`, read-only
|
|
13
|
+
`hiveku-data/account/ACCOUNT_MEMORY.md`, read-only, and brings the folder
|
|
14
|
+
layout and memory rules in `CLAUDE.md` up to date (see "CLAUDE.md stays
|
|
15
|
+
current" below).
|
|
13
16
|
|
|
14
17
|
The goal: open one folder per Hiveku tenant in VS Code with Claude Code,
|
|
15
18
|
and the local `.md` files become the source-of-truth Claude reads. Tools
|
|
@@ -105,7 +108,20 @@ This writes, merge-safely (only Hiveku-managed regions are touched):
|
|
|
105
108
|
`OLYMPUS_API_KEY` env var. Also wires `playwright` for visual checks.
|
|
106
109
|
- **`.agents/skills/<name>/SKILL.md`** — the account's pulled `skills/` and
|
|
107
110
|
`commands/` mirrored as repo-scoped Codex skills (Codex prompts are global-only
|
|
108
|
-
and deprecated)
|
|
111
|
+
and deprecated), named for the agent that owns each one:
|
|
112
|
+
`hiveku-<agent>-<slug>` for a skill, `hiveku-<agent>-cmd-<slug>` for a
|
|
113
|
+
shortcut (`shared` for one every agent shares), in hyphen case and at most 64
|
|
114
|
+
characters. A mirrored skill whose source is gone, or that an older release
|
|
115
|
+
mirrored under another name (`hiveku-skill-<slug>`, `hiveku-cmd-<slug>`), is
|
|
116
|
+
removed, unless its folder holds anything besides our generated `SKILL.md`.
|
|
117
|
+
Your own skills (no hiveku-sync stamp) are never touched. Entries under a
|
|
118
|
+
`not-followed/` folder are not mirrored: no agent reads them.
|
|
119
|
+
|
|
120
|
+
`AGENTS.md` also carries a short **rules digest per agent**: who follows what,
|
|
121
|
+
then one line per agent with its rule count and first rule names, from the rules
|
|
122
|
+
the last `pull` wrote under `rules/<agent>/` (never those in
|
|
123
|
+
`rules/<agent>/not-followed/`). Run `hiveku-sync codex` after `pull` to refresh it
|
|
124
|
+
(`pull` reminds you when the folder has a Codex lane).
|
|
109
125
|
|
|
110
126
|
Pass `--trust` to pre-trust the folder in `~/.codex/config.toml` (so Codex loads
|
|
111
127
|
the repo config without its first-run trust prompt).
|
|
@@ -177,10 +193,32 @@ cd projects/<slug>
|
|
|
177
193
|
hiveku-sync pull-env # secrets → .env.local (git-ignored)
|
|
178
194
|
# ...edit files, run the dev server...
|
|
179
195
|
hiveku-sync status # diff local vs the deploy source
|
|
180
|
-
hiveku-sync push
|
|
196
|
+
hiveku-sync push -m "Updated the pricing section on the Home page"
|
|
197
|
+
# save changes, then save them as one version
|
|
181
198
|
hiveku-sync push --deploy development --wait # ...then deploy and wait
|
|
182
199
|
```
|
|
183
200
|
|
|
201
|
+
`pull-project` pages through the site (`project_vcs_checkout` with `limit`,
|
|
202
|
+
then `cursor`), so a site of any size comes down, including the ones over
|
|
203
|
+
150 MB that cannot be sent in one answer. `--branch <name>` pulls that
|
|
204
|
+
branch's working tree instead of Your site; `.hiveku/projects.json` records
|
|
205
|
+
the branch, the version the pull came from (`head_commit_id`) and, on a
|
|
206
|
+
branch, its `working_tree_etag`. Later `push`, `status`, `commit`,
|
|
207
|
+
`versions` and `rollback` in that folder act on the same branch unless you
|
|
208
|
+
pass `--branch` (`--branch main` for Your site).
|
|
209
|
+
|
|
210
|
+
Each pull also records the files it wrote (`.hiveku/pulled/<slug>.json`). The
|
|
211
|
+
next pull moves the ones its tree no longer has (another branch's files, files
|
|
212
|
+
deleted on Hiveku) out of the project folder into
|
|
213
|
+
`.hiveku/set-aside/<slug>/<time>/`, so a push can never send them back; files
|
|
214
|
+
you created yourself are listed, never moved. The run hints go to the project
|
|
215
|
+
folder's `.hiveku/RUN.md`, which push never sends.
|
|
216
|
+
|
|
217
|
+
On a branch, `push` compares the branch's `working_tree_etag` with the one
|
|
218
|
+
recorded at pull time. If someone saved on the branch since, it lists the files
|
|
219
|
+
that may hold their work and changes nothing (exit 1); pull again, or pass
|
|
220
|
+
`--force` to overwrite them.
|
|
221
|
+
|
|
184
222
|
**`push` writes the deploy source, lane-routed.** This is the important part.
|
|
185
223
|
`deploy_site` builds from `builder_code_versions` (+ the S3/CDN asset store) —
|
|
186
224
|
**not** a GitHub branch. So `push`:
|
|
@@ -195,17 +233,65 @@ hiveku-sync push --deploy development --wait # ...then deploy and wait
|
|
|
195
233
|
- diffs correctly: code via a single `project_files_status` call (so
|
|
196
234
|
`only_remote` stays reliable — chunking it produces phantom deletions), assets
|
|
197
235
|
via `assets_list` (so unchanged images aren't re-uploaded every push).
|
|
236
|
+
- sends each batch again with only the files that did not land (a large batch
|
|
237
|
+
can stop at the route's time budget and name the rest in `remaining_paths`),
|
|
238
|
+
and lists every file that still did not save.
|
|
239
|
+
- with `--include-deletes`, deletes only files this folder could hold: never
|
|
240
|
+
dot files (`.gitignore`, `.ai/memory.md`), build folders, or files Hiveku
|
|
241
|
+
manages (`soul.md`, `data/marketing/...`), which a pull never brings down.
|
|
198
242
|
|
|
199
243
|
Flags: `--include-deletes` (remove code paths missing locally), `--no-assets` /
|
|
200
|
-
`--assets-all`,
|
|
201
|
-
`--deploy <env> [--wait]`,
|
|
244
|
+
`--assets-all`, `-m "name"` (the version name; `--commit "name"` still works),
|
|
245
|
+
`--no-version`, `--branch <name>`, `--force`, `--deploy <env> [--wait]`,
|
|
246
|
+
`--dry-run`. Images, fonts and videos live in one asset store shared by Your
|
|
247
|
+
site and every branch, so a push to a branch says so before it uploads them
|
|
248
|
+
(and in `--dry-run`). `--deploy` publishes what that tier is set to publish:
|
|
249
|
+
Your site, unless the tier is bound to the branch.
|
|
202
250
|
|
|
203
251
|
```bash
|
|
204
252
|
hiveku-sync deploy <development|staging|production> [<slug>] [--wait]
|
|
205
253
|
hiveku-sync pull-env [<slug>] [--target development|staging|production]
|
|
206
|
-
hiveku-sync commit -m "msg" # legacy GitHub-branch commit (GitHub-connected projects only)
|
|
207
254
|
```
|
|
208
255
|
|
|
256
|
+
## Versions
|
|
257
|
+
|
|
258
|
+
"Your site" is the project's main source: what the preview shows and what a
|
|
259
|
+
deploy ships. A save is live there at once, but it is not a **version** until
|
|
260
|
+
one is saved, and a version is what you can go back to.
|
|
261
|
+
|
|
262
|
+
- **`push` saves one version** after every file has landed:
|
|
263
|
+
`project_vcs_commit` with no files and no deletions (source `sync_cli`),
|
|
264
|
+
which saves everything on Your site (or the branch) not yet in a version,
|
|
265
|
+
from every writer (the Hiveku AI, the editor, other tools). So the name comes
|
|
266
|
+
from `-m`, otherwise from everything `project_vcs_status` says is waiting
|
|
267
|
+
("Added the Pricing page and edited the Home page"), or only from the pages
|
|
268
|
+
the push changed on a server that cannot say. The first version after a
|
|
269
|
+
version from before versions of Your site is named by Hiveku ("Your site up
|
|
270
|
+
to <date>"). `--no-version` skips it.
|
|
271
|
+
- **Names are read by the site owner.** Describe what changed for visitors;
|
|
272
|
+
never file paths, file extensions, `fix:`-style prefixes or tool names.
|
|
273
|
+
A typed name that breaks this is refused before anything is written.
|
|
274
|
+
- **Exit codes:** `0` done; `1` some files did not save, or the push was
|
|
275
|
+
refused (they are listed, and there is no version and no deploy); `2` every
|
|
276
|
+
file IS saved but the version was not (the message says why and prints the
|
|
277
|
+
exact `hiveku-sync commit` that tries again); `3` the files and the version
|
|
278
|
+
ARE saved and only the deploy failed (`hiveku-sync deploy` tries again).
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
hiveku-sync commit [-m "name"] # save what is waiting as one version (sends no files)
|
|
282
|
+
hiveku-sync commit --github -m "msg" # the old GitHub push (GitHub-connected projects only)
|
|
283
|
+
hiveku-sync versions [--limit 50] # the versions, newest first, and what is waiting
|
|
284
|
+
hiveku-sync rollback <version id> # dry run: what going back would change
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`rollback` is a dry run unless you add `--apply`, and `--apply` needs the
|
|
288
|
+
`--confirm` id the dry run printed (it prints the whole command), so it only goes
|
|
289
|
+
ahead if nobody changed the site since you looked. Rolling back writes a NEW
|
|
290
|
+
version equal to the old one, so it can be undone, saves unsaved changes as
|
|
291
|
+
their own version first, and never publishes: run `hiveku-sync deploy` after
|
|
292
|
+
it when the live site should match. Images, fonts and videos in the shared
|
|
293
|
+
asset store, the database, CMS entries and secrets are not part of versions.
|
|
294
|
+
|
|
209
295
|
`pull-env` applies the same `_DEV` / `_PROD` / `_STAGING` suffix resolution the
|
|
210
296
|
preview machine uses: for `development` it strips `_DEV` to the base key and
|
|
211
297
|
skips `_PROD`/`_STAGING`; `--target production` does the inverse.
|
|
@@ -220,6 +306,11 @@ skips `_PROD`/`_STAGING`; `--target production` does the inverse.
|
|
|
220
306
|
- **No deletes in v1** — entries removed in Olympus are *not* deleted
|
|
221
307
|
from disk; they're listed under `removed_remotely_since_last_sync` in
|
|
222
308
|
the manifest. Prune manually if you want.
|
|
309
|
+
- **Moved, never deleted** — when an entry the last pull wrote now lands
|
|
310
|
+
somewhere else (its owner changed, or an older release wrote it flat as
|
|
311
|
+
`<type>/<name>.md`), or is no longer mirrored, the old copy is moved to
|
|
312
|
+
`.hiveku/set-aside/knowledge/<time>/`. Files a pull did not write are never
|
|
313
|
+
moved. When the listing fails, nothing is moved or reported as removed.
|
|
223
314
|
|
|
224
315
|
## How writes work
|
|
225
316
|
|
|
@@ -227,6 +318,89 @@ For **projects**, `hiveku-sync push` / `deploy` write straight back to Hiveku
|
|
|
227
318
|
(see "Coder projects" above). For **memory/rules/skills**, `pull` is read-only —
|
|
228
319
|
persist changes with the `memory_create` / `memory_update` / `memory_delete` MCP
|
|
229
320
|
tools through Claude, then `hiveku-sync pull` to refresh the local copies.
|
|
321
|
+
`memory_update` replaces the whole entry: send
|
|
322
|
+
`memory_update({ memory_id, content, reason, expected_version })` after checking
|
|
323
|
+
`memory_log_list({ memory_id, since })` for changes made since you read it.
|
|
324
|
+
`CLAUDE.md` says so in the part `pull` keeps current, also in folders an older
|
|
325
|
+
release made (see "CLAUDE.md stays current" below), and `AGENTS.md` in the
|
|
326
|
+
region `hiveku-sync codex` rewrites.
|
|
327
|
+
|
|
328
|
+
## CLAUDE.md stays current
|
|
329
|
+
|
|
330
|
+
`init` writes `CLAUDE.md` once, and the rest of the file is yours from then on,
|
|
331
|
+
except its "Folder layout" and "Memory writes" sections. They sit between
|
|
332
|
+
`<!-- hiveku:memory:begin -->` and `<!-- hiveku:memory:end -->`, and `pull` (and
|
|
333
|
+
`codex`) bring them up to date, so a folder an older release made stops teaching
|
|
334
|
+
the old layout and the old `memory_update` call:
|
|
335
|
+
|
|
336
|
+
- the markers are there: the text between them is replaced;
|
|
337
|
+
- no markers, and the two sections are exactly what an earlier release wrote
|
|
338
|
+
(0.1.0 to 0.4.0): they are replaced by the marked part, and the rest of the
|
|
339
|
+
file is kept byte for byte;
|
|
340
|
+
- you edited those sections: the marked part is added at the end, your text is
|
|
341
|
+
left as it is, and the marked part says it wins where the two disagree.
|
|
342
|
+
|
|
343
|
+
A `CLAUDE.md` that is read-only, a link to another file, or whose markers are not
|
|
344
|
+
there once each and in order is left alone (`pull` says so). `pull` never creates
|
|
345
|
+
a missing one.
|
|
346
|
+
|
|
347
|
+
## Filed by owner
|
|
348
|
+
|
|
349
|
+
`pull` lists every account-level entry with one `memory_list` call and files each
|
|
350
|
+
one under the agent that owns it, the way the Hiveku Memory page does:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
rules/sales/<name>.md a rule Sales owns
|
|
354
|
+
rules/seo/<name>.md a rule the SEO topic owns (each Marketing topic is its own owner)
|
|
355
|
+
rules/marketing/<name>.md a rule the Marketing parent owns
|
|
356
|
+
rules/shared/<name>.md a rule shared with every agent
|
|
357
|
+
skills/<agent>/, memory/<agent>/, commands/<agent>/, agents/<agent>/, identity/<agent>/
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The owner is the `owner` field `memory_list` returns (the builder's one owner
|
|
361
|
+
rule). A builder that does not return it yet gets the same rule from
|
|
362
|
+
`src/memory-owner.mjs`: for a rule, skill, shortcut or specialist, the
|
|
363
|
+
`department` column when it is set and is not `marketing`; a `marketing` or empty
|
|
364
|
+
column with a Marketing-family marker (`<!-- department: seo -->`) belongs to
|
|
365
|
+
that topic (the seeded starter rows); a `marketing` column without one stays with
|
|
366
|
+
the Marketing parent; an empty column falls to the marker, then the front matter,
|
|
367
|
+
then the shape of the name, else the entry is shared with every agent.
|
|
368
|
+
|
|
369
|
+
Who follows what: an agent follows the files in its own folder and `shared/`;
|
|
370
|
+
every Marketing topic also follows `marketing/`; the website agent (`coder`) also
|
|
371
|
+
follows `marketing/` and the website topics; calls follow only the receptionist's
|
|
372
|
+
own call rules.
|
|
373
|
+
|
|
374
|
+
An entry filed under an owner that no agent reads goes one folder down, in
|
|
375
|
+
`<type>/<owner>/not-followed/`: the Memory page does not show it, and its front
|
|
376
|
+
matter says `followed_by: "no agent"`. That is a rule, skill or note filed for the
|
|
377
|
+
chief of staff outside her own `_account:*` rows (she never reads those), an owner
|
|
378
|
+
that is no agent (`mini`, `graphic_design`), or a note a department owns under a
|
|
379
|
+
name its agent never reads (Sales reads its `sales` notes, not `pipeline-notes`).
|
|
380
|
+
The builder's `placement` says which (hidden, for a reason other than internal
|
|
381
|
+
state); an older builder gets the same rule from `src/memory-owner.mjs`. The Codex
|
|
382
|
+
rules digest and skill mirror leave these out.
|
|
383
|
+
|
|
384
|
+
The chief of staff's own rows (`_account:*`) and Voice and pronunciation are
|
|
385
|
+
placed where the Memory page shows them, **read-only** (owners and admins change
|
|
386
|
+
them on the Memory page; the front matter carries the `edit_url`):
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
memory/orchestrator/how-it-works.md How it works (_account:soul)
|
|
390
|
+
memory/orchestrator/background.md Background (_account:claude)
|
|
391
|
+
rules/orchestrator/, skills/orchestrator/, memory/orchestrator/<topic>.md
|
|
392
|
+
memory/orchestrator/moved/<topic>.md topics now kept in About your business
|
|
393
|
+
hiveku-data/account/voice/<name>.md Voice and pronunciation
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
A file under `moved/` is her old copy: it opens with a line naming the About your
|
|
397
|
+
business heading the topic moved to and saying that the current text is in
|
|
398
|
+
`hiveku-data/account/ACCOUNT_MEMORY.md`.
|
|
399
|
+
|
|
400
|
+
Internal rows the page never shows (`_workspace:*`, `_digest:*`, a row that
|
|
401
|
+
holds only the agents' security preamble, an old `_account:*` shape) are not
|
|
402
|
+
written. A name that a tool would read as its own instructions (`claude`,
|
|
403
|
+
`agents`) or that Windows keeps for a device gets `-entry` added.
|
|
230
404
|
|
|
231
405
|
## Account memory
|
|
232
406
|
|
|
@@ -245,7 +419,7 @@ suggested it and when. The two account-memory rows (`account` and
|
|
|
245
419
|
`account-suggestions`) are never written under `memory/`, even when an older
|
|
246
420
|
server still lists them.
|
|
247
421
|
|
|
248
|
-
Nothing uploads this file: `push`, `commit` and `status` leave any
|
|
422
|
+
Nothing uploads this file: `push`, `commit --github` and `status` leave any
|
|
249
423
|
`hiveku-data/account/` folder out, and the next `pull` replaces it. To add a
|
|
250
424
|
fact, suggest it with the `account_memory_append` tool; an owner keeps or
|
|
251
425
|
removes it on the dashboard.
|
package/bin/hiveku-sync.mjs
CHANGED
|
@@ -18,6 +18,9 @@ import {
|
|
|
18
18
|
runProjectPush,
|
|
19
19
|
} from '../src/projects.mjs';
|
|
20
20
|
import { runPush, runDeploy, runPullEnv } from '../src/push.mjs';
|
|
21
|
+
import { runRollback, runVersionCommit, runVersions } from '../src/versions.mjs';
|
|
22
|
+
import { ownerOrder } from '../src/memory-owner.mjs';
|
|
23
|
+
import { relative } from 'node:path';
|
|
21
24
|
import { pathToFileURL } from 'node:url';
|
|
22
25
|
|
|
23
26
|
export function parseFlags(argv) {
|
|
@@ -34,6 +37,9 @@ export function parseFlags(argv) {
|
|
|
34
37
|
'branch',
|
|
35
38
|
'profile',
|
|
36
39
|
'parent',
|
|
40
|
+
'confirm',
|
|
41
|
+
'fingerprint',
|
|
42
|
+
'limit',
|
|
37
43
|
]);
|
|
38
44
|
for (let i = 0; i < argv.length; i++) {
|
|
39
45
|
const arg = argv[i];
|
|
@@ -75,8 +81,12 @@ Memory/rules/skills:
|
|
|
75
81
|
+ .codex/config.toml + skills); --trust
|
|
76
82
|
pre-trusts the folder in ~/.codex/config.toml.
|
|
77
83
|
pull [--profile=<name>] Pull memory/rules/skills via MCP into the
|
|
78
|
-
current account folder
|
|
79
|
-
|
|
84
|
+
current account folder, filed by the agent
|
|
85
|
+
that owns each one (<type>/<agent>/, and
|
|
86
|
+
<type>/shared/ for every agent). Brings the
|
|
87
|
+
folder layout and memory rules in CLAUDE.md
|
|
88
|
+
up to date. Reads OLYMPUS_API_KEY and
|
|
89
|
+
OLYMPUS_BASE_URL from ./.env.
|
|
80
90
|
codex [<folder>] [--trust] Retrofit an existing account folder with the
|
|
81
91
|
Codex lane (AGENTS.md + .codex/config.toml +
|
|
82
92
|
mirror the account's skills/commands to
|
|
@@ -89,27 +99,59 @@ Memory/rules/skills:
|
|
|
89
99
|
|
|
90
100
|
Coder projects:
|
|
91
101
|
projects List the account's coder projects.
|
|
92
|
-
pull-project <slug-or-id> Pull
|
|
93
|
-
[--branch=<name>]
|
|
102
|
+
pull-project <slug-or-id> Pull Your site's files (or, with --branch, that
|
|
103
|
+
[--branch=<name>] branch's working tree) into ./projects/<slug>/,
|
|
104
|
+
page by page (any size). Records the project,
|
|
105
|
+
branch and version in .hiveku/projects.json.
|
|
106
|
+
Files an earlier pull wrote that are not in
|
|
107
|
+
this one move to .hiveku/set-aside/<slug>/.
|
|
94
108
|
status [<slug>] [--json] Diff local ./projects/<slug>/ vs the deploy source
|
|
95
|
-
|
|
109
|
+
[--branch=<name>] (builder_code_versions), or the pulled branch.
|
|
110
|
+
cd in to omit <slug>.
|
|
96
111
|
|
|
97
112
|
Ship a project (the agent-native deploy loop):
|
|
98
113
|
push [<slug>] Save local changes to the DEPLOY SOURCE, lane-routed:
|
|
99
|
-
[
|
|
100
|
-
[--no-
|
|
101
|
-
[--
|
|
102
|
-
[--
|
|
103
|
-
[--
|
|
114
|
+
[-m "<version name>"] images/fonts/video under public/<subdir>/ → the
|
|
115
|
+
[--no-version] S3/CDN asset store; everything else → batched,
|
|
116
|
+
[--include-deletes] verified bulk_save. This is what deploy reads.
|
|
117
|
+
[--no-assets] [--assets-all] Then saves ONE version of Your site (or of the
|
|
118
|
+
[--deploy <env>] [--wait] branch) named by -m, or from the pages changed.
|
|
119
|
+
[--dry-run] [--branch=<name>] Names are plain language for the site owner (no
|
|
120
|
+
[--force] file paths, extensions or "fix:" prefixes).
|
|
121
|
+
--no-version skips the version. --deploy <env>
|
|
122
|
+
[--wait] deploys after the push. --branch writes
|
|
123
|
+
to that branch (default: the branch pulled);
|
|
124
|
+
if someone saved on it since the pull, push
|
|
125
|
+
refuses unless --force.
|
|
126
|
+
Exit 0 done; 1 some files did not save or the
|
|
127
|
+
push was refused (no version, no deploy); 2
|
|
128
|
+
files ARE saved but the version was not (it
|
|
129
|
+
prints the \`commit\` that retries it); 3 files
|
|
130
|
+
and version ARE saved, the deploy failed.
|
|
104
131
|
deploy <env> [<slug>] [--wait] Deploy to development | staging | production and
|
|
105
132
|
(with --wait) poll to a terminal status.
|
|
106
133
|
pull-env [<slug>] Write project secrets → .env.local (git-ignored),
|
|
107
134
|
[--target <env>] with the same _DEV/_PROD/_STAGING resolution the
|
|
108
135
|
preview uses. --target staging|production for the
|
|
109
136
|
other tiers.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
137
|
+
|
|
138
|
+
Versions (Your site = the project's main source; rollback never publishes):
|
|
139
|
+
commit [<slug>] [-m "<name>"] Save what is on Hiveku and not yet in a version as
|
|
140
|
+
[--branch=<name>] one version (no files are sent: push first).
|
|
141
|
+
[--dry-run] Without -m it is named from what changed. Exit 2
|
|
142
|
+
when the version could not be saved.
|
|
143
|
+
commit --github -m "<msg>" The old GitHub push via project_commit (only for
|
|
144
|
+
[--branch=<name>] GitHub-connected projects).
|
|
145
|
+
[--include-deletes]
|
|
146
|
+
versions [<slug>] [--branch=<b>] The versions, newest first, and what is waiting.
|
|
147
|
+
[--limit N] [--json]
|
|
148
|
+
rollback <version id> [<slug>] Show what going back to that version would change.
|
|
149
|
+
[--branch=<name>] Nothing changes (a dry run). It prints the exact
|
|
150
|
+
[--apply --confirm <id>] --apply command, with the --confirm id it saw;
|
|
151
|
+
[--fingerprint <fp>] applying writes a NEW version equal to the old one
|
|
152
|
+
[-m "<name>"] (so it can be undone) and is refused if the site
|
|
153
|
+
changed since the dry run. Publish separately
|
|
154
|
+
with deploy.
|
|
113
155
|
|
|
114
156
|
help Show this message.
|
|
115
157
|
|
|
@@ -140,19 +182,56 @@ function userToolTablesLines({ userToolTables = [], userTablesWithoutApprovalMod
|
|
|
140
182
|
return lines;
|
|
141
183
|
}
|
|
142
184
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
185
|
+
/**
|
|
186
|
+
* What refreshing CLAUDE.md's kept-current part (folder layout and memory
|
|
187
|
+
* rules, claude-md.mjs) did, as lines to print. Nothing when it was already
|
|
188
|
+
* current or there is no CLAUDE.md.
|
|
189
|
+
*/
|
|
190
|
+
export function claudeMdLines(result) {
|
|
191
|
+
switch (result?.status) {
|
|
192
|
+
case 'updated':
|
|
193
|
+
case 'replaced':
|
|
194
|
+
return [' CLAUDE.md: brought the folder layout and memory rules up to date (the rest of the file is as it was).'];
|
|
195
|
+
case 'appended':
|
|
196
|
+
return [
|
|
197
|
+
' CLAUDE.md: added the current folder layout and memory rules at the end, between hiveku-sync markers.',
|
|
198
|
+
' Your file had its own version of those sections, so they were left as they are: where they disagree, the new part wins.',
|
|
199
|
+
];
|
|
200
|
+
case 'symlink':
|
|
201
|
+
return [' CLAUDE.md: not updated, because it is a link to another file. Its folder layout and memory rules may be out of date.'];
|
|
202
|
+
case 'read-only':
|
|
203
|
+
return [' CLAUDE.md: not updated, because the file is read-only. Its folder layout and memory rules may be out of date.'];
|
|
204
|
+
case 'markers':
|
|
205
|
+
return [
|
|
206
|
+
' CLAUDE.md: not updated, because the hiveku-sync markers (<!-- hiveku:memory:begin --> and <!-- hiveku:memory:end -->) are not there exactly once each, in order.',
|
|
207
|
+
' Keep one of each, then run this again.',
|
|
208
|
+
];
|
|
209
|
+
case 'failed':
|
|
210
|
+
return [` CLAUDE.md: could not update the folder layout and memory rules: ${result.message}`];
|
|
211
|
+
default:
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function commitMessageFrom(flags) {
|
|
217
|
+
// --commit "msg" | --commit=msg | -m "msg" | --message "msg": the version
|
|
218
|
+
// name. A bare --commit (boolean true, from before versions were the
|
|
219
|
+
// default) names nothing: the version is then named from what changed.
|
|
146
220
|
const c = typeof flags['commit'] === 'string' ? flags['commit'] : undefined;
|
|
147
221
|
const m = typeof flags['m'] === 'string' ? flags['m'] : undefined;
|
|
148
222
|
const msg = typeof flags['message'] === 'string' ? flags['message'] : undefined;
|
|
149
223
|
if (c) return c;
|
|
150
224
|
if (m) return m;
|
|
151
225
|
if (msg) return msg;
|
|
152
|
-
if (flags['commit'] === true) return 'hiveku-sync: push';
|
|
153
226
|
return undefined;
|
|
154
227
|
}
|
|
155
228
|
|
|
229
|
+
/** A command's exit code, from the result object it returned (0 when it says nothing). */
|
|
230
|
+
function setExitCode(result) {
|
|
231
|
+
const code = result && typeof result.exit_code === 'number' ? result.exit_code : 0;
|
|
232
|
+
if (code) process.exitCode = code;
|
|
233
|
+
}
|
|
234
|
+
|
|
156
235
|
async function main() {
|
|
157
236
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
158
237
|
const { positional, flags } = parseFlags(rest);
|
|
@@ -203,9 +282,13 @@ async function main() {
|
|
|
203
282
|
? ` MCP auth: inlined OLYMPUS_API_KEY from .env.`
|
|
204
283
|
: ` 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.`,
|
|
205
284
|
);
|
|
206
|
-
console.log(` skills: mirrored ${result.skillsMirrored} account skill/
|
|
285
|
+
console.log(` skills: mirrored ${result.skillsMirrored} account skill/shortcut entr${result.skillsMirrored === 1 ? 'y' : 'ies'} into .agents/skills/ as hiveku-<agent>-<name>.`);
|
|
286
|
+
if (result.skillsRemoved > 0) {
|
|
287
|
+
console.log(` skills: removed ${result.skillsRemoved} mirrored skill${result.skillsRemoved === 1 ? '' : 's'} with no source left (or an older name).`);
|
|
288
|
+
}
|
|
207
289
|
if (result.trust === 'added') console.log(` trust: pre-trusted this folder in ~/.codex/config.toml.`);
|
|
208
290
|
else if (result.trust === 'already') console.log(` trust: folder already trusted.`);
|
|
291
|
+
for (const line of claudeMdLines(result.claudeMd)) console.log(line);
|
|
209
292
|
console.log(` verify: cd there, run \`codex mcp list\` (the hiveku row), then \`get_account_info\`.`);
|
|
210
293
|
break;
|
|
211
294
|
}
|
|
@@ -215,6 +298,23 @@ async function main() {
|
|
|
215
298
|
for (const [type, count] of Object.entries(summary.by_type)) {
|
|
216
299
|
console.log(` ${type}: ${count}`);
|
|
217
300
|
}
|
|
301
|
+
const owners = Object.entries(summary.by_owner).sort(([a], [b]) => ownerOrder(a) - ownerOrder(b) || (a < b ? -1 : 1));
|
|
302
|
+
if (owners.length > 0) {
|
|
303
|
+
console.log(` filed by agent (<type>/<agent>/): ${owners.map(([owner, n]) => `${owner} ${n}`).join(', ')}`);
|
|
304
|
+
}
|
|
305
|
+
if (summary.not_followed > 0) {
|
|
306
|
+
const n = summary.not_followed;
|
|
307
|
+
console.log(
|
|
308
|
+
` not followed: ${n} entr${n === 1 ? 'y' : 'ies'} that no agent reads, in <type>/<agent>/not-followed/ (the Memory page does not show ${n === 1 ? 'it' : 'them'}).`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (summary.set_aside.length > 0) {
|
|
312
|
+
const n = summary.set_aside.length;
|
|
313
|
+
console.log(
|
|
314
|
+
` moved ${n} older cop${n === 1 ? 'y' : 'ies'} an earlier pull wrote out of the way (now filed by agent, or no longer mirrored): ` +
|
|
315
|
+
`${relative(process.cwd(), summary.set_aside_dir) || summary.set_aside_dir}/`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
218
318
|
if (summary.account_memory) {
|
|
219
319
|
const am = summary.account_memory;
|
|
220
320
|
console.log(
|
|
@@ -227,6 +327,10 @@ async function main() {
|
|
|
227
327
|
for (const f of summary.removed_remotely) console.log(` ${f}`);
|
|
228
328
|
console.log('They were left on disk. Delete them manually if no longer needed.');
|
|
229
329
|
}
|
|
330
|
+
for (const line of claudeMdLines(summary.claude_md)) console.log(line);
|
|
331
|
+
if (summary.codex_lane) {
|
|
332
|
+
console.log('\nCodex: run `hiveku-sync codex` to refresh the rules list in AGENTS.md and the skills in .agents/skills/.');
|
|
333
|
+
}
|
|
230
334
|
if (summary.errors.length > 0) {
|
|
231
335
|
console.error('\nErrors:');
|
|
232
336
|
for (const e of summary.errors) console.error(` ${e.type}: ${e.message}`);
|
|
@@ -257,12 +361,13 @@ async function main() {
|
|
|
257
361
|
slug: positional[0],
|
|
258
362
|
profile: flags['profile'],
|
|
259
363
|
json: !!flags['json'],
|
|
364
|
+
branch: flags['branch'],
|
|
260
365
|
});
|
|
261
366
|
break;
|
|
262
367
|
}
|
|
263
368
|
case 'push': {
|
|
264
369
|
const deployEnv = typeof flags['deploy'] === 'string' ? flags['deploy'] : undefined;
|
|
265
|
-
await runPush({
|
|
370
|
+
const result = await runPush({
|
|
266
371
|
slug: positional[0],
|
|
267
372
|
profile: flags['profile'],
|
|
268
373
|
commit: commitMessageFrom(flags),
|
|
@@ -273,7 +378,10 @@ async function main() {
|
|
|
273
378
|
assetsAll: !!flags['assets-all'],
|
|
274
379
|
dryRun: !!flags['dry-run'],
|
|
275
380
|
branch: flags['branch'],
|
|
381
|
+
noVersion: !!flags['no-version'],
|
|
382
|
+
force: !!flags['force'],
|
|
276
383
|
});
|
|
384
|
+
setExitCode(result);
|
|
277
385
|
break;
|
|
278
386
|
}
|
|
279
387
|
case 'deploy': {
|
|
@@ -301,21 +409,63 @@ async function main() {
|
|
|
301
409
|
break;
|
|
302
410
|
}
|
|
303
411
|
case 'commit': {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
412
|
+
if (flags['github']) {
|
|
413
|
+
// The old GitHub-branch path (project_commit). Requires a connected repo.
|
|
414
|
+
const message = commitMessageFrom(flags);
|
|
415
|
+
if (!message) {
|
|
416
|
+
console.error('Error: commit --github requires -m "<message>"\n');
|
|
417
|
+
console.error(HELP);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
await runProjectPush({
|
|
421
|
+
slug: positional[0],
|
|
422
|
+
message,
|
|
423
|
+
branch: flags['branch'],
|
|
424
|
+
profile: flags['profile'],
|
|
425
|
+
includeDeletes: !!flags['include-deletes'],
|
|
426
|
+
dryRun: !!flags['dry-run'],
|
|
427
|
+
});
|
|
428
|
+
break;
|
|
310
429
|
}
|
|
311
|
-
|
|
430
|
+
// Default: save what is on Hiveku as one version (no files sent).
|
|
431
|
+
const result = await runVersionCommit({
|
|
312
432
|
slug: positional[0],
|
|
313
|
-
|
|
433
|
+
name: commitMessageFrom(flags),
|
|
314
434
|
branch: flags['branch'],
|
|
315
435
|
profile: flags['profile'],
|
|
316
|
-
includeDeletes: !!flags['include-deletes'],
|
|
317
436
|
dryRun: !!flags['dry-run'],
|
|
318
437
|
});
|
|
438
|
+
setExitCode(result);
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
case 'versions': {
|
|
442
|
+
const result = await runVersions({
|
|
443
|
+
slug: positional[0],
|
|
444
|
+
branch: flags['branch'],
|
|
445
|
+
limit: typeof flags['limit'] === 'string' ? flags['limit'] : undefined,
|
|
446
|
+
json: !!flags['json'],
|
|
447
|
+
profile: flags['profile'],
|
|
448
|
+
});
|
|
449
|
+
setExitCode(result);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case 'rollback': {
|
|
453
|
+
if (!positional[0]) {
|
|
454
|
+
console.error('Error: rollback requires a version id (see `hiveku-sync versions`).\n');
|
|
455
|
+
console.error(HELP);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
const result = await runRollback({
|
|
459
|
+
versionId: positional[0],
|
|
460
|
+
slug: positional[1],
|
|
461
|
+
branch: flags['branch'],
|
|
462
|
+
apply: !!flags['apply'],
|
|
463
|
+
head: typeof flags['confirm'] === 'string' ? flags['confirm'] : undefined,
|
|
464
|
+
fingerprint: typeof flags['fingerprint'] === 'string' ? flags['fingerprint'] : undefined,
|
|
465
|
+
name: commitMessageFrom(flags),
|
|
466
|
+
profile: flags['profile'],
|
|
467
|
+
});
|
|
468
|
+
setExitCode(result);
|
|
319
469
|
break;
|
|
320
470
|
}
|
|
321
471
|
case 'help':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiveku-apps/sync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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",
|