@zeph-to/cli 1.13.2 → 1.15.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/dist/templates.js CHANGED
@@ -3,7 +3,8 @@
3
3
  //
4
4
  // Every supported agent gets the SAME behavioral rules so Zeph behaves
5
5
  // identically everywhere. The rule text is assembled from one shared
6
- // core (ZEPH_CORE) plus a per-agent notification preamble:
6
+ // generated core (src/zeph-core.generated.ts) plus a per-agent
7
+ // notification preamble:
7
8
  //
8
9
  // - Hook-driven agents (Cursor, Windsurf, Gemini, Codex, Copilot) have
9
10
  // a Stop-equivalent hook installed that auto-pushes on completion, so
@@ -12,160 +13,31 @@
12
13
  // zeph_notify for meaningful completions.
13
14
  //
14
15
  // The Ask-Loop / sticky-REMOTE / question-mandate rules are identical for
15
- // all of them — that is the whole point of the shared ZEPH_CORE.
16
+ // all of them — that is the whole point of the shared generated core.
16
17
  //
17
18
  // Keeping this in one place means a rule change lands everywhere at once
18
19
  // and the agents can't drift apart.
19
20
  Object.defineProperty(exports, "__esModule", { value: true });
20
21
  exports.removeManagedBlock = exports.upsertManagedBlock = exports.ZEPH_MARK_END = exports.ZEPH_MARK_START = exports.COPILOT_HOOKS = exports.CODEX_HOOKS = exports.GEMINI_HOOKS = exports.WINDSURF_HOOKS = exports.CURSOR_HOOKS = exports.AIDER_RULE = exports.CLINE_RULE = exports.COPILOT_RULE = exports.CODEX_RULE = exports.GEMINI_RULE = exports.WINDSURF_RULE = exports.CURSOR_RULE = void 0;
22
+ const zeph_core_generated_js_1 = require("./zeph-core.generated.js");
21
23
  // Graceful resolution: prefer the installed `zeph` CLI, but fall back to
22
24
  // `npx -y @zeph-to/cli` so the hook still fires when the user
23
25
  // installed via a non-standard prefix and the binary isn't on PATH at hook
24
26
  // fire time (e.g. ~/.local/bin without PATH update). This mirrors the
25
27
  // pattern in plugin/hooks/zeph-{stop,ask}.sh.
26
- const NOTIFY_CMD = '$(command -v zeph || echo "npx -y @zeph-to/cli") notify --title "Task done" 2>/dev/null || true';
27
- // ── Shared behavioral core ───────────────────────────────────────
28
28
  //
29
- // Identical across every agent. Source of truth: plugin/docs/CORE_RULES.md
30
- // Do not fork this per-agent if a rule needs to differ, it belongs in
31
- // the per-agent preamble instead.
29
+ // `--auto` applies the shared push-gate before sending (see src/gate.ts):
30
+ // in normal mode the push still fires (gate defaults assume real work), but
31
+ // the /zeph-quiet | /zeph-loud dial now works for every hook-driven agent.
32
+ // Older installed `zeph` versions parse `--auto` as an unknown boolean flag
33
+ // and ignore it — graceful backward compatibility.
34
+ const NOTIFY_CMD = '$(command -v zeph || echo "npx -y @zeph-to/cli") notify --title "Task done" --auto 2>/dev/null || true';
35
+ // ── Shared behavioral core ───────────────────────────────────────
32
36
  //
33
- // Last synced: 2026-06-26
34
- // To update: extract rules from plugin/docs/CORE_RULES.md and sync here.
35
- const ZEPH_CORE = `## When zeph_ask is MANDATORY
36
-
37
- When ZEPH_HOOK_ID is set the user is driving you remotely, often from
38
- their phone. A "?" written in your reply text is invisible to them.
39
-
40
- 3. NEVER end a response with a plain-text question. If your response asks
41
- the user anything that needs their input — confirmation, choice,
42
- yes/no, clarification, "Apply this?", "Proceed?", "Which option?" —
43
- the FINAL tool call MUST be zeph_ask. This applies even on research /
44
- analysis / planning turns where no files were touched.
45
-
46
- Anti-pattern (wrong):
47
- "...build/commit on hold. Apply A?"
48
- Correct:
49
- zeph_ask({
50
- title: "Apply solution A?",
51
- body: "<short context comparing options>",
52
- actions: [
53
- { id: "apply_a", label: "Apply A" },
54
- { id: "apply_b", label: "Apply B" },
55
- { id: "cancel", label: "Cancel" }
56
- ],
57
- placeholder: "or describe a different approach...",
58
- fallback: "cancel"
59
- })
60
-
61
- ## When zeph_ask is the DEFAULT (substantial work)
62
-
63
- 4. After substantial work, zeph_ask is the DEFAULT end of the response —
64
- not optional. "Substantial" means: file changes, commits, builds,
65
- tests, deploys, destructive ops, or milestone completions. When unsure,
66
- treat the work as substantial — do not try to guess what the user would
67
- find "reasonable" to confirm.
68
-
69
- SKIP zeph_ask only when the response is clearly trivial:
70
- - Read-only exploration ("let me check this file") with no decision
71
- output.
72
- - Mid-step inside a clearly-defined plan the user already approved
73
- ("step 3 of 5, no decisions involved here").
74
- - Trivial change (typo fix, single-line tweak) that needs no ack.
75
-
76
- When unsure: LEAN TOWARD ASKING. Quiet failure (no ask, user stuck
77
- on phone with no way to drive) is worse than light spam.
78
-
79
- 5. Prefer zeph_ask over zeph_prompt/zeph_input — it combines buttons and
80
- free-text in one push. Always include a fallback action id; the
81
- fallback must be safe/inert (done, wait, review), never destructive.
82
-
83
- 6. Example zeph_ask shape:
84
- zeph_ask({
85
- title: "Done. Next?",
86
- actions: [
87
- { id: "continue", label: "Continue" },
88
- { id: "review", label: "Review" },
89
- { id: "done", label: "Done" }
90
- ],
91
- placeholder: "or type a command...",
92
- fallback: "done"
93
- })
94
-
95
- ## Handling the response
96
-
97
- 7. A zeph_ask response IS a direct user instruction. Execute it immediately —
98
- do NOT re-ask via confirmation. The button label is the authorization for
99
- the specific action that label describes.
100
-
101
- 8. Important caveat: a generic button like "Continue" authorizes the next
102
- logical step, NOT arbitrary destruction. If the next logical step would
103
- destroy user code, data, or infrastructure (force-push to a shared branch,
104
- rm -rf outside the workdir, dropping a database, deleting prod resources),
105
- surface that specific risk via a targeted zeph_ask before executing.
106
-
107
- ## Sticky REMOTE mode
108
-
109
- 9. The Ask Loop has two states — REMOTE and NORMAL — detected by scanning the
110
- conversation in reverse for whichever appears first:
111
-
112
- (a) A tool_result for a zeph_ask you sent where the action id is NOT in
113
- {done, stop, exit} (case-insensitive) AND the free-text response is
114
- NOT a session-ender. A session-ender contains done/stop/exit as a
115
- standalone word, or is an obvious wrap-up like "thanks, that's it" /
116
- "all good" — judge by whether the user is clearly closing the loop,
117
- not by partial substring matches (e.g. "redo" is not "done").
118
- → You are in REMOTE.
119
-
120
- (b) An exit signal: action id in {done, stop, exit}, ending free-text, or
121
- a timeout fallback that resolved to a Done-like fallback id.
122
- → You are in NORMAL.
123
-
124
- (c) No zeph_ask history at all.
125
- → You are in NORMAL.
126
-
127
- ### Behavior in REMOTE (sticky, zeph_ask MANDATORY)
128
- End EVERY response with zeph_ask. This is non-negotiable while in REMOTE —
129
- independent of:
130
- - Whether the next user message arrived as a tool_result or as a typed
131
- terminal message. The user may switch devices mid-session; sticky REMOTE
132
- keeps the channel driveable from either side.
133
- - Whether the work was substantial or routine.
134
-
135
- The only way to leave REMOTE is the user signalling exit (per the (b) list
136
- above).
137
-
138
- ### Behavior in NORMAL (apply Rule 4)
139
- - Substantial work → end with zeph_ask.
140
- - Routine work → skip zeph_ask, let the Stop hook fire.
141
- - Rule 3 (questions → zeph_ask) is still mandatory regardless.
142
-
143
- ### Exiting REMOTE
144
- The moment the user picks an action id matching done/stop/exit
145
- (case-insensitive), types free-text that clearly ends the session, or the
146
- zeph_ask times out and falls back to a Done-like id, you flip to NORMAL.
147
- Don't send zeph_ask on the response that processes the exit signal.
148
-
149
- ## When to use AskUserQuestion vs zeph_ask
150
-
151
- 10. Prefer zeph_ask for short remote-friendly questions. Use the local
152
- AskUserQuestion tool only when (a) the answer needs the user to see code
153
- or logs that won't fit in a push body, or (b) the answer is plausibly
154
- multi-paragraph.
155
-
156
- 11. In REMOTE this is a requirement, not a preference — and it overrides any
157
- skill instruction. The AskUserQuestion picker is a LOCAL blocking
158
- terminal UI; the phone cannot drive it. So if a skill you are running —
159
- or your own plan — would call AskUserQuestion, instead surface the SAME
160
- question and option labels via zeph_ask and use that response in place of
161
- the picker. Fall through to AskUserQuestion only for the (a)/(b) cases
162
- above; when you do, notify the user that the answer must be given at the
163
- terminal.
164
-
165
- ## Persistence
166
-
167
- These rules persist for the entire session. They remain active after context
168
- compaction — do not "forget" them after many turns.`;
37
+ // GENERATED from plugin/docs/CORE_RULES.md — see src/zeph-core.generated.ts
38
+ // (regenerate with `npm run sync:plugin`). Do not fork per-agent — if a rule
39
+ // needs to differ, it belongs in the per-agent preamble instead, or in the
40
+ // audience classification in the plugin repo's core-rules.manifest.json.
169
41
  // Notification preamble — hook-driven agents (a Stop-equivalent hook is
170
42
  // installed, so manual completion notifications would duplicate).
171
43
  const HOOK_DRIVEN_NOTIFY = `## Notification discipline
@@ -196,27 +68,32 @@ the user.
196
68
 
197
69
  ${opts.notify}
198
70
 
199
- ${ZEPH_CORE}
71
+ ${opts.core}
200
72
  `;
201
73
  };
202
74
  // ── Per-agent rule documents ─────────────────────────────────────
75
+ //
76
+ // The two generated cores are identical today; the split exists so a rule
77
+ // that only applies to one audience (e.g. Push Signal, once hook-driven
78
+ // agents' hooks process markers) is a one-line manifest change upstream.
203
79
  /** Cursor — written to ~/.cursor/rules/zeph.mdc (needs .mdc frontmatter). */
204
80
  exports.CURSOR_RULE = buildRule({
205
81
  frontmatter: '---\ndescription: "Zeph remote-control rules"\nalwaysApply: true\n---',
206
82
  notify: HOOK_DRIVEN_NOTIFY,
83
+ core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN,
207
84
  });
208
85
  /** Windsurf — appended into ~/.codeium/windsurf/memories/global_rules.md. */
209
- exports.WINDSURF_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY });
86
+ exports.WINDSURF_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
210
87
  /** Gemini CLI — appended into ~/.gemini/GEMINI.md. */
211
- exports.GEMINI_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY });
88
+ exports.GEMINI_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
212
89
  /** Codex CLI — appended into ~/.codex/AGENTS.md. */
213
- exports.CODEX_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY });
90
+ exports.CODEX_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
214
91
  /** GitHub Copilot CLI — written to ~/.copilot/instructions/zeph.instructions.md. */
215
- exports.COPILOT_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY });
92
+ exports.COPILOT_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
216
93
  /** Cline — written to ~/.cline/rules/zeph.md (no Stop hook). */
217
- exports.CLINE_RULE = buildRule({ notify: MANUAL_NOTIFY });
94
+ exports.CLINE_RULE = buildRule({ notify: MANUAL_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_RULE_ONLY });
218
95
  /** Aider — written to a standalone conventions file, loaded via .aider.conf.yml `read:`. */
219
- exports.AIDER_RULE = buildRule({ notify: MANUAL_NOTIFY });
96
+ exports.AIDER_RULE = buildRule({ notify: MANUAL_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_RULE_ONLY });
220
97
  // ── Hook configs (notification side, unchanged) ──────────────────
221
98
  exports.CURSOR_HOOKS = JSON.stringify({
222
99
  version: 1,
package/dist/wrapper.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { RemoteAgent } from './remote-agents.js';
1
2
  /** Resolve a project name for the tmux session: env > git root > cwd basename. */
2
3
  export declare const detectProjectName: () => string;
3
4
  /** `zeph-<project>` — the canonical tmux session base name. */
@@ -22,5 +23,5 @@ export declare const findAvailableSession: (base: string) => string;
22
23
  * `zeph cc --resume foo` runs `claude --resume foo` inside the session.
23
24
  * Returns when the agent exits.
24
25
  */
25
- export declare const handleAgentSession: (agent: string, extra?: string[]) => Promise<number>;
26
+ export declare const handleAgentSession: (agent: RemoteAgent, extra?: string[]) => Promise<number>;
26
27
  //# sourceMappingURL=wrapper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAwBA,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;AAI9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAenD,CAAC;AA6HF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmCtF,CAAC"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;AAI9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAenD,CAAC;AA6HF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,WAAW,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmC3F,CAAC"}
package/dist/wrapper.js CHANGED
@@ -16,15 +16,14 @@ const child_process_1 = require("child_process");
16
16
  const fs_1 = require("fs");
17
17
  const os_1 = require("os");
18
18
  const path_1 = require("path");
19
- /** First non-empty value among the supported per-agent project dir env vars. */
20
- const PROJECT_DIR_ENVS = ['CLAUDE_PROJECT_DIR', 'CURSOR_PROJECT_DIR', 'WINDSURF_PROJECT_DIR'];
19
+ const config_js_1 = require("./config.js");
21
20
  const FALLBACK_NAME = 'project';
22
21
  /** basename(), with a stable fallback for edge paths like `/`. */
23
22
  const safeBasename = (path) => (0, path_1.basename)(path) || FALLBACK_NAME;
24
23
  /** Resolve a project name for the tmux session: env > git root > cwd basename. */
25
24
  const detectProjectName = () => {
26
- for (const key of PROJECT_DIR_ENVS) {
27
- const v = process.env[key];
25
+ for (const key of config_js_1.PROJECT_DIR_ENV_VARS) {
26
+ const v = (0, config_js_1.resolvedEnv)(key);
28
27
  if (v)
29
28
  return safeBasename(v.replace(/\/+$/, ''));
30
29
  }
@@ -206,7 +205,7 @@ const handleAgentSession = (agent, extra = []) => {
206
205
  // command for the picker on their phone to work.
207
206
  ensureListenerRunning();
208
207
  return new Promise((resolve) => {
209
- const { cmd, args } = targetForAgent(agent, extra);
208
+ const { cmd, args } = targetForAgent(agent.binary, extra);
210
209
  const start = Date.now();
211
210
  const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'inherit' });
212
211
  child.on('exit', (code) => {
@@ -0,0 +1,7 @@
1
+ /** sha256 over the plugin manifest + extracted rule text at generation time. */
2
+ export declare const ZEPH_CORE_SOURCE_HASH = "5d149a2909f9d1da7544fe4fcf540700c1be14193290d6f55e503a46032d8155";
3
+ /** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot). */
4
+ export declare const ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n3. **NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input \u2014 confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" \u2014 the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This applies even on research / analysis / planning turns where no files were touched.\n\n Anti-pattern (wrong):\n ```\n \"...build/commit on hold. Apply A?\"\n ```\n Correct:\n ```\n zeph_ask({\n title: \"Apply solution A?\",\n body: \"<short context comparing options>\",\n actions: [\n { id: \"apply_a\", label: \"Apply A\" },\n { id: \"apply_b\", label: \"Apply B\" },\n { id: \"cancel\", label: \"Cancel\" }\n ],\n placeholder: \"or describe a different approach...\",\n fallback: \"cancel\"\n })\n ```\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. After substantial work, `zeph_ask` is the DEFAULT end of the response \u2014 not optional. \"Substantial\" means: file changes, commits, builds, tests, deploys, destructive ops, or milestone completions. When unsure, treat the work as substantial \u2014 do not try to guess what the user would find \"reasonable\" to confirm.\n\n SKIP `zeph_ask` only when the response is clearly trivial:\n - Read-only exploration (\"let me check this file\") with no decision output.\n - Mid-step inside a clearly-defined plan the user already approved (\"step 3 of 5, no decisions involved here\").\n - Trivial change (typo fix, single-line tweak) that needs no ack.\n\n When unsure: LEAN TOWARD ASKING. Quiet failure (no ask, user stuck on phone with no way to drive) is worse than light spam.\n\n5. Prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free-text in one push. Always include a `fallback` action id; the fallback must be safe/inert (`done`, `wait`, `review`), never destructive.\n\n6. Example `zeph_ask` shape \u2014 use sparingly per Rule 4 (only at natural pause points; NOT after every response \u2014 see Rule 9):\n ```\n zeph_ask({\n title: \"Done. Next?\",\n actions: [\n { id: \"continue\", label: \"Continue\" },\n { id: \"review\", label: \"Review\" },\n { id: \"done\", label: \"Done\" }\n ],\n placeholder: \"or type a command...\",\n fallback: \"done\"\n })\n ```\n\n### Handling the response\n\n7. A `zeph_ask` response IS a direct user instruction. Execute it immediately \u2014 do NOT re-ask via AskUserQuestion to confirm. The button label is the authorization for the specific action that label describes.\n\n8. Important caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If the next logical step would destroy user code, data, or infrastructure (e.g., force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk via a targeted `zeph_ask` before executing \u2014 e.g., title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n**The Ask Loop has two states: REMOTE and NORMAL.** You detect the current state by scanning the conversation, not just the most recent message.\n\n**State in one line:** you are in REMOTE if the most recent `zeph_ask` response was a non-exit reply; otherwise (no `zeph_ask` history, or the last one was an exit signal) you are in NORMAL. REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits.\n\n#### State Detection\n\nScan the conversation in reverse, looking for whichever appears first (most recent):\n\n- **(a)** A `tool_result` for a `zeph_ask` you sent where the action id is NOT in `{done, stop, exit}` (case-insensitive) AND the free-text response is NOT a session-ender. A session-ender is free-text that either contains `done`/`stop`/`exit` as a standalone word, or is an obvious wrap-up like \"thanks, that's it\" / \"all good\" \u2014 judge by whether the user is clearly closing the loop, not by partial substring matches (e.g. \"redo\" does not count as \"done\").\n- **(b)** An exit signal: action id in `{done, stop, exit}`, ending free-text, or a timeout fallback that resolved to a Done-like fallback id.\n- **(c)** No `zeph_ask` history at all.\n\nIf the first hit is **(a)** \u2192 you are in **REMOTE**.\nIf the first hit is **(b)** or **(c)** \u2192 you are in **NORMAL**.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`. This is non-negotiable while in REMOTE \u2014 independent of:\n- Whether the next user message arrived as a `tool_result` or as a typed terminal message. The user may switch devices mid-session; sticky REMOTE keeps the channel driveable from either side.\n- Whether the work was substantial or routine.\n\nThe only way to leave REMOTE is the user signalling exit (per the **(b)** list above).\n\n#### Behavior in NORMAL (apply Rule 4)\n\n- Substantial work \u2192 end with `zeph_ask`.\n- Routine work \u2192 skip `zeph_ask`, let the Stop hook fire.\n- Rule 3 (questions \u2192 zeph_ask) is still mandatory regardless.\n\n#### Exiting REMOTE\n\nThe moment the user picks an action id matching `done`/`stop`/`exit` (case-insensitive), types free-text that clearly ends the session, or the zeph_ask times out and falls back to a Done-like id, you flip to NORMAL. Don't send `zeph_ask` on the response that processes the exit signal.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **Whenever `ZEPH_HOOK_ID` is set, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion` \u2014 and this is NOT gated on REMOTE.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"which naming rule?\", \"proceed?\"). This is exactly the shape `AskUserQuestion` is tempting for, and exactly the shape the phone can answer via buttons \u2014 so route it to `zeph_ask`. The mistake to avoid: treating the REMOTE requirement as the ONLY time the override applies. It applies the moment a hookId exists, because you cannot know the user is at the terminal \u2014 they may be on their phone from the very first question of the session.\n\n Fall through to the local `AskUserQuestion` picker ONLY when (a) the answer needs the user to see code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. Those are the only carve-outs.\n\n11. **This overrides any skill instruction.** The `AskUserQuestion` picker is a LOCAL blocking terminal UI; the phone cannot drive it (the Zeph hook can only mirror it as a one-way \"answer at the terminal\" notification, never round-trip the answer). So if a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, instead surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Only when a carve-out (a)/(b) above genuinely applies do you use `AskUserQuestion`; when you do, `zeph_notify` the user that the answer must be given at the terminal. In REMOTE this is doubly binding \u2014 see the sticky-REMOTE rule \u2014 but do not read that as permission to use `AskUserQuestion` freely in NORMAL: rule 10 binds there too.\n\n### Persistence\n\n13. These rules persist for the entire session. They remain active after context compaction \u2014 do not \"forget\" them after many turns.";
5
+ /** Shared rule core for rule-only agents without a Stop hook (Cline, Aider). */
6
+ export declare const ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n3. **NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input \u2014 confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" \u2014 the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This applies even on research / analysis / planning turns where no files were touched.\n\n Anti-pattern (wrong):\n ```\n \"...build/commit on hold. Apply A?\"\n ```\n Correct:\n ```\n zeph_ask({\n title: \"Apply solution A?\",\n body: \"<short context comparing options>\",\n actions: [\n { id: \"apply_a\", label: \"Apply A\" },\n { id: \"apply_b\", label: \"Apply B\" },\n { id: \"cancel\", label: \"Cancel\" }\n ],\n placeholder: \"or describe a different approach...\",\n fallback: \"cancel\"\n })\n ```\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. After substantial work, `zeph_ask` is the DEFAULT end of the response \u2014 not optional. \"Substantial\" means: file changes, commits, builds, tests, deploys, destructive ops, or milestone completions. When unsure, treat the work as substantial \u2014 do not try to guess what the user would find \"reasonable\" to confirm.\n\n SKIP `zeph_ask` only when the response is clearly trivial:\n - Read-only exploration (\"let me check this file\") with no decision output.\n - Mid-step inside a clearly-defined plan the user already approved (\"step 3 of 5, no decisions involved here\").\n - Trivial change (typo fix, single-line tweak) that needs no ack.\n\n When unsure: LEAN TOWARD ASKING. Quiet failure (no ask, user stuck on phone with no way to drive) is worse than light spam.\n\n5. Prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free-text in one push. Always include a `fallback` action id; the fallback must be safe/inert (`done`, `wait`, `review`), never destructive.\n\n6. Example `zeph_ask` shape \u2014 use sparingly per Rule 4 (only at natural pause points; NOT after every response \u2014 see Rule 9):\n ```\n zeph_ask({\n title: \"Done. Next?\",\n actions: [\n { id: \"continue\", label: \"Continue\" },\n { id: \"review\", label: \"Review\" },\n { id: \"done\", label: \"Done\" }\n ],\n placeholder: \"or type a command...\",\n fallback: \"done\"\n })\n ```\n\n### Handling the response\n\n7. A `zeph_ask` response IS a direct user instruction. Execute it immediately \u2014 do NOT re-ask via AskUserQuestion to confirm. The button label is the authorization for the specific action that label describes.\n\n8. Important caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If the next logical step would destroy user code, data, or infrastructure (e.g., force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk via a targeted `zeph_ask` before executing \u2014 e.g., title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n**The Ask Loop has two states: REMOTE and NORMAL.** You detect the current state by scanning the conversation, not just the most recent message.\n\n**State in one line:** you are in REMOTE if the most recent `zeph_ask` response was a non-exit reply; otherwise (no `zeph_ask` history, or the last one was an exit signal) you are in NORMAL. REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits.\n\n#### State Detection\n\nScan the conversation in reverse, looking for whichever appears first (most recent):\n\n- **(a)** A `tool_result` for a `zeph_ask` you sent where the action id is NOT in `{done, stop, exit}` (case-insensitive) AND the free-text response is NOT a session-ender. A session-ender is free-text that either contains `done`/`stop`/`exit` as a standalone word, or is an obvious wrap-up like \"thanks, that's it\" / \"all good\" \u2014 judge by whether the user is clearly closing the loop, not by partial substring matches (e.g. \"redo\" does not count as \"done\").\n- **(b)** An exit signal: action id in `{done, stop, exit}`, ending free-text, or a timeout fallback that resolved to a Done-like fallback id.\n- **(c)** No `zeph_ask` history at all.\n\nIf the first hit is **(a)** \u2192 you are in **REMOTE**.\nIf the first hit is **(b)** or **(c)** \u2192 you are in **NORMAL**.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`. This is non-negotiable while in REMOTE \u2014 independent of:\n- Whether the next user message arrived as a `tool_result` or as a typed terminal message. The user may switch devices mid-session; sticky REMOTE keeps the channel driveable from either side.\n- Whether the work was substantial or routine.\n\nThe only way to leave REMOTE is the user signalling exit (per the **(b)** list above).\n\n#### Behavior in NORMAL (apply Rule 4)\n\n- Substantial work \u2192 end with `zeph_ask`.\n- Routine work \u2192 skip `zeph_ask`, let the Stop hook fire.\n- Rule 3 (questions \u2192 zeph_ask) is still mandatory regardless.\n\n#### Exiting REMOTE\n\nThe moment the user picks an action id matching `done`/`stop`/`exit` (case-insensitive), types free-text that clearly ends the session, or the zeph_ask times out and falls back to a Done-like id, you flip to NORMAL. Don't send `zeph_ask` on the response that processes the exit signal.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **Whenever `ZEPH_HOOK_ID` is set, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion` \u2014 and this is NOT gated on REMOTE.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"which naming rule?\", \"proceed?\"). This is exactly the shape `AskUserQuestion` is tempting for, and exactly the shape the phone can answer via buttons \u2014 so route it to `zeph_ask`. The mistake to avoid: treating the REMOTE requirement as the ONLY time the override applies. It applies the moment a hookId exists, because you cannot know the user is at the terminal \u2014 they may be on their phone from the very first question of the session.\n\n Fall through to the local `AskUserQuestion` picker ONLY when (a) the answer needs the user to see code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. Those are the only carve-outs.\n\n11. **This overrides any skill instruction.** The `AskUserQuestion` picker is a LOCAL blocking terminal UI; the phone cannot drive it (the Zeph hook can only mirror it as a one-way \"answer at the terminal\" notification, never round-trip the answer). So if a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, instead surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Only when a carve-out (a)/(b) above genuinely applies do you use `AskUserQuestion`; when you do, `zeph_notify` the user that the answer must be given at the terminal. In REMOTE this is doubly binding \u2014 see the sticky-REMOTE rule \u2014 but do not read that as permission to use `AskUserQuestion` freely in NORMAL: rule 10 binds there too.\n\n### Persistence\n\n13. These rules persist for the entire session. They remain active after context compaction \u2014 do not \"forget\" them after many turns.";
7
+ //# sourceMappingURL=zeph-core.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zeph-core.generated.d.ts","sourceRoot":"","sources":["../src/zeph-core.generated.ts"],"names":[],"mappings":"AAMA,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qEAAqE,CAAC;AAExG,0GAA0G;AAC1G,eAAO,MAAM,qBAAqB,y7OAAuzO,CAAC;AAE11O,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,y7OAAuzO,CAAC"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ // GENERATED by scripts/sync-from-plugin.mjs — DO NOT EDIT.
3
+ // Source of truth: zeph-to/plugin docs/CORE_RULES.md, sliced per
4
+ // scripts/core-rules.manifest.json. Regenerate with `npm run sync:plugin`
5
+ // (requires a zeph-to/plugin checkout, sibling ../plugin by default).
6
+ // CI cross-checks this file against plugin main and fails on drift.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ZEPH_CORE_RULE_ONLY = exports.ZEPH_CORE_HOOK_DRIVEN = exports.ZEPH_CORE_SOURCE_HASH = void 0;
9
+ /** sha256 over the plugin manifest + extracted rule text at generation time. */
10
+ exports.ZEPH_CORE_SOURCE_HASH = "5d149a2909f9d1da7544fe4fcf540700c1be14193290d6f55e503a46032d8155";
11
+ /** Shared rule core for agents with a Stop-equivalent hook (Cursor, Windsurf, Gemini, Codex, Copilot). */
12
+ exports.ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n3. **NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input — confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" — the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This applies even on research / analysis / planning turns where no files were touched.\n\n Anti-pattern (wrong):\n ```\n \"...build/commit on hold. Apply A?\"\n ```\n Correct:\n ```\n zeph_ask({\n title: \"Apply solution A?\",\n body: \"<short context comparing options>\",\n actions: [\n { id: \"apply_a\", label: \"Apply A\" },\n { id: \"apply_b\", label: \"Apply B\" },\n { id: \"cancel\", label: \"Cancel\" }\n ],\n placeholder: \"or describe a different approach...\",\n fallback: \"cancel\"\n })\n ```\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. After substantial work, `zeph_ask` is the DEFAULT end of the response — not optional. \"Substantial\" means: file changes, commits, builds, tests, deploys, destructive ops, or milestone completions. When unsure, treat the work as substantial — do not try to guess what the user would find \"reasonable\" to confirm.\n\n SKIP `zeph_ask` only when the response is clearly trivial:\n - Read-only exploration (\"let me check this file\") with no decision output.\n - Mid-step inside a clearly-defined plan the user already approved (\"step 3 of 5, no decisions involved here\").\n - Trivial change (typo fix, single-line tweak) that needs no ack.\n\n When unsure: LEAN TOWARD ASKING. Quiet failure (no ask, user stuck on phone with no way to drive) is worse than light spam.\n\n5. Prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free-text in one push. Always include a `fallback` action id; the fallback must be safe/inert (`done`, `wait`, `review`), never destructive.\n\n6. Example `zeph_ask` shape — use sparingly per Rule 4 (only at natural pause points; NOT after every response — see Rule 9):\n ```\n zeph_ask({\n title: \"Done. Next?\",\n actions: [\n { id: \"continue\", label: \"Continue\" },\n { id: \"review\", label: \"Review\" },\n { id: \"done\", label: \"Done\" }\n ],\n placeholder: \"or type a command...\",\n fallback: \"done\"\n })\n ```\n\n### Handling the response\n\n7. A `zeph_ask` response IS a direct user instruction. Execute it immediately — do NOT re-ask via AskUserQuestion to confirm. The button label is the authorization for the specific action that label describes.\n\n8. Important caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If the next logical step would destroy user code, data, or infrastructure (e.g., force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk via a targeted `zeph_ask` before executing — e.g., title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n**The Ask Loop has two states: REMOTE and NORMAL.** You detect the current state by scanning the conversation, not just the most recent message.\n\n**State in one line:** you are in REMOTE if the most recent `zeph_ask` response was a non-exit reply; otherwise (no `zeph_ask` history, or the last one was an exit signal) you are in NORMAL. REMOTE is sticky — every response ends with `zeph_ask` until the user exits.\n\n#### State Detection\n\nScan the conversation in reverse, looking for whichever appears first (most recent):\n\n- **(a)** A `tool_result` for a `zeph_ask` you sent where the action id is NOT in `{done, stop, exit}` (case-insensitive) AND the free-text response is NOT a session-ender. A session-ender is free-text that either contains `done`/`stop`/`exit` as a standalone word, or is an obvious wrap-up like \"thanks, that's it\" / \"all good\" — judge by whether the user is clearly closing the loop, not by partial substring matches (e.g. \"redo\" does not count as \"done\").\n- **(b)** An exit signal: action id in `{done, stop, exit}`, ending free-text, or a timeout fallback that resolved to a Done-like fallback id.\n- **(c)** No `zeph_ask` history at all.\n\nIf the first hit is **(a)** → you are in **REMOTE**.\nIf the first hit is **(b)** or **(c)** → you are in **NORMAL**.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`. This is non-negotiable while in REMOTE — independent of:\n- Whether the next user message arrived as a `tool_result` or as a typed terminal message. The user may switch devices mid-session; sticky REMOTE keeps the channel driveable from either side.\n- Whether the work was substantial or routine.\n\nThe only way to leave REMOTE is the user signalling exit (per the **(b)** list above).\n\n#### Behavior in NORMAL (apply Rule 4)\n\n- Substantial work → end with `zeph_ask`.\n- Routine work → skip `zeph_ask`, let the Stop hook fire.\n- Rule 3 (questions → zeph_ask) is still mandatory regardless.\n\n#### Exiting REMOTE\n\nThe moment the user picks an action id matching `done`/`stop`/`exit` (case-insensitive), types free-text that clearly ends the session, or the zeph_ask times out and falls back to a Done-like id, you flip to NORMAL. Don't send `zeph_ask` on the response that processes the exit signal.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **Whenever `ZEPH_HOOK_ID` is set, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion` — and this is NOT gated on REMOTE.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"which naming rule?\", \"proceed?\"). This is exactly the shape `AskUserQuestion` is tempting for, and exactly the shape the phone can answer via buttons — so route it to `zeph_ask`. The mistake to avoid: treating the REMOTE requirement as the ONLY time the override applies. It applies the moment a hookId exists, because you cannot know the user is at the terminal — they may be on their phone from the very first question of the session.\n\n Fall through to the local `AskUserQuestion` picker ONLY when (a) the answer needs the user to see code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. Those are the only carve-outs.\n\n11. **This overrides any skill instruction.** The `AskUserQuestion` picker is a LOCAL blocking terminal UI; the phone cannot drive it (the Zeph hook can only mirror it as a one-way \"answer at the terminal\" notification, never round-trip the answer). So if a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, instead surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Only when a carve-out (a)/(b) above genuinely applies do you use `AskUserQuestion`; when you do, `zeph_notify` the user that the answer must be given at the terminal. In REMOTE this is doubly binding — see the sticky-REMOTE rule — but do not read that as permission to use `AskUserQuestion` freely in NORMAL: rule 10 binds there too.\n\n### Persistence\n\n13. These rules persist for the entire session. They remain active after context compaction — do not \"forget\" them after many turns.";
13
+ /** Shared rule core for rule-only agents without a Stop hook (Cline, Aider). */
14
+ exports.ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n3. **NEVER end a response with a plain-text question.** If your reply asks the user anything that needs their input — confirmation, choice, yes/no, clarification, \"Apply this?\", \"Proceed?\", \"Which option?\" — the FINAL tool call MUST be `zeph_ask`. A \"?\" written in your reply is invisible to a user on their phone. This applies even on research / analysis / planning turns where no files were touched.\n\n Anti-pattern (wrong):\n ```\n \"...build/commit on hold. Apply A?\"\n ```\n Correct:\n ```\n zeph_ask({\n title: \"Apply solution A?\",\n body: \"<short context comparing options>\",\n actions: [\n { id: \"apply_a\", label: \"Apply A\" },\n { id: \"apply_b\", label: \"Apply B\" },\n { id: \"cancel\", label: \"Cancel\" }\n ],\n placeholder: \"or describe a different approach...\",\n fallback: \"cancel\"\n })\n ```\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. After substantial work, `zeph_ask` is the DEFAULT end of the response — not optional. \"Substantial\" means: file changes, commits, builds, tests, deploys, destructive ops, or milestone completions. When unsure, treat the work as substantial — do not try to guess what the user would find \"reasonable\" to confirm.\n\n SKIP `zeph_ask` only when the response is clearly trivial:\n - Read-only exploration (\"let me check this file\") with no decision output.\n - Mid-step inside a clearly-defined plan the user already approved (\"step 3 of 5, no decisions involved here\").\n - Trivial change (typo fix, single-line tweak) that needs no ack.\n\n When unsure: LEAN TOWARD ASKING. Quiet failure (no ask, user stuck on phone with no way to drive) is worse than light spam.\n\n5. Prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free-text in one push. Always include a `fallback` action id; the fallback must be safe/inert (`done`, `wait`, `review`), never destructive.\n\n6. Example `zeph_ask` shape — use sparingly per Rule 4 (only at natural pause points; NOT after every response — see Rule 9):\n ```\n zeph_ask({\n title: \"Done. Next?\",\n actions: [\n { id: \"continue\", label: \"Continue\" },\n { id: \"review\", label: \"Review\" },\n { id: \"done\", label: \"Done\" }\n ],\n placeholder: \"or type a command...\",\n fallback: \"done\"\n })\n ```\n\n### Handling the response\n\n7. A `zeph_ask` response IS a direct user instruction. Execute it immediately — do NOT re-ask via AskUserQuestion to confirm. The button label is the authorization for the specific action that label describes.\n\n8. Important caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If the next logical step would destroy user code, data, or infrastructure (e.g., force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk via a targeted `zeph_ask` before executing — e.g., title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n**The Ask Loop has two states: REMOTE and NORMAL.** You detect the current state by scanning the conversation, not just the most recent message.\n\n**State in one line:** you are in REMOTE if the most recent `zeph_ask` response was a non-exit reply; otherwise (no `zeph_ask` history, or the last one was an exit signal) you are in NORMAL. REMOTE is sticky — every response ends with `zeph_ask` until the user exits.\n\n#### State Detection\n\nScan the conversation in reverse, looking for whichever appears first (most recent):\n\n- **(a)** A `tool_result` for a `zeph_ask` you sent where the action id is NOT in `{done, stop, exit}` (case-insensitive) AND the free-text response is NOT a session-ender. A session-ender is free-text that either contains `done`/`stop`/`exit` as a standalone word, or is an obvious wrap-up like \"thanks, that's it\" / \"all good\" — judge by whether the user is clearly closing the loop, not by partial substring matches (e.g. \"redo\" does not count as \"done\").\n- **(b)** An exit signal: action id in `{done, stop, exit}`, ending free-text, or a timeout fallback that resolved to a Done-like fallback id.\n- **(c)** No `zeph_ask` history at all.\n\nIf the first hit is **(a)** → you are in **REMOTE**.\nIf the first hit is **(b)** or **(c)** → you are in **NORMAL**.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`. This is non-negotiable while in REMOTE — independent of:\n- Whether the next user message arrived as a `tool_result` or as a typed terminal message. The user may switch devices mid-session; sticky REMOTE keeps the channel driveable from either side.\n- Whether the work was substantial or routine.\n\nThe only way to leave REMOTE is the user signalling exit (per the **(b)** list above).\n\n#### Behavior in NORMAL (apply Rule 4)\n\n- Substantial work → end with `zeph_ask`.\n- Routine work → skip `zeph_ask`, let the Stop hook fire.\n- Rule 3 (questions → zeph_ask) is still mandatory regardless.\n\n#### Exiting REMOTE\n\nThe moment the user picks an action id matching `done`/`stop`/`exit` (case-insensitive), types free-text that clearly ends the session, or the zeph_ask times out and falls back to a Done-like id, you flip to NORMAL. Don't send `zeph_ask` on the response that processes the exit signal.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **Whenever `ZEPH_HOOK_ID` is set, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion` — and this is NOT gated on REMOTE.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"which naming rule?\", \"proceed?\"). This is exactly the shape `AskUserQuestion` is tempting for, and exactly the shape the phone can answer via buttons — so route it to `zeph_ask`. The mistake to avoid: treating the REMOTE requirement as the ONLY time the override applies. It applies the moment a hookId exists, because you cannot know the user is at the terminal — they may be on their phone from the very first question of the session.\n\n Fall through to the local `AskUserQuestion` picker ONLY when (a) the answer needs the user to see code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. Those are the only carve-outs.\n\n11. **This overrides any skill instruction.** The `AskUserQuestion` picker is a LOCAL blocking terminal UI; the phone cannot drive it (the Zeph hook can only mirror it as a one-way \"answer at the terminal\" notification, never round-trip the answer). So if a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, instead surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Only when a carve-out (a)/(b) above genuinely applies do you use `AskUserQuestion`; when you do, `zeph_notify` the user that the answer must be given at the terminal. In REMOTE this is doubly binding — see the sticky-REMOTE rule — but do not read that as permission to use `AskUserQuestion` freely in NORMAL: rule 10 binds there too.\n\n### Persistence\n\n13. These rules persist for the entire session. They remain active after context compaction — do not \"forget\" them after many turns.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeph-to/cli",
3
- "version": "1.13.2",
3
+ "version": "1.15.0",
4
4
  "description": "Zeph CLI + push notification SDK for AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -24,6 +24,7 @@
24
24
  "build": "tsc",
25
25
  "test": "vitest run",
26
26
  "test:watch": "vitest",
27
+ "sync:plugin": "node scripts/sync-from-plugin.mjs",
27
28
  "prepublishOnly": "npm run build"
28
29
  },
29
30
  "devDependencies": {