ftown-bridge 0.3.14 → 0.3.16
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/bin/ftown-sessions +5 -0
- package/dist/agent-commands.d.ts +17 -0
- package/dist/agent-commands.js +41 -0
- package/dist/agent-commands.js.map +1 -0
- package/dist/claude-runner.js +5 -1
- package/dist/claude-runner.js.map +1 -1
- package/dist/create-ftown-session.d.ts +33 -0
- package/dist/create-ftown-session.js +81 -0
- package/dist/create-ftown-session.js.map +1 -0
- package/dist/cursor-hook-installer.d.ts +3 -0
- package/dist/cursor-hook-installer.js +22 -6
- package/dist/cursor-hook-installer.js.map +1 -1
- package/dist/ftown-sessions-cli.d.ts +2 -0
- package/dist/ftown-sessions-cli.js +223 -0
- package/dist/ftown-sessions-cli.js.map +1 -0
- package/dist/harness-cli.d.ts +2 -0
- package/dist/harness-cli.js +432 -0
- package/dist/harness-cli.js.map +1 -0
- package/dist/harness-format.d.ts +3 -0
- package/dist/harness-format.js +14 -0
- package/dist/harness-format.js.map +1 -0
- package/dist/harness-installer.d.ts +16 -0
- package/dist/harness-installer.js +108 -0
- package/dist/harness-installer.js.map +1 -0
- package/dist/hook-installer.js +2 -2
- package/dist/hook-installer.js.map +1 -1
- package/dist/index.js +59 -27
- package/dist/index.js.map +1 -1
- package/dist/install-ftown-cli.d.ts +3 -0
- package/dist/install-ftown-cli.js +20 -0
- package/dist/install-ftown-cli.js.map +1 -0
- package/dist/install-ftown-skill.d.ts +4 -0
- package/dist/install-ftown-skill.js +50 -0
- package/dist/install-ftown-skill.js.map +1 -0
- package/dist/install-notify-script.d.ts +6 -0
- package/dist/install-notify-script.js +23 -0
- package/dist/install-notify-script.js.map +1 -0
- package/dist/local-api-server.d.ts +3 -0
- package/dist/local-api-server.js +56 -3
- package/dist/local-api-server.js.map +1 -1
- package/dist/session-registry.d.ts +4 -0
- package/dist/session-registry.js +72 -0
- package/dist/session-registry.js.map +1 -0
- package/dist/terminal-manager.d.ts +3 -1
- package/dist/terminal-manager.js +15 -6
- package/dist/terminal-manager.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/xterm-theme.d.ts +30 -0
- package/dist/xterm-theme.js +38 -0
- package/dist/xterm-theme.js.map +1 -0
- package/hooks/notify.sh +36 -4
- package/package.json +9 -4
- package/skills/bridge-harness/SKILL.md +43 -0
- package/skills/ftown-sessions/SKILL.md +101 -0
- package/skills/ftown-sessions/scripts/ftown-sessions +4 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
const REGISTRY_PATH = join(homedir(), '.ftown', 'session-registry.json');
|
|
6
|
+
function loadRegistry() {
|
|
7
|
+
try {
|
|
8
|
+
if (!existsSync(REGISTRY_PATH)) {
|
|
9
|
+
return { byWorkspace: {}, byConversation: {} };
|
|
10
|
+
}
|
|
11
|
+
const parsed = JSON.parse(readFileSync(REGISTRY_PATH, 'utf8'));
|
|
12
|
+
return {
|
|
13
|
+
byWorkspace: parsed.byWorkspace ?? {},
|
|
14
|
+
byConversation: parsed.byConversation ?? {},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { byWorkspace: {}, byConversation: {} };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function saveRegistry(data) {
|
|
22
|
+
mkdirSync(join(homedir(), '.ftown'), { recursive: true, mode: 0o700 });
|
|
23
|
+
const tmp = `${REGISTRY_PATH}.tmp`;
|
|
24
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', { mode: 0o600 });
|
|
25
|
+
renameSync(tmp, REGISTRY_PATH);
|
|
26
|
+
}
|
|
27
|
+
export function registerSessionWorkspace(sessionId, workingDir) {
|
|
28
|
+
if (!workingDir?.trim())
|
|
29
|
+
return;
|
|
30
|
+
const data = loadRegistry();
|
|
31
|
+
data.byWorkspace[resolve(workingDir.trim())] = sessionId;
|
|
32
|
+
saveRegistry(data);
|
|
33
|
+
}
|
|
34
|
+
export function registerSessionConversation(sessionId, conversationId) {
|
|
35
|
+
if (!conversationId)
|
|
36
|
+
return;
|
|
37
|
+
const data = loadRegistry();
|
|
38
|
+
data.byConversation[conversationId] = sessionId;
|
|
39
|
+
saveRegistry(data);
|
|
40
|
+
}
|
|
41
|
+
export function unregisterSession(sessionId) {
|
|
42
|
+
const data = loadRegistry();
|
|
43
|
+
for (const [path, id] of Object.entries(data.byWorkspace)) {
|
|
44
|
+
if (id === sessionId)
|
|
45
|
+
delete data.byWorkspace[path];
|
|
46
|
+
}
|
|
47
|
+
for (const [conv, id] of Object.entries(data.byConversation)) {
|
|
48
|
+
if (id === sessionId)
|
|
49
|
+
delete data.byConversation[conv];
|
|
50
|
+
}
|
|
51
|
+
saveRegistry(data);
|
|
52
|
+
}
|
|
53
|
+
export function resolveSessionIdFromHookPayload(payload) {
|
|
54
|
+
const ftownId = payload.ftown_session_id;
|
|
55
|
+
if (typeof ftownId === 'string' && ftownId)
|
|
56
|
+
return ftownId;
|
|
57
|
+
const data = loadRegistry();
|
|
58
|
+
const conversationId = payload.conversation_id;
|
|
59
|
+
if (typeof conversationId === 'string' && conversationId) {
|
|
60
|
+
const byConv = data.byConversation[conversationId];
|
|
61
|
+
if (byConv)
|
|
62
|
+
return byConv;
|
|
63
|
+
}
|
|
64
|
+
const roots = payload.workspace_roots;
|
|
65
|
+
if (Array.isArray(roots) && typeof roots[0] === 'string' && roots[0]) {
|
|
66
|
+
const byWs = data.byWorkspace[resolve(roots[0])];
|
|
67
|
+
if (byWs)
|
|
68
|
+
return byWs;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=session-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../src/session-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@ export interface ScreenData {
|
|
|
7
7
|
export interface GrepMatch {
|
|
8
8
|
lineNumber: number;
|
|
9
9
|
text: string;
|
|
10
|
+
before?: string[];
|
|
11
|
+
after?: string[];
|
|
10
12
|
}
|
|
11
13
|
export interface GrepResult {
|
|
12
14
|
matches: GrepMatch[];
|
|
@@ -25,7 +27,7 @@ export declare class TerminalManager {
|
|
|
25
27
|
getRawBuffer(sessionId: string): string;
|
|
26
28
|
serialize(sessionId: string, scrollback?: number): string;
|
|
27
29
|
getScreen(sessionId: string, offset?: number, limit?: number): ScreenData;
|
|
28
|
-
grep(sessionId: string, pattern: string, offset?: number, limit?: number): GrepResult;
|
|
30
|
+
grep(sessionId: string, pattern: string, offset?: number, limit?: number, contextLines?: number): GrepResult;
|
|
29
31
|
replay(sessionId: string, rawLog: string): void;
|
|
30
32
|
resize(sessionId: string, cols: number, rows: number): void;
|
|
31
33
|
destroy(sessionId: string): void;
|
package/dist/terminal-manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import xtermHeadless from '@xterm/headless';
|
|
2
2
|
import serializeAddon from '@xterm/addon-serialize';
|
|
3
|
+
import { FTOWN_XTERM_THEME } from './xterm-theme.js';
|
|
3
4
|
const { Terminal } = xtermHeadless;
|
|
4
5
|
const { SerializeAddon } = serializeAddon;
|
|
5
6
|
export class TerminalManager {
|
|
@@ -18,6 +19,7 @@ export class TerminalManager {
|
|
|
18
19
|
rows: 40,
|
|
19
20
|
scrollback: this.scrollback,
|
|
20
21
|
allowProposedApi: true,
|
|
22
|
+
theme: FTOWN_XTERM_THEME,
|
|
21
23
|
});
|
|
22
24
|
const serializer = new SerializeAddon();
|
|
23
25
|
terminal.loadAddon(serializer);
|
|
@@ -67,7 +69,7 @@ export class TerminalManager {
|
|
|
67
69
|
const lines = allLines.slice(offset, offset + limit);
|
|
68
70
|
return { lines, totalLines, offset, limit };
|
|
69
71
|
}
|
|
70
|
-
grep(sessionId, pattern, offset = 0, limit = 1000) {
|
|
72
|
+
grep(sessionId, pattern, offset = 0, limit = 1000, contextLines = 0) {
|
|
71
73
|
const managed = this.terminals.get(sessionId);
|
|
72
74
|
if (!managed) {
|
|
73
75
|
return { matches: [], totalMatches: 0, offset, limit };
|
|
@@ -80,14 +82,21 @@ export class TerminalManager {
|
|
|
80
82
|
return { matches: [], totalMatches: 0, offset, limit };
|
|
81
83
|
}
|
|
82
84
|
const totalLines = managed.terminal.buffer.active.length;
|
|
83
|
-
const
|
|
85
|
+
const lineTexts = [];
|
|
84
86
|
for (let i = 0; i < totalLines; i++) {
|
|
85
87
|
const line = managed.terminal.buffer.active.getLine(i);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
lineTexts.push(line ? line.translateToString(true) : '');
|
|
89
|
+
}
|
|
90
|
+
const matches = [];
|
|
91
|
+
const ctx = Math.max(0, Math.min(10, contextLines));
|
|
92
|
+
for (let i = 0; i < lineTexts.length; i++) {
|
|
93
|
+
if (regex.test(lineTexts[i])) {
|
|
94
|
+
const entry = { lineNumber: i + 1, text: lineTexts[i] };
|
|
95
|
+
if (ctx > 0) {
|
|
96
|
+
entry.before = lineTexts.slice(Math.max(0, i - ctx), i);
|
|
97
|
+
entry.after = lineTexts.slice(i + 1, Math.min(lineTexts.length, i + 1 + ctx));
|
|
90
98
|
}
|
|
99
|
+
matches.push(entry);
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
const totalMatches = matches.length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-manager.js","sourceRoot":"","sources":["../src/terminal-manager.ts"],"names":[],"mappings":"AAAA,OAAO,aAAgD,MAAM,iBAAiB,CAAC;AAC/E,OAAO,cAAc,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal-manager.js","sourceRoot":"","sources":["../src/terminal-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface Command {
|
|
|
34
34
|
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';
|
|
35
35
|
export interface CreateSessionPayload {
|
|
36
36
|
command: string;
|
|
37
|
+
prompt?: string;
|
|
37
38
|
name?: string;
|
|
38
39
|
workingDir?: string;
|
|
39
40
|
bridgeId?: string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Match ui/src/components/Terminal.tsx so serialize/replay preserves agent colors. */
|
|
2
|
+
export declare const FTOWN_XTERM_THEME: {
|
|
3
|
+
readonly background: "#07070a";
|
|
4
|
+
readonly foreground: "#e8e8f0";
|
|
5
|
+
readonly cursor: "#00ff88";
|
|
6
|
+
readonly cursorAccent: "#07070a";
|
|
7
|
+
readonly selectionBackground: "rgba(0, 255, 136, 0.15)";
|
|
8
|
+
readonly black: "#0a0a0d";
|
|
9
|
+
readonly red: "#ff4466";
|
|
10
|
+
readonly green: "#00ff88";
|
|
11
|
+
readonly yellow: "#ffaa00";
|
|
12
|
+
readonly blue: "#44aaff";
|
|
13
|
+
readonly magenta: "#cc66ff";
|
|
14
|
+
readonly cyan: "#00ddff";
|
|
15
|
+
readonly white: "#c8c8d8";
|
|
16
|
+
readonly brightBlack: "#44444f";
|
|
17
|
+
readonly brightRed: "#ff6680";
|
|
18
|
+
readonly brightGreen: "#33ffaa";
|
|
19
|
+
readonly brightYellow: "#ffcc44";
|
|
20
|
+
readonly brightBlue: "#66bbff";
|
|
21
|
+
readonly brightMagenta: "#dd88ff";
|
|
22
|
+
readonly brightCyan: "#44eeff";
|
|
23
|
+
readonly brightWhite: "#e8e8f0";
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Ink-based CLIs (Cursor Agent) emit no ANSI colors unless truecolor is advertised.
|
|
27
|
+
* The bridge process often inherits NO_COLOR=1 from Cursor IDE — strip it for PTY children
|
|
28
|
+
* unless the user explicitly opts out via session env (NO_COLOR=1 or FTOWN_NO_COLOR=1).
|
|
29
|
+
*/
|
|
30
|
+
export declare function applyTerminalColorEnv(env: Record<string, string>): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Match ui/src/components/Terminal.tsx so serialize/replay preserves agent colors. */
|
|
2
|
+
export const FTOWN_XTERM_THEME = {
|
|
3
|
+
background: '#07070a',
|
|
4
|
+
foreground: '#e8e8f0',
|
|
5
|
+
cursor: '#00ff88',
|
|
6
|
+
cursorAccent: '#07070a',
|
|
7
|
+
selectionBackground: 'rgba(0, 255, 136, 0.15)',
|
|
8
|
+
black: '#0a0a0d',
|
|
9
|
+
red: '#ff4466',
|
|
10
|
+
green: '#00ff88',
|
|
11
|
+
yellow: '#ffaa00',
|
|
12
|
+
blue: '#44aaff',
|
|
13
|
+
magenta: '#cc66ff',
|
|
14
|
+
cyan: '#00ddff',
|
|
15
|
+
white: '#c8c8d8',
|
|
16
|
+
brightBlack: '#44444f',
|
|
17
|
+
brightRed: '#ff6680',
|
|
18
|
+
brightGreen: '#33ffaa',
|
|
19
|
+
brightYellow: '#ffcc44',
|
|
20
|
+
brightBlue: '#66bbff',
|
|
21
|
+
brightMagenta: '#dd88ff',
|
|
22
|
+
brightCyan: '#44eeff',
|
|
23
|
+
brightWhite: '#e8e8f0',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Ink-based CLIs (Cursor Agent) emit no ANSI colors unless truecolor is advertised.
|
|
27
|
+
* The bridge process often inherits NO_COLOR=1 from Cursor IDE — strip it for PTY children
|
|
28
|
+
* unless the user explicitly opts out via session env (NO_COLOR=1 or FTOWN_NO_COLOR=1).
|
|
29
|
+
*/
|
|
30
|
+
export function applyTerminalColorEnv(env) {
|
|
31
|
+
env.TERM = env.TERM || 'xterm-256color';
|
|
32
|
+
if (env.FTOWN_NO_COLOR === '1' || env.NO_COLOR === '1' || env.NO_COLOR === 'true') {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
env.COLORTERM = 'truecolor';
|
|
36
|
+
env.FORCE_COLOR = '3';
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=xterm-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xterm-theme.js","sourceRoot":"","sources":["../src/xterm-theme.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,SAAS;IACrB,UAAU,EAAE,SAAS;IACrB,MAAM,EAAE,SAAS;IACjB,YAAY,EAAE,SAAS;IACvB,mBAAmB,EAAE,yBAAyB;IAC9C,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,SAAS;IACvB,UAAU,EAAE,SAAS;IACrB,aAAa,EAAE,SAAS;IACxB,UAAU,EAAE,SAAS;IACrB,WAAW,EAAE,SAAS;CACd,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAA2B;IAC/D,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,gBAAgB,CAAC;IAExC,IAAI,GAAG,CAAC,cAAc,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAClF,OAAO;IACT,CAAC;IAED,GAAG,CAAC,SAAS,GAAG,WAAW,CAAC;IAC5B,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC;AACxB,CAAC"}
|
package/hooks/notify.sh
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
INPUT=$(cat)
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
|
|
4
|
+
PORT="${FTOWN_HOOK_PORT:-}"
|
|
5
|
+
SESSION_ID="${FTOWN_SESSION_ID:-}"
|
|
6
|
+
TOKEN="${FTOWN_HOOK_TOKEN:-}"
|
|
7
|
+
|
|
8
|
+
BRIDGE_JSON="${HOME}/.ftown/bridge.json"
|
|
9
|
+
REGISTRY="${HOME}/.ftown/session-registry.json"
|
|
10
|
+
|
|
11
|
+
if [ -z "$PORT" ] && [ -f "$BRIDGE_JSON" ]; then
|
|
12
|
+
PORT=$(jq -r '.port // empty' "$BRIDGE_JSON" 2>/dev/null)
|
|
13
|
+
if [ -z "$TOKEN" ]; then
|
|
14
|
+
TOKEN=$(jq -r '.token // empty' "$BRIDGE_JSON" 2>/dev/null)
|
|
15
|
+
fi
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ -z "$SESSION_ID" ] && [ -n "$INPUT" ]; then
|
|
19
|
+
CONV=$(echo "$INPUT" | jq -r '.conversation_id // empty' 2>/dev/null)
|
|
20
|
+
WS=$(echo "$INPUT" | jq -r '.workspace_roots[0] // empty' 2>/dev/null)
|
|
21
|
+
if [ -f "$REGISTRY" ]; then
|
|
22
|
+
if [ -n "$CONV" ]; then
|
|
23
|
+
SESSION_ID=$(jq -r --arg c "$CONV" '.byConversation[$c] // empty' "$REGISTRY" 2>/dev/null)
|
|
24
|
+
fi
|
|
25
|
+
if [ -z "$SESSION_ID" ] && [ -n "$WS" ]; then
|
|
26
|
+
SESSION_ID=$(jq -r --arg w "$WS" '.byWorkspace[$w] // empty' "$REGISTRY" 2>/dev/null)
|
|
27
|
+
fi
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
6
31
|
if [ -z "$PORT" ] || [ -z "$SESSION_ID" ]; then
|
|
7
32
|
exit 0
|
|
8
33
|
fi
|
|
9
|
-
|
|
34
|
+
|
|
35
|
+
PAYLOAD=$(echo "$INPUT" | jq -c --arg sid "$SESSION_ID" '. + {ftown_session_id: $sid}' 2>/dev/null)
|
|
36
|
+
if [ -z "$PAYLOAD" ]; then
|
|
37
|
+
PAYLOAD=$(jq -nc --arg sid "$SESSION_ID" --arg ev "${HOOK_EVENT_NAME:-hook}" \
|
|
38
|
+
'{ftown_session_id: $sid, hook_event_name: $ev}')
|
|
39
|
+
fi
|
|
40
|
+
|
|
10
41
|
AUTH_ARGS=()
|
|
11
42
|
if [ -n "$TOKEN" ]; then
|
|
12
43
|
AUTH_ARGS+=(-H "Authorization: Bearer ${TOKEN}")
|
|
13
44
|
fi
|
|
45
|
+
|
|
14
46
|
curl -s -X POST "http://localhost:${PORT}/hook" \
|
|
15
47
|
-H "Content-Type: application/json" \
|
|
16
48
|
"${AUTH_ARGS[@]}" \
|
package/package.json
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ftown-bridge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "CLI bridge for ftown — generic PTY-over-Centrifugo relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"ftown-bridge": "dist/index.js"
|
|
8
|
+
"ftown-bridge": "dist/index.js",
|
|
9
|
+
"ftown-harness": "dist/harness-cli.js",
|
|
10
|
+
"ftown-sessions": "bin/ftown-sessions"
|
|
9
11
|
},
|
|
10
12
|
"files": [
|
|
11
13
|
"dist",
|
|
12
|
-
"
|
|
14
|
+
"bin",
|
|
15
|
+
"hooks",
|
|
16
|
+
"skills"
|
|
13
17
|
],
|
|
14
18
|
"scripts": {
|
|
15
19
|
"build": "tsc",
|
|
16
20
|
"prepublishOnly": "npm run build",
|
|
17
21
|
"start": "tsx src/index.ts",
|
|
18
|
-
"dev": "tsx watch src/index.ts"
|
|
22
|
+
"dev": "tsx watch src/index.ts",
|
|
23
|
+
"harness": "tsx src/harness-cli.ts"
|
|
19
24
|
},
|
|
20
25
|
"engines": {
|
|
21
26
|
"node": ">=22"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bridge-harness
|
|
3
|
+
description: Control local ftown-bridge sessions via auto-deployed ~/.ftown/bin/ftown-harness. Triggers on bridge harness, /bridge-harness, bridge sessions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# bridge-harness
|
|
7
|
+
|
|
8
|
+
## Entry (auto-deployed)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
~/.ftown/bin/ftown-harness <cmd>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Read `~/.ftown/harness-agent.md` on each bridge start. Never curl/lsof the local bridge API.
|
|
15
|
+
|
|
16
|
+
## Playbook
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
ftown-harness status
|
|
20
|
+
ftown-harness here -n 25 # tails log even if process dead (status=error)
|
|
21
|
+
ftown-harness ls --tail 3 # log=N on each row; previews dead sessions with logs
|
|
22
|
+
ftown-harness grep ftown "error|FAIL" -C 2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
| Cmd | Notes |
|
|
28
|
+
|-----|-------|
|
|
29
|
+
| `here -n N` | Workspace walk-up; **tails when dead** if log exists |
|
|
30
|
+
| `ls --tail N` | Shows `log=lines`; preview any session with logs |
|
|
31
|
+
| `tail` / `grep` | ANSI+OSC stripped; `grep -C 2` context |
|
|
32
|
+
| `send` | `--dry-run` first; `-s` submit; only when user asks |
|
|
33
|
+
| `--json` | `ftown-harness --json ls` etc. |
|
|
34
|
+
|
|
35
|
+
Lookup: exact name → substring → id prefix.
|
|
36
|
+
|
|
37
|
+
## Dead vs error
|
|
38
|
+
|
|
39
|
+
`status=error` + `alive=false` does **not** mean no logs. Use `here`/`tail` — they read persisted terminal logs.
|
|
40
|
+
|
|
41
|
+
## context-mode
|
|
42
|
+
|
|
43
|
+
Use `ftown-harness` in Bash only. No curl/wget to 127.0.0.1.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ftown-sessions
|
|
3
|
+
description: >-
|
|
4
|
+
Observe and control other ftown CLI agent sessions on the same machine. Use the
|
|
5
|
+
ftown-sessions CLI (~/.ftown/ftown-sessions) to list, create, read, and drive
|
|
6
|
+
sibling sessions while running inside a ftown-managed Claude Code or Cursor Agent
|
|
7
|
+
session.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ftown cross-session CLI
|
|
11
|
+
|
|
12
|
+
**Prefer the CLI** — installed to `~/.ftown/ftown-sessions` whenever `ftown-bridge` is running. It reads `~/.ftown/bridge.json` automatically.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
~/.ftown/ftown-sessions --help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Skill copy (same binary via wrapper): `scripts/ftown-sessions` in this skill directory.
|
|
19
|
+
|
|
20
|
+
**Trust model:** anyone who can read `bridge.json` can control **every** ftown session on that bridge.
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# List sessions (JSON)
|
|
26
|
+
~/.ftown/ftown-sessions list
|
|
27
|
+
|
|
28
|
+
# Spawn a child agent (uses FTOWN_SESSION_ID for --parent)
|
|
29
|
+
~/.ftown/ftown-sessions create \
|
|
30
|
+
--shell cursor \
|
|
31
|
+
--prompt "Review the auth module and summarize" \
|
|
32
|
+
--workdir /path/to/repo \
|
|
33
|
+
--name auth-review \
|
|
34
|
+
--parent
|
|
35
|
+
|
|
36
|
+
# Metadata
|
|
37
|
+
~/.ftown/ftown-sessions get <session-id>
|
|
38
|
+
|
|
39
|
+
# Terminal output (plain lines; add --json for structured)
|
|
40
|
+
~/.ftown/ftown-sessions screen <session-id> --limit 200
|
|
41
|
+
|
|
42
|
+
# Search output
|
|
43
|
+
~/.ftown/ftown-sessions grep <session-id> --pattern 'error|failed'
|
|
44
|
+
|
|
45
|
+
# Type into another running session
|
|
46
|
+
~/.ftown/ftown-sessions keys <session-id> 'y'
|
|
47
|
+
|
|
48
|
+
# Liveness
|
|
49
|
+
~/.ftown/ftown-sessions running <session-id>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Create options
|
|
53
|
+
|
|
54
|
+
| Flag | Description |
|
|
55
|
+
|------|-------------|
|
|
56
|
+
| `--shell` | `cursor`, `claude`, `shell`, `opencode`, … (default `claude`) |
|
|
57
|
+
| `--prompt` | Initial message sent after spawn |
|
|
58
|
+
| `--workdir` | Working directory |
|
|
59
|
+
| `--name` | Dashboard label |
|
|
60
|
+
| `--command` | Full command override (skips `--shell` builder) |
|
|
61
|
+
| `--parent` | Set parent to `$FTOWN_SESSION_ID` |
|
|
62
|
+
| `--parent-id` | Explicit parent session UUID |
|
|
63
|
+
| `--model` | Cursor model name |
|
|
64
|
+
|
|
65
|
+
Returns JSON with the new `session.id` — use that id for `screen` / `grep` / `keys`.
|
|
66
|
+
|
|
67
|
+
## Typical workflow
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
CLI=~/.ftown/ftown-sessions
|
|
71
|
+
|
|
72
|
+
$CLI list
|
|
73
|
+
$CLI create --shell cursor --prompt "Run tests and report" --workdir "$PWD" --parent
|
|
74
|
+
# -> note session.id from JSON
|
|
75
|
+
|
|
76
|
+
$CLI screen <child-id> --limit 100
|
|
77
|
+
$CLI grep <child-id> --pattern 'FAIL|Error'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Environment
|
|
81
|
+
|
|
82
|
+
Spawned ftown sessions receive:
|
|
83
|
+
|
|
84
|
+
- `FTOWN_SESSION_ID` — this session (use with `--parent`)
|
|
85
|
+
- `FTOWN_HOOK_PORT` / `FTOWN_HOOK_TOKEN` — hook forwarding (not for cross-session control)
|
|
86
|
+
|
|
87
|
+
## HTTP API (optional)
|
|
88
|
+
|
|
89
|
+
The CLI wraps the loopback API. Raw access if needed:
|
|
90
|
+
|
|
91
|
+
| Method | Path | Purpose |
|
|
92
|
+
|--------|------|---------|
|
|
93
|
+
| GET | `/api/sessions` | List |
|
|
94
|
+
| POST | `/api/sessions` | Create |
|
|
95
|
+
| GET | `/api/sessions/:id/screen` | Terminal lines |
|
|
96
|
+
| POST | `/api/sessions/:id/grep` | Search |
|
|
97
|
+
| POST | `/api/sessions/:id/keys` | Send keys |
|
|
98
|
+
|
|
99
|
+
## If the CLI is missing
|
|
100
|
+
|
|
101
|
+
Start or restart **ftown-bridge** on this machine. It installs `~/.ftown/ftown-sessions`, `~/.ftown/notify.sh`, and updates this skill under `~/.agents/skills/ftown-sessions/`.
|