@zeph-to/cli 2.4.0 → 2.6.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 +7 -0
- package/dist/listener.d.ts +14 -0
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +0 -0
- package/dist/remote-agents.d.ts +105 -1
- package/dist/remote-agents.d.ts.map +1 -1
- package/dist/remote-agents.js +283 -34
- package/dist/templates.d.ts +7 -7
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +65 -13
- package/dist/zeph-core.generated.d.ts +3 -3
- package/dist/zeph-core.generated.d.ts.map +1 -1
- package/dist/zeph-core.generated.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,6 +146,13 @@ an answerable `zeph_ask`).
|
|
|
146
146
|
Detection is exact-match: a terminal keystroke racing a phone message
|
|
147
147
|
can never false-flag. Muted projects are never flagged.
|
|
148
148
|
|
|
149
|
+
Agents without that hook (Cursor, Windsurf, Copilot, Cline, Aider) have one
|
|
150
|
+
other way in: a `zeph_ask` answer that is not a Done-like button. Their rule
|
|
151
|
+
files therefore keep the after-real-work `zeph_ask` — the "Entering REMOTE
|
|
152
|
+
without a prompt hook" preamble in `src/templates.ts` — so the phone always
|
|
153
|
+
has a button to tap that starts the loop. Agents with the hook drop that
|
|
154
|
+
obligation while at the terminal.
|
|
155
|
+
|
|
149
156
|
`zeph cursor` has no remote-origin hook, so it never enters sticky REMOTE
|
|
150
157
|
mode by itself. **Just ask for it** — one line, once per session:
|
|
151
158
|
|
package/dist/listener.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ interface AgentSession {
|
|
|
32
32
|
agentSessionId?: string | null;
|
|
33
33
|
project: string;
|
|
34
34
|
label?: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* The name the agent itself calls this session by, when its registry
|
|
37
|
+
* exposes one (Claude Code's `zeph-to-95`). A live read, not user intent —
|
|
38
|
+
* the phone's rename still wins over it. Null where the agent has no name
|
|
39
|
+
* concept, where the pane's session could not be identified, or where the
|
|
40
|
+
* name is blank.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately NOT `sessionName`: everywhere else in this file and on the
|
|
43
|
+
* wire that word means the tmux session name (`agentSessionName` in a
|
|
44
|
+
* push, `AgentWatchRecord.sessionName` on the server). Two names for a
|
|
45
|
+
* session is already the problem this field exists to solve; reusing the
|
|
46
|
+
* word for both would put the ambiguity in the type.
|
|
47
|
+
*/
|
|
48
|
+
providerSessionName?: string | null;
|
|
35
49
|
createdAt?: string;
|
|
36
50
|
lastActivityAt?: string;
|
|
37
51
|
/**
|
package/dist/listener.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|
package/dist/listener.js
CHANGED
|
Binary file
|
package/dist/remote-agents.d.ts
CHANGED
|
@@ -28,6 +28,17 @@ export interface RemoteAgent {
|
|
|
28
28
|
* then reports agentSessionId: null.
|
|
29
29
|
*/
|
|
30
30
|
resolveSessionId?: (paneCwd: string, panePid?: number) => string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the name the agent calls this session by — what the user sees in
|
|
33
|
+
* their own terminal, which the phone shows instead of the computed
|
|
34
|
+
* `<project> · <Agent> #N` label.
|
|
35
|
+
*
|
|
36
|
+
* Same shape as `resolveSessionId` and the same EXTENSION POINT rule: a row
|
|
37
|
+
* carries this only once that agent's name store is confirmed. Cursor and
|
|
38
|
+
* Gemini never will — their state files hold ids, cwds and timestamps and no
|
|
39
|
+
* name at all, so there is nothing to read and the computed label stands.
|
|
40
|
+
*/
|
|
41
|
+
resolveSessionName?: (paneCwd: string, panePid?: number) => string | null;
|
|
31
42
|
}
|
|
32
43
|
/**
|
|
33
44
|
* Locate the most recent Claude Code session UUID for the working
|
|
@@ -38,11 +49,43 @@ export interface RemoteAgent {
|
|
|
38
49
|
* claudeSessionCache.
|
|
39
50
|
*/
|
|
40
51
|
export declare const detectClaudeSessionId: (cwd: string) => string | null;
|
|
41
|
-
interface PidSessionRecord {
|
|
52
|
+
export interface PidSessionRecord {
|
|
42
53
|
pid: number;
|
|
43
54
|
sessionId: string;
|
|
44
55
|
cwd: string;
|
|
56
|
+
/**
|
|
57
|
+
* What Claude Code calls this session (`zeph-to-95` when derived from the
|
|
58
|
+
* cwd, free text once named). Optional: an older CC writes the record
|
|
59
|
+
* without it, and a record without a name is still a valid id match.
|
|
60
|
+
*/
|
|
61
|
+
name?: string;
|
|
45
62
|
}
|
|
63
|
+
export interface ProcTable {
|
|
64
|
+
/** ppid → its direct children, for walking a pane's process tree. */
|
|
65
|
+
children: Map<number, number[]>;
|
|
66
|
+
/** pid → process start time in epoch ms. Only pids whose `lstart` parsed. */
|
|
67
|
+
startTimes: Map<number, number>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse `ps -axo pid=,ppid=,lstart=`. A row is `<pid> <ppid> [<lstart>]`, e.g.
|
|
71
|
+
* ` 1 0 Fri Aug 7 11:12:59 2026 ` — note the two spaces before a
|
|
72
|
+
* single-digit day and the trailing padding.
|
|
73
|
+
*
|
|
74
|
+
* The third field is OPTIONAL in the pattern, and that is load-bearing rather
|
|
75
|
+
* than defensive. Requiring it would mean that on any machine whose `ps` prints
|
|
76
|
+
* a different `lstart` shape, EVERY row fails to match, `children` comes back
|
|
77
|
+
* empty, `collectDescendantPids` answers `{rootPid}`, and
|
|
78
|
+
* `detectClaudeSessionByPid` finds nothing — dropping Claude Code back to the
|
|
79
|
+
* mtime heuristic and its identity theft (see CLAUDE_SESSIONS_DIR above). The
|
|
80
|
+
* parent links must survive an unreadable timestamp; only the start time is
|
|
81
|
+
* allowed to go missing, and every consumer of `startTimes` treats an absent
|
|
82
|
+
* entry as "no match" rather than guessing.
|
|
83
|
+
*/
|
|
84
|
+
export declare const parseProcTable: (table: string) => ProcTable;
|
|
85
|
+
/** Start times for this cycle's snapshot. Empty when `ps` is unavailable. */
|
|
86
|
+
export declare const psStartTimes: () => Map<number, number>;
|
|
87
|
+
/** pid set of `rootPid` + all descendants, from one `ps` snapshot. */
|
|
88
|
+
export declare const collectDescendantPids: (rootPid: number) => Set<number>;
|
|
46
89
|
/** Test seam for the pid-based resolver. */
|
|
47
90
|
export interface PidResolveDeps {
|
|
48
91
|
records?: PidSessionRecord[];
|
|
@@ -53,8 +96,66 @@ export interface PidResolveDeps {
|
|
|
53
96
|
* process tree AND whose cwd matches (guards against OS pid reuse
|
|
54
97
|
* leaving a stale record pointing elsewhere). Null → caller falls back
|
|
55
98
|
* to the mtime heuristic.
|
|
99
|
+
*
|
|
100
|
+
* Returns the whole record so the id and the name come from one lookup — they
|
|
101
|
+
* describe the same session, and reading the directory twice per cycle to get
|
|
102
|
+
* them separately is what the cache above exists to avoid.
|
|
56
103
|
*/
|
|
104
|
+
export declare const detectClaudeSessionByPid: (panePid: number, paneCwd: string | null, deps?: PidResolveDeps) => PidSessionRecord | null;
|
|
57
105
|
export declare const detectClaudeSessionIdByPid: (panePid: number, paneCwd: string | null, deps?: PidResolveDeps) => string | null;
|
|
106
|
+
/**
|
|
107
|
+
* The name of the session running in this pane. No mtime fallback exists here:
|
|
108
|
+
* the transcript filenames the heuristic walks carry a UUID and nothing else,
|
|
109
|
+
* so an older CC without `~/.claude/sessions` yields no name and the phone
|
|
110
|
+
* keeps the computed label.
|
|
111
|
+
*/
|
|
112
|
+
export declare const detectClaudeSessionNameByPid: (panePid: number, paneCwd: string | null, deps?: PidResolveDeps) => string | null;
|
|
113
|
+
/**
|
|
114
|
+
* The row whose timestamp is closest to when one of the pane's processes started,
|
|
115
|
+
* within {@link PROC_MATCH_TOLERANCE_MS}. Null when nothing qualifies — including
|
|
116
|
+
* when no pid in the tree has a known start time, which is why the start times
|
|
117
|
+
* are collected before any row is compared rather than defaulted to zero.
|
|
118
|
+
*/
|
|
119
|
+
export declare const pickRowByProcStart: <T>(rows: readonly T[], tsMsOf: (row: T) => number | null, startTimes: Map<number, number>, pids: Set<number>) => T | null;
|
|
120
|
+
/**
|
|
121
|
+
* Read rows from a SQLite file as JSON. Null on every failure — no `sqlite3` on
|
|
122
|
+
* PATH, a locked or corrupt database, a schema that moved — because a missing
|
|
123
|
+
* name costs the computed label and nothing else.
|
|
124
|
+
*
|
|
125
|
+
* The query carries no parameters BY DESIGN: SQL passed to the `sqlite3` binary
|
|
126
|
+
* as an argv string has nowhere to bind `?` to, so interpolating a cwd would
|
|
127
|
+
* make this file the place that has to know SQLite quoting rules. Callers select
|
|
128
|
+
* the columns they need and filter in JS instead; the stores hold tens of rows,
|
|
129
|
+
* not millions. The `timeout` keeps a locked database from holding up a report
|
|
130
|
+
* cycle that runs every five seconds.
|
|
131
|
+
*/
|
|
132
|
+
export declare const sqliteJson: (dbPath: string, sql: string) => unknown[] | null;
|
|
133
|
+
/** Test seam for the store-backed resolvers. `rows: null` = the read failed. */
|
|
134
|
+
export interface StoreResolveDeps {
|
|
135
|
+
rows?: unknown[] | null;
|
|
136
|
+
startTimes?: Map<number, number>;
|
|
137
|
+
descendants?: Set<number>;
|
|
138
|
+
}
|
|
139
|
+
export declare const detectHermesSessionName: (paneCwd: string, panePid?: number, deps?: StoreResolveDeps) => string | null;
|
|
140
|
+
/**
|
|
141
|
+
* The highest-numbered match, numerically. Codex versions its state file
|
|
142
|
+
* (`state_5.sqlite` today) and moves to the next number on a schema migration,
|
|
143
|
+
* so hard-coding a name would silently stop reading after an upgrade. Sorting
|
|
144
|
+
* the strings would break at `state_10` vs `state_9`.
|
|
145
|
+
*/
|
|
146
|
+
export declare const newestVersionedDb: (entries: readonly string[], pattern: RegExp) => string | null;
|
|
147
|
+
/**
|
|
148
|
+
* No `source` filter, unlike the Hermes reader's `source = 'cli'`. The column
|
|
149
|
+
* exists here too (alongside `thread_source`), but Codex is unauthenticated on
|
|
150
|
+
* every machine measured so far and its `threads` table is empty, so which
|
|
151
|
+
* values mark a terminal session is unknown — and a wrong guess filters out
|
|
152
|
+
* every row, which fails as "no name ever" rather than loudly. A thread from a
|
|
153
|
+
* non-terminal frontend is therefore eligible to match, and it lands in the same
|
|
154
|
+
* accepted misjoin as two terminals in the same directory: it needs the same cwd
|
|
155
|
+
* AND a creation time within the tolerance of a process in this pane. Add the
|
|
156
|
+
* filter once the values can be observed.
|
|
157
|
+
*/
|
|
158
|
+
export declare const detectCodexSessionName: (paneCwd: string, panePid?: number, deps?: StoreResolveDeps) => string | null;
|
|
58
159
|
declare const REMOTE_AGENT_TABLE: readonly [{
|
|
59
160
|
readonly kind: "claude";
|
|
60
161
|
readonly displayName: "Claude Code";
|
|
@@ -62,11 +163,13 @@ declare const REMOTE_AGENT_TABLE: readonly [{
|
|
|
62
163
|
readonly subcommands: readonly ["cc", "claude"];
|
|
63
164
|
readonly quitCommand: "/exit";
|
|
64
165
|
readonly resolveSessionId: (paneCwd: string, panePid: number | undefined) => string | null;
|
|
166
|
+
readonly resolveSessionName: (paneCwd: string, panePid: number | undefined) => string | null;
|
|
65
167
|
}, {
|
|
66
168
|
readonly kind: "codex";
|
|
67
169
|
readonly displayName: "Codex CLI";
|
|
68
170
|
readonly binary: "codex";
|
|
69
171
|
readonly subcommands: readonly ["codex"];
|
|
172
|
+
readonly resolveSessionName: (paneCwd: string, panePid?: number, deps?: StoreResolveDeps) => string | null;
|
|
70
173
|
}, {
|
|
71
174
|
readonly kind: "cursor";
|
|
72
175
|
readonly displayName: "Cursor CLI";
|
|
@@ -82,6 +185,7 @@ declare const REMOTE_AGENT_TABLE: readonly [{
|
|
|
82
185
|
readonly displayName: "Hermes";
|
|
83
186
|
readonly binary: "hermes";
|
|
84
187
|
readonly subcommands: readonly ["hermes"];
|
|
188
|
+
readonly resolveSessionName: (paneCwd: string, panePid?: number, deps?: StoreResolveDeps) => string | null;
|
|
85
189
|
}];
|
|
86
190
|
/** Closed union of remote-controllable agent kinds, derived from the table above. */
|
|
87
191
|
export type AgentKind = (typeof REMOTE_AGENT_TABLE)[number]['kind'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IACxB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"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"}
|
package/dist/remote-agents.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE_AGENTS = exports.detectClaudeSessionIdByPid = exports.detectClaudeSessionId = void 0;
|
|
3
|
+
exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE_AGENTS = exports.detectCodexSessionName = exports.newestVersionedDb = exports.detectHermesSessionName = exports.sqliteJson = exports.pickRowByProcStart = exports.detectClaudeSessionNameByPid = exports.detectClaudeSessionIdByPid = exports.detectClaudeSessionByPid = exports.collectDescendantPids = exports.psStartTimes = exports.parseProcTable = exports.detectClaudeSessionId = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Remote-control agent registry — the single table behind every
|
|
6
6
|
* `zeph <agent>` subcommand, the listener's pane matching, and the
|
|
@@ -20,6 +20,45 @@ const fs_1 = require("fs");
|
|
|
20
20
|
const os_1 = require("os");
|
|
21
21
|
const path_1 = require("path");
|
|
22
22
|
const child_process_1 = require("child_process");
|
|
23
|
+
/**
|
|
24
|
+
* A per-key TTL cache, bounded so a long-lived listener that has seen many keys
|
|
25
|
+
* cannot grow without limit. `get` returns a wrapper rather than the value so a
|
|
26
|
+
* cached `null` is distinguishable from a miss, and `set` returns what it stored
|
|
27
|
+
* so callers read as one expression.
|
|
28
|
+
*
|
|
29
|
+
* Eviction drops the first key in insertion order, which is the oldest entry a
|
|
30
|
+
* key was FIRST written under — refreshing a key does not move it. With a bound
|
|
31
|
+
* of 64 keys against a handful of live panes that never matters; a workload that
|
|
32
|
+
* genuinely cycles more keys than the bound would want a real LRU.
|
|
33
|
+
*/
|
|
34
|
+
const keyedTtlCache = (ttlMs, max = 64) => {
|
|
35
|
+
const entries = new Map();
|
|
36
|
+
return {
|
|
37
|
+
get: (key) => {
|
|
38
|
+
const hit = entries.get(key);
|
|
39
|
+
return hit && hit.expiresAt > Date.now() ? hit : undefined;
|
|
40
|
+
},
|
|
41
|
+
set: (key, value) => {
|
|
42
|
+
if (entries.size >= max) {
|
|
43
|
+
const oldest = entries.keys().next().value;
|
|
44
|
+
if (oldest !== undefined)
|
|
45
|
+
entries.delete(oldest);
|
|
46
|
+
}
|
|
47
|
+
entries.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
48
|
+
return value;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A name is only worth carrying if it has visible characters. Renderers treat
|
|
54
|
+
* `''` as falsy and fall back on their own, but the change fingerprints on both
|
|
55
|
+
* sides of the wire collapse `undefined`/`null` and would ship a blank string as
|
|
56
|
+
* a real change — a report per cycle that says nothing.
|
|
57
|
+
*/
|
|
58
|
+
const nonBlank = (value) => {
|
|
59
|
+
const trimmed = value?.trim();
|
|
60
|
+
return trimmed ? trimmed : null;
|
|
61
|
+
};
|
|
23
62
|
// ── Claude Code session resolver ─────────────────────────────────
|
|
24
63
|
const CLAUDE_PROJECTS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'projects');
|
|
25
64
|
/**
|
|
@@ -34,8 +73,7 @@ const CLAUDE_PROJECTS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'pr
|
|
|
34
73
|
* in that directory (rare, on the order of hours), so a 60-second TTL
|
|
35
74
|
* is safe and cuts the per-cycle stat count by ~12×.
|
|
36
75
|
*/
|
|
37
|
-
const claudeSessionCache =
|
|
38
|
-
const CLAUDE_SESSION_CACHE_TTL_MS = 60_000;
|
|
76
|
+
const claudeSessionCache = keyedTtlCache(60_000);
|
|
39
77
|
const doDetectClaudeSessionId = (cwd) => {
|
|
40
78
|
try {
|
|
41
79
|
const projectHash = cwd.replace(/\//g, '-');
|
|
@@ -67,22 +105,10 @@ const doDetectClaudeSessionId = (cwd) => {
|
|
|
67
105
|
* claudeSessionCache.
|
|
68
106
|
*/
|
|
69
107
|
const detectClaudeSessionId = (cwd) => {
|
|
70
|
-
const now = Date.now();
|
|
71
108
|
const cached = claudeSessionCache.get(cwd);
|
|
72
|
-
if (cached
|
|
73
|
-
return cached.
|
|
74
|
-
|
|
75
|
-
// doesn't grow unbounded. 64 is plenty for any realistic setup.
|
|
76
|
-
if (claudeSessionCache.size >= 64) {
|
|
77
|
-
// Evict the oldest-expiring entry — Map iteration order is
|
|
78
|
-
// insertion order, so the first key we hit is the oldest.
|
|
79
|
-
const firstKey = claudeSessionCache.keys().next().value;
|
|
80
|
-
if (firstKey !== undefined)
|
|
81
|
-
claudeSessionCache.delete(firstKey);
|
|
82
|
-
}
|
|
83
|
-
const sessionId = doDetectClaudeSessionId(cwd);
|
|
84
|
-
claudeSessionCache.set(cwd, { sessionId, expiresAt: now + CLAUDE_SESSION_CACHE_TTL_MS });
|
|
85
|
-
return sessionId;
|
|
109
|
+
if (cached)
|
|
110
|
+
return cached.value;
|
|
111
|
+
return claudeSessionCache.set(cwd, doDetectClaudeSessionId(cwd));
|
|
86
112
|
};
|
|
87
113
|
exports.detectClaudeSessionId = detectClaudeSessionId;
|
|
88
114
|
// ── Claude Code pid → session resolver ───────────────────────────
|
|
@@ -103,7 +129,12 @@ const readPidSessionRecords = () => {
|
|
|
103
129
|
try {
|
|
104
130
|
const raw = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(CLAUDE_SESSIONS_DIR, entry), 'utf-8'));
|
|
105
131
|
if (typeof raw.pid === 'number' && typeof raw.sessionId === 'string' && typeof raw.cwd === 'string') {
|
|
106
|
-
out.push({
|
|
132
|
+
out.push({
|
|
133
|
+
pid: raw.pid,
|
|
134
|
+
sessionId: raw.sessionId,
|
|
135
|
+
cwd: raw.cwd,
|
|
136
|
+
...(typeof raw.name === 'string' ? { name: raw.name } : {}),
|
|
137
|
+
});
|
|
107
138
|
}
|
|
108
139
|
}
|
|
109
140
|
catch { /* partial write / corrupt — skip */ }
|
|
@@ -132,29 +163,56 @@ const ttlMemo = (ttlMs, compute) => {
|
|
|
132
163
|
return snap.value;
|
|
133
164
|
};
|
|
134
165
|
};
|
|
135
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Parse `ps -axo pid=,ppid=,lstart=`. A row is `<pid> <ppid> [<lstart>]`, e.g.
|
|
168
|
+
* ` 1 0 Fri Aug 7 11:12:59 2026 ` — note the two spaces before a
|
|
169
|
+
* single-digit day and the trailing padding.
|
|
170
|
+
*
|
|
171
|
+
* The third field is OPTIONAL in the pattern, and that is load-bearing rather
|
|
172
|
+
* than defensive. Requiring it would mean that on any machine whose `ps` prints
|
|
173
|
+
* a different `lstart` shape, EVERY row fails to match, `children` comes back
|
|
174
|
+
* empty, `collectDescendantPids` answers `{rootPid}`, and
|
|
175
|
+
* `detectClaudeSessionByPid` finds nothing — dropping Claude Code back to the
|
|
176
|
+
* mtime heuristic and its identity theft (see CLAUDE_SESSIONS_DIR above). The
|
|
177
|
+
* parent links must survive an unreadable timestamp; only the start time is
|
|
178
|
+
* allowed to go missing, and every consumer of `startTimes` treats an absent
|
|
179
|
+
* entry as "no match" rather than guessing.
|
|
180
|
+
*/
|
|
181
|
+
const parseProcTable = (table) => {
|
|
136
182
|
const children = new Map();
|
|
183
|
+
const startTimes = new Map();
|
|
137
184
|
for (const line of table.split('\n')) {
|
|
138
|
-
const m = line.trim().match(/^(\d+)\s+(\d+)
|
|
185
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)(?:\s+(.*))?$/);
|
|
139
186
|
if (!m)
|
|
140
187
|
continue;
|
|
141
188
|
const [pid, ppid] = [Number(m[1]), Number(m[2])];
|
|
142
189
|
const list = children.get(ppid) ?? [];
|
|
143
190
|
list.push(pid);
|
|
144
191
|
children.set(ppid, list);
|
|
192
|
+
if (m[3] === undefined)
|
|
193
|
+
continue;
|
|
194
|
+
const started = Date.parse(m[3].trim());
|
|
195
|
+
if (Number.isFinite(started))
|
|
196
|
+
startTimes.set(pid, started);
|
|
145
197
|
}
|
|
146
|
-
return children;
|
|
198
|
+
return { children, startTimes };
|
|
147
199
|
};
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
200
|
+
exports.parseProcTable = parseProcTable;
|
|
201
|
+
/**
|
|
202
|
+
* One process-table snapshot per report cycle, shared by every pane. `lstart`
|
|
203
|
+
* rides along because the agents without a pid registry (Hermes, Codex) are
|
|
204
|
+
* matched to their session row by when their process started, and paying for a
|
|
205
|
+
* second `ps` spawn to learn that would undo what this memo is for.
|
|
206
|
+
*/
|
|
207
|
+
const readPsSnapshot = ttlMemo(SNAPSHOT_TTL_MS, () => {
|
|
208
|
+
const r = (0, child_process_1.spawnSync)('ps', ['-axo', 'pid=,ppid=,lstart='], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
209
|
+
return r.status === 0 ? (0, exports.parseProcTable)(r.stdout ?? '') : null;
|
|
151
210
|
});
|
|
211
|
+
/** Start times for this cycle's snapshot. Empty when `ps` is unavailable. */
|
|
212
|
+
const psStartTimes = () => readPsSnapshot()?.startTimes ?? new Map();
|
|
213
|
+
exports.psStartTimes = psStartTimes;
|
|
152
214
|
const cachedPidSessionRecords = ttlMemo(SNAPSHOT_TTL_MS, readPidSessionRecords);
|
|
153
|
-
|
|
154
|
-
const collectDescendantPids = (rootPid, psOutput) => {
|
|
155
|
-
const children = psOutput !== undefined ? parseChildren(psOutput) : readPsChildren();
|
|
156
|
-
if (children === null)
|
|
157
|
-
return new Set([rootPid]);
|
|
215
|
+
const walkDescendants = (rootPid, children) => {
|
|
158
216
|
const found = new Set([rootPid]);
|
|
159
217
|
const queue = [rootPid];
|
|
160
218
|
while (queue.length > 0) {
|
|
@@ -167,21 +225,205 @@ const collectDescendantPids = (rootPid, psOutput) => {
|
|
|
167
225
|
}
|
|
168
226
|
return found;
|
|
169
227
|
};
|
|
228
|
+
/**
|
|
229
|
+
* Per-pane process tree, memoized for the same window as the `ps` snapshot it
|
|
230
|
+
* walks. The snapshot itself is already shared across the cycle, but the walk was
|
|
231
|
+
* not: each resolver on a row ran its own BFS, so asking a pane for both its
|
|
232
|
+
* session id and its session name doubled the work on the path whose CPU cost is
|
|
233
|
+
* the reason these caches exist (see claudeSessionCache).
|
|
234
|
+
*
|
|
235
|
+
* Callers must treat the returned Set as read-only — it is shared.
|
|
236
|
+
*/
|
|
237
|
+
const descendantCache = keyedTtlCache(SNAPSHOT_TTL_MS);
|
|
238
|
+
/** pid set of `rootPid` + all descendants, from one `ps` snapshot. */
|
|
239
|
+
const collectDescendantPids = (rootPid) => {
|
|
240
|
+
const cached = descendantCache.get(rootPid);
|
|
241
|
+
if (cached)
|
|
242
|
+
return cached.value;
|
|
243
|
+
const children = readPsSnapshot()?.children;
|
|
244
|
+
// A failed `ps` is not cached: the next cycle should retry rather than
|
|
245
|
+
// serve "this pane has no children" for the whole TTL.
|
|
246
|
+
if (children === undefined)
|
|
247
|
+
return new Set([rootPid]);
|
|
248
|
+
return descendantCache.set(rootPid, walkDescendants(rootPid, children));
|
|
249
|
+
};
|
|
250
|
+
exports.collectDescendantPids = collectDescendantPids;
|
|
170
251
|
/**
|
|
171
252
|
* Exact resolution: the session record whose pid lives in the pane's
|
|
172
253
|
* process tree AND whose cwd matches (guards against OS pid reuse
|
|
173
254
|
* leaving a stale record pointing elsewhere). Null → caller falls back
|
|
174
255
|
* to the mtime heuristic.
|
|
256
|
+
*
|
|
257
|
+
* Returns the whole record so the id and the name come from one lookup — they
|
|
258
|
+
* describe the same session, and reading the directory twice per cycle to get
|
|
259
|
+
* them separately is what the cache above exists to avoid.
|
|
175
260
|
*/
|
|
176
|
-
const
|
|
261
|
+
const detectClaudeSessionByPid = (panePid, paneCwd, deps = {}) => {
|
|
177
262
|
const records = deps.records ?? cachedPidSessionRecords();
|
|
178
263
|
if (records.length === 0)
|
|
179
264
|
return null;
|
|
180
|
-
const descendants = deps.descendants ?? collectDescendantPids(panePid);
|
|
181
|
-
|
|
182
|
-
return match?.sessionId ?? null;
|
|
265
|
+
const descendants = deps.descendants ?? (0, exports.collectDescendantPids)(panePid);
|
|
266
|
+
return records.find((r) => descendants.has(r.pid) && (paneCwd === null || r.cwd === paneCwd)) ?? null;
|
|
183
267
|
};
|
|
268
|
+
exports.detectClaudeSessionByPid = detectClaudeSessionByPid;
|
|
269
|
+
const detectClaudeSessionIdByPid = (panePid, paneCwd, deps = {}) => (0, exports.detectClaudeSessionByPid)(panePid, paneCwd, deps)?.sessionId ?? null;
|
|
184
270
|
exports.detectClaudeSessionIdByPid = detectClaudeSessionIdByPid;
|
|
271
|
+
/**
|
|
272
|
+
* The name of the session running in this pane. No mtime fallback exists here:
|
|
273
|
+
* the transcript filenames the heuristic walks carry a UUID and nothing else,
|
|
274
|
+
* so an older CC without `~/.claude/sessions` yields no name and the phone
|
|
275
|
+
* keeps the computed label.
|
|
276
|
+
*/
|
|
277
|
+
const detectClaudeSessionNameByPid = (panePid, paneCwd, deps = {}) => nonBlank((0, exports.detectClaudeSessionByPid)(panePid, paneCwd, deps)?.name);
|
|
278
|
+
exports.detectClaudeSessionNameByPid = detectClaudeSessionNameByPid;
|
|
279
|
+
// ── Agents whose session store keeps no pid ──────────────────────
|
|
280
|
+
//
|
|
281
|
+
// Hermes and Codex both record a session per cwd with a creation timestamp and
|
|
282
|
+
// no pid, so a pane cannot be joined to its row the exact way Claude Code's
|
|
283
|
+
// `<pid>.json` allows. What is left is time: both stores stamp the row when the
|
|
284
|
+
// session starts, so the row belonging to this pane is the one stamped when a
|
|
285
|
+
// process in this pane started. The window has to be narrow — two agents opened
|
|
286
|
+
// in the same directory within it are indistinguishable — and a miss must stay a
|
|
287
|
+
// miss, since a wrong name shown confidently is worse than the computed label.
|
|
288
|
+
/** How far a row's creation time may sit from a process start time. */
|
|
289
|
+
const PROC_MATCH_TOLERANCE_MS = 10_000;
|
|
290
|
+
/**
|
|
291
|
+
* The row whose timestamp is closest to when one of the pane's processes started,
|
|
292
|
+
* within {@link PROC_MATCH_TOLERANCE_MS}. Null when nothing qualifies — including
|
|
293
|
+
* when no pid in the tree has a known start time, which is why the start times
|
|
294
|
+
* are collected before any row is compared rather than defaulted to zero.
|
|
295
|
+
*/
|
|
296
|
+
const pickRowByProcStart = (rows, tsMsOf, startTimes, pids) => {
|
|
297
|
+
const procStarts = [...pids]
|
|
298
|
+
.map((pid) => startTimes.get(pid))
|
|
299
|
+
.filter((t) => t !== undefined && Number.isFinite(t));
|
|
300
|
+
if (procStarts.length === 0)
|
|
301
|
+
return null;
|
|
302
|
+
let best = null;
|
|
303
|
+
for (const row of rows) {
|
|
304
|
+
const at = tsMsOf(row);
|
|
305
|
+
if (at === null || !Number.isFinite(at))
|
|
306
|
+
continue;
|
|
307
|
+
const distance = Math.min(...procStarts.map((start) => Math.abs(at - start)));
|
|
308
|
+
if (distance > PROC_MATCH_TOLERANCE_MS)
|
|
309
|
+
continue;
|
|
310
|
+
if (best === null || distance < best.distance)
|
|
311
|
+
best = { row, distance };
|
|
312
|
+
}
|
|
313
|
+
return best?.row ?? null;
|
|
314
|
+
};
|
|
315
|
+
exports.pickRowByProcStart = pickRowByProcStart;
|
|
316
|
+
/**
|
|
317
|
+
* Read rows from a SQLite file as JSON. Null on every failure — no `sqlite3` on
|
|
318
|
+
* PATH, a locked or corrupt database, a schema that moved — because a missing
|
|
319
|
+
* name costs the computed label and nothing else.
|
|
320
|
+
*
|
|
321
|
+
* The query carries no parameters BY DESIGN: SQL passed to the `sqlite3` binary
|
|
322
|
+
* as an argv string has nowhere to bind `?` to, so interpolating a cwd would
|
|
323
|
+
* make this file the place that has to know SQLite quoting rules. Callers select
|
|
324
|
+
* the columns they need and filter in JS instead; the stores hold tens of rows,
|
|
325
|
+
* not millions. The `timeout` keeps a locked database from holding up a report
|
|
326
|
+
* cycle that runs every five seconds.
|
|
327
|
+
*/
|
|
328
|
+
const sqliteJson = (dbPath, sql) => {
|
|
329
|
+
const r = (0, child_process_1.spawnSync)('sqlite3', ['-readonly', '-json', dbPath, sql], {
|
|
330
|
+
encoding: 'utf-8',
|
|
331
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
332
|
+
timeout: 1_000,
|
|
333
|
+
});
|
|
334
|
+
if (r.status !== 0)
|
|
335
|
+
return null;
|
|
336
|
+
const out = (r.stdout ?? '').trim();
|
|
337
|
+
if (out === '')
|
|
338
|
+
return []; // no rows — sqlite3 prints nothing, not `[]`
|
|
339
|
+
try {
|
|
340
|
+
const parsed = JSON.parse(out);
|
|
341
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
exports.sqliteJson = sqliteJson;
|
|
348
|
+
// ── Hermes ───────────────────────────────────────────────────────
|
|
349
|
+
const HERMES_STATE_DB = (0, path_1.join)((0, os_1.homedir)(), '.hermes', 'state.db');
|
|
350
|
+
const readHermesSessions = ttlMemo(SNAPSHOT_TTL_MS, () => (0, exports.sqliteJson)(HERMES_STATE_DB, "SELECT cwd, title, display_name, started_at FROM sessions WHERE source = 'cli'"));
|
|
351
|
+
const detectHermesSessionName = (paneCwd, panePid, deps = {}) => {
|
|
352
|
+
// Without the pane's pid there is no process tree to match against, and this
|
|
353
|
+
// store offers no other key — no name is the only honest answer.
|
|
354
|
+
if (panePid === undefined)
|
|
355
|
+
return null;
|
|
356
|
+
const raw = deps.rows !== undefined ? deps.rows : readHermesSessions();
|
|
357
|
+
if (raw === null)
|
|
358
|
+
return null;
|
|
359
|
+
const rows = raw.filter((r) => r.cwd === paneCwd);
|
|
360
|
+
if (rows.length === 0)
|
|
361
|
+
return null;
|
|
362
|
+
const row = (0, exports.pickRowByProcStart)(rows, (r) => (typeof r.started_at === 'number' ? r.started_at * 1_000 : null), deps.startTimes ?? (0, exports.psStartTimes)(), deps.descendants ?? (0, exports.collectDescendantPids)(panePid));
|
|
363
|
+
// The auto-title is written asynchronously after the first response, so an
|
|
364
|
+
// untitled row is a young session, not a broken one.
|
|
365
|
+
return nonBlank(row?.title ?? undefined) ?? nonBlank(row?.display_name ?? undefined);
|
|
366
|
+
};
|
|
367
|
+
exports.detectHermesSessionName = detectHermesSessionName;
|
|
368
|
+
// ── Codex ────────────────────────────────────────────────────────
|
|
369
|
+
const CODEX_DIR = (0, path_1.join)((0, os_1.homedir)(), '.codex');
|
|
370
|
+
const CODEX_STATE_DB = /^state_(\d+)\.sqlite$/;
|
|
371
|
+
/**
|
|
372
|
+
* The highest-numbered match, numerically. Codex versions its state file
|
|
373
|
+
* (`state_5.sqlite` today) and moves to the next number on a schema migration,
|
|
374
|
+
* so hard-coding a name would silently stop reading after an upgrade. Sorting
|
|
375
|
+
* the strings would break at `state_10` vs `state_9`.
|
|
376
|
+
*/
|
|
377
|
+
const newestVersionedDb = (entries, pattern) => {
|
|
378
|
+
let best = null;
|
|
379
|
+
for (const entry of entries) {
|
|
380
|
+
const m = entry.match(pattern);
|
|
381
|
+
if (!m)
|
|
382
|
+
continue;
|
|
383
|
+
const version = Number(m[1]);
|
|
384
|
+
if (best === null || version > best.version)
|
|
385
|
+
best = { entry, version };
|
|
386
|
+
}
|
|
387
|
+
return best?.entry ?? null;
|
|
388
|
+
};
|
|
389
|
+
exports.newestVersionedDb = newestVersionedDb;
|
|
390
|
+
const readCodexThreads = ttlMemo(SNAPSHOT_TTL_MS, () => {
|
|
391
|
+
let entries;
|
|
392
|
+
try {
|
|
393
|
+
entries = (0, fs_1.readdirSync)(CODEX_DIR);
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
return null; // codex never installed here
|
|
397
|
+
}
|
|
398
|
+
const db = (0, exports.newestVersionedDb)(entries, CODEX_STATE_DB);
|
|
399
|
+
if (db === null)
|
|
400
|
+
return null;
|
|
401
|
+
return (0, exports.sqliteJson)((0, path_1.join)(CODEX_DIR, db), 'SELECT cwd, name, title, archived, created_at, created_at_ms FROM threads');
|
|
402
|
+
});
|
|
403
|
+
/**
|
|
404
|
+
* No `source` filter, unlike the Hermes reader's `source = 'cli'`. The column
|
|
405
|
+
* exists here too (alongside `thread_source`), but Codex is unauthenticated on
|
|
406
|
+
* every machine measured so far and its `threads` table is empty, so which
|
|
407
|
+
* values mark a terminal session is unknown — and a wrong guess filters out
|
|
408
|
+
* every row, which fails as "no name ever" rather than loudly. A thread from a
|
|
409
|
+
* non-terminal frontend is therefore eligible to match, and it lands in the same
|
|
410
|
+
* accepted misjoin as two terminals in the same directory: it needs the same cwd
|
|
411
|
+
* AND a creation time within the tolerance of a process in this pane. Add the
|
|
412
|
+
* filter once the values can be observed.
|
|
413
|
+
*/
|
|
414
|
+
const detectCodexSessionName = (paneCwd, panePid, deps = {}) => {
|
|
415
|
+
if (panePid === undefined)
|
|
416
|
+
return null;
|
|
417
|
+
const raw = deps.rows !== undefined ? deps.rows : readCodexThreads();
|
|
418
|
+
if (raw === null)
|
|
419
|
+
return null;
|
|
420
|
+
const rows = raw.filter((r) => r.cwd === paneCwd && !r.archived);
|
|
421
|
+
if (rows.length === 0)
|
|
422
|
+
return null;
|
|
423
|
+
const row = (0, exports.pickRowByProcStart)(rows, (r) => r.created_at_ms ?? (typeof r.created_at === 'number' ? r.created_at * 1_000 : null), deps.startTimes ?? (0, exports.psStartTimes)(), deps.descendants ?? (0, exports.collectDescendantPids)(panePid));
|
|
424
|
+
return nonBlank(row?.name ?? undefined) ?? nonBlank(row?.title ?? undefined);
|
|
425
|
+
};
|
|
426
|
+
exports.detectCodexSessionName = detectCodexSessionName;
|
|
185
427
|
// ── The registry ─────────────────────────────────────────────────
|
|
186
428
|
const REMOTE_AGENT_TABLE = [
|
|
187
429
|
{
|
|
@@ -194,12 +436,16 @@ const REMOTE_AGENT_TABLE = [
|
|
|
194
436
|
// (older CC without ~/.claude/sessions, or ps failure).
|
|
195
437
|
resolveSessionId: (paneCwd, panePid) => (panePid !== undefined ? (0, exports.detectClaudeSessionIdByPid)(panePid, paneCwd) : null)
|
|
196
438
|
?? (0, exports.detectClaudeSessionId)(paneCwd),
|
|
439
|
+
resolveSessionName: (paneCwd, panePid) => panePid !== undefined ? (0, exports.detectClaudeSessionNameByPid)(panePid, paneCwd) : null,
|
|
197
440
|
},
|
|
198
441
|
{
|
|
199
442
|
kind: 'codex',
|
|
200
443
|
displayName: 'Codex CLI',
|
|
201
444
|
binary: 'codex',
|
|
202
445
|
subcommands: ['codex'],
|
|
446
|
+
// Name only, like the hermes row: the store's thread id is a UUID this
|
|
447
|
+
// resolver could return, but nothing on the wire consumes it yet.
|
|
448
|
+
resolveSessionName: exports.detectCodexSessionName,
|
|
203
449
|
},
|
|
204
450
|
{
|
|
205
451
|
// The pane binary is `cursor-agent`, Cursor's terminal TUI — NOT the
|
|
@@ -228,6 +474,9 @@ const REMOTE_AGENT_TABLE = [
|
|
|
228
474
|
displayName: 'Hermes',
|
|
229
475
|
binary: 'hermes',
|
|
230
476
|
subcommands: ['hermes'],
|
|
477
|
+
// No `resolveSessionId` counterpart: the store's own id is a
|
|
478
|
+
// `<timestamp>_<hash>` string, and nothing on the wire consumes it yet.
|
|
479
|
+
resolveSessionName: exports.detectHermesSessionName,
|
|
231
480
|
},
|
|
232
481
|
];
|
|
233
482
|
exports.REMOTE_AGENTS = REMOTE_AGENT_TABLE;
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/** Cursor — written to ~/.cursor/rules/zeph.mdc (needs .mdc frontmatter). */
|
|
1
|
+
/** Cursor — written to ~/.cursor/rules/zeph.mdc (needs .mdc frontmatter). No prompt hook. */
|
|
2
2
|
export declare const CURSOR_RULE: string;
|
|
3
|
-
/** Windsurf — appended into ~/.codeium/windsurf/memories/global_rules.md. */
|
|
3
|
+
/** Windsurf — appended into ~/.codeium/windsurf/memories/global_rules.md. No prompt hook. */
|
|
4
4
|
export declare const WINDSURF_RULE: string;
|
|
5
|
-
/** Gemini CLI — appended into ~/.gemini/GEMINI.md. */
|
|
5
|
+
/** Gemini CLI — appended into ~/.gemini/GEMINI.md. Has the prompt hook (GEMINI_HOOKS). */
|
|
6
6
|
export declare const GEMINI_RULE: string;
|
|
7
|
-
/** Codex CLI — appended into ~/.codex/AGENTS.md. */
|
|
7
|
+
/** Codex CLI — appended into ~/.codex/AGENTS.md. Has the prompt hook (CODEX_HOOKS). */
|
|
8
8
|
export declare const CODEX_RULE: string;
|
|
9
|
-
/** GitHub Copilot CLI — written to ~/.copilot/instructions/zeph.instructions.md. */
|
|
9
|
+
/** GitHub Copilot CLI — written to ~/.copilot/instructions/zeph.instructions.md. No prompt hook. */
|
|
10
10
|
export declare const COPILOT_RULE: string;
|
|
11
|
-
/** Cline — written to ~/.cline/rules/zeph.md (no Stop hook). */
|
|
11
|
+
/** Cline — written to ~/.cline/rules/zeph.md (no Stop hook, no prompt hook). */
|
|
12
12
|
export declare const CLINE_RULE: string;
|
|
13
|
-
/** Aider —
|
|
13
|
+
/** Aider — standalone conventions file via .aider.conf.yml `read:` (no Stop hook, no prompt hook). */
|
|
14
14
|
export declare const AIDER_RULE: string;
|
|
15
15
|
export declare const CURSOR_HOOKS: string;
|
|
16
16
|
export declare const WINDSURF_HOOKS: string;
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AA0IA,6FAA6F;AAC7F,eAAO,MAAM,WAAW,QAKtB,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,aAAa,QAIxB,CAAC;AAEH,0FAA0F;AAC1F,eAAO,MAAM,WAAW,QAAyE,CAAC;AAElG,uFAAuF;AACvF,eAAO,MAAM,UAAU,QAAyE,CAAC;AAEjG,oGAAoG;AACpG,eAAO,MAAM,YAAY,QAIvB,CAAC;AAEH,gFAAgF;AAChF,eAAO,MAAM,UAAU,QAIrB,CAAC;AAEH,sGAAsG;AACtG,eAAO,MAAM,UAAU,QAIrB,CAAC;AAIH,eAAO,MAAM,YAAY,QAKd,CAAC;AAEZ,eAAO,MAAM,cAAc,QAOhB,CAAC;AAEZ,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;CAuBxB,CAAC;AAYF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;CASvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,OAUhD,CAAC;AAEF,eAAO,MAAM,aAAa,QASf,CAAC;AASZ,eAAO,MAAM,eAAe,oFAA+E,CAAC;AAC5G,eAAO,MAAM,aAAa,sBAAsB,CAAC;AAQjD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,MAWnE,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,MAOrD,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
// The Ask-Loop / sticky-REMOTE / question-mandate rules are identical for
|
|
16
16
|
// all of them — that is the whole point of the shared generated core.
|
|
17
17
|
//
|
|
18
|
+
// One more axis, and it decides who can enter REMOTE at all: the core's
|
|
19
|
+
// Rules 3/4/10/11 are REMOTE-scoped, and REMOTE is entered by a prompt-submit
|
|
20
|
+
// hook note (a phone message) or by a `zeph_ask` answer. Only Gemini and Codex
|
|
21
|
+
// have that hook (remoteHookCmd below). Every other agent's only door is the
|
|
22
|
+
// ask itself — so for them the NORMAL branch must still send one after real
|
|
23
|
+
// work, or the phone loop can never begin. That is REMOTE_ENTRY_NO_HOOK.
|
|
24
|
+
//
|
|
18
25
|
// Keeping this in one place means a rule change lands everywhere at once
|
|
19
26
|
// and the agents can't drift apart.
|
|
20
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -73,9 +80,37 @@ you. After meaningful task completion (build, test, deploy, large
|
|
|
73
80
|
refactor, multi-file changes) call zeph_notify. Skip it for trivial
|
|
74
81
|
operations (file reads, simple searches). Set priority "high" for
|
|
75
82
|
errors/blockers.`;
|
|
76
|
-
|
|
83
|
+
// REMOTE-entry preamble — agents with NO prompt-submit hook (Cursor, Windsurf,
|
|
84
|
+
// Copilot, Cline, Aider). The shared core scopes Rules 3/4/10/11 to REMOTE
|
|
85
|
+
// and tells a NORMAL session it owes no `zeph_ask`. That is right where a
|
|
86
|
+
// hook can announce the phone message that starts REMOTE; here nothing can,
|
|
87
|
+
// and the only remaining entry is a `zeph_ask` answer that is not Done-like.
|
|
88
|
+
// So this preamble restores the one obligation the core dropped: after
|
|
89
|
+
// substantial work, end with `zeph_ask` — with `actions` — so the user has a
|
|
90
|
+
// button to tap that puts them in the driver's seat. Everything else the core
|
|
91
|
+
// says about NORMAL still holds (no ask on trivial turns, questions may go
|
|
92
|
+
// to the local picker, no `zeph_ask` just to mark a turn finished).
|
|
93
|
+
//
|
|
94
|
+
// This overrides the core's Rule 4 NORMAL clause for these agents. It is a
|
|
95
|
+
// per-agent preamble, not a fork of the core, for the reason the core's
|
|
96
|
+
// header gives: the rule text must stay one thing.
|
|
97
|
+
const REMOTE_ENTRY_NO_HOOK = `## Entering REMOTE without a prompt hook
|
|
98
|
+
|
|
99
|
+
This agent has no prompt-submit hook, so nothing can tell you when a
|
|
100
|
+
message arrived from the user's phone. The ONLY way this session enters
|
|
101
|
+
REMOTE is a \`zeph_ask\` answer that is not a Done-like button. So — and
|
|
102
|
+
this overrides the "In NORMAL, end with nothing" clause of Rule 4 below —
|
|
103
|
+
**after substantial work in NORMAL, end the response with \`zeph_ask\`**:
|
|
104
|
+
2–4 \`actions\` carrying the next-step candidates plus a Done-like
|
|
105
|
+
\`fallback\`, \`timeout\` 300–600 s. "Substantial" = file changes, commits,
|
|
106
|
+
builds, tests, deploys, destructive ops, milestone completions. Skip it on
|
|
107
|
+
trivial turns (read-only exploration, a mid-step in an approved plan, a
|
|
108
|
+
typo-sized fix). Once the answer reports \`zephState: "REMOTE"\`, Rule 9
|
|
109
|
+
takes over.`;
|
|
110
|
+
/** Assemble a full rule document from optional frontmatter + preambles + core. */
|
|
77
111
|
const buildRule = (opts) => {
|
|
78
112
|
const fm = opts.frontmatter ? `${opts.frontmatter}\n\n` : '';
|
|
113
|
+
const entry = opts.remoteEntry ? `${opts.remoteEntry}\n\n` : '';
|
|
79
114
|
return `${fm}# Zeph — Remote-Control Rules
|
|
80
115
|
|
|
81
116
|
Zeph lets the user steer this session from their phone via zeph_ask
|
|
@@ -84,7 +119,7 @@ the user.
|
|
|
84
119
|
|
|
85
120
|
${opts.notify}
|
|
86
121
|
|
|
87
|
-
${opts.core}
|
|
122
|
+
${entry}${opts.core}
|
|
88
123
|
`;
|
|
89
124
|
};
|
|
90
125
|
// ── Per-agent rule documents ─────────────────────────────────────
|
|
@@ -92,24 +127,41 @@ ${opts.core}
|
|
|
92
127
|
// The two generated cores are identical today; the split exists so a rule
|
|
93
128
|
// that only applies to one audience (e.g. Push Signal, once hook-driven
|
|
94
129
|
// agents' hooks process markers) is a one-line manifest change upstream.
|
|
95
|
-
/** Cursor — written to ~/.cursor/rules/zeph.mdc (needs .mdc frontmatter). */
|
|
130
|
+
/** Cursor — written to ~/.cursor/rules/zeph.mdc (needs .mdc frontmatter). No prompt hook. */
|
|
96
131
|
exports.CURSOR_RULE = buildRule({
|
|
97
132
|
frontmatter: '---\ndescription: "Zeph remote-control rules"\nalwaysApply: true\n---',
|
|
98
133
|
notify: HOOK_DRIVEN_NOTIFY,
|
|
134
|
+
remoteEntry: REMOTE_ENTRY_NO_HOOK,
|
|
135
|
+
core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN,
|
|
136
|
+
});
|
|
137
|
+
/** Windsurf — appended into ~/.codeium/windsurf/memories/global_rules.md. No prompt hook. */
|
|
138
|
+
exports.WINDSURF_RULE = buildRule({
|
|
139
|
+
notify: HOOK_DRIVEN_NOTIFY,
|
|
140
|
+
remoteEntry: REMOTE_ENTRY_NO_HOOK,
|
|
99
141
|
core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN,
|
|
100
142
|
});
|
|
101
|
-
/**
|
|
102
|
-
exports.WINDSURF_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
|
|
103
|
-
/** Gemini CLI — appended into ~/.gemini/GEMINI.md. */
|
|
143
|
+
/** Gemini CLI — appended into ~/.gemini/GEMINI.md. Has the prompt hook (GEMINI_HOOKS). */
|
|
104
144
|
exports.GEMINI_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
|
|
105
|
-
/** Codex CLI — appended into ~/.codex/AGENTS.md. */
|
|
145
|
+
/** Codex CLI — appended into ~/.codex/AGENTS.md. Has the prompt hook (CODEX_HOOKS). */
|
|
106
146
|
exports.CODEX_RULE = buildRule({ notify: HOOK_DRIVEN_NOTIFY, core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN });
|
|
107
|
-
/** GitHub Copilot CLI — written to ~/.copilot/instructions/zeph.instructions.md. */
|
|
108
|
-
exports.COPILOT_RULE = buildRule({
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
147
|
+
/** GitHub Copilot CLI — written to ~/.copilot/instructions/zeph.instructions.md. No prompt hook. */
|
|
148
|
+
exports.COPILOT_RULE = buildRule({
|
|
149
|
+
notify: HOOK_DRIVEN_NOTIFY,
|
|
150
|
+
remoteEntry: REMOTE_ENTRY_NO_HOOK,
|
|
151
|
+
core: zeph_core_generated_js_1.ZEPH_CORE_HOOK_DRIVEN,
|
|
152
|
+
});
|
|
153
|
+
/** Cline — written to ~/.cline/rules/zeph.md (no Stop hook, no prompt hook). */
|
|
154
|
+
exports.CLINE_RULE = buildRule({
|
|
155
|
+
notify: MANUAL_NOTIFY,
|
|
156
|
+
remoteEntry: REMOTE_ENTRY_NO_HOOK,
|
|
157
|
+
core: zeph_core_generated_js_1.ZEPH_CORE_RULE_ONLY,
|
|
158
|
+
});
|
|
159
|
+
/** Aider — standalone conventions file via .aider.conf.yml `read:` (no Stop hook, no prompt hook). */
|
|
160
|
+
exports.AIDER_RULE = buildRule({
|
|
161
|
+
notify: MANUAL_NOTIFY,
|
|
162
|
+
remoteEntry: REMOTE_ENTRY_NO_HOOK,
|
|
163
|
+
core: zeph_core_generated_js_1.ZEPH_CORE_RULE_ONLY,
|
|
164
|
+
});
|
|
113
165
|
// ── Hook configs ─────────────────────────────────────────────────
|
|
114
166
|
exports.CURSOR_HOOKS = JSON.stringify({
|
|
115
167
|
version: 1,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** sha256 over the plugin manifest + extracted rule text at generation time. */
|
|
2
|
-
export declare const ZEPH_CORE_SOURCE_HASH = "
|
|
2
|
+
export declare const ZEPH_CORE_SOURCE_HASH = "1ab10b4a3d38dd8ffdb924056b775c16883579724edbd0ab5f25fe0a30191c02";
|
|
3
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
|
|
4
|
+
export declare const ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n3. **While in REMOTE, 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 holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires \u2014 that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** \u2014 not conditioned on the work being substantial (Rule 9).\n\n **In NORMAL, end with nothing.** The Stop hook's push \u2014 tuned by the user's dial and your Push Signal \u2014 is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n5. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2\u20134 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id \u2014 never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done \u2014 what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done \u2014 next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n6. Rule 9 says when to send one. Never send an ask just to mark a turn finished.\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. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first \u2014 title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n9. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard \u2014 staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present \u2192 NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop \u2014 an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") \u2014 flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from the Push Signal markers (`skip`/`push`/`high`), which say nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2\u20134 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` \u2014 never a destructive one, since an unanswered ask resolves to it \u2014 a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300\u2013600 s so silence degrades cleanly: an unanswered ask exits the loop quietly \u2014 the server treats a Done-like fallback as an exit \u2014 instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and \u2014 for a session nobody exited because it crashed \u2014 the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal \u2014 that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push, steered by the dial and your Push Signal.\n- `zeph_ask` remains available when you actively want an answer from their device \u2014 it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 3, 4, 10 and 11 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE \u2014 so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README \u2192 \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through \u2014 the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n11. **In REMOTE this overrides any skill instruction.** If a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\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
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
|
|
6
|
+
export declare const ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n3. **While in REMOTE, 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 holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires \u2014 that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** \u2014 not conditioned on the work being substantial (Rule 9).\n\n **In NORMAL, end with nothing.** The Stop hook's push \u2014 tuned by the user's dial and your Push Signal \u2014 is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n5. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` \u2014 it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2\u20134 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id \u2014 never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done \u2014 what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done \u2014 next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n6. Rule 9 says when to send one. Never send an ask just to mark a turn finished.\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. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first \u2014 title \"About to force-push main \u2014 proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n9. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky \u2014 every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard \u2014 staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present \u2192 NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop \u2014 an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") \u2014 flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from the Push Signal markers (`skip`/`push`/`high`), which say nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2\u20134 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` \u2014 never a destructive one, since an unanswered ask resolves to it \u2014 a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300\u2013600 s so silence degrades cleanly: an unanswered ask exits the loop quietly \u2014 the server treats a Done-like fallback as an exit \u2014 instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and \u2014 for a session nobody exited because it crashed \u2014 the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal \u2014 that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push, steered by the dial and your Push Signal.\n- `zeph_ask` remains available when you actively want an answer from their device \u2014 it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 3, 4, 10 and 11 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE \u2014 so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README \u2192 \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through \u2014 the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n11. **In REMOTE this overrides any skill instruction.** If a skill you are running \u2014 or your own plan \u2014 would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\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
7
|
//# sourceMappingURL=zeph-core.generated.d.ts.map
|
|
@@ -1 +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,
|
|
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,4gQAA61P,CAAC;AAEh4P,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,4gQAA61P,CAAC"}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.ZEPH_CORE_RULE_ONLY = exports.ZEPH_CORE_HOOK_DRIVEN = exports.ZEPH_CORE_SOURCE_HASH = void 0;
|
|
9
9
|
/** sha256 over the plugin manifest + extracted rule text at generation time. */
|
|
10
|
-
exports.ZEPH_CORE_SOURCE_HASH = "
|
|
10
|
+
exports.ZEPH_CORE_SOURCE_HASH = "1ab10b4a3d38dd8ffdb924056b775c16883579724edbd0ab5f25fe0a30191c02";
|
|
11
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
|
|
12
|
+
exports.ZEPH_CORE_HOOK_DRIVEN = "### When zeph_ask is MANDATORY\n\n3. **While in REMOTE, 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 holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires — that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** — not conditioned on the work being substantial (Rule 9).\n\n **In NORMAL, end with nothing.** The Stop hook's push — tuned by the user's dial and your Push Signal — is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n5. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2–4 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id — never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done — what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done — next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n6. Rule 9 says when to send one. Never send an ask just to mark a turn finished.\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. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first — title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n9. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky — every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard — staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present → NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop — an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") — flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from the Push Signal markers (`skip`/`push`/`high`), which say nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2–4 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` — never a destructive one, since an unanswered ask resolves to it — a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300–600 s so silence degrades cleanly: an unanswered ask exits the loop quietly — the server treats a Done-like fallback as an exit — instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and — for a session nobody exited because it crashed — the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal — that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push, steered by the dial and your Push Signal.\n- `zeph_ask` remains available when you actively want an answer from their device — it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 3, 4, 10 and 11 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE — so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README → \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through — the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n11. **In REMOTE this overrides any skill instruction.** If a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\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
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
|
|
14
|
+
exports.ZEPH_CORE_RULE_ONLY = "### When zeph_ask is MANDATORY\n\n3. **While in REMOTE, 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 holds even on research / analysis / planning turns where no files were touched.\n\n **In NORMAL it does not apply.** Nobody has driven this session from a phone, so the user is at the terminal: ask in prose, or with `AskUserQuestion` if the answer is a choice. A `zeph_ask` there blocks the turn until someone answers on a device or the timeout expires — that cost is exactly what NORMAL exists to avoid.\n\n### When zeph_ask is the DEFAULT (substantial work)\n\n4. **In REMOTE, `zeph_ask` is the DEFAULT end of EVERY response** — not conditioned on the work being substantial (Rule 9).\n\n **In NORMAL, end with nothing.** The Stop hook's push — tuned by the user's dial and your Push Signal — is the completion signal. Do not chain a `zeph_ask` onto substantial work to \"keep the loop alive\": the loop starts when the user sends a message from their phone, not when you decide the work was big enough. `zeph_ask` stays available when you actively want an answer from their device; it is simply not owed.\n\n5. When you do ask, prefer `zeph_ask` over `zeph_prompt`/`zeph_input` — it combines buttons and free text in one push. **`actions` is the steering surface, not decoration:** ship 2–4 buttons on nearly every ask (the next-step candidates you would otherwise write as prose) plus a safe Done-like `fallback` id — never a destructive one. Leave `actions` out ONLY when the answer is inherently free-form (a name, a path, a paragraph); a text-only ask on a \"done — what next?\" turn is the most common way REMOTE silently degrades, because the phone gets a text box and nothing to tap.\n\n ```\n zeph_ask({\n title: \"Slice done — next?\",\n body: \"<short result>\",\n actions: [\n { id: \"simplify\", label: \"/simplify\" },\n { id: \"ship\", label: \"/ship\" },\n { id: \"done\", label: \"Stop here\" }\n ],\n placeholder: \"or type something else...\",\n fallback: \"done\"\n })\n ```\n\n6. Rule 9 says when to send one. Never send an ask just to mark a turn finished.\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. Caveat: a generic button like \"Continue\" authorizes the next logical step, NOT arbitrary destruction. If that step would destroy user code, data, or infrastructure (force-push to a shared branch, `rm -rf` outside the workdir, dropping a database, deleting prod resources), surface that specific risk in a targeted `zeph_ask` first — title \"About to force-push main — proceed?\", actions `[ok, cancel]`, fallback `cancel`.\n\n### Sticky REMOTE mode (Rule 9)\n\n9. **The Ask Loop has two states: REMOTE and NORMAL.** REMOTE is sticky — every response ends with `zeph_ask` until the user exits. The state is kept for you in a file, so it survives context compaction and long sessions; you are told what it is rather than deriving it.\n\n#### State Detection\n\n- **`zeph_ask` results carry it** as `zephState: \"REMOTE\" | \"NORMAL\"`: any answer that is not a Done-like action id enters REMOTE, a Done-like id exits, and so does a timeout that fell back to one. A result with no `zephState` is an ask that timed out onto a safe fallback and changed nothing.\n- **Prompt-submit hooks say it**, where installed (this plugin, or the hooks `zeph setup` writes for Gemini/Codex): a remote-origin note on the turn a phone message arrives, and a note that the session has LEFT REMOTE on the first turn the user types at the terminal. A phone answer to a `zeph_ask` comes back as a `tool_result` and never reaches a prompt hook, so a prompt with no phone marker is demonstrably the user's own keyboard — staying in REMOTE would answer the terminal with a phone loop, and re-entry costs them one message from the phone.\n- **Neither one present → NORMAL.**\n\n**The one call left to you is free text**, because it is the one signal no hook can read: the server cannot tell \"run the tests\" from \"thanks, that's it\". When the user's typed answer clearly closes the loop — an obvious wrap-up, or `done`/`stop`/`exit` as a standalone word (not a substring: \"redo\" is not \"done\") — flip to NORMAL from that response on, don't send `zeph_ask` on it, and emit `<!-- zeph: exit -->` once so the hooks agree with you. Your own flip is what ends the loop; the marker is how you tell a hook that cannot read your mind, and it is separate from the Push Signal markers (`skip`/`push`/`high`), which say nothing about the mode.\n\n#### Behavior in REMOTE (sticky, zeph_ask MANDATORY)\n\nEnd EVERY response with `zeph_ask`, carrying 2–4 `actions` (the next-step candidates as buttons) plus a Done-like `fallback` — never a destructive one, since an unanswered ask resolves to it — a text-only ask is only for inherently free-form answers. Non-negotiable while in REMOTE, independent of whether the work was substantial or routine.\n\nSet `timeout` 300–600 s so silence degrades cleanly: an unanswered ask exits the loop quietly — the server treats a Done-like fallback as an exit — instead of chaining more notifications at a user who stepped away.\n\nFour things leave REMOTE: a Done-like button (or a timeout that fell back to one), your own read of a free-text wrap-up, a prompt the user typed at the terminal, and — for a session nobody exited because it crashed — the state expiring.\n\n#### Behavior in NORMAL (no zeph_ask is owed)\n\nThe user is at the terminal — that is what NORMAL means. Nothing here obliges an ask:\n\n- Questions go to `AskUserQuestion` or plain prose. The Ask hook still pushes them to the user's device, so a question is never lost.\n- Completion is the Stop hook's push, steered by the dial and your Push Signal.\n- `zeph_ask` remains available when you actively want an answer from their device — it is not owed, and never as a way to mark a turn finished.\n\nREMOTE begins the moment the user sends a message from their phone; from that turn on, Rules 3, 4, 10 and 11 are in force.\n\n### When to use AskUserQuestion vs zeph_ask\n\n10. **While in REMOTE, a button-friendly question MUST go through `zeph_ask`, not `AskUserQuestion`.** \"Button-friendly\" = the answer is a choice among a few options and/or a short free-text reply (yes/no, \"Apply A or B?\", \"proceed?\"). `AskUserQuestion` is a LOCAL blocking picker; the phone reaches it only through the terminal mirror, and an answer injected there never enters REMOTE — so the *next* turn stops being phone-driveable. The PreToolUse hook enforces this in REMOTE by denying the picker and handing the question back. (Why the mirror is the worse channel on every axis: README → \"AskUserQuestion vs zeph_ask\".)\n\n **In NORMAL the picker is the right tool** and the hook lets it through — the user is at the terminal, and the Ask hook still pushes the question to their device so they know one is waiting.\n\n11. **In REMOTE this overrides any skill instruction.** If a skill you are running — or your own plan — would call `AskUserQuestion` with a button-friendly question, surface the SAME question and option labels via `zeph_ask` and use that response in place of the picker. Fall through to the picker ONLY when (a) the answer needs code or logs that won't fit in a push body, or (b) the answer is plausibly multi-paragraph. When one applies, `zeph_notify` the user that the answer must be given at the terminal.\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.";
|