ftown 0.26.0 → 0.28.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/attention-cli.d.ts +32 -0
- package/dist/attention-cli.js +310 -0
- package/dist/attention-cli.js.map +1 -0
- package/dist/attention-events.d.ts +2 -1
- package/dist/attention-events.js.map +1 -1
- package/dist/attention-store.d.ts +6 -3
- package/dist/attention-store.js +60 -2
- package/dist/attention-store.js.map +1 -1
- package/dist/brief-cli.d.ts +10 -0
- package/dist/brief-cli.js +163 -0
- package/dist/brief-cli.js.map +1 -0
- package/dist/command-rpc.d.ts +11 -0
- package/dist/command-rpc.js +83 -16
- package/dist/command-rpc.js.map +1 -1
- package/dist/coordination/attention-routing.d.ts +37 -0
- package/dist/coordination/attention-routing.js +154 -0
- package/dist/coordination/attention-routing.js.map +1 -0
- package/dist/coordination/board.js +82 -3
- package/dist/coordination/board.js.map +1 -1
- package/dist/coordination/contract-b.d.ts +206 -0
- package/dist/coordination/contract-b.js +58 -0
- package/dist/coordination/contract-b.js.map +1 -0
- package/dist/coordination/contract-c.d.ts +117 -0
- package/dist/coordination/contract-c.js +47 -0
- package/dist/coordination/contract-c.js.map +1 -0
- package/dist/coordination/contract.d.ts +8 -1
- package/dist/coordination/contract.js +1 -1
- package/dist/coordination/contract.js.map +1 -1
- package/dist/coordination/format.js +51 -12
- package/dist/coordination/format.js.map +1 -1
- package/dist/coordination/plan.d.ts +74 -0
- package/dist/coordination/plan.js +534 -0
- package/dist/coordination/plan.js.map +1 -0
- package/dist/coordination/routes-brief.d.ts +10 -0
- package/dist/coordination/routes-brief.js +149 -0
- package/dist/coordination/routes-brief.js.map +1 -0
- package/dist/coordination/routes-plan.d.ts +13 -0
- package/dist/coordination/routes-plan.js +182 -0
- package/dist/coordination/routes-plan.js.map +1 -0
- package/dist/coordination/routes-worktrees.d.ts +29 -0
- package/dist/coordination/routes-worktrees.js +138 -0
- package/dist/coordination/routes-worktrees.js.map +1 -0
- package/dist/coordination/wiring.d.ts +17 -2
- package/dist/coordination/wiring.js +81 -11
- package/dist/coordination/wiring.js.map +1 -1
- package/dist/coordination-cli.d.ts +6 -1
- package/dist/coordination-cli.js +23 -7
- package/dist/coordination-cli.js.map +1 -1
- package/dist/create-ftown-session.d.ts +26 -1
- package/dist/create-ftown-session.js +208 -104
- package/dist/create-ftown-session.js.map +1 -1
- package/dist/ftown-cli.js +14 -2
- package/dist/ftown-cli.js.map +1 -1
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/local-api-server.d.ts +24 -0
- package/dist/local-api-server.js +473 -32
- package/dist/local-api-server.js.map +1 -1
- package/dist/mail-delivery.d.ts +18 -4
- package/dist/mail-delivery.js +50 -8
- package/dist/mail-delivery.js.map +1 -1
- package/dist/plan-cli.d.ts +52 -0
- package/dist/plan-cli.js +357 -0
- package/dist/plan-cli.js.map +1 -0
- package/dist/security/bridge/permissions.js +55 -5
- package/dist/security/bridge/permissions.js.map +1 -1
- package/dist/session-controller.d.ts +17 -1
- package/dist/session-controller.js +93 -2
- package/dist/session-controller.js.map +1 -1
- package/dist/sessions-commands.js +75 -5
- package/dist/sessions-commands.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/worktrees-cli.d.ts +39 -0
- package/dist/worktrees-cli.js +168 -0
- package/dist/worktrees-cli.js.map +1 -0
- package/dist/worktrees.d.ts +16 -0
- package/dist/worktrees.js +292 -0
- package/dist/worktrees.js.map +1 -0
- package/hooks/ftown-antigravity-hook.sh +12 -6
- package/package.json +2 -2
- package/skills/ftown/SKILL.md +88 -5
- package/skills/ftown/references/orchestrator.md +32 -29
- package/skills/ftown/references/sessions.md +155 -12
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, realpath } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { WorktreeError, } from './coordination/contract-b.js';
|
|
7
|
+
export { WorktreeError, };
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const GIT_TIMEOUT_MS = 30_000;
|
|
10
|
+
const GIT_MAX_BUFFER = 10 * 1024 * 1024;
|
|
11
|
+
const SLUG_MAX_LENGTH = 48;
|
|
12
|
+
export function slugifyWorktreeName(name) {
|
|
13
|
+
const cleaned = name
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
16
|
+
.replace(/^-+|-+$/g, '')
|
|
17
|
+
.slice(0, SLUG_MAX_LENGTH)
|
|
18
|
+
.replace(/-+$/g, '');
|
|
19
|
+
return cleaned || 'session';
|
|
20
|
+
}
|
|
21
|
+
async function runGit(cwd, args) {
|
|
22
|
+
const { stdout, stderr } = await execFileAsync('git', [...args], {
|
|
23
|
+
cwd,
|
|
24
|
+
timeout: GIT_TIMEOUT_MS,
|
|
25
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
26
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
27
|
+
});
|
|
28
|
+
return { stdout, stderr };
|
|
29
|
+
}
|
|
30
|
+
function errorMessage(err) {
|
|
31
|
+
if (err && typeof err === 'object') {
|
|
32
|
+
const stderr = err.stderr;
|
|
33
|
+
if (typeof stderr === 'string' && stderr.trim().length > 0) {
|
|
34
|
+
return stderr.trim();
|
|
35
|
+
}
|
|
36
|
+
const message = err.message;
|
|
37
|
+
if (typeof message === 'string' && message.trim().length > 0) {
|
|
38
|
+
return message.trim();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return String(err);
|
|
42
|
+
}
|
|
43
|
+
export class GitWorktrees {
|
|
44
|
+
constructor(_options = {}) { }
|
|
45
|
+
async create(req) {
|
|
46
|
+
const fromDir = req.fromDir;
|
|
47
|
+
if (!fromDir || !existsSync(fromDir)) {
|
|
48
|
+
throw new WorktreeError('not_a_repo', `Directory does not exist: ${fromDir}`);
|
|
49
|
+
}
|
|
50
|
+
let topLevelRaw;
|
|
51
|
+
let gitDirRaw;
|
|
52
|
+
let commonDirRaw;
|
|
53
|
+
try {
|
|
54
|
+
topLevelRaw = (await runGit(fromDir, ['rev-parse', '--show-toplevel'])).stdout.trim();
|
|
55
|
+
gitDirRaw = (await runGit(fromDir, ['rev-parse', '--git-dir'])).stdout.trim();
|
|
56
|
+
commonDirRaw = (await runGit(fromDir, ['rev-parse', '--git-common-dir'])).stdout.trim();
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
throw new WorktreeError('not_a_repo', `Not a git repository (${fromDir}): ${errorMessage(err)}`);
|
|
60
|
+
}
|
|
61
|
+
const topLevel = await realpath(resolve(fromDir, topLevelRaw));
|
|
62
|
+
const gitDir = await realpath(resolve(fromDir, gitDirRaw));
|
|
63
|
+
const commonDir = await realpath(resolve(fromDir, commonDirRaw));
|
|
64
|
+
// When fromDir is itself a linked worktree, gitDir !== commonDir; use the main worktree's root (commonDir parent).
|
|
65
|
+
const repoRoot = gitDir !== commonDir ? await realpath(dirname(commonDir)) : topLevel;
|
|
66
|
+
let base;
|
|
67
|
+
if (req.base !== undefined && req.base.trim().length > 0) {
|
|
68
|
+
base = req.base.trim();
|
|
69
|
+
if (base.startsWith('-')) {
|
|
70
|
+
throw new WorktreeError('git_failed', `Invalid base ref: ${base}`);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await runGit(fromDir, ['rev-parse', '--verify', `${base}^{commit}`]);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new WorktreeError('git_failed', `Unknown base ref '${base}': ${errorMessage(err)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
try {
|
|
81
|
+
base = (await runGit(fromDir, ['symbolic-ref', '--short', 'HEAD'])).stdout.trim();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
throw new WorktreeError('detached_head', 'detached HEAD; pass --base');
|
|
85
|
+
}
|
|
86
|
+
if (!base) {
|
|
87
|
+
throw new WorktreeError('detached_head', 'detached HEAD; pass --base');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const slug = slugifyWorktreeName(req.name);
|
|
91
|
+
const rawBranch = req.branch !== undefined ? req.branch.trim() : `ftown/${slug}`;
|
|
92
|
+
if (!rawBranch || rawBranch.startsWith('-') || rawBranch.includes('@{')) {
|
|
93
|
+
throw new WorktreeError('bad_branch', `Invalid branch name: ${req.branch ?? ''}`);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const checked = (await runGit(repoRoot, ['check-ref-format', '--branch', rawBranch])).stdout.trim();
|
|
97
|
+
if (checked !== rawBranch) {
|
|
98
|
+
throw new WorktreeError('bad_branch', `Invalid branch name '${rawBranch}'`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (err instanceof WorktreeError)
|
|
103
|
+
throw err;
|
|
104
|
+
throw new WorktreeError('bad_branch', `Invalid branch name '${rawBranch}': ${errorMessage(err)}`);
|
|
105
|
+
}
|
|
106
|
+
const branch = rawBranch;
|
|
107
|
+
const worktreePath = join(repoRoot, '.claude', 'worktrees', slug);
|
|
108
|
+
if (existsSync(worktreePath)) {
|
|
109
|
+
throw new WorktreeError('exists', `Worktree path already exists: ${worktreePath}`);
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await runGit(repoRoot, ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`]);
|
|
113
|
+
throw new WorktreeError('exists', `Branch already exists: ${branch}`);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
if (err instanceof WorktreeError)
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
await mkdir(dirname(worktreePath), { recursive: true });
|
|
120
|
+
try {
|
|
121
|
+
await runGit(repoRoot, ['worktree', 'add', '-b', branch, worktreePath, base]);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const msg = errorMessage(err);
|
|
125
|
+
if (/already exists|already checked out/i.test(msg)) {
|
|
126
|
+
throw new WorktreeError('exists', msg);
|
|
127
|
+
}
|
|
128
|
+
throw new WorktreeError('git_failed', msg);
|
|
129
|
+
}
|
|
130
|
+
const realWorktreePath = await realpath(worktreePath);
|
|
131
|
+
return {
|
|
132
|
+
repoRoot,
|
|
133
|
+
path: realWorktreePath,
|
|
134
|
+
branch,
|
|
135
|
+
base,
|
|
136
|
+
createdByBridge: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async status(wt) {
|
|
140
|
+
if (!existsSync(wt.path)) {
|
|
141
|
+
return {
|
|
142
|
+
path: wt.path,
|
|
143
|
+
branch: wt.branch,
|
|
144
|
+
base: wt.base,
|
|
145
|
+
exists: false,
|
|
146
|
+
dirty: false,
|
|
147
|
+
ahead: 0,
|
|
148
|
+
unpushed: null,
|
|
149
|
+
mergedIntoBase: false,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let realWtPath;
|
|
153
|
+
let topLevel;
|
|
154
|
+
try {
|
|
155
|
+
realWtPath = await realpath(wt.path);
|
|
156
|
+
const topOut = (await runGit(realWtPath, ['rev-parse', '--show-toplevel'])).stdout.trim();
|
|
157
|
+
topLevel = await realpath(resolve(realWtPath, topOut));
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
throw new WorktreeError('git_failed', `Failed to resolve worktree root (${wt.path}): ${errorMessage(err)}`);
|
|
161
|
+
}
|
|
162
|
+
if (topLevel !== realWtPath) {
|
|
163
|
+
throw new WorktreeError('git_failed', `Worktree path '${wt.path}' is not a standalone worktree toplevel (resolved to '${topLevel}')`);
|
|
164
|
+
}
|
|
165
|
+
let dirty;
|
|
166
|
+
try {
|
|
167
|
+
const porcelain = (await runGit(realWtPath, ['status', '--porcelain'])).stdout;
|
|
168
|
+
dirty = porcelain.trim().length > 0;
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
throw new WorktreeError('git_failed', `Failed to read worktree status: ${errorMessage(err)}`);
|
|
172
|
+
}
|
|
173
|
+
let ahead;
|
|
174
|
+
try {
|
|
175
|
+
const countRaw = (await runGit(realWtPath, ['rev-list', '--count', `${wt.base}..${wt.branch}`])).stdout.trim();
|
|
176
|
+
const parsed = Number.parseInt(countRaw, 10);
|
|
177
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
178
|
+
throw new Error(`Unexpected rev-list count: ${countRaw}`);
|
|
179
|
+
}
|
|
180
|
+
ahead = parsed;
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
throw new WorktreeError('git_failed', `Failed to count commits ahead of base: ${errorMessage(err)}`);
|
|
184
|
+
}
|
|
185
|
+
let unpushed = null;
|
|
186
|
+
let upstreamRef;
|
|
187
|
+
try {
|
|
188
|
+
upstreamRef = (await runGit(realWtPath, ['for-each-ref', '--format=%(upstream:short)', `refs/heads/${wt.branch}`])).stdout.trim();
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
throw new WorktreeError('git_failed', `Failed to inspect branch upstream: ${errorMessage(err)}`);
|
|
192
|
+
}
|
|
193
|
+
if (upstreamRef.length > 0) {
|
|
194
|
+
try {
|
|
195
|
+
const countRaw = (await runGit(realWtPath, ['rev-list', '--count', `${wt.branch}@{u}..${wt.branch}`])).stdout.trim();
|
|
196
|
+
const parsed = Number.parseInt(countRaw, 10);
|
|
197
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
198
|
+
throw new Error(`Unexpected unpushed count: ${countRaw}`);
|
|
199
|
+
}
|
|
200
|
+
unpushed = parsed;
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
throw new WorktreeError('git_failed', `Failed to count unpushed commits: ${errorMessage(err)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
let mergedIntoBase = false;
|
|
207
|
+
try {
|
|
208
|
+
await runGit(realWtPath, ['merge-base', '--is-ancestor', wt.branch, wt.base]);
|
|
209
|
+
mergedIntoBase = true;
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
const code = err?.code;
|
|
213
|
+
if (code === 1) {
|
|
214
|
+
mergedIntoBase = false;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
throw new WorktreeError('git_failed', `Failed to check merge status: ${errorMessage(err)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
path: wt.path,
|
|
222
|
+
branch: wt.branch,
|
|
223
|
+
base: wt.base,
|
|
224
|
+
exists: true,
|
|
225
|
+
dirty,
|
|
226
|
+
ahead,
|
|
227
|
+
unpushed,
|
|
228
|
+
mergedIntoBase,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
decide(status) {
|
|
232
|
+
if (!status.exists) {
|
|
233
|
+
return { action: 'retain', reason: 'worktree missing' };
|
|
234
|
+
}
|
|
235
|
+
if (status.dirty) {
|
|
236
|
+
return { action: 'retain', reason: 'uncommitted changes' };
|
|
237
|
+
}
|
|
238
|
+
if (status.mergedIntoBase) {
|
|
239
|
+
return { action: 'remove', deleteBranch: true };
|
|
240
|
+
}
|
|
241
|
+
if (status.unpushed === 0) {
|
|
242
|
+
return { action: 'remove', deleteBranch: false };
|
|
243
|
+
}
|
|
244
|
+
if (status.unpushed !== null && status.unpushed > 0) {
|
|
245
|
+
return {
|
|
246
|
+
action: 'retain',
|
|
247
|
+
reason: `${status.unpushed} unpushed commit${status.unpushed === 1 ? '' : 's'}`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (status.ahead > 0) {
|
|
251
|
+
return {
|
|
252
|
+
action: 'retain',
|
|
253
|
+
reason: `${status.ahead} unmerged commit${status.ahead === 1 ? '' : 's'}`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return { action: 'retain', reason: 'unmerged branch' };
|
|
257
|
+
}
|
|
258
|
+
async remove(wt, opts) {
|
|
259
|
+
const removeArgs = ['worktree', 'remove', ...(opts.force ? ['--force'] : []), wt.path];
|
|
260
|
+
try {
|
|
261
|
+
await runGit(wt.repoRoot, removeArgs);
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
throw new WorktreeError('git_failed', errorMessage(err));
|
|
265
|
+
}
|
|
266
|
+
if (opts.deleteBranch) {
|
|
267
|
+
let merged = false;
|
|
268
|
+
try {
|
|
269
|
+
await runGit(wt.repoRoot, ['merge-base', '--is-ancestor', wt.branch, wt.base]);
|
|
270
|
+
merged = true;
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const code = err?.code;
|
|
274
|
+
if (code !== 1) {
|
|
275
|
+
console.warn(`[GitWorktrees] Failed to verify merge status for branch '${wt.branch}': ${errorMessage(err)}`);
|
|
276
|
+
}
|
|
277
|
+
merged = false;
|
|
278
|
+
}
|
|
279
|
+
if (!merged) {
|
|
280
|
+
console.warn(`[GitWorktrees] Refusing to delete unmerged branch '${wt.branch}'`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
await runGit(wt.repoRoot, ['branch', '-d', wt.branch]);
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
console.warn(`[GitWorktrees] Failed to delete branch '${wt.branch}': ${errorMessage(err)}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=worktrees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktrees.js","sourceRoot":"","sources":["../src/worktrees.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EACL,aAAa,GAMd,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,aAAa,GAMd,CAAC;AAEF,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACxC,MAAM,eAAe,GAAG,EAAE,CAAC;AAM3B,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,IAAI;SACjB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;SACzB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAOD,KAAK,UAAU,MAAM,CAAC,GAAW,EAAE,IAAuB;IACxD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE;QAC/D,GAAG;QACH,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,cAAc;QACzB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE;KAClD,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,YAAY,CAAC,GAAY;IAChC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAC;QACpD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3D,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QACD,MAAM,OAAO,GAAI,GAA6B,CAAC,OAAO,CAAC;QACvD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7D,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,OAAO,YAAY;IACvB,YAAY,WAAgC,EAAE,IAAG,CAAC;IAElD,KAAK,CAAC,MAAM,CAAC,GAA0B;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,6BAA6B,OAAO,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,IAAI,WAAmB,CAAC;QACxB,IAAI,SAAiB,CAAC;QACtB,IAAI,YAAoB,CAAC;QACzB,IAAI,CAAC;YACH,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACtF,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9E,YAAY,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,yBAAyB,OAAO,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnG,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAC3D,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;QACjE,mHAAmH;QACnH,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAEtF,IAAI,IAAY,CAAC;QACjB,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,qBAAqB,IAAI,EAAE,CAAC,CAAC;YACrE,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC;YACvE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,qBAAqB,IAAI,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpF,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,4BAA4B,CAAC,CAAC;YACzE,CAAC;YACD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,4BAA4B,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;QACjF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,wBAAwB,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,kBAAkB,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpG,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,wBAAwB,SAAS,GAAG,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa;gBAAE,MAAM,GAAG,CAAC;YAC5C,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,wBAAwB,SAAS,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpG,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC;QAEzB,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QAClE,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,iCAAiC,YAAY,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,MAAM,EAAE,CAAC,CAAC,CAAC;YACpF,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,0BAA0B,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa;gBAAE,MAAM,GAAG,CAAC;QAC9C,CAAC;QAED,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,qCAAqC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpD,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACzC,CAAC;YACD,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QACtD,OAAO;YACL,QAAQ;YACR,IAAI,EAAE,gBAAgB;YACtB,MAAM;YACN,IAAI;YACJ,eAAe,EAAE,IAAI;SACtB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAmB;QAC9B,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,CAAC;gBACR,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE,KAAK;aACtB,CAAC;QACJ,CAAC;QAED,IAAI,UAAkB,CAAC;QACvB,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC1F,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,oCAAoC,EAAE,CAAC,IAAI,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9G,CAAC;QAED,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,kBAAkB,EAAE,CAAC,IAAI,yDAAyD,QAAQ,IAAI,CAC/F,CAAC;QACJ,CAAC;QAED,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/E,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,mCAAmC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChG,CAAC;QAED,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC/G,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,KAAK,GAAG,MAAM,CAAC;QACjB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,0CAA0C,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvG,CAAC;QAED,IAAI,QAAQ,GAAkB,IAAI,CAAC;QACnC,IAAI,WAAmB,CAAC;QACxB,IAAI,CAAC;YACH,WAAW,GAAG,CACZ,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,cAAc,EAAE,4BAA4B,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CACpG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,sCAAsC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnG,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACrH,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC3C,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBACD,QAAQ,GAAG,MAAM,CAAC;YACpB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,qCAAqC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClG,CAAC;QACH,CAAC;QAED,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9E,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAI,GAA0B,EAAE,IAAI,CAAC;YAC/C,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,cAAc,GAAG,KAAK,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,iCAAiC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,MAAM,EAAE,IAAI;YACZ,KAAK;YACL,KAAK;YACL,QAAQ;YACR,cAAc;SACf,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,MAAsB;QAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QAC1D,CAAC;QACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QAC7D,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAClD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QACnD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACpD,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,mBAAmB,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;aAChF,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,mBAAmB,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;aAC1E,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAmB,EAAE,IAA+C;QAC/E,MAAM,UAAU,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACvF,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,MAAM,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/E,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,GAAI,GAA0B,EAAE,IAAI,CAAC;gBAC/C,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,CAAC,MAAM,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC/G,CAAC;gBACD,MAAM,GAAG,KAAK,CAAC;YACjB,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;gBACjF,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;YACzD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,2CAA2C,EAAE,CAAC,MAAM,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
# ~/.ftown/antigravity-hook.sh and registered in ~/.gemini/config/hooks.json.
|
|
4
4
|
# Translates agy's camelCase hook payload (stdin) to the bridge /hook wire format and POSTs it.
|
|
5
5
|
# On a turn-end Stop it drains this session's ftown inbox FIRST: with mail it
|
|
6
|
-
# prints {"decision":"continue","reason":"<mail>"}
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# prints {"decision":"continue","reason":"<mail>"} (the agent keeps working) and
|
|
7
|
+
# still POSTs Stop so the bridge clears the busy marker (the drain marks the
|
|
8
|
+
# mail delivered, so that Stop finds an empty inbox); without mail it posts
|
|
9
|
+
# Stop. On terminationReason MAX_FORCED_INVOCATIONS / USER_CANCELED / ERROR it
|
|
10
|
+
# never drains, only posts Stop.
|
|
9
11
|
#
|
|
10
12
|
# Always exits 0, also on HUP/INT/TERM/PIPE (a non-zero or signalled PreToolUse
|
|
11
13
|
# hook makes agy BLOCK the tool), and writes nothing to stdout except that one
|
|
@@ -15,7 +17,7 @@
|
|
|
15
17
|
# Worst-case runtime: ~6s inside agy's 10s hook timeout. The POST tries at most
|
|
16
18
|
# 2 endpoint candidates x curl --max-time 2 (4s); on Stop the inbox read through
|
|
17
19
|
# `ftown security proxy` uses a 2s request timeout and happens before the POST,
|
|
18
|
-
# and when
|
|
20
|
+
# and the POST also follows when the drain delivers mail (busy-marker clear).
|
|
19
21
|
#
|
|
20
22
|
# bash 3.2 compatible (macOS /bin/bash). No set -e on purpose; no temp files.
|
|
21
23
|
# The hook's working directory is agy's hooks.json folder, so pwd is never used.
|
|
@@ -160,9 +162,13 @@ drain_mail() {
|
|
|
160
162
|
|
|
161
163
|
MAIL_OUT=""
|
|
162
164
|
if drain_mail; then
|
|
163
|
-
# Mail delivered: the agent continues
|
|
164
|
-
#
|
|
165
|
+
# Mail delivered: the agent continues on the decision line, but the Stop is
|
|
166
|
+
# still POSTed so the bridge clears the busy marker (this hook posts no other
|
|
167
|
+
# Stop while the mail turn runs). The decision line goes out first; the POST
|
|
168
|
+
# is fire-and-forget and silenced. stderr is silenced so a reader that closed
|
|
169
|
+
# the pipe cannot see "Broken pipe".
|
|
165
170
|
printf '%s\n' "$MAIL_OUT" 2>/dev/null
|
|
171
|
+
post_hook "$PAYLOAD" >/dev/null 2>&1 || true
|
|
166
172
|
exit 0
|
|
167
173
|
fi
|
|
168
174
|
post_hook "$PAYLOAD"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ftown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "CLI bridge for ftown — generic PTY-over-Centrifugo relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"overrides": {
|
|
74
74
|
"qs": "6.16.1"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "1fee3d0e20fd50368c0e0ac1774eb47c184149ce"
|
|
77
77
|
}
|
package/skills/ftown/SKILL.md
CHANGED
|
@@ -53,6 +53,12 @@ ftown sessions preview --clear
|
|
|
53
53
|
ftown sessions preview-reply --message "The preview is ready."
|
|
54
54
|
ftown status set --state working --headline "Reviewing the API client" --progress 0/1
|
|
55
55
|
ftown board
|
|
56
|
+
ftown plan
|
|
57
|
+
ftown plan add "Review the API client" --after p1 --priority 1
|
|
58
|
+
ftown sessions create --parent --plan-item p2 --prompt "Review the API client"
|
|
59
|
+
ftown brief set --file brief.md
|
|
60
|
+
ftown attention list
|
|
61
|
+
ftown worktrees list
|
|
56
62
|
ftown harness mail send <session-id> --type task "review the API client"
|
|
57
63
|
ftown harness mail read
|
|
58
64
|
ftown env --help
|
|
@@ -125,6 +131,60 @@ ftown board --wait --until any-done --timeout 300
|
|
|
125
131
|
ftown events --wait
|
|
126
132
|
```
|
|
127
133
|
|
|
134
|
+
Plain `ftown board` appends a `planned` section with up to 5 ready plan
|
|
135
|
+
items (`→ <id> <title>`, then `+N more`). `ftown board --all` also shows
|
|
136
|
+
retired (archived) children after the live ones, suffixed `(retired)`.
|
|
137
|
+
Tree events include the `plan` kind for plan changes and `removed` when a
|
|
138
|
+
child is removed or archived while not already exited.
|
|
139
|
+
|
|
140
|
+
## Plan list
|
|
141
|
+
|
|
142
|
+
Keep the plan on the manager: `ftown plan` defaults to your own session
|
|
143
|
+
(`$FTOWN_SESSION_ID`, the manager), and `--session-id <id>` targets another
|
|
144
|
+
session's plan. This is the manager's plan, not a ticket system — it is a
|
|
145
|
+
short ordered todo list the manager keeps for its own coordination. Add
|
|
146
|
+
items, order them with precedence, and link each child to one item:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
ftown plan [--json] [--session-id <id>]
|
|
150
|
+
ftown plan add <title...> [--note <t>] [--after <id,id>] [--priority 1|2|3]
|
|
151
|
+
ftown plan start <id> --session <child-id>
|
|
152
|
+
ftown plan done <id>
|
|
153
|
+
ftown plan drop <id>
|
|
154
|
+
ftown plan todo <id>
|
|
155
|
+
ftown plan edit <id> [--title <t>] [--note <t>|--no-note] [--after <ids>|--no-after] [--priority n]
|
|
156
|
+
ftown plan rm <id>
|
|
157
|
+
ftown sessions create --parent --plan-item <id> --prompt "<task>"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Precedence comes from `--after <id,id>` and `--priority 1|2|3` (1 is
|
|
161
|
+
highest). An item is ready when it is `todo` and every item in its `after`
|
|
162
|
+
list is `done` or `dropped`; the list shows `→` ready, `·` waiting, `▸`
|
|
163
|
+
started, `✓` done and `✗` dropped. Creating a child with `--plan-item <id>`
|
|
164
|
+
requires a parent, fails before creating anything when the item is unknown
|
|
165
|
+
or not `todo`, and otherwise marks the item `started` with that child
|
|
166
|
+
linked. When the linked child's card becomes `done`, its item becomes
|
|
167
|
+
`done` too; a `failed` card leaves the item `started` for the manager to
|
|
168
|
+
decide. `ftown plan start <id> --session <child-id>` links an existing
|
|
169
|
+
child by hand, and `done`/`drop`/`todo` set the state directly. Every plan
|
|
170
|
+
change emits one `plan` tree event (`<id> <state>: <title>`, plus
|
|
171
|
+
` → ready: …` when items become ready). Exit codes: 0 ok, 1 error, 2 usage.
|
|
172
|
+
|
|
173
|
+
**Delegating work.** Delegate implementation to ftown child sessions (`ftown sessions create --parent ...`) rather than in-process subagents — only sessions appear on the board and dashboard (cards, events, digests, parent-first questions).
|
|
174
|
+
A child session may use its own subagents for reviews and lookups. Pick the CLI and model with `ftown quota --json` (see `references/quota.md`).
|
|
175
|
+
|
|
176
|
+
## Manager brief
|
|
177
|
+
|
|
178
|
+
If you coordinate children, set a brief at the start and add a note at each
|
|
179
|
+
milestone. The dashboard overview renders the brief and your children
|
|
180
|
+
automatically; you do not build a status page yourself.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
ftown brief set --file brief.md
|
|
184
|
+
ftown brief note "All workers spawned; waiting on the first results"
|
|
185
|
+
ftown brief show
|
|
186
|
+
```
|
|
187
|
+
|
|
128
188
|
## Human attention and pull requests
|
|
129
189
|
|
|
130
190
|
Use `ftown sessions attention --message "..."` when you need human input. It
|
|
@@ -134,6 +194,18 @@ remains opt-in and requires a supported hosted session. Repeating an unresolved
|
|
|
134
194
|
request updates its occurrence count; after an uncertain result, inspect the
|
|
135
195
|
existing request before retrying.
|
|
136
196
|
|
|
197
|
+
If you have a parent, your request goes to your parent first, not the human:
|
|
198
|
+
the parent receives one waking escalation mail with the question and the
|
|
199
|
+
commands to answer or escalate it, and the human sees it greyed as "with
|
|
200
|
+
parent" (and may still answer). The parent answers with
|
|
201
|
+
`ftown attention answer <child-id> <event-id> --option <n>` (or
|
|
202
|
+
`--text "<reply>"`) only from facts it holds — the plan, the agreed contract,
|
|
203
|
+
earlier human answers — and escalates preferences and irreversible choices
|
|
204
|
+
with `ftown attention escalate <child-id> <event-id> --note "<context>"`.
|
|
205
|
+
Only the parent raises a parent-routed question to the human. Use
|
|
206
|
+
`ftown sessions attention ... --to human` for things only the human can
|
|
207
|
+
provide, such as credentials or approvals.
|
|
208
|
+
|
|
137
209
|
### Questions with answers
|
|
138
210
|
|
|
139
211
|
Add `--option <label>` (repeatable) to ask a question the human answers in the
|
|
@@ -145,7 +217,8 @@ not cancel the question; check later with
|
|
|
145
217
|
event ID.
|
|
146
218
|
|
|
147
219
|
Delivery: a live waiter receives the answer; otherwise it arrives as ftown mail
|
|
148
|
-
from "You" starting with `[ftown attention answer id=...]`.
|
|
220
|
+
from "You" starting with `[ftown attention answer id=...]`. When the parent
|
|
221
|
+
answers, the same delivery applies but the mail sender is the parent session.
|
|
149
222
|
|
|
150
223
|
Claude Code `AskUserQuestion` calls inside an FTown session are answered from
|
|
151
224
|
the same inbox, and the terminal picker appears only after "Answer in terminal"
|
|
@@ -176,12 +249,14 @@ created or updated, before the completion notice: use `ftown sessions pr <URL>`.
|
|
|
176
249
|
Omit this step for tasks without a PR; never invent a PR or guess a session
|
|
177
250
|
identity.
|
|
178
251
|
|
|
179
|
-
Once the task is complete,
|
|
180
|
-
before the final handoff, using
|
|
252
|
+
Once the task is complete, a top-level session requests user attention **once
|
|
253
|
+
per completed task**, before the final handoff, using
|
|
254
|
+
`ftown sessions attention --title 'Work complete'
|
|
181
255
|
--message "<truthful outcome, checks and PR summary>"`.
|
|
182
256
|
Do not send a completion notice every turn; retain the uncertain-result guidance
|
|
183
|
-
above.
|
|
184
|
-
|
|
257
|
+
above. If you have a parent, do not raise "Work complete": set your card to
|
|
258
|
+
`done` and report your result to the parent as assigned; the parent raises one
|
|
259
|
+
completion notice for the whole job. See the completion example in
|
|
185
260
|
`references/sessions.md`.
|
|
186
261
|
|
|
187
262
|
## Cleanup
|
|
@@ -199,6 +274,14 @@ ftown sessions revive <archived-session-id>
|
|
|
199
274
|
Removal archives the session; revival creates a new session id and resumes its
|
|
200
275
|
agent conversation when supported. Preserve useful results before cleanup.
|
|
201
276
|
|
|
277
|
+
For children that write code, create them with their own worktree:
|
|
278
|
+
`ftown sessions create --parent --worktree [--branch <b>] [--base <ref>]`.
|
|
279
|
+
`ftown sessions remove <id>` retires a bridge-created worktree when it is
|
|
280
|
+
clean and merged or pushed; otherwise it keeps the worktree and says why.
|
|
281
|
+
`--keep-worktree` always keeps it, `--force-worktree` forces removal.
|
|
282
|
+
Inspect or clean up archived worktrees with `ftown worktrees list` and
|
|
283
|
+
`ftown worktrees prune`.
|
|
284
|
+
|
|
202
285
|
## Connection and compatibility
|
|
203
286
|
|
|
204
287
|
The commands read `~/.ftown/bridge.json`. Anyone who can read that file can
|
|
@@ -1,40 +1,43 @@
|
|
|
1
1
|
# Worker coordination examples
|
|
2
2
|
|
|
3
|
-
Read `../SKILL.md` for identity, reporting and cleanup
|
|
4
|
-
`sessions.md` for the command reference.
|
|
5
|
-
steps. Use an ordinary session with the requested task and parent link; no
|
|
6
|
-
special orchestrator mode is needed.
|
|
3
|
+
Read `../SKILL.md` for identity, reporting and cleanup, and
|
|
4
|
+
`sessions.md` for the command reference. No special orchestrator mode is needed.
|
|
7
5
|
|
|
8
6
|
List existing sessions before creating another worker. Give each worker a
|
|
9
|
-
bounded task, clear writable files and
|
|
10
|
-
`session.id
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
bounded task, clear writable files and checks; save the returned
|
|
8
|
+
`session.id`. Omit `--shell` to inherit your harness. Before picking a CLI,
|
|
9
|
+
model and effort, run `ftown quota --json` and follow `quota.md` (owner
|
|
10
|
+
tiers first); never route to `exhausted`.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
Keep the plan on the manager — the manager's plan, not a ticket system.
|
|
13
|
+
Add steps with `ftown plan add` (`--after`/`--priority` set precedence;
|
|
14
|
+
ready = every `after` done or dropped) and create each child with
|
|
15
|
+
`--plan-item`, so its item starts and completes with the child:
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
|
-
ftown
|
|
21
|
-
ftown
|
|
22
|
-
|
|
18
|
+
ftown brief set --file brief.md
|
|
19
|
+
ftown plan add "Fix the auth bug" --priority 1
|
|
20
|
+
ftown sessions create --parent --plan-item p1 --worktree --workdir /path/to/repo --name fix-auth --prompt "Fix auth; run checks."
|
|
23
21
|
ftown board --wait --until any-done --timeout 300
|
|
24
|
-
ftown
|
|
25
|
-
ftown harness mail send <worker-id> --type task "Check the expired-token case
|
|
22
|
+
ftown attention list
|
|
23
|
+
ftown harness mail send <worker-id> --type task "Check the expired-token case"
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
`ftown board --wait --until any-done
|
|
30
|
-
`
|
|
31
|
-
on the
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
Loop: keep the plan on the manager, create children with `--plan-item`,
|
|
27
|
+
`ftown board --wait --until any-done` (or `any-blocked`/`any-ready`/
|
|
28
|
+
`all-done`), act, repeat until all are done. Children keep cards current;
|
|
29
|
+
progress goes on the card, never as mail. A child's question comes to you
|
|
30
|
+
first: answer from facts you hold with `ftown attention answer`, or
|
|
31
|
+
escalate preferences and irreversible choices
|
|
32
|
+
with `ftown attention escalate --note`. Mail is for directed
|
|
33
|
+
instructions, results and escalations; read it with
|
|
34
|
+
`ftown harness mail read`. Each mail batch nudges at most once (a digest
|
|
35
|
+
may be appended). Use `ftown sessions screen <worker-id> --limit 100`
|
|
36
|
+
only to investigate. Parallel tasks need disjoint write
|
|
37
|
+
ownership and serialized shared resources. Children set their card to
|
|
38
|
+
done and report to the parent; you raise one "Work complete" notice
|
|
39
|
+
for the whole job.
|
|
38
40
|
|
|
39
|
-
After accepting a
|
|
40
|
-
|
|
41
|
+
After accepting a result, clean up with
|
|
42
|
+
`ftown sessions remove <worker-id>`, which retires a clean, merged or pushed
|
|
43
|
+
worktree and otherwise keeps it and says why.
|