@dmsdc-ai/aigentry-telepty 0.1.6 ā 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/README.md +11 -0
- package/cli.js +11 -0
- package/install.js +96 -64
- package/install.ps1 +1 -0
- package/install.sh +1 -0
- package/package.json +4 -4
- package/skill-installer.js +269 -0
- package/skills/telepty/SKILL.md +86 -0
- package/.deliberation_request.json +0 -1
- package/.deliberation_request2.json +0 -1
- package/.deliberation_request3.json +0 -1
- package/.gemini/skills/telepty/SKILL.md +0 -55
- package/.github/workflows/test-install.yml +0 -26
- package/aigentry-telepty-0.0.4.tgz +0 -0
- package/clipboard_image.png +0 -0
- package/test/auth.test.js +0 -56
- package/test/cli.test.js +0 -57
- package/test/daemon.test.js +0 -415
- package/test-pty.js +0 -14
- package/test-support/daemon-harness.js +0 -313
package/README.md
CHANGED
|
@@ -55,3 +55,14 @@ npm run test:watch
|
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
The automated suite covers config generation, daemon HTTP APIs, WebSocket attach/output flow, bus events, session deletion regressions, and CLI smoke tests against a real daemon process.
|
|
58
|
+
|
|
59
|
+
## Skill Installation
|
|
60
|
+
|
|
61
|
+
The package installer opens the telepty skill TUI automatically when you run it in a terminal.
|
|
62
|
+
|
|
63
|
+
To reopen it later, run `telepty` and choose `Install telepty skills`.
|
|
64
|
+
|
|
65
|
+
The TUI lets you choose:
|
|
66
|
+
- which packaged skills to install
|
|
67
|
+
- which target clients to install into (`Claude Code`, `Codex`, `Gemini`)
|
|
68
|
+
- whether each target uses a global path, the current project path, or a custom path
|
package/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ const prompts = require('prompts');
|
|
|
9
9
|
const updateNotifier = require('update-notifier');
|
|
10
10
|
const pkg = require('./package.json');
|
|
11
11
|
const { getConfig } = require('./auth');
|
|
12
|
+
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
12
13
|
const args = process.argv.slice(2);
|
|
13
14
|
|
|
14
15
|
// Check for updates unless explicitly disabled for tests/CI.
|
|
@@ -152,6 +153,7 @@ async function manageInteractive() {
|
|
|
152
153
|
{ title: 'š Allow inject (Run CLI with inject)', value: 'allow' },
|
|
153
154
|
{ title: 'š¬ Send message to a room (Inject command)', value: 'inject' },
|
|
154
155
|
{ title: 'š View all open rooms (List sessions)', value: 'list' },
|
|
156
|
+
{ title: 'š§ Install telepty skills', value: 'install-skills' },
|
|
155
157
|
{ title: 'š Update telepty to latest version', value: 'update' },
|
|
156
158
|
{ title: 'ā Exit', value: 'exit' }
|
|
157
159
|
]
|
|
@@ -189,6 +191,15 @@ async function manageInteractive() {
|
|
|
189
191
|
continue;
|
|
190
192
|
}
|
|
191
193
|
|
|
194
|
+
if (response.action === 'install-skills') {
|
|
195
|
+
try {
|
|
196
|
+
await runInteractiveSkillInstaller({ packageRoot: __dirname, cwd: process.cwd() });
|
|
197
|
+
} catch (e) {
|
|
198
|
+
console.error(`\nā ${e.message}\n`);
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
192
203
|
if (response.action === 'list') {
|
|
193
204
|
console.log('\n');
|
|
194
205
|
const sessions = await discoverSessions();
|
package/install.js
CHANGED
|
@@ -4,6 +4,7 @@ const { execSync, spawn } = require('child_process');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
7
8
|
|
|
8
9
|
console.log("š Installing @dmsdc-ai/aigentry-telepty...");
|
|
9
10
|
|
|
@@ -16,38 +17,69 @@ function run(cmd) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
function resolveInstalledPackageRoot() {
|
|
21
|
+
try {
|
|
22
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
23
|
+
return path.join(globalRoot, '@dmsdc-ai', 'aigentry-telepty');
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return __dirname;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function installSkills() {
|
|
30
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
31
|
+
console.log('āļø Skipping interactive skill installation (no TTY).');
|
|
32
|
+
console.log(' Run `telepty` later and choose "Install telepty skills".');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('\nš Telepty skill installation');
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
38
|
+
try {
|
|
39
|
+
await runInteractiveSkillInstaller({
|
|
40
|
+
packageRoot: resolveInstalledPackageRoot(),
|
|
41
|
+
cwd: process.cwd()
|
|
42
|
+
});
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.warn('ā ļø Could not install telepty skills:', e.message);
|
|
45
|
+
}
|
|
29
46
|
}
|
|
30
47
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
(async () => {
|
|
49
|
+
// 1. Install globally via npm
|
|
50
|
+
console.log("š¦ Installing package globally...");
|
|
51
|
+
run("npm install -g @dmsdc-ai/aigentry-telepty");
|
|
52
|
+
|
|
53
|
+
// 2. Install telepty skills for supported clients
|
|
54
|
+
await installSkills();
|
|
55
|
+
|
|
56
|
+
// 3. Find executable
|
|
57
|
+
let teleptyPath = '';
|
|
58
|
+
try {
|
|
59
|
+
teleptyPath = execSync(os.platform() === 'win32' ? 'where telepty' : 'which telepty', { encoding: 'utf8' }).split('\n')[0].trim();
|
|
60
|
+
} catch (e) {
|
|
61
|
+
teleptyPath = 'telepty'; // fallback
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 4. Setup OS-specific autostart or background daemon
|
|
65
|
+
const platform = os.platform();
|
|
66
|
+
|
|
67
|
+
if (platform === 'win32') {
|
|
68
|
+
console.log("āļø Setting up Windows background process...");
|
|
69
|
+
const subprocess = spawn(teleptyPath, ['daemon'], {
|
|
70
|
+
detached: true,
|
|
71
|
+
stdio: 'ignore',
|
|
72
|
+
windowsHide: true
|
|
73
|
+
});
|
|
74
|
+
subprocess.unref();
|
|
75
|
+
console.log("ā
Windows daemon started in background.");
|
|
76
|
+
|
|
77
|
+
} else if (platform === 'darwin') {
|
|
78
|
+
console.log("āļø Setting up macOS launchd service...");
|
|
79
|
+
const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.aigentry.telepty.plist');
|
|
80
|
+
fs.mkdirSync(path.dirname(plistPath), { recursive: true });
|
|
81
|
+
|
|
82
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
51
83
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
52
84
|
<plist version="1.0">
|
|
53
85
|
<dict>
|
|
@@ -64,20 +96,19 @@ if (platform === 'win32') {
|
|
|
64
96
|
<true/>
|
|
65
97
|
</dict>
|
|
66
98
|
</plist>`;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} else {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const serviceContent = `[Unit]
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(plistPath, plistContent);
|
|
101
|
+
try { execSync(`launchctl unload "${plistPath}" 2>/dev/null`); } catch(e){}
|
|
102
|
+
run(`launchctl load "${plistPath}"`);
|
|
103
|
+
console.log("ā
macOS LaunchAgent installed and started.");
|
|
104
|
+
|
|
105
|
+
} else {
|
|
106
|
+
// Linux
|
|
107
|
+
try {
|
|
108
|
+
execSync('systemctl --version', { stdio: 'ignore' });
|
|
109
|
+
if (process.getuid && process.getuid() === 0) {
|
|
110
|
+
console.log("āļø Setting up systemd service for Linux...");
|
|
111
|
+
const serviceContent = `[Unit]
|
|
81
112
|
Description=Telepty Daemon
|
|
82
113
|
After=network.target
|
|
83
114
|
|
|
@@ -90,25 +121,26 @@ Environment=NODE_ENV=production
|
|
|
90
121
|
|
|
91
122
|
[Install]
|
|
92
123
|
WantedBy=multi-user.target`;
|
|
93
|
-
|
|
94
|
-
fs.writeFileSync('/etc/systemd/system/telepty.service', serviceContent);
|
|
95
|
-
run('systemctl daemon-reload');
|
|
96
|
-
run('systemctl enable telepty');
|
|
97
|
-
run('systemctl start telepty');
|
|
98
|
-
console.log("ā
Systemd service installed and started.");
|
|
99
|
-
process.exit(0);
|
|
100
|
-
}
|
|
101
|
-
} catch(e) {}
|
|
102
|
-
|
|
103
|
-
// Fallback for Linux without systemd or non-root
|
|
104
|
-
console.log("ā ļø Skipping systemd (no root or no systemd). Starting in background...");
|
|
105
|
-
const subprocess = spawn(teleptyPath, ['daemon'], {
|
|
106
|
-
detached: true,
|
|
107
|
-
stdio: 'ignore'
|
|
108
|
-
});
|
|
109
|
-
subprocess.unref();
|
|
110
|
-
console.log("ā
Linux daemon started in background using nohup equivalent.");
|
|
111
|
-
}
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
fs.writeFileSync('/etc/systemd/system/telepty.service', serviceContent);
|
|
126
|
+
run('systemctl daemon-reload');
|
|
127
|
+
run('systemctl enable telepty');
|
|
128
|
+
run('systemctl start telepty');
|
|
129
|
+
console.log("ā
Systemd service installed and started.");
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
} catch(e) {}
|
|
133
|
+
|
|
134
|
+
// Fallback for Linux without systemd or non-root
|
|
135
|
+
console.log("ā ļø Skipping systemd (no root or no systemd). Starting in background...");
|
|
136
|
+
const subprocess = spawn(teleptyPath, ['daemon'], {
|
|
137
|
+
detached: true,
|
|
138
|
+
stdio: 'ignore'
|
|
139
|
+
});
|
|
140
|
+
subprocess.unref();
|
|
141
|
+
console.log("ā
Linux daemon started in background using nohup equivalent.");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log("\nš Installation complete! Telepty daemon is running.");
|
|
145
|
+
console.log("š Try running: telepty attach\n");
|
|
146
|
+
})();
|
package/install.ps1
CHANGED
|
@@ -31,3 +31,4 @@ Write-Host "Success: Windows daemon started in background." -ForegroundColor Gre
|
|
|
31
31
|
|
|
32
32
|
Write-Host "`nInstallation complete! Telepty daemon is running." -ForegroundColor Cyan
|
|
33
33
|
Write-Host "Next step: Try running: telepty attach" -ForegroundColor Yellow
|
|
34
|
+
Write-Host "Optional: Run telepty and choose 'Install telepty skills' to add skills for Claude Code, Codex, or Gemini" -ForegroundColor Yellow
|
package/install.sh
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmsdc-ai/aigentry-telepty",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"main": "daemon.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"telepty": "cli.js",
|
|
7
7
|
"telepty-install": "install.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "node --test test/auth.test.js test/daemon.test.js test/cli.test.js",
|
|
11
|
-
"test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/cli.test.js",
|
|
12
|
-
"test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/cli.test.js"
|
|
10
|
+
"test": "node --test test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js",
|
|
11
|
+
"test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js",
|
|
12
|
+
"test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [],
|
|
15
15
|
"author": "",
|
|
@@ -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 +0,0 @@
|
|
|
1
|
-
{"topic": "Brainstorming and critiquing the 3 communication modes in telepty (Prompt Injection, Visual Log, Background JSON). What crucial interaction pattern between human users and AI agents is missing from this list? Consider synchronous vs asynchronous, blocking vs non-blocking, and explicit handshakes.", "speakers": ["claude", "codex", "gemini"], "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"}
|