@dmsdc-ai/aigentry-telepty 0.1.6 → 0.1.8
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 +40 -48
- package/install.js +96 -64
- package/install.ps1 +1 -0
- package/install.sh +1 -0
- package/interactive-terminal.js +54 -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,8 @@ 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 { attachInteractiveTerminal } = require('./interactive-terminal');
|
|
13
|
+
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
12
14
|
const args = process.argv.slice(2);
|
|
13
15
|
|
|
14
16
|
// Check for updates unless explicitly disabled for tests/CI.
|
|
@@ -91,29 +93,24 @@ async function ensureDaemonRunning() {
|
|
|
91
93
|
async function manageInteractiveAttach(sessionId, targetHost) {
|
|
92
94
|
const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
|
|
93
95
|
const ws = new WebSocket(wsUrl);
|
|
94
|
-
let
|
|
95
|
-
let resizeHandler = null;
|
|
96
|
+
let cleanupTerminal = null;
|
|
96
97
|
return new Promise((resolve) => {
|
|
97
98
|
ws.on('open', () => {
|
|
98
99
|
// Set Ghostty tab title to show session ID
|
|
99
100
|
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
|
|
100
101
|
console.log(`\n\x1b[32mEntered room '${sessionId}'.\x1b[0m\n`);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
process.stdout.on('resize', resizeHandler);
|
|
106
|
-
resizeHandler();
|
|
102
|
+
cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
|
|
103
|
+
onData: (d) => ws.send(JSON.stringify({ type: 'input', data: d.toString() })),
|
|
104
|
+
onResize: () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }))
|
|
105
|
+
});
|
|
107
106
|
});
|
|
108
107
|
ws.on('message', m => {
|
|
109
108
|
const msg = JSON.parse(m);
|
|
110
109
|
if (msg.type === 'output') process.stdout.write(msg.data);
|
|
111
110
|
});
|
|
112
111
|
ws.on('close', async () => {
|
|
113
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
114
112
|
process.stdout.write(`\x1b]0;\x07`); // Restore default terminal title
|
|
115
|
-
if (
|
|
116
|
-
if (resizeHandler) process.stdout.off('resize', resizeHandler);
|
|
113
|
+
if (cleanupTerminal) cleanupTerminal();
|
|
117
114
|
|
|
118
115
|
// Check if other clients are still attached before destroying
|
|
119
116
|
try {
|
|
@@ -152,6 +149,7 @@ async function manageInteractive() {
|
|
|
152
149
|
{ title: '🔌 Allow inject (Run CLI with inject)', value: 'allow' },
|
|
153
150
|
{ title: '💬 Send message to a room (Inject command)', value: 'inject' },
|
|
154
151
|
{ title: '📋 View all open rooms (List sessions)', value: 'list' },
|
|
152
|
+
{ title: '🧠 Install telepty skills', value: 'install-skills' },
|
|
155
153
|
{ title: '🔄 Update telepty to latest version', value: 'update' },
|
|
156
154
|
{ title: '❌ Exit', value: 'exit' }
|
|
157
155
|
]
|
|
@@ -189,6 +187,15 @@ async function manageInteractive() {
|
|
|
189
187
|
continue;
|
|
190
188
|
}
|
|
191
189
|
|
|
190
|
+
if (response.action === 'install-skills') {
|
|
191
|
+
try {
|
|
192
|
+
await runInteractiveSkillInstaller({ packageRoot: __dirname, cwd: process.cwd() });
|
|
193
|
+
} catch (e) {
|
|
194
|
+
console.error(`\n❌ ${e.message}\n`);
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
192
199
|
if (response.action === 'list') {
|
|
193
200
|
console.log('\n');
|
|
194
201
|
const sessions = await discoverSessions();
|
|
@@ -469,11 +476,13 @@ async function main() {
|
|
|
469
476
|
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
|
|
470
477
|
console.log(`\x1b[32m⚡ '${command}' is now session '\x1b[36m${sessionId}\x1b[32m'. Inject allowed.\x1b[0m\n`);
|
|
471
478
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
479
|
+
const cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
|
|
480
|
+
onData: (data) => {
|
|
481
|
+
child.write(data.toString());
|
|
482
|
+
},
|
|
483
|
+
onResize: () => {
|
|
484
|
+
child.resize(process.stdout.columns, process.stdout.rows);
|
|
485
|
+
}
|
|
477
486
|
});
|
|
478
487
|
|
|
479
488
|
// Relay PTY output to current terminal + send to daemon for attach clients
|
|
@@ -484,14 +493,9 @@ async function main() {
|
|
|
484
493
|
}
|
|
485
494
|
});
|
|
486
495
|
|
|
487
|
-
// Handle terminal resize
|
|
488
|
-
process.stdout.on('resize', () => {
|
|
489
|
-
child.resize(process.stdout.columns, process.stdout.rows);
|
|
490
|
-
});
|
|
491
|
-
|
|
492
496
|
// Handle child exit
|
|
493
497
|
child.onExit(({ exitCode }) => {
|
|
494
|
-
|
|
498
|
+
cleanupTerminal();
|
|
495
499
|
process.stdout.write(`\x1b]0;\x07`);
|
|
496
500
|
console.log(`\n\x1b[33mSession '${sessionId}' exited (code ${exitCode}).\x1b[0m`);
|
|
497
501
|
|
|
@@ -540,8 +544,7 @@ async function main() {
|
|
|
540
544
|
|
|
541
545
|
const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
|
|
542
546
|
const ws = new WebSocket(wsUrl);
|
|
543
|
-
let
|
|
544
|
-
let resizeHandler = null;
|
|
547
|
+
let cleanupTerminal = null;
|
|
545
548
|
|
|
546
549
|
ws.on('open', () => {
|
|
547
550
|
// Set Ghostty tab title to show session ID
|
|
@@ -549,25 +552,18 @@ async function main() {
|
|
|
549
552
|
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}${hostSuffix}\x07`);
|
|
550
553
|
console.log(`\x1b[32mEntered room '${sessionId}'${hostSuffix ? ` (${targetHost})` : ''}.\x1b[0m\n`);
|
|
551
554
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
cols: process.stdout.columns,
|
|
565
|
-
rows: process.stdout.rows
|
|
566
|
-
}));
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
process.stdout.on('resize', resizeHandler);
|
|
570
|
-
resizeHandler(); // Initial resize
|
|
555
|
+
cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
|
|
556
|
+
onData: (data) => {
|
|
557
|
+
ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
|
|
558
|
+
},
|
|
559
|
+
onResize: () => {
|
|
560
|
+
ws.send(JSON.stringify({
|
|
561
|
+
type: 'resize',
|
|
562
|
+
cols: process.stdout.columns,
|
|
563
|
+
rows: process.stdout.rows
|
|
564
|
+
}));
|
|
565
|
+
}
|
|
566
|
+
});
|
|
571
567
|
});
|
|
572
568
|
|
|
573
569
|
ws.on('message', (message) => {
|
|
@@ -578,12 +574,8 @@ async function main() {
|
|
|
578
574
|
});
|
|
579
575
|
|
|
580
576
|
ws.on('close', async (code, reason) => {
|
|
581
|
-
if (process.stdin.isTTY) {
|
|
582
|
-
process.stdin.setRawMode(false);
|
|
583
|
-
}
|
|
584
577
|
process.stdout.write(`\x1b]0;\x07`); // Restore default terminal title
|
|
585
|
-
if (
|
|
586
|
-
if (resizeHandler) process.stdout.off('resize', resizeHandler);
|
|
578
|
+
if (cleanupTerminal) cleanupTerminal();
|
|
587
579
|
|
|
588
580
|
// Check if other clients are still attached before destroying
|
|
589
581
|
try {
|
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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function removeListener(stream, eventName, handler) {
|
|
4
|
+
if (!handler || !stream) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (typeof stream.off === 'function') {
|
|
9
|
+
stream.off(eventName, handler);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (typeof stream.removeListener === 'function') {
|
|
14
|
+
stream.removeListener(eventName, handler);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function attachInteractiveTerminal(input, output, handlers = {}) {
|
|
19
|
+
const { onData = null, onResize = null } = handlers;
|
|
20
|
+
|
|
21
|
+
if (input && input.isTTY && typeof input.setRawMode === 'function') {
|
|
22
|
+
input.setRawMode(true);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (input && typeof input.resume === 'function') {
|
|
26
|
+
input.resume();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (input && onData) {
|
|
30
|
+
input.on('data', onData);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (output && onResize) {
|
|
34
|
+
output.on('resize', onResize);
|
|
35
|
+
onResize();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
removeListener(input, 'data', onData);
|
|
40
|
+
removeListener(output, 'resize', onResize);
|
|
41
|
+
|
|
42
|
+
if (input && input.isTTY && typeof input.setRawMode === 'function') {
|
|
43
|
+
input.setRawMode(false);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (input && typeof input.pause === 'function') {
|
|
47
|
+
input.pause();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
attachInteractiveTerminal
|
|
54
|
+
};
|
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.8",
|
|
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 test/interactive-terminal.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 test/interactive-terminal.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 test/interactive-terminal.test.js"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [],
|
|
15
15
|
"author": "",
|