@letta-ai/dreams 0.0.3 → 0.0.4
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 +56 -34
- package/bin/dreams-codex-hook.cjs +91 -0
- package/bin/dreams.cjs +21 -0
- package/dist/auth/commands.js +21 -1
- package/dist/auth/credentials.js +73 -14
- package/dist/auth/index.js +106 -13
- package/dist/cli.js +48 -12
- package/dist/local/backfill.js +20 -3
- package/dist/local/claude-hooks.js +169 -0
- package/dist/local/claude.js +76 -20
- package/dist/local/codex-hooks.js +235 -0
- package/dist/local/codex.js +298 -101
- package/dist/local/commands.js +39 -24
- package/dist/local/db.js +4 -0
- package/dist/local/detect.js +6 -2
- package/dist/local/hooks-install.js +102 -0
- package/dist/local/source-adapters.js +13 -0
- package/dist/local/sync-control.js +73 -22
- package/dist/skill.js +202 -110
- package/dist/suppress-sqlite-warning.js +34 -0
- package/dist/sync.js +191 -31
- package/package.json +1 -1
package/dist/skill.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.skillDir = skillDir;
|
|
|
37
37
|
exports.skillFilePath = skillFilePath;
|
|
38
38
|
exports.initSnapshotTemplatePath = initSnapshotTemplatePath;
|
|
39
39
|
exports.claudeHooksReferencePath = claudeHooksReferencePath;
|
|
40
|
+
exports.codexHooksReferencePath = codexHooksReferencePath;
|
|
41
|
+
exports.codexSyncHookScriptPath = codexSyncHookScriptPath;
|
|
40
42
|
exports.installedSkillVersion = installedSkillVersion;
|
|
41
43
|
exports.installSkill = installSkill;
|
|
42
44
|
const crypto = __importStar(require("node:crypto"));
|
|
@@ -58,6 +60,12 @@ function initSnapshotTemplatePath() {
|
|
|
58
60
|
function claudeHooksReferencePath() {
|
|
59
61
|
return path.join(skillDir(), "references", "hooks-claude.md");
|
|
60
62
|
}
|
|
63
|
+
function codexHooksReferencePath() {
|
|
64
|
+
return path.join(skillDir(), "references", "hooks-codex.md");
|
|
65
|
+
}
|
|
66
|
+
function codexSyncHookScriptPath() {
|
|
67
|
+
return path.join(skillDir(), "references", "codex-sync-hook.cjs");
|
|
68
|
+
}
|
|
61
69
|
function skillContent(version) {
|
|
62
70
|
return `---
|
|
63
71
|
name: dreams-setup
|
|
@@ -67,7 +75,16 @@ version: ${version}
|
|
|
67
75
|
|
|
68
76
|
# Dreams setup
|
|
69
77
|
|
|
70
|
-
CLI owns side effects. This skill owns sequencing and user conversation. There is no durable resume pointer: in a new conversation, start at step 1 and skip work that durable facts show is already satisfied. Never blindly repeat mutations. Never print or summarize access tokens, refresh tokens, credential files, or keychain contents.
|
|
78
|
+
CLI owns side effects. This skill owns sequencing and user conversation. There is no durable resume pointer: in a new conversation, start at step 1 and skip work that durable facts show is already satisfied. Never blindly repeat mutations. Never print or summarize access tokens, refresh tokens, credential files, or keychain contents. Never interpolate credential env vars into shell commands, even partially.
|
|
79
|
+
|
|
80
|
+
## Invariants
|
|
81
|
+
|
|
82
|
+
- Quote absolute \`cli_path\` from onboard in every hook/command.
|
|
83
|
+
- Never print tokens. Never invent teammate emails — derive them from git (step 3).
|
|
84
|
+
- Deny-by-default: without an approved directory, nothing uploads. Do not offer “continue without approving a directory.”
|
|
85
|
+
- Live hooks require consent. Do not offer “skip hooks and continue setup” — if the user declines hooks, exit setup (they can re-enter later).
|
|
86
|
+
- SessionStart → \`dreams wake\`; Stop → \`dreams sync\`. Do not install \`sync\` on SessionStart.
|
|
87
|
+
- Prefer \`dreams hooks install --dry-run\` before writing hook files; CLI owns the merge.
|
|
71
88
|
|
|
72
89
|
## Step 0 — Local bootstrap (optional repair)
|
|
73
90
|
|
|
@@ -81,105 +98,144 @@ npx @letta-ai/dreams@latest onboard --json
|
|
|
81
98
|
|
|
82
99
|
## Step 1 — Authenticate
|
|
83
100
|
|
|
84
|
-
1. Run \`dreams auth status --json
|
|
85
|
-
2.
|
|
86
|
-
|
|
101
|
+
1. Run \`"<cli_path>" auth status --json\` (or \`dreams auth status --json\` after onboard).
|
|
102
|
+
2. Read the inventory, not only the top-level winner:
|
|
103
|
+
- \`credentials[]\` lists every discovered backend (\`env\`, \`keychain\`, \`file\`) with \`present\`, \`selected\`, masked token, and \`reason\`.
|
|
104
|
+
- \`active\` is which credential won for **this process** and why.
|
|
105
|
+
- \`hooks\` explains that live sync inherits the **coding-agent harness environment**. Verify the credential hooks will use, not only an interactive CLI.
|
|
106
|
+
3. If unauthenticated, run \`dreams auth login --json\`, wait for browser approval (JSON Lines: \`awaiting_approval\` then \`authenticated\`), then re-check \`auth status\`.
|
|
107
|
+
4. Prefer an existing usable credential; do not force a new login when already authenticated.
|
|
87
108
|
|
|
88
|
-
## Step 2 — Detect and register
|
|
109
|
+
## Step 2 — Detect and register sources
|
|
89
110
|
|
|
90
|
-
|
|
111
|
+
Detect each registered harness with an **explicit** adapter. Do **not** install hooks here.
|
|
91
112
|
|
|
92
113
|
\`\`\`bash
|
|
93
|
-
|
|
114
|
+
"<cli_path>" source detect --source claude-code --json
|
|
115
|
+
"<cli_path>" source detect --source codex --json
|
|
94
116
|
\`\`\`
|
|
95
117
|
|
|
96
|
-
|
|
118
|
+
Record which harnesses were present (\`harness_present: true\` / exit 0). A missing harness is expected on machines that only run one coding agent — continue with whichever registered successfully.
|
|
119
|
+
|
|
120
|
+
## Step 3 — Init snapshot (seed smarter memory)
|
|
97
121
|
|
|
98
|
-
|
|
122
|
+
**Why this step exists (tell the user):** A short workspace snapshot helps Dreams start with useful structure — so early memories are more targeted and relevant to their real repos, teammates, and tools — instead of a blank slate. Prefer asking over inventing. Memories are more meaningful when they begin from a smart seed of how this person actually works.
|
|
99
123
|
|
|
100
|
-
1. Read
|
|
101
|
-
2.
|
|
102
|
-
3.
|
|
103
|
-
4.
|
|
104
|
-
5.
|
|
124
|
+
1. Read \`references/init-snapshot.template.json\` (field ranking is in \`$comment\` / \`$field_guide\`).
|
|
125
|
+
2. **Approved directories (required for later sync):** identify which work directories should be eligible (usually the user’s main code tree). You will approve them in step 4 with \`source approve-dir\`. Put the intended paths in \`scope.approved_roots\` (JSON key unchanged for Cloud; user-facing language is **directories**, not “roots”).
|
|
126
|
+
3. **Repositories:** prefer deeper metadata for directories with recent Claude/Codex sessions; name-only or light metadata for the long tail is fine. Deduplicate multi-clone remotes (one logical repo + optional \`local_clone_count\`).
|
|
127
|
+
4. **Skills:** recursively search the repositories you listed for skill directories (\`.claude/skills\`, \`.agents/skills\`, \`**/skills\` when clearly agent-skill trees). Prefer \`{ directory, skill_count, sample_names }\` over dumping every name when large.
|
|
128
|
+
5. **Teammates — do not AskUserQuestion.** Derive from recent git commits in the target repos (\`git log\` / shortlog). Include display name + email identity signals. **Exclude bots and agents** (e.g. names/emails containing \`[bot]\`, \`dependabot\`, \`github-actions\`, \`renovate\`, \`cursoragent\`, \`noreply\` automation accounts). Cap to a reasonable set of frequent human contributors.
|
|
129
|
+
6. **Omit unknowns** rather than writing \`0\` or empty arrays for optional fields. Do **not** invent \`letta_agent_count\`, \`letta_memory_artifact_count\`, or \`allowlisted_file_names\`.
|
|
130
|
+
7. Include every harness detected in step 2. Write concrete JSON (no teammate \`role\`; no \`scope.mode\`).
|
|
131
|
+
8. Show the JSON and obtain explicit approval, then submit:
|
|
105
132
|
|
|
106
133
|
\`\`\`bash
|
|
107
|
-
|
|
134
|
+
"<cli_path>" snapshots create --type init --file /path/to/approved-init.json --client-request-id <unique-id> --json
|
|
108
135
|
\`\`\`
|
|
109
136
|
|
|
110
|
-
Reuse the same \`client-request-id\` only when retrying the exact same approved payload.
|
|
137
|
+
Reuse the same \`client-request-id\` only when retrying the exact same approved payload. Snapshot submit stores the fact; it does not create the shared-memory repository. If snapshot submit fails with auth/role errors, explain briefly and **continue** — directories + hooks + history still deliver value.
|
|
138
|
+
|
|
139
|
+
## Step 4 — Enable live sync (directories + hooks)
|
|
140
|
+
|
|
141
|
+
This step turns on ongoing sync. There is no separate “activate / sync --force” ceremony afterward.
|
|
142
|
+
|
|
143
|
+
### 4a — Approve directories (required)
|
|
144
|
+
|
|
145
|
+
Deny-by-default: nothing leaves the machine until at least one work directory is approved.
|
|
111
146
|
|
|
112
|
-
|
|
147
|
+
1. Explain in plain language: Dreams will only consider transcripts whose working directory sits under an approved folder.
|
|
148
|
+
2. Obtain consent for specific paths (from step 3 intent / session inventory). **Do not** offer “proceed without approving a directory.”
|
|
149
|
+
3. Approve with the preferred alias:
|
|
113
150
|
|
|
114
|
-
|
|
151
|
+
\`\`\`bash
|
|
152
|
+
"<cli_path>" source approve-dir "<path>" --json
|
|
153
|
+
\`\`\`
|
|
115
154
|
|
|
116
|
-
|
|
155
|
+
(\`source approve-root\` still works as a synonym.)
|
|
117
156
|
|
|
118
|
-
|
|
157
|
+
### 4b — Install hooks (required for live sync)
|
|
119
158
|
|
|
120
|
-
|
|
121
|
-
2. Substitute the absolute \`cli_path\` from onboard into every hook command (quote the path).
|
|
122
|
-
3. Choose the **platform-appropriate** command from the reference (POSIX vs PowerShell). Do not install \`>/dev/null\` redirects on Windows.
|
|
123
|
-
4. Merge **idempotently** into \`~/.claude/settings.json\`: if a Dreams sync hook is already present (command contains \`# dreams-sync-hook\` or the same \`cli_path\` + \`sync\`), **update that handler in place** and leave unrelated hooks untouched — never append duplicates.
|
|
124
|
-
5. Confirm through the durable launcher: \`"<cli_path>" sync status --json\` (hooks themselves should remain silent and exit 0). Read \`sync.effective_state\` (\`paused\` / \`running\` / \`pending\` / \`not_due\` / \`degraded\` / \`idle\`).
|
|
159
|
+
**Consent:** name which harnesses from step 2 will get live sync (Claude Code and/or Codex). Explain this enables **ongoing transcript sync** on future sessions. If the user declines, **exit setup** (do not continue as if onboarding succeeded). Per-harness opt-out is OK when both were detected (wire only the consented ones).
|
|
125
160
|
|
|
126
|
-
|
|
161
|
+
1. **Dry-run first** — show the user the exact planned commands/files:
|
|
127
162
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
"<cli_path>" hooks install --dry-run --json
|
|
165
|
+
# or per source:
|
|
166
|
+
"<cli_path>" hooks install --source claude-code --dry-run --json
|
|
167
|
+
"<cli_path>" hooks install --source codex --dry-run --json
|
|
168
|
+
\`\`\`
|
|
131
169
|
|
|
132
|
-
|
|
170
|
+
2. After approval, install (CLI owns the merge — do not hand-edit when the CLI succeeds):
|
|
133
171
|
|
|
134
|
-
|
|
172
|
+
\`\`\`bash
|
|
173
|
+
"<cli_path>" hooks install --json
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
3. Confirm wiring from references if you need detail: \`references/hooks-claude.md\`, \`references/hooks-codex.md\`, and the Codex launcher \`references/codex-sync-hook.cjs\`.
|
|
177
|
+
- **SessionStart → \`wake\`** (CLI/skill repair + kick pending backfill/sync worker)
|
|
178
|
+
- **Stop → \`sync\`** (live turn; stdin session hint)
|
|
179
|
+
4. For Codex: ask the user to run \`/hooks\`, review, and **trust** the handlers. File write ≠ trusted ≠ Cloud upload succeeded.
|
|
135
180
|
|
|
136
|
-
|
|
137
|
-
2. Interpret local sync diagnostics: \`"<cli_path>" sync status --json\`
|
|
138
|
-
- \`effective_state: "paused"\` → expected no-op until \`sync resume\`
|
|
139
|
-
- \`not_due\` → cadence/backoff (including a retained trigger waiting for \`next_due_at\`); quiet hook wakes are expected
|
|
140
|
-
- \`pending\` → trigger is runnable now (forced, cadence due, or retry due) but no worker lease is held yet
|
|
141
|
-
- \`running\` → detached worker lease is active
|
|
142
|
-
- \`degraded\` → inspect \`last_error_code\` / \`last_error_message\` (structured \`spawn_failed\` when the parent could not detach a worker)
|
|
143
|
-
- \`idle\` → no pause, no pending trigger, and nothing waiting on cadence
|
|
144
|
-
3. Silence from hooks is **required** (exit 0, no stdout/stderr). Silence means “don’t interrupt Claude,” **not** proof of a successful Cloud upload.
|
|
181
|
+
### Hook diagnostics (reference)
|
|
145
182
|
|
|
146
|
-
|
|
183
|
+
When live sync seems broken:
|
|
147
184
|
|
|
148
|
-
|
|
185
|
+
1. \`"<cli_path>" status --json\`
|
|
186
|
+
2. \`"<cli_path>" sync status --source claude-code --json\` / \`--source codex\`
|
|
187
|
+
- \`paused\` / \`not_due\` → expected quiet wake
|
|
188
|
+
- \`pending\` / \`running\` → work in flight
|
|
189
|
+
- \`degraded\` → inspect \`last_error_code\` (e.g. \`spawn_failed\`)
|
|
190
|
+
3. Claude hooks must stay silent (exit 0). Codex launcher prints only \`{}\`.
|
|
149
191
|
|
|
150
|
-
|
|
151
|
-
2. Wake live-only scheduling through the durable launcher:
|
|
152
|
-
\`"<cli_path>" sync --force --json\`
|
|
153
|
-
This records a durable trigger and may spawn the detached worker. It does **not** run an unrestricted historical scan — live mode only touches already-tracked sessions plus a validated current-session hint from hooks. Do **not** run \`dreams source scan\` here (it performs unrestricted historical discovery under approved roots). Do **not** invoke \`dreams upload\` separately during activation — \`sync\` owns live-first upload ordering for whatever is already queued.
|
|
154
|
-
3. Treat this wake as a **local scheduling check**. Step 2's \`source detect\` already exercised Cloud registration; the first real hook-driven upload (when the user has an active Claude session under an approved root) confirms the end-to-end path. Do not present \`sync --force\` as a Cloud smoke test — on a fresh device with nothing queued it may perform no network I/O.
|
|
192
|
+
## Step 5 — Optional: import recent history
|
|
155
193
|
|
|
156
|
-
|
|
194
|
+
Only if the user wants older transcripts. Historical import is **optional**, runs in the **background**, and is **resumable** across later \`wake\`/\`sync\` — sleep and restarts are fine. Choose the history that is useful; do not pressure for a tiny window.
|
|
157
195
|
|
|
158
|
-
|
|
196
|
+
**Copy for the user:** calm and encouraging. Avoid harsh openers like “Optional; drains in bounded slices over time.” Prefer: “We can import recent history in the background — it’s resumable, so a wider window is a safe pick.”
|
|
159
197
|
|
|
160
|
-
|
|
198
|
+
**Window choices to offer:** 30 days · 90 days · 180 days · all available history (still bounded by what exists on disk; CLI freezes \`until_at = now\`). When possible, give a rough sense of scale (how many sessions / projects sit under approved directories) before they choose.
|
|
199
|
+
|
|
200
|
+
Live-hook consent does **not** grant historical-import consent. Ask which **detected** sources should import history.
|
|
161
201
|
|
|
162
202
|
If the user opts in:
|
|
163
203
|
|
|
164
|
-
1. Agree
|
|
165
|
-
2.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
204
|
+
1. Agree \`--since <RFC3339>\` (sessions modified since that time through now; selection is by transcript-file mtime).
|
|
205
|
+
2. For each selected source, queue a slice, then **\`wake\`** so draining does not wait for the next session:
|
|
206
|
+
|
|
207
|
+
\`\`\`bash
|
|
208
|
+
"<cli_path>" backfill start --source claude-code --since <RFC3339> --json
|
|
209
|
+
"<cli_path>" wake --source claude-code --json
|
|
210
|
+
"<cli_path>" backfill start --source codex --since <RFC3339> --json
|
|
211
|
+
"<cli_path>" wake --source codex --json
|
|
212
|
+
\`\`\`
|
|
213
|
+
|
|
214
|
+
3. Explain: live turns stay first; history continues in the background. Pause/resume without discarding progress:
|
|
215
|
+
\`"<cli_path>" sync pause\` / \`"<cli_path>" sync resume\`
|
|
216
|
+
Monitor: \`"<cli_path>" sync status --source claude-code --json\` (and codex as needed).
|
|
217
|
+
|
|
218
|
+
Do **not** run unrestricted \`source scan\` or a ceremonial \`sync --force\` “smoke test” during setup. \`wake\` after backfill is the right kick; the first real Stop-hook \`sync\` under an approved directory confirms the live path.
|
|
174
219
|
|
|
175
220
|
## Re-entry
|
|
176
221
|
|
|
177
|
-
|
|
222
|
+
Inspect durable facts (\`auth status\`, \`source list\`, \`status\`, per-source \`sync status\`) and continue at the first incomplete step. Repeated \`onboard\`, \`source detect\`, approved snapshot retries, \`hooks install\`, and \`approve-dir\` are safe when they follow the rules above.
|
|
178
223
|
`;
|
|
179
224
|
}
|
|
180
225
|
function initSnapshotTemplate() {
|
|
181
226
|
return `${JSON.stringify({
|
|
182
|
-
$comment: "Non-enforced
|
|
227
|
+
$comment: "Non-enforced init snapshot template. Cloud accepts any JSON object + 256 KiB cap. User-facing language: approved directories (JSON key remains scope.approved_roots). Do NOT include teammate role or scope.mode. Omit unknown optional fields rather than inventing zeros.",
|
|
228
|
+
$field_guide: {
|
|
229
|
+
high_value: "repositories (with remotes/languages for active work), harnesses, identity_signals, teammates (from git, humans only), scope.approved_roots, transcript_inventory, skills (repo-scanned counts), cli_version",
|
|
230
|
+
repository_detail: "Full metadata for repos with recent agent sessions; name/remote/language is enough for the long tail. Deduplicate clones; optional local_clone_count.",
|
|
231
|
+
instruction_files: "Agent instruction docs found in the repo (AGENTS.md, CLAUDE.md, MEMORY.md, etc.). Include name + size_bytes when present.",
|
|
232
|
+
omit_if_unknown: [
|
|
233
|
+
"letta_agent_count",
|
|
234
|
+
"letta_memory_artifact_count",
|
|
235
|
+
"allowlisted_file_names",
|
|
236
|
+
"skill_names on repositories (use top-level skills instead)",
|
|
237
|
+
],
|
|
238
|
+
},
|
|
183
239
|
repositories: [
|
|
184
240
|
{
|
|
185
241
|
display_name: "my-repo",
|
|
@@ -187,16 +243,18 @@ function initSnapshotTemplate() {
|
|
|
187
243
|
primary_language: "typescript",
|
|
188
244
|
build_system: "nx",
|
|
189
245
|
instruction_files: [{ name: "AGENTS.md", size_bytes: 1024 }],
|
|
190
|
-
skill_names: ["deploy"],
|
|
191
|
-
letta_agent_count: 2,
|
|
192
|
-
letta_memory_artifact_count: 0,
|
|
193
|
-
allowlisted_file_names: [],
|
|
194
246
|
},
|
|
195
247
|
],
|
|
196
248
|
harnesses: [{ name: "claude-code", version: "1.2.3" }],
|
|
197
|
-
skills: [
|
|
249
|
+
skills: [
|
|
250
|
+
{
|
|
251
|
+
directory: "my-repo/.claude/skills",
|
|
252
|
+
skill_count: 1,
|
|
253
|
+
sample_names: ["deploy"],
|
|
254
|
+
},
|
|
255
|
+
],
|
|
198
256
|
identity_signals: [{ type: "git_email", value: "dev@acme.com" }],
|
|
199
|
-
transcript_inventory: [{ source_key: "
|
|
257
|
+
transcript_inventory: [{ source_key: "claude-code", session_count: 3 }],
|
|
200
258
|
teammates: [
|
|
201
259
|
{
|
|
202
260
|
display_name: "Alex",
|
|
@@ -204,17 +262,18 @@ function initSnapshotTemplate() {
|
|
|
204
262
|
},
|
|
205
263
|
],
|
|
206
264
|
scope: { approved_roots: [] },
|
|
207
|
-
cli_version:
|
|
265
|
+
cli_version: (0, version_1.packageVersion)(),
|
|
208
266
|
}, null, 2)}\n`;
|
|
209
267
|
}
|
|
210
268
|
function claudeHooksReference() {
|
|
211
269
|
return `# Claude Code — Dreams live sync hooks
|
|
212
270
|
|
|
213
|
-
Install **globally** in \`~/.claude/settings.json\` (user scope).
|
|
271
|
+
Install **globally** in \`~/.claude/settings.json\` (user scope). Prefer \`dreams hooks install --dry-run\` / \`dreams hooks install\` so the CLI owns the merge. Manual merge is fallback only.
|
|
214
272
|
|
|
215
273
|
## Goal
|
|
216
274
|
|
|
217
|
-
|
|
275
|
+
- **SessionStart → \`dreams wake\`** — repair managed CLI/skill quietly; kick pending sync/backfill worker (no live-turn stdin hint).
|
|
276
|
+
- **Stop → \`dreams sync\`** — after each live turn; pipe hook JSON stdin so \`session_id\` can be a live hint.
|
|
218
277
|
|
|
219
278
|
## Platform-specific commands
|
|
220
279
|
|
|
@@ -222,27 +281,18 @@ Replace \`CLI_PATH\` with the absolute \`cli_path\` from \`dreams onboard --json
|
|
|
222
281
|
|
|
223
282
|
### macOS / Linux (bash)
|
|
224
283
|
|
|
225
|
-
Always quote the launcher path (it may contain spaces):
|
|
226
|
-
|
|
227
284
|
\`\`\`bash
|
|
285
|
+
"CLI_PATH" wake >/dev/null 2>&1 || true # dreams-sync-hook
|
|
228
286
|
"CLI_PATH" sync >/dev/null 2>&1 || true # dreams-sync-hook
|
|
229
287
|
\`\`\`
|
|
230
288
|
|
|
231
289
|
### Windows (PowerShell)
|
|
232
290
|
|
|
233
|
-
Use Claude's \`shell: "powershell"\` so the command is not run through bash redirects:
|
|
234
|
-
|
|
235
291
|
\`\`\`powershell
|
|
292
|
+
& 'CLI_PATH' wake *> $null; exit 0 # dreams-sync-hook
|
|
236
293
|
& 'CLI_PATH' sync *> $null; exit 0 # dreams-sync-hook
|
|
237
294
|
\`\`\`
|
|
238
295
|
|
|
239
|
-
Rules:
|
|
240
|
-
|
|
241
|
-
- Pipe stdin from the harness into that command (Claude already provides JSON on stdin to command hooks).
|
|
242
|
-
- Do **not** use \`npx\`, \`jq\`, or shell JSON parsing.
|
|
243
|
-
- Suppress stdout/stderr and force exit 0. Sync JSON must not enter Claude context.
|
|
244
|
-
- Keep the \`# dreams-sync-hook\` marker. On re-run, **update** the existing Dreams handler in place (including switching POSIX ↔ PowerShell when the host OS differs) instead of appending another copy.
|
|
245
|
-
|
|
246
296
|
## Example merge fragment (macOS / Linux)
|
|
247
297
|
|
|
248
298
|
\`\`\`json
|
|
@@ -253,7 +303,7 @@ Rules:
|
|
|
253
303
|
"hooks": [
|
|
254
304
|
{
|
|
255
305
|
"type": "command",
|
|
256
|
-
"command": "\\"CLI_PATH\\"
|
|
306
|
+
"command": "\\"CLI_PATH\\" wake >/dev/null 2>&1 || true # dreams-sync-hook"
|
|
257
307
|
}
|
|
258
308
|
]
|
|
259
309
|
}
|
|
@@ -283,7 +333,7 @@ Rules:
|
|
|
283
333
|
{
|
|
284
334
|
"type": "command",
|
|
285
335
|
"shell": "powershell",
|
|
286
|
-
"command": "& 'CLI_PATH'
|
|
336
|
+
"command": "& 'CLI_PATH' wake *> $null; exit 0 # dreams-sync-hook"
|
|
287
337
|
}
|
|
288
338
|
]
|
|
289
339
|
}
|
|
@@ -303,42 +353,82 @@ Rules:
|
|
|
303
353
|
}
|
|
304
354
|
\`\`\`
|
|
305
355
|
|
|
306
|
-
##
|
|
356
|
+
## Notes
|
|
307
357
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
358
|
+
- Prefer \`dreams hooks install\` (with \`--dry-run\` for consent). Keep \`# dreams-sync-hook\` markers; update in place; never append duplicates.
|
|
359
|
+
- Hooks must stay silent and exit 0. Silence is not proof of Cloud upload success.
|
|
360
|
+
- \`dreams sync pause\` / \`dreams sync resume\` toggle without uninstalling.
|
|
361
|
+
- Diagnostics: \`dreams sync status --json\`.
|
|
362
|
+
`;
|
|
363
|
+
}
|
|
364
|
+
function codexHooksReference() {
|
|
365
|
+
return `# Codex — Dreams live sync hooks
|
|
316
366
|
|
|
317
|
-
|
|
367
|
+
Install **user-level** hooks in \`~/.codex/hooks.json\`. Prefer \`dreams hooks install --dry-run\` / \`dreams hooks install\`.
|
|
368
|
+
|
|
369
|
+
Also ensure:
|
|
318
370
|
|
|
319
|
-
|
|
371
|
+
\`\`\`toml
|
|
372
|
+
[features]
|
|
373
|
+
hooks = true
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
## Goal
|
|
377
|
+
|
|
378
|
+
- **SessionStart → \`wake --source codex\`** via the Node launcher
|
|
379
|
+
- **Stop → \`sync --source codex\`** via the Node launcher (forwards stdin; prints \`{}\`)
|
|
380
|
+
|
|
381
|
+
## Why a Node launcher
|
|
382
|
+
|
|
383
|
+
Codex Stop expects exactly one JSON object on stdout. Use \`references/codex-sync-hook.cjs\`:
|
|
384
|
+
|
|
385
|
+
\`\`\`bash
|
|
386
|
+
node "HOOK_SCRIPT" "CLI_PATH" wake # dreams-sync-hook-codex
|
|
387
|
+
node "HOOK_SCRIPT" "CLI_PATH" sync # dreams-sync-hook-codex
|
|
388
|
+
\`\`\`
|
|
320
389
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
390
|
+
## Example merge fragment
|
|
391
|
+
|
|
392
|
+
\`\`\`json
|
|
393
|
+
{
|
|
394
|
+
"hooks": {
|
|
395
|
+
"SessionStart": [
|
|
396
|
+
{
|
|
397
|
+
"hooks": [
|
|
398
|
+
{
|
|
399
|
+
"type": "command",
|
|
400
|
+
"command": "node \\"HOOK_SCRIPT\\" \\"CLI_PATH\\" wake # dreams-sync-hook-codex"
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
],
|
|
405
|
+
"Stop": [
|
|
406
|
+
{
|
|
407
|
+
"hooks": [
|
|
408
|
+
{
|
|
409
|
+
"type": "command",
|
|
410
|
+
"command": "node \\"HOOK_SCRIPT\\" \\"CLI_PATH\\" sync # dreams-sync-hook-codex"
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
\`\`\`
|
|
328
418
|
|
|
329
|
-
|
|
419
|
+
## Trust (\`/hooks\`)
|
|
330
420
|
|
|
331
|
-
|
|
332
|
-
- Always quote the durable absolute launcher path, suppress stdout/stderr, and force exit 0.
|
|
333
|
-
- Preserve unrelated handlers; update the marked Dreams handler (\`# dreams-sync-hook\`) **in place** — never append duplicates.
|
|
334
|
-
- Do not put version checks in the hot hook path. If the launcher path is wrong/nonexistent, Dreams never starts and cannot record diagnostics — repair with \`dreams onboard --json\` / \`dreams status --json\` and re-substitute \`cli_path\`.
|
|
421
|
+
Ask the user to run \`/hooks\`, review, and trust. File write ≠ trusted ≠ Cloud upload succeeded.
|
|
335
422
|
|
|
336
|
-
|
|
423
|
+
## Notes
|
|
337
424
|
|
|
338
|
-
-
|
|
339
|
-
-
|
|
425
|
+
- Launcher stdout must be \`{}\\n\` with exit 0.
|
|
426
|
+
- After backfill, kick with \`dreams wake --source codex --json\` (not a ceremonial sync --force smoke test).
|
|
340
427
|
`;
|
|
341
428
|
}
|
|
429
|
+
function codexSyncHookScript() {
|
|
430
|
+
return fs.readFileSync(path.join(__dirname, "..", "bin", "dreams-codex-hook.cjs"), "utf8");
|
|
431
|
+
}
|
|
342
432
|
function readFile(file) {
|
|
343
433
|
try {
|
|
344
434
|
return fs.readFileSync(file, "utf8");
|
|
@@ -388,6 +478,8 @@ function desiredArtifacts() {
|
|
|
388
478
|
{ path: skillFilePath(), contents: skillContent((0, version_1.packageVersion)()) },
|
|
389
479
|
{ path: initSnapshotTemplatePath(), contents: initSnapshotTemplate() },
|
|
390
480
|
{ path: claudeHooksReferencePath(), contents: claudeHooksReference() },
|
|
481
|
+
{ path: codexHooksReferencePath(), contents: codexHooksReference() },
|
|
482
|
+
{ path: codexSyncHookScriptPath(), contents: codexSyncHookScript() },
|
|
391
483
|
];
|
|
392
484
|
}
|
|
393
485
|
function installSkill() {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* node:sqlite still emits ExperimentalWarning on load in Node 22.x even though
|
|
4
|
+
* the flag is no longer required at >= 22.13. Filter only that warning so agent
|
|
5
|
+
* transcripts and --json workflows stay clean; leave other warnings alone.
|
|
6
|
+
*
|
|
7
|
+
* Must run before the first `node:sqlite` import (see cli.ts import order and
|
|
8
|
+
* bin/dreams.cjs preflight).
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.suppressSqliteExperimentalWarning = suppressSqliteExperimentalWarning;
|
|
12
|
+
const SQLITE_EXPERIMENTAL = /SQLite is an experimental feature|ExperimentalWarning:.*SQLite|node:sqlite/i;
|
|
13
|
+
let installed = false;
|
|
14
|
+
function suppressSqliteExperimentalWarning() {
|
|
15
|
+
if (installed)
|
|
16
|
+
return;
|
|
17
|
+
installed = true;
|
|
18
|
+
const originalEmit = process.emit.bind(process);
|
|
19
|
+
function filteredEmit(event, ...args) {
|
|
20
|
+
if (event === "warning" && args.length > 0) {
|
|
21
|
+
const warning = args[0];
|
|
22
|
+
if (warning &&
|
|
23
|
+
typeof warning === "object" &&
|
|
24
|
+
warning.name === "ExperimentalWarning" &&
|
|
25
|
+
typeof warning.message === "string" &&
|
|
26
|
+
SQLITE_EXPERIMENTAL.test(warning.message)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return Reflect.apply(originalEmit, process, [event, ...args]);
|
|
31
|
+
}
|
|
32
|
+
process.emit = filteredEmit;
|
|
33
|
+
}
|
|
34
|
+
suppressSqliteExperimentalWarning();
|