ftown-bridge 0.11.2 → 0.13.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.
Files changed (45) hide show
  1. package/dist/centrifugo-client.d.ts +4 -1
  2. package/dist/centrifugo-client.js +27 -0
  3. package/dist/centrifugo-client.js.map +1 -1
  4. package/dist/codex-installer.js +8 -2
  5. package/dist/codex-installer.js.map +1 -1
  6. package/dist/create-ftown-session.d.ts +1 -0
  7. package/dist/create-ftown-session.js +4 -3
  8. package/dist/create-ftown-session.js.map +1 -1
  9. package/dist/ftown-sessions-cli.js +334 -0
  10. package/dist/ftown-sessions-cli.js.map +1 -1
  11. package/dist/index.js +167 -11
  12. package/dist/index.js.map +1 -1
  13. package/dist/install-ftown-skill.d.ts +2 -0
  14. package/dist/install-ftown-skill.js +39 -0
  15. package/dist/install-ftown-skill.js.map +1 -1
  16. package/dist/local-api-server.d.ts +10 -0
  17. package/dist/local-api-server.js +147 -0
  18. package/dist/local-api-server.js.map +1 -1
  19. package/dist/loop-run-store.d.ts +9 -0
  20. package/dist/loop-run-store.js +159 -0
  21. package/dist/loop-run-store.js.map +1 -0
  22. package/dist/loop-schedule.d.ts +18 -0
  23. package/dist/loop-schedule.js +35 -0
  24. package/dist/loop-schedule.js.map +1 -0
  25. package/dist/loop-scheduler.d.ts +148 -0
  26. package/dist/loop-scheduler.js +534 -0
  27. package/dist/loop-scheduler.js.map +1 -0
  28. package/dist/loop-store.d.ts +36 -0
  29. package/dist/loop-store.js +128 -0
  30. package/dist/loop-store.js.map +1 -0
  31. package/dist/loop-validation.d.ts +14 -0
  32. package/dist/loop-validation.js +95 -0
  33. package/dist/loop-validation.js.map +1 -0
  34. package/dist/types.d.ts +107 -2
  35. package/package.json +2 -1
  36. package/skills/ftown/SKILL.md +48 -0
  37. package/skills/ftown/agents/openai.yaml +4 -0
  38. package/skills/ftown/references/loops.md +88 -0
  39. package/skills/{ftown-orchestrator/SKILL.md → ftown/references/orchestrator.md} +22 -13
  40. package/skills/{ftown-sessions/SKILL.md → ftown/references/sessions.md} +22 -16
  41. package/skills/{ftown-workflows/SKILL.md → ftown/references/workflows.md} +13 -20
  42. package/skills/ftown/scripts/ftown +4 -0
  43. package/skills/ftown-sessions/scripts/ftown-sessions +0 -4
  44. package/skills/ftown-workflows/scripts/ftown-workflows +0 -4
  45. /package/skills/{ftown-workflows → ftown}/scripts/example.flow.mjs +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-scheduler.js","sourceRoot":"","sources":["../src/loop-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Loop, LoopDraft } from './types.js';
2
+ export declare function listLoops(): Loop[];
3
+ export declare function getLoop(id: string): Loop | undefined;
4
+ /**
5
+ * Mint a Loop from a client draft: fresh id/timestamps, zeroed counters, and a
6
+ * nextRunAt computed from now — even when created disabled, so re-enabling has a
7
+ * target. Throws (via computeNextRun) on a malformed cron expression.
8
+ */
9
+ export declare function createLoop(draft: LoopDraft): Loop;
10
+ /**
11
+ * Merge `patch` over an existing loop. id + createdAt are immutable; updatedAt is
12
+ * bumped. A schedule change recomputes nextRunAt from now (the old target is
13
+ * stale). Returns null when no loop has that id.
14
+ */
15
+ export declare function updateLoop(id: string, patch: Partial<LoopDraft>): Loop | null;
16
+ export declare function deleteLoop(id: string): boolean;
17
+ /** Insert a loop, or replace the existing one with the same id, in place. */
18
+ export declare function upsertLoop(loop: Loop): void;
19
+ /** The scheduler-owned runtime fields a tick is allowed to mutate. Everything
20
+ * else on a Loop (name, schedule, enabled, task, retention, …) is user-owned
21
+ * and edited exclusively through updateLoop. */
22
+ export type LoopRuntimeMutator = (loop: Loop) => void;
23
+ /**
24
+ * Atomically apply a scheduler-owned mutation: reload the loop FRESH from disk,
25
+ * apply `fn`, then save. Returns the merged loop, or null when the id no longer
26
+ * exists (deleted concurrently) — the caller then skips its publish so a deleted
27
+ * loop is never resurrected.
28
+ *
29
+ * The scheduler holds a detached Loop snapshot across long awaits (a 20s
30
+ * preflight, an in-process spawn). Writing that whole stale snapshot back would
31
+ * (a) resurrect a loop deleted mid-flight and (b) clobber a concurrent
32
+ * update_loop patch to user-owned fields. Because `fn` runs on the
33
+ * freshly-loaded record and touches only runtime fields, both hazards are gone:
34
+ * a delete wins (null), and an unrelated enabled/schedule/task patch survives.
35
+ */
36
+ export declare function mutateLoopRuntime(id: string, fn: LoopRuntimeMutator): Loop | null;
@@ -0,0 +1,128 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ import { computeNextRun } from './loop-schedule.js';
6
+ function loopsPath() {
7
+ return join(homedir(), '.ftown', 'loops.json');
8
+ }
9
+ /** Tolerant loader: returns { loops: [] } on a missing OR corrupt file. Never throws. */
10
+ function loadLoops() {
11
+ try {
12
+ const path = loopsPath();
13
+ if (!existsSync(path))
14
+ return { loops: [] };
15
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
16
+ return { loops: Array.isArray(parsed.loops) ? parsed.loops : [] };
17
+ }
18
+ catch {
19
+ return { loops: [] };
20
+ }
21
+ }
22
+ function saveLoops(data) {
23
+ mkdirSync(join(homedir(), '.ftown'), { recursive: true, mode: 0o700 });
24
+ const path = loopsPath();
25
+ const tmp = `${path}.tmp`;
26
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', { mode: 0o600 });
27
+ renameSync(tmp, path); // atomic
28
+ }
29
+ export function listLoops() {
30
+ return loadLoops().loops;
31
+ }
32
+ export function getLoop(id) {
33
+ return loadLoops().loops.find((loop) => loop.id === id);
34
+ }
35
+ /**
36
+ * Mint a Loop from a client draft: fresh id/timestamps, zeroed counters, and a
37
+ * nextRunAt computed from now — even when created disabled, so re-enabling has a
38
+ * target. Throws (via computeNextRun) on a malformed cron expression.
39
+ */
40
+ export function createLoop(draft) {
41
+ const data = loadLoops();
42
+ const now = new Date();
43
+ const loop = {
44
+ ...draft,
45
+ id: uuidv4(),
46
+ createdAt: now.toISOString(),
47
+ updatedAt: now.toISOString(),
48
+ nextRunAt: new Date(computeNextRun(draft.schedule, now.getTime())).toISOString(),
49
+ runCount: 0,
50
+ skipCount: 0,
51
+ };
52
+ data.loops.push(loop);
53
+ saveLoops(data);
54
+ return loop;
55
+ }
56
+ /**
57
+ * Merge `patch` over an existing loop. id + createdAt are immutable; updatedAt is
58
+ * bumped. A schedule change recomputes nextRunAt from now (the old target is
59
+ * stale). Returns null when no loop has that id.
60
+ */
61
+ export function updateLoop(id, patch) {
62
+ const data = loadLoops();
63
+ const index = data.loops.findIndex((loop) => loop.id === id);
64
+ if (index === -1)
65
+ return null;
66
+ const existing = data.loops[index];
67
+ const updated = {
68
+ ...existing,
69
+ ...patch,
70
+ id: existing.id,
71
+ // A loop is owned by exactly one bridge (the one it is persisted on); its
72
+ // bridgeId is the RPC routing key. Never let a patch move it — a stray
73
+ // patch.bridgeId would otherwise desync the record from the routing guard.
74
+ bridgeId: existing.bridgeId,
75
+ createdAt: existing.createdAt,
76
+ updatedAt: new Date().toISOString(),
77
+ };
78
+ if (patch.schedule) {
79
+ updated.nextRunAt = new Date(computeNextRun(updated.schedule, Date.now())).toISOString();
80
+ }
81
+ data.loops[index] = updated;
82
+ saveLoops(data);
83
+ return updated;
84
+ }
85
+ export function deleteLoop(id) {
86
+ const data = loadLoops();
87
+ const remaining = data.loops.filter((loop) => loop.id !== id);
88
+ if (remaining.length === data.loops.length)
89
+ return false;
90
+ data.loops = remaining;
91
+ saveLoops(data);
92
+ return true;
93
+ }
94
+ /** Insert a loop, or replace the existing one with the same id, in place. */
95
+ export function upsertLoop(loop) {
96
+ const data = loadLoops();
97
+ const index = data.loops.findIndex((existing) => existing.id === loop.id);
98
+ if (index === -1)
99
+ data.loops.push(loop);
100
+ else
101
+ data.loops[index] = loop;
102
+ saveLoops(data);
103
+ }
104
+ /**
105
+ * Atomically apply a scheduler-owned mutation: reload the loop FRESH from disk,
106
+ * apply `fn`, then save. Returns the merged loop, or null when the id no longer
107
+ * exists (deleted concurrently) — the caller then skips its publish so a deleted
108
+ * loop is never resurrected.
109
+ *
110
+ * The scheduler holds a detached Loop snapshot across long awaits (a 20s
111
+ * preflight, an in-process spawn). Writing that whole stale snapshot back would
112
+ * (a) resurrect a loop deleted mid-flight and (b) clobber a concurrent
113
+ * update_loop patch to user-owned fields. Because `fn` runs on the
114
+ * freshly-loaded record and touches only runtime fields, both hazards are gone:
115
+ * a delete wins (null), and an unrelated enabled/schedule/task patch survives.
116
+ */
117
+ export function mutateLoopRuntime(id, fn) {
118
+ const data = loadLoops();
119
+ const index = data.loops.findIndex((loop) => loop.id === id);
120
+ if (index === -1)
121
+ return null;
122
+ const loop = data.loops[index];
123
+ fn(loop);
124
+ data.loops[index] = loop;
125
+ saveLoops(data);
126
+ return loop;
127
+ }
128
+ //# sourceMappingURL=loop-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-store.js","sourceRoot":"","sources":["../src/loop-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { LoopDraft, LoopSchedule } from './types.js';
2
+ /**
3
+ * Loop payload validation, extracted from index.ts so it is unit-testable
4
+ * without importing the CLI entrypoint (which calls program.parse() on load).
5
+ * Every validator returns an error string, or null when the input is valid.
6
+ */
7
+ export declare const LOOP_HARNESSES: ReadonlySet<string>;
8
+ /** Validate a loop schedule (interval floor + cron parseability). */
9
+ export declare function validateSchedule(schedule: LoopSchedule | undefined): string | null;
10
+ export declare function validateRetention(retention: LoopDraft['retention'] | undefined): string | null;
11
+ /** Full-draft validation for create_loop. */
12
+ export declare function validateLoopDraft(draft: Partial<LoopDraft>): string | null;
13
+ /** Partial validation for update_loop — only the fields present in the patch. */
14
+ export declare function validateLoopPatch(patch: Partial<LoopDraft>): string | null;
@@ -0,0 +1,95 @@
1
+ import { computeNextRun } from './loop-schedule.js';
2
+ /**
3
+ * Loop payload validation, extracted from index.ts so it is unit-testable
4
+ * without importing the CLI entrypoint (which calls program.parse() on load).
5
+ * Every validator returns an error string, or null when the input is valid.
6
+ */
7
+ export const LOOP_HARNESSES = new Set([
8
+ 'claude',
9
+ 'cursor',
10
+ 'codex',
11
+ 'opencode',
12
+ 'shell',
13
+ ]);
14
+ /** Validate a loop schedule (interval floor + cron parseability). */
15
+ export function validateSchedule(schedule) {
16
+ if (!schedule || typeof schedule !== 'object')
17
+ return 'schedule is required';
18
+ if (schedule.kind === 'interval') {
19
+ if (typeof schedule.everyMs !== 'number' || !Number.isFinite(schedule.everyMs) || schedule.everyMs < 1000) {
20
+ return 'interval everyMs must be a finite number >= 1000';
21
+ }
22
+ return null;
23
+ }
24
+ if (schedule.kind === 'cron') {
25
+ if (typeof schedule.expression !== 'string' || !schedule.expression.trim()) {
26
+ return 'cron expression is required';
27
+ }
28
+ try {
29
+ computeNextRun(schedule, Date.now());
30
+ }
31
+ catch {
32
+ return `Invalid cron expression: ${schedule.expression}`;
33
+ }
34
+ return null;
35
+ }
36
+ return 'schedule.kind must be "interval" or "cron"';
37
+ }
38
+ export function validateRetention(retention) {
39
+ const value = retention?.autoClearAfterRuns;
40
+ const ok = value === null || (typeof value === 'number' && Number.isFinite(value) && value >= 0);
41
+ return retention && ok ? null : 'retention.autoClearAfterRuns must be null or a non-negative number';
42
+ }
43
+ /** Full-draft validation for create_loop. */
44
+ export function validateLoopDraft(draft) {
45
+ if (!draft || typeof draft !== 'object')
46
+ return 'Invalid loop payload';
47
+ // Required so a create can never slip past the bridge-routing guard and get
48
+ // duplicated across every connected bridge (create mints a fresh id).
49
+ if (typeof draft.bridgeId !== 'string' || !draft.bridgeId.trim())
50
+ return 'bridgeId is required';
51
+ if (typeof draft.name !== 'string' || !draft.name.trim())
52
+ return 'Loop name is required';
53
+ if (typeof draft.task !== 'string' || !draft.task.trim())
54
+ return 'Loop task is required';
55
+ if (typeof draft.harness !== 'string' || !LOOP_HARNESSES.has(draft.harness)) {
56
+ return `Invalid harness: ${String(draft.harness)}`;
57
+ }
58
+ if (draft.overlapPolicy !== 'skip' && draft.overlapPolicy !== 'allow') {
59
+ return 'overlapPolicy must be "skip" or "allow"';
60
+ }
61
+ if (typeof draft.enabled !== 'boolean')
62
+ return 'enabled must be a boolean';
63
+ const retentionError = validateRetention(draft.retention);
64
+ if (retentionError)
65
+ return retentionError;
66
+ return validateSchedule(draft.schedule);
67
+ }
68
+ /** Partial validation for update_loop — only the fields present in the patch. */
69
+ export function validateLoopPatch(patch) {
70
+ if (!patch || typeof patch !== 'object')
71
+ return 'Invalid patch';
72
+ if ('name' in patch && (typeof patch.name !== 'string' || !patch.name.trim())) {
73
+ return 'Loop name must be a non-empty string';
74
+ }
75
+ if ('task' in patch && (typeof patch.task !== 'string' || !patch.task.trim())) {
76
+ return 'Loop task must be a non-empty string';
77
+ }
78
+ if ('harness' in patch && (typeof patch.harness !== 'string' || !LOOP_HARNESSES.has(patch.harness))) {
79
+ return `Invalid harness: ${String(patch.harness)}`;
80
+ }
81
+ if ('overlapPolicy' in patch && patch.overlapPolicy !== 'skip' && patch.overlapPolicy !== 'allow') {
82
+ return 'overlapPolicy must be "skip" or "allow"';
83
+ }
84
+ if ('enabled' in patch && typeof patch.enabled !== 'boolean')
85
+ return 'enabled must be a boolean';
86
+ if ('retention' in patch) {
87
+ const retentionError = validateRetention(patch.retention);
88
+ if (retentionError)
89
+ return retentionError;
90
+ }
91
+ if ('schedule' in patch)
92
+ return validateSchedule(patch.schedule);
93
+ return null;
94
+ }
95
+ //# sourceMappingURL=loop-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-validation.js","sourceRoot":"","sources":["../src/loop-validation.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -19,12 +19,94 @@ export interface Session {
19
19
  parentSessionId?: string;
20
20
  runtime?: SessionRuntime;
21
21
  errorReason?: string;
22
+ loopId?: string;
22
23
  }
23
24
  export type SessionStatus = 'pending' | 'running' | 'completed' | 'error';
24
25
  /** Tombstone written to <dataDir>/archive.jsonl when a session is removed. */
25
26
  export interface ArchivedSession extends Session {
26
27
  removedAt: string;
27
28
  }
29
+ export type LoopHarness = 'claude' | 'cursor' | 'codex' | 'opencode' | 'shell';
30
+ export type LoopRunStatus = 'ok' | 'error' | 'running' | 'skipped';
31
+ export type LoopSchedule = {
32
+ kind: 'interval';
33
+ everyMs: number;
34
+ } | {
35
+ kind: 'cron';
36
+ expression: string;
37
+ tz?: string;
38
+ };
39
+ export interface LoopFlight {
40
+ command: string;
41
+ timeoutMs?: number;
42
+ }
43
+ export interface LoopPostflight {
44
+ command: string;
45
+ timeoutMs?: number;
46
+ /** Run postflight even when the fire was preflight-skipped. Default false. */
47
+ runOnSkip?: boolean;
48
+ }
49
+ export interface LoopRetention {
50
+ /** Keep newest N run-sessions; prune older ones. null = keep all. Default 10. */
51
+ autoClearAfterRuns: number | null;
52
+ }
53
+ /** Client-authored fields (create/edit form). */
54
+ export interface LoopDraft {
55
+ name: string;
56
+ bridgeId: string;
57
+ schedule: LoopSchedule;
58
+ harness: LoopHarness;
59
+ workdir?: string;
60
+ task: string;
61
+ model?: string;
62
+ enabled: boolean;
63
+ overlapPolicy: 'skip' | 'allow';
64
+ retention: LoopRetention;
65
+ preflight?: LoopFlight;
66
+ postflight?: LoopPostflight;
67
+ /** Optional deterministic backstop: force-stop + mark 'error' if a flight runs longer. */
68
+ maxRuntimeMs?: number;
69
+ }
70
+ /** Full server-authoritative record (LoopDraft + runtime state). */
71
+ export interface Loop extends LoopDraft {
72
+ id: string;
73
+ createdAt: string;
74
+ updatedAt: string;
75
+ lastRunAt?: string;
76
+ nextRunAt?: string;
77
+ lastStatus?: LoopRunStatus;
78
+ lastSessionId?: string;
79
+ runCount: number;
80
+ skipCount: number;
81
+ /** Transient manual-fire flag; set by run_loop_now, cleared on the next tick. */
82
+ runNowRequested?: boolean;
83
+ }
84
+ /** Legacy shape: older bridges exposed loop runs as sessions tagged with loopId. */
85
+ export type LoopRun = Session & {
86
+ loopId: string;
87
+ };
88
+ export interface LoopRunRecord {
89
+ /** Stable UI id. For spawned runs this is the session id; skipped runs mint their own id. */
90
+ id: string;
91
+ loopId: string;
92
+ bridgeId: string;
93
+ sessionId?: string;
94
+ name: string;
95
+ status: LoopRunStatus;
96
+ startedAt: string;
97
+ updatedAt: string;
98
+ finishedAt?: string;
99
+ durationMs?: number;
100
+ harness?: LoopHarness;
101
+ workdir?: string;
102
+ task?: string;
103
+ model?: string;
104
+ sessionStatus?: SessionStatus;
105
+ errorReason?: string;
106
+ logTail?: string;
107
+ logBytes?: number;
108
+ logTruncated?: boolean;
109
+ }
28
110
  /** Inter-agent mail stored in <dataDir>/sessions/<id>/inbox.jsonl. */
29
111
  export interface MailMessage {
30
112
  id: string;
@@ -52,7 +134,7 @@ export interface Command {
52
134
  payload: CommandPayload;
53
135
  requestId: string;
54
136
  }
55
- export type CommandType = 'create_session' | 'stop_session' | 'list_sessions' | 'get_history' | 'retry_session' | 'send_message' | 'rename_session' | 'remove_session' | 'bridge_exec' | 'clear_terminal' | 'update_session_parent';
137
+ export type CommandType = 'create_session' | 'stop_session' | 'list_sessions' | 'get_history' | 'retry_session' | 'send_message' | 'rename_session' | 'remove_session' | 'bridge_exec' | 'clear_terminal' | 'update_session_parent' | 'create_loop' | 'list_loops' | 'update_loop' | 'delete_loop' | 'run_loop_now' | 'get_loop_runs';
56
138
  export interface CreateSessionPayload {
57
139
  command: string;
58
140
  prompt?: string;
@@ -101,7 +183,30 @@ export interface RemoveSessionPayload {
101
183
  export interface ClearTerminalPayload {
102
184
  sessionId: string;
103
185
  }
104
- export type CommandPayload = CreateSessionPayload | StopSessionPayload | GetHistoryPayload | RenameSessionPayload | RemoveSessionPayload | BridgeExecPayload | ClearTerminalPayload | UpdateSessionParentPayload | Record<string, unknown>;
186
+ export interface CreateLoopPayload extends LoopDraft {
187
+ bridgeId: string;
188
+ }
189
+ export interface ListLoopsPayload {
190
+ bridgeId?: string;
191
+ }
192
+ export interface UpdateLoopPayload {
193
+ bridgeId: string;
194
+ loopId: string;
195
+ patch: Partial<LoopDraft>;
196
+ }
197
+ export interface DeleteLoopPayload {
198
+ bridgeId: string;
199
+ loopId: string;
200
+ }
201
+ export interface RunLoopNowPayload {
202
+ bridgeId: string;
203
+ loopId: string;
204
+ }
205
+ export interface GetLoopRunsPayload {
206
+ bridgeId: string;
207
+ loopId: string;
208
+ }
209
+ export type CommandPayload = CreateSessionPayload | StopSessionPayload | GetHistoryPayload | RenameSessionPayload | RemoveSessionPayload | BridgeExecPayload | ClearTerminalPayload | UpdateSessionParentPayload | CreateLoopPayload | ListLoopsPayload | UpdateLoopPayload | DeleteLoopPayload | RunLoopNowPayload | GetLoopRunsPayload | Record<string, unknown>;
105
210
  export interface CommandResponse {
106
211
  requestId: string;
107
212
  success: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftown-bridge",
3
- "version": "0.11.2",
3
+ "version": "0.13.0",
4
4
  "description": "CLI bridge for ftown — generic PTY-over-Centrifugo relay",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,6 +48,7 @@
48
48
  "@xterm/headless": "^6.0.0",
49
49
  "centrifuge": "^5.2.2",
50
50
  "commander": "^13.1.0",
51
+ "cron-parser": "^4.9.0",
51
52
  "node-pty": "^1.2.0-beta.12",
52
53
  "uuid": "^11.1.0",
53
54
  "ws": "^8.18.0"
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: ftown
3
+ description: >-
4
+ Control ftown from inside agent sessions: create/list/read/drive sibling
5
+ sessions, send ftown mail, manage recurring scheduled loops, coordinate worker
6
+ agents, and run deterministic ftown-workflows. Use when a task mentions ftown,
7
+ ftown sessions, bridge sessions, child/sibling agents, mail/inbox, scheduled
8
+ loops, recurring agent runs, orchestration, fan-out, or ftown-workflows.
9
+ ---
10
+
11
+ # ftown
12
+
13
+ Use the local bridge CLIs installed under `~/.ftown`. They read
14
+ `~/.ftown/bridge.json`; anyone who can read that file can control the bridge's
15
+ sessions and loops.
16
+
17
+ ## Pick The Reference
18
+
19
+ - Session control, mail, terminal output, archive/revive, or local API:
20
+ read `references/sessions.md`.
21
+ - Recurring scheduled work, cron/interval loops, loop run history:
22
+ read `references/loops.md`.
23
+ - Ad-hoc multi-agent coordination from an orchestrator session:
24
+ read `references/orchestrator.md`.
25
+ - Scripted, resumable, deterministic multi-session fan-out:
26
+ read `references/workflows.md`.
27
+
28
+ ## Quick Commands
29
+
30
+ ```bash
31
+ # Sessions
32
+ ~/.ftown/ftown-sessions list
33
+ ~/.ftown/ftown-sessions create --shell codex --prompt "Run tests" --workdir "$PWD" --parent
34
+ ~/.ftown/ftown-sessions screen <session-id> --limit 200
35
+ ~/.ftown/ftown-sessions tell <session-id> --type task "continue with the API client"
36
+
37
+ # Loops
38
+ ~/.ftown/ftown-sessions loops
39
+ ~/.ftown/ftown-sessions loop create --name repo-watch --every 30m --shell codex --workdir "$PWD" --task "Review recent changes"
40
+ ~/.ftown/ftown-sessions loop run <loop-id>
41
+
42
+ # Workflows
43
+ ~/.ftown/ftown-workflows run path/to/script.mjs --workdir "$PWD"
44
+ ```
45
+
46
+ Prefer loops for unattended recurrence. Prefer workflows for deterministic
47
+ control flow inside one run. Prefer orchestrator guidance for ad-hoc
48
+ human-in-the-loop multi-agent coordination.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "ftown"
3
+ short_description: "Control ftown sessions, loops, orchestration, and workflows."
4
+ default_prompt: "Use ftown to manage sessions, loops, or workflows."
@@ -0,0 +1,88 @@
1
+ # ftown scheduled loops
2
+
3
+ Use this skill to manage bridge-owned recurring session producers. Each loop
4
+ stores a schedule, harness, task prompt, optional shell preflight/postflight
5
+ hooks, overlap policy, and retention. Every fire creates a normal ftown session
6
+ tagged with `loopId` and grouped under the loop in the dashboard.
7
+
8
+ Prefer loops for unattended recurrence over sleeping/polling inside a long-lived
9
+ agent. Prefer `ftown-workflows` for deterministic control flow inside one run.
10
+
11
+ ## CLI
12
+
13
+ The bridge installs loop commands through `~/.ftown/ftown-sessions loop ...`.
14
+ The unified skill also bundles `scripts/ftown`, which delegates to the top-level
15
+ `~/.ftown/ftown` dispatcher.
16
+
17
+ ```bash
18
+ CLI=~/.ftown/ftown-sessions
19
+
20
+ # List loops
21
+ $CLI loops
22
+ $CLI loop list --plain
23
+
24
+ # Create an interval loop
25
+ $CLI loop create \
26
+ --name repo-watch \
27
+ --every 30m \
28
+ --shell codex \
29
+ --workdir /path/to/repo \
30
+ --task "Inspect recent changes, run the focused checks, and report issues" \
31
+ --retention 10
32
+
33
+ # Create a cron loop
34
+ $CLI loop create \
35
+ --name weekday-triage \
36
+ --cron "0 9 * * 1-5" \
37
+ --tz America/New_York \
38
+ --task "Triage new issues and summarize priority"
39
+ ```
40
+
41
+ ## Common Operations
42
+
43
+ ```bash
44
+ # Inspect
45
+ ~/.ftown/ftown-sessions loop get <loop-id>
46
+ ~/.ftown/ftown-sessions loop runs <loop-id>
47
+
48
+ # Manual fire; skip-policy loops refuse this while a prior run is still alive
49
+ ~/.ftown/ftown-sessions loop run <loop-id>
50
+
51
+ # Pause / resume
52
+ ~/.ftown/ftown-sessions loop pause <loop-id>
53
+ ~/.ftown/ftown-sessions loop resume <loop-id>
54
+
55
+ # Update schedule, prompt, harness, retention, or runtime guard
56
+ ~/.ftown/ftown-sessions loop update <loop-id> --every 1h --task "updated task"
57
+ ~/.ftown/ftown-sessions loop update <loop-id> --retention all
58
+ ~/.ftown/ftown-sessions loop update <loop-id> --max-runtime 20m
59
+
60
+ # Delete. Any in-flight run owned by the loop is stopped by the bridge.
61
+ ~/.ftown/ftown-sessions loop delete <loop-id>
62
+ ```
63
+
64
+ ## Options
65
+
66
+ | Flag | Meaning |
67
+ | --- | --- |
68
+ | `--every <duration>` | Interval schedule such as `30s`, `5m`, `2h`, `1d`. Minimum is `1s`. |
69
+ | `--cron <expr>` / `--tz <zone>` | Cron schedule with optional IANA timezone. |
70
+ | `--shell <type>` | `claude`, `cursor`, `codex`, `opencode`, or `shell`. |
71
+ | `--workdir <path>` | Working directory for each run. |
72
+ | `--model <name>` | Harness model override when supported. |
73
+ | `--disabled` / `--enabled` | Create/update enabled state. |
74
+ | `--allow-overlap` / `--skip-overlap` | Allow concurrent runs, or skip while the previous run is alive. Default is skip. |
75
+ | `--retention <n|all>` | Keep newest N run sessions, or keep all. Default create value is `10`. |
76
+ | `--preflight <cmd>` | Shell guard before a run. Non-zero exit records `skipped` and spawns no session. |
77
+ | `--postflight <cmd>` | Shell hook after a run. Receives `FTOWN_RUN_STATUS`, `FTOWN_RUN_SESSION_ID`, and `FTOWN_RUN_OUTPUT`. |
78
+ | `--postflight-on-skip` | Also run postflight after a preflight skip. |
79
+ | `--max-runtime <duration>` | Force-stop a run and mark the loop error after this duration. |
80
+
81
+ ## Notes
82
+
83
+ - `loop run` sets a one-shot manual request. It bypasses the enabled flag but
84
+ still honors skip-overlap when the prior run is alive.
85
+ - `{{preflight}}` in the task prompt is replaced with preflight stdout, and the
86
+ same stdout is available to the run as `FTOWN_PREFLIGHT_OUTPUT`.
87
+ - Loop state persists in `~/.ftown/loops.json` on the bridge machine. Anyone who
88
+ can read `~/.ftown/bridge.json` can control loops through the local API.