@dmsdc-ai/aigentry-telepty 0.1.5 → 0.1.7
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/.claude/commands/telepty-allow.md +58 -0
- package/.claude/commands/telepty-attach.md +22 -0
- package/.claude/commands/telepty-inject.md +34 -0
- package/.claude/commands/telepty-list.md +22 -0
- package/.claude/commands/telepty-manual-test.md +73 -0
- package/.claude/commands/telepty-start.md +25 -0
- package/.claude/commands/telepty-test.md +25 -0
- package/.claude/commands/telepty.md +82 -0
- package/README.md +27 -0
- package/cli.js +249 -46
- package/daemon.js +261 -27
- package/install.js +96 -64
- package/install.ps1 +1 -0
- package/install.sh +1 -0
- package/package.json +4 -2
- package/skill-installer.js +269 -0
- package/skills/telepty/SKILL.md +86 -0
- package/.cross_session_deliberation.json +0 -7
- package/.deliberation_request.json +0 -1
- package/.deliberation_request2.json +0 -1
- package/.deliberation_request3.json +0 -1
- package/.deliberation_request_bridge.json +0 -1
- package/.deliberation_request_bridge_retry.json +0 -1
- package/.gemini/skills/telepty/SKILL.md +0 -48
- package/.github/workflows/test-install.yml +0 -32
- package/aigentry-telepty-0.0.4.tgz +0 -0
- package/clipboard_image.png +0 -0
- package/test-pty.js +0 -14
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const prompts = require('prompts');
|
|
7
|
+
|
|
8
|
+
const TARGET_CLIENTS = {
|
|
9
|
+
claude: {
|
|
10
|
+
label: 'Claude Code',
|
|
11
|
+
globalDir: () => path.join(os.homedir(), '.claude', 'skills'),
|
|
12
|
+
projectDir: (cwd) => path.join(cwd, '.claude', 'skills'),
|
|
13
|
+
defaultScope: 'global'
|
|
14
|
+
},
|
|
15
|
+
codex: {
|
|
16
|
+
label: 'Codex',
|
|
17
|
+
globalDir: () => path.join(os.homedir(), '.codex', 'skills'),
|
|
18
|
+
projectDir: (cwd) => path.join(cwd, '.codex', 'skills'),
|
|
19
|
+
defaultScope: 'global'
|
|
20
|
+
},
|
|
21
|
+
gemini: {
|
|
22
|
+
label: 'Gemini',
|
|
23
|
+
globalDir: () => path.join(os.homedir(), '.gemini', 'skills'),
|
|
24
|
+
projectDir: (cwd) => path.join(cwd, '.gemini', 'skills'),
|
|
25
|
+
defaultScope: 'project'
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function resolveSkillsSourceRoot(packageRoot = __dirname) {
|
|
30
|
+
return path.join(packageRoot, 'skills');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function listPackagedSkills(packageRoot = __dirname) {
|
|
34
|
+
const sourceRoot = resolveSkillsSourceRoot(packageRoot);
|
|
35
|
+
if (!fs.existsSync(sourceRoot)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return fs.readdirSync(sourceRoot)
|
|
40
|
+
.filter((name) => {
|
|
41
|
+
const skillDir = path.join(sourceRoot, name);
|
|
42
|
+
return fs.statSync(skillDir).isDirectory() && fs.existsSync(path.join(skillDir, 'SKILL.md'));
|
|
43
|
+
})
|
|
44
|
+
.sort()
|
|
45
|
+
.map((name) => ({
|
|
46
|
+
name,
|
|
47
|
+
sourceDir: path.join(sourceRoot, name)
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveTargetDirectory(targetClient, scope, cwd, customPath) {
|
|
52
|
+
const client = TARGET_CLIENTS[targetClient];
|
|
53
|
+
if (!client) {
|
|
54
|
+
throw new Error(`Unsupported target client: ${targetClient}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (scope === 'custom') {
|
|
58
|
+
if (!customPath) {
|
|
59
|
+
throw new Error(`Missing custom path for ${targetClient}`);
|
|
60
|
+
}
|
|
61
|
+
return path.resolve(customPath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (scope === 'project') {
|
|
65
|
+
return client.projectDir(cwd);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return client.globalDir();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ensureDirectory(dirPath) {
|
|
72
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function copySkillDirectory(sourceDir, destDir, overwrite = false) {
|
|
76
|
+
if (fs.existsSync(destDir)) {
|
|
77
|
+
if (!overwrite) {
|
|
78
|
+
return 'exists';
|
|
79
|
+
}
|
|
80
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ensureDirectory(path.dirname(destDir));
|
|
84
|
+
fs.cpSync(sourceDir, destDir, { recursive: true });
|
|
85
|
+
return 'installed';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function promptForOverwrite(promptImpl, targetLabel, destDir) {
|
|
89
|
+
const response = await promptImpl({
|
|
90
|
+
type: 'select',
|
|
91
|
+
name: 'action',
|
|
92
|
+
message: `${targetLabel} already has ${path.basename(destDir)}. What should happen?`,
|
|
93
|
+
choices: [
|
|
94
|
+
{ title: 'Overwrite existing copy', value: 'overwrite' },
|
|
95
|
+
{ title: 'Skip this target', value: 'skip' },
|
|
96
|
+
{ title: 'Cancel installation', value: 'cancel' }
|
|
97
|
+
],
|
|
98
|
+
initial: 1
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return response.action;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function installSkillsWithPlan(plan, options = {}) {
|
|
105
|
+
const promptImpl = options.promptImpl || prompts;
|
|
106
|
+
const results = [];
|
|
107
|
+
|
|
108
|
+
for (const item of plan) {
|
|
109
|
+
const skillName = item.skill.name;
|
|
110
|
+
const destDir = path.join(item.destRoot, skillName);
|
|
111
|
+
let overwrite = false;
|
|
112
|
+
|
|
113
|
+
if (fs.existsSync(destDir)) {
|
|
114
|
+
const action = await promptForOverwrite(promptImpl, item.targetLabel, destDir);
|
|
115
|
+
if (!action || action === 'cancel') {
|
|
116
|
+
throw new Error('Installation cancelled.');
|
|
117
|
+
}
|
|
118
|
+
if (action === 'skip') {
|
|
119
|
+
results.push({ ...item, destDir, status: 'skipped' });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
overwrite = true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
copySkillDirectory(item.skill.sourceDir, destDir, overwrite);
|
|
126
|
+
results.push({ ...item, destDir, status: overwrite ? 'overwritten' : 'installed' });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function runInteractiveSkillInstaller(options = {}) {
|
|
133
|
+
const promptImpl = options.promptImpl || prompts;
|
|
134
|
+
const packageRoot = options.packageRoot || __dirname;
|
|
135
|
+
const cwd = options.cwd || process.cwd();
|
|
136
|
+
const packagedSkills = listPackagedSkills(packageRoot);
|
|
137
|
+
|
|
138
|
+
if (packagedSkills.length === 0) {
|
|
139
|
+
console.log('No packaged skills were found.');
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let selectedSkills = packagedSkills;
|
|
144
|
+
if (packagedSkills.length > 1) {
|
|
145
|
+
const selectedSkillsAnswer = await promptImpl({
|
|
146
|
+
type: 'multiselect',
|
|
147
|
+
name: 'skills',
|
|
148
|
+
message: 'Select skills to install',
|
|
149
|
+
choices: packagedSkills.map((skill) => ({
|
|
150
|
+
title: skill.name,
|
|
151
|
+
value: skill.name
|
|
152
|
+
})),
|
|
153
|
+
min: 1
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (!selectedSkillsAnswer.skills || selectedSkillsAnswer.skills.length === 0) {
|
|
157
|
+
console.log('Skipped skill installation.');
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
selectedSkills = packagedSkills.filter((skill) => selectedSkillsAnswer.skills.includes(skill.name));
|
|
162
|
+
} else {
|
|
163
|
+
console.log(`Installing packaged skill: ${packagedSkills[0].name}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const selectedClientsAnswer = await promptImpl({
|
|
167
|
+
type: 'multiselect',
|
|
168
|
+
name: 'clients',
|
|
169
|
+
message: 'Select target clients',
|
|
170
|
+
choices: Object.entries(TARGET_CLIENTS).map(([key, value]) => ({
|
|
171
|
+
title: value.label,
|
|
172
|
+
value: key
|
|
173
|
+
})),
|
|
174
|
+
min: 1
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (!selectedClientsAnswer.clients || selectedClientsAnswer.clients.length === 0) {
|
|
178
|
+
console.log('Skipped skill installation.');
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const destinationSelections = {};
|
|
183
|
+
for (const clientKey of selectedClientsAnswer.clients) {
|
|
184
|
+
const client = TARGET_CLIENTS[clientKey];
|
|
185
|
+
const scopeAnswer = await promptImpl([
|
|
186
|
+
{
|
|
187
|
+
type: 'select',
|
|
188
|
+
name: 'scope',
|
|
189
|
+
message: `Install ${client.label} skills where?`,
|
|
190
|
+
choices: [
|
|
191
|
+
{
|
|
192
|
+
title: `Global (${client.globalDir()})`,
|
|
193
|
+
value: 'global'
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
title: `Current Project (${client.projectDir(cwd)})`,
|
|
197
|
+
value: 'project'
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
title: 'Custom Path',
|
|
201
|
+
value: 'custom'
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
initial: client.defaultScope === 'project' ? 1 : 0
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: (prev) => prev === 'custom' ? 'text' : null,
|
|
208
|
+
name: 'customPath',
|
|
209
|
+
message: `Custom ${client.label} skills path:`,
|
|
210
|
+
initial: client.projectDir(cwd),
|
|
211
|
+
validate: (value) => value ? true : 'Required'
|
|
212
|
+
}
|
|
213
|
+
]);
|
|
214
|
+
|
|
215
|
+
if (!scopeAnswer.scope) {
|
|
216
|
+
console.log('Skipped skill installation.');
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
destinationSelections[clientKey] = {
|
|
221
|
+
scope: scopeAnswer.scope,
|
|
222
|
+
customPath: scopeAnswer.customPath || null
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const plan = [];
|
|
227
|
+
|
|
228
|
+
for (const clientKey of selectedClientsAnswer.clients) {
|
|
229
|
+
const client = TARGET_CLIENTS[clientKey];
|
|
230
|
+
const selection = destinationSelections[clientKey];
|
|
231
|
+
const destRoot = resolveTargetDirectory(clientKey, selection.scope, cwd, selection.customPath);
|
|
232
|
+
|
|
233
|
+
for (const skill of selectedSkills) {
|
|
234
|
+
plan.push({
|
|
235
|
+
targetClient: clientKey,
|
|
236
|
+
targetLabel: client.label,
|
|
237
|
+
scope: selection.scope,
|
|
238
|
+
destRoot,
|
|
239
|
+
skill
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log('\nInstalling telepty skills:');
|
|
245
|
+
for (const item of plan) {
|
|
246
|
+
console.log(` - ${item.skill.name} -> ${item.targetLabel} (${item.scope})`);
|
|
247
|
+
console.log(` ${item.destRoot}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const results = await installSkillsWithPlan(plan, { promptImpl });
|
|
251
|
+
|
|
252
|
+
console.log('\nSkill installation results:');
|
|
253
|
+
for (const item of results) {
|
|
254
|
+
const label = item.status === 'skipped' ? 'Skipped' : 'Installed';
|
|
255
|
+
console.log(` ${label}: ${item.skill.name} -> ${item.targetLabel}`);
|
|
256
|
+
console.log(` ${item.destDir}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = {
|
|
263
|
+
TARGET_CLIENTS,
|
|
264
|
+
copySkillDirectory,
|
|
265
|
+
installSkillsWithPlan,
|
|
266
|
+
listPackagedSkills,
|
|
267
|
+
resolveTargetDirectory,
|
|
268
|
+
runInteractiveSkillInstaller
|
|
269
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# telepty
|
|
2
|
+
|
|
3
|
+
Use `telepty` to inspect active sessions, check the current telepty session ID, attach to sessions, inject commands, listen to the event bus, rename sessions, and update the daemon.
|
|
4
|
+
|
|
5
|
+
## When To Use
|
|
6
|
+
|
|
7
|
+
Use this skill when the user asks to:
|
|
8
|
+
- Check whether the current shell is running inside a telepty session
|
|
9
|
+
- List or inspect telepty sessions
|
|
10
|
+
- Attach to a telepty session
|
|
11
|
+
- Inject a prompt or command into another telepty session
|
|
12
|
+
- Listen to telepty bus events or publish a JSON payload
|
|
13
|
+
- Rename a session
|
|
14
|
+
- Update telepty
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
1. Check the current telepty session:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
echo "$TELEPTY_SESSION_ID"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. List sessions:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
telepty list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Attach to a session:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
telepty attach <session_id>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
4. Inject a prompt or command:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
telepty inject <session_id> "<prompt text>"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
5. Inject into multiple sessions:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
telepty multicast <id1,id2,...> "<prompt text>"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
6. Broadcast to all sessions:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
telepty broadcast "<prompt text>"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
7. Rename a session:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
telepty rename <old_id> <new_id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
8. Listen to the event bus:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
telepty listen
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
9. Publish a JSON payload to the bus:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
TOKEN=$(grep authToken ~/.telepty/config.json | cut -d '"' -f 4)
|
|
70
|
+
curl -s -X POST http://127.0.0.1:3848/api/bus/publish \
|
|
71
|
+
-H "Content-Type: application/json" \
|
|
72
|
+
-H "x-telepty-token: $TOKEN" \
|
|
73
|
+
-d '{"type":"bg_message","payload":"..."}'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
10. Update telepty:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
telepty update
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Notes
|
|
83
|
+
|
|
84
|
+
- `TELEPTY_SESSION_ID` is only set inside telepty-managed sessions.
|
|
85
|
+
- Use `telepty inject` when the target session should receive the command immediately.
|
|
86
|
+
- Use the JSON bus when the payload should be delivered without interrupting the target shell.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"type": "cross_session_deliberation",
|
|
3
|
-
"sender": "telepty (orchestrator)",
|
|
4
|
-
"topic": "[Feature Proposal] Autonomous Deliberation Handoff",
|
|
5
|
-
"content": "1. 배경 및 발생한 해프닝 (The Happening)\n최근 aigentry-brain 프로젝트에서 아키텍처 결정을 위해 mcp-deliberation을 사용하여 Claude와 GPT-4 간의 토론을 진행했습니다. 토론이 완료되어 상태가 completed로 변경되고 synthesis (Next Steps 포함)가 로컬 상태 파일(~/.local/lib/mcp-deliberation/state/...)에 기록되었습니다.\n\n이때 놀라운 창발적(Emergent) 동작이 발생했습니다. 백그라운드에서 프로젝트 디렉토리를 주시하며 ultrawork (자율 실행) 모드로 돌고 있던 다른 Claude Code 에이전트가, 사용자의 명시적인 프롬프트 지시 없이도 토론 결과(Synthesis)가 업데이트된 것을 감지했습니다. 그리고는 스스로 토론에서 합의된 'Next Steps'를 파싱하여, 하위 에이전트들을 스폰(Spawn)하고 실제 코드 구현, 테스트 작성, 빌드 검증까지 완벽하게 병렬로 끝마쳤습니다.\n\n즉, '인간은 방향성 토론만 중재하고, 코딩은 합의안을 바탕으로 백그라운드 에이전트가 알아서 처리하는' 이상적인 파이프라인이 우연히 시연된 것입니다.\n\n2. 현재 현황 (Current Status)\n* 현재 이 동작은 공식적인 연동 인터페이스 없이, 백그라운드 에이전트(Claude Code 등)가 파일 시스템이나 프로젝트 컨텍스트를 지속적으로 모니터링하다가 우연히 상태 변경을 주워 먹는(Polling/Heuristic) 방식으로 발생했습니다.\n* synthesis 필드는 단순한 Markdown 텍스트 덩어리이므로, 실행 에이전트가 구체적으로 '무엇을 코딩해야 하는지'를 발췌할 때 할루시네이션이나 오작동의 여지가 존재합니다.\n\n3. 기능화 방향 및 제안 (How to Feature-ize)\n이 강력한 패턴을 mcp-deliberation의 공식 기능(Handoff Pattern)으로 승격시키기 위해 다음 세 가지의 기능 추가를 제안합니다.\n\n* A. Synthesis 구조화 (Structured Synthesis Schema)\n * 토론 합성 단계(deliberation_synthesize)에서 결과물을 단순 Markdown이 아닌 구조화된 JSON 형태로 분리합니다.\n * 예: {\"summary\": \"...\", \"decisions\": [...], \"actionable_tasks\": [{\"id\": 1, \"task\": \"refactor X\", \"files\": [\"src/X.ts\"]}]}\n * 이를 통해 실행 에이전트가 정확히 태스크 리스트만 빼내어 작업을 할당할 수 있습니다.\n* B. Handoff Trigger (명시적 실행 인박스/웹훅)\n * 토론이 성공적으로 끝났을 때, 실행을 전담하는 에이전트들을 깨우기 위한 메커니즘이 필요합니다.\n * 방안 1: ~/.local/lib/mcp-deliberation/inbox/ 같은 태스크 큐 디렉토리를 만들어, 합의안을 task.json으로 떨궈 실행 에이전트가 이를 워치(Watch)하게 합니다.\n * 방안 2: deliberation_start 옵션에 auto_execute: true 플래그를 추가하여, 토론이 끝나면 자동으로 시스템에 등록된 범용 에이전트(Generalist / Executor) 도구를 호출해 코딩을 지시하도록 라우팅합니다.\n* C. Handoff Status 연동\n * 현재 토론의 상태는 active -> awaiting_synthesis -> completed로 끝납니다.\n * 여기에 executing -> implemented 같은 후속 상태 트래킹을 추가하여, Session Monitor 화면에서도 토론 결과가 실제 코드로 구현되고 있는지 프로그레스 바를 통해 볼 수 있게 시각화합니다.\n\n4. 기대 효과\n이 기능이 구현되면 mcp-deliberation은 단순한 'LLM 간의 토론장'을 넘어, '요구사항 분석 -> 아키텍처 결정 -> 자동 구현'으로 이어지는 완전자율형 소프트웨어 팩토리(Software Factory)의 핵심 오케스트레이터로 진화할 것입니다.",
|
|
6
|
-
"instructions": "dustcraw, brain, deliberation 세션에 있는 에이전트들은 위 제안을 읽고 각자의 프로젝트(역할) 관점에서 이 기능(자율 실행 핸드오프)의 타당성과 아키텍처적 영향을 분석하여 버스(bus)로 답변을 회신해 주세요."
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"topic": "[Feature Proposal] Autonomous Deliberation Handoff. 1. Background: Emergent behavior observed where a background agent automatically parsed a deliberation synthesis and implemented it without human intervention. 2. Current Status: Works via fragile heuristic polling of unstructured markdown. 3. Proposal: A) Structured JSON Synthesis B) Explicit Handoff Trigger (inbox dir or auto_execute flag) C) Handoff Status tracking in monitor. Discuss the feasibility and architectural implications of this across dustcraw, brain, deliberation, and telepty.", "speakers": ["gemini", "claude", "codex", "gpt4"], "role_preset": "brainstorm"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"topic": "Re-evaluate the telepty architecture based on the user`s strict boundaries: 1. A session equals a physical, visible terminal window. If the window closes, the session MUST die. No zombies. 2. Background persistence is NOT telepty`s job (users can use tmux if they want, but telepty doesn`t manage it). 3. Unexpected disconnects (network drop, sleep) are treated as session death, and recovery is handled via a higher-level `Handoff` of context to a new session, NOT by keeping zombie processes alive. Double-check this philosophy for flaws, focusing on how AI agents will communicate (Pub/Sub) within this strict ephemeral foreground model.", "speakers": ["claude", "codex", "gemini"], "role_preset": "review"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"topic": "Final architectural verification of the Telepty Boundary Model. The user asserts a hard line on responsibility: 1. If a session/process dies, context is lost. That is the CLI/Terminal`s problem, not telepty`s. 2. If a Pub message is sent to a dead Sub, it is lost. Telepty just drops it or relies on Ack. If the sub reconnects in time, it gets it; if not, drop it. 3. If Wi-Fi drops, CLI dies, agent dies, scripts die. Telepty does not care about split-brain or script recovery. Is this stateless, hyper-minimalist `dumb pipe` philosophy technically viable for our goals?", "speakers": ["claude", "gemini", "codex"], "role_preset": "consensus"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"topic": "Architectural Design for Session x LLM CLI Deliberation. Currently, aigentry-deliberation orchestrates local LLM CLIs via MCP. How do we extend this to orchestrate across PHYSICAL terminal sessions distributed via aigentry-telepty? We need a bridge between the logical deliberation rounds and the physical telepty bus. The goal is to let the deliberation server route a turn to a specific telepty session ID, let that session`s AI generate a response, and capture that response back into the deliberation forum. Brainstorm the most elegant, Unix-like bridge architecture.", "speakers": ["claude", "gemini", "codex"], "role_preset": "brainstorm"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"topic": "Architectural Design for Session x LLM CLI Deliberation. Currently, aigentry-deliberation orchestrates local LLM CLIs via MCP. How do we extend this to orchestrate across PHYSICAL terminal sessions distributed via aigentry-telepty? We need a bridge between the logical deliberation rounds and the physical telepty bus. The goal is to let the deliberation server route a turn to a specific telepty session ID, let that session`s AI generate a response, and capture that response back into the deliberation forum. Brainstorm the most elegant, Unix-like bridge architecture.", "speakers": ["gemini", "codex", "claude"], "role_preset": "brainstorm"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# telepty
|
|
2
|
-
|
|
3
|
-
**Description:**
|
|
4
|
-
Help the user interact with the `telepty` daemon, check their current session ID, list active sessions, and inject commands or JSON events into remote or local PTY sessions. All operations are performed using standard CLI commands or `curl`.
|
|
5
|
-
|
|
6
|
-
**Trigger:**
|
|
7
|
-
When the user asks about their current session ID, wants to check active sessions, wants to inject a prompt/command into a specific session, wants to send a JSON event via the bus, wants to subscribe/listen to the bus, or wants to update telepty.
|
|
8
|
-
|
|
9
|
-
**Instructions:**
|
|
10
|
-
1. **To check the current session ID:**
|
|
11
|
-
- Execute `run_shell_command` with `echo $TELEPTY_SESSION_ID`.
|
|
12
|
-
- If the value is empty, inform the user that the current shell is *not* running inside a telepty spawned session.
|
|
13
|
-
- If it has a value, output it clearly.
|
|
14
|
-
2. **To list all sessions:**
|
|
15
|
-
- Run `telepty list`.
|
|
16
|
-
3. **To send a message/command to another agent, you must choose ONE of three methods depending on the user's intent:**
|
|
17
|
-
|
|
18
|
-
**Method A: Prompt Injection (Active Interruption)**
|
|
19
|
-
- Use this when you want the receiving AI to IMMEDIATELY read and execute the message as a prompt.
|
|
20
|
-
- Run: `telepty inject <target_session_id> "<prompt text>"`
|
|
21
|
-
- (For multiple: `telepty multicast <id1>,<id2> "<prompt>"`)
|
|
22
|
-
|
|
23
|
-
**Method B: Log Injection (Visual Notification)**
|
|
24
|
-
- Use this when you want the message to appear immediately on the receiving terminal's screen for the user to see, but WITHOUT forcing the AI to execute it as a prompt.
|
|
25
|
-
- Run: `telepty inject <target_session_id> "echo '\x1b[33m[📬 Message from $TELEPTY_SESSION_ID]\x1b[0m <message text>'"`
|
|
26
|
-
|
|
27
|
-
**Method C: Background JSON Bus (Passive/Silent)**
|
|
28
|
-
- Use this for structured data transfer that the other AI will read later from its log file, without disturbing its current terminal screen.
|
|
29
|
-
- Run:
|
|
30
|
-
```bash
|
|
31
|
-
TOKEN=$(cat ~/.telepty/config.json | grep authToken | cut -d '"' -f 4)
|
|
32
|
-
curl -s -X POST http://127.0.0.1:3848/api/bus/publish -H "Content-Type: application/json" -H "x-telepty-token: $TOKEN" -d '{"type": "bg_message", "payload": "..."}'
|
|
33
|
-
```
|
|
34
|
-
4. **To subscribe to the Event Bus (Listen for JSON events):**
|
|
35
|
-
- Run `nohup telepty listen > .telepty_bus_events.log 2>&1 &`
|
|
36
|
-
5. **To physically OPEN a new Terminal Window for the user (macOS):**
|
|
37
|
-
- If the user asks you to "open a new telepty terminal" or "방 파줘", you can physically spawn a new Ghostty/Terminal window on their screen that is already attached to a telepty session.
|
|
38
|
-
- Run this shell command (replace `<ID>` and `<CMD>`):
|
|
39
|
-
```bash
|
|
40
|
-
cat << 'EOF' > /tmp/telepty-auto.command
|
|
41
|
-
#!/bin/bash
|
|
42
|
-
telepty spawn --id <ID> <CMD>
|
|
43
|
-
EOF
|
|
44
|
-
chmod +x /tmp/telepty-auto.command
|
|
45
|
-
open -a Ghostty /tmp/telepty-auto.command || open /tmp/telepty-auto.command
|
|
46
|
-
```
|
|
47
|
-
6. **To update telepty:**
|
|
48
|
-
- Run `telepty update`.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
name: Test Installation Scripts
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches: [ "main" ]
|
|
5
|
-
pull_request:
|
|
6
|
-
branches: [ "main" ]
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
test-windows:
|
|
10
|
-
runs-on: windows-latest
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v3
|
|
13
|
-
- name: Run Install Script
|
|
14
|
-
shell: powershell
|
|
15
|
-
run: |
|
|
16
|
-
./install.ps1
|
|
17
|
-
|
|
18
|
-
test-ubuntu:
|
|
19
|
-
runs-on: ubuntu-latest
|
|
20
|
-
steps:
|
|
21
|
-
- uses: actions/checkout@v3
|
|
22
|
-
- name: Run Install Script
|
|
23
|
-
run: |
|
|
24
|
-
bash ./install.sh
|
|
25
|
-
|
|
26
|
-
test-macos:
|
|
27
|
-
runs-on: macos-latest
|
|
28
|
-
steps:
|
|
29
|
-
- uses: actions/checkout@v3
|
|
30
|
-
- name: Run Install Script
|
|
31
|
-
run: |
|
|
32
|
-
bash ./install.sh
|
|
Binary file
|
package/clipboard_image.png
DELETED
|
Binary file
|
package/test-pty.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
const pty = require('node-pty');
|
|
2
|
-
try {
|
|
3
|
-
const p = pty.spawn('/bin/bash', [], {
|
|
4
|
-
name: 'xterm-color',
|
|
5
|
-
cols: 80,
|
|
6
|
-
rows: 30,
|
|
7
|
-
cwd: process.env.HOME,
|
|
8
|
-
env: process.env
|
|
9
|
-
});
|
|
10
|
-
console.log('Success, PID:', p.pid);
|
|
11
|
-
p.kill();
|
|
12
|
-
} catch (e) {
|
|
13
|
-
console.error('Error:', e);
|
|
14
|
-
}
|