wtsm 1.0.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.
- package/README.md +75 -0
- package/index.js +427 -0
- package/package.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Windows Terminal Session Manager (`s`)
|
|
2
|
+
|
|
3
|
+
A CLI tool to define, save, and launch named Windows Terminal sessions with specific layouts and commands.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Ensure the tool is linked globally:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm link
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage Guide
|
|
14
|
+
|
|
15
|
+
### 1. Creating & Managing Sessions
|
|
16
|
+
|
|
17
|
+
**Create a new session:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
s create <name>
|
|
21
|
+
# Example:
|
|
22
|
+
s create work
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Add current directory to a session:**
|
|
26
|
+
Navigate to the folder you want to add, then run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
s add <name>
|
|
30
|
+
# Example:
|
|
31
|
+
cd C:\Projects\MyBackend
|
|
32
|
+
s add work
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
_It will ask for an optional startup command (e.g., `npm start` or `git status`)._
|
|
36
|
+
|
|
37
|
+
**Add interactively:**
|
|
38
|
+
If you don't provide a name, it will show a list:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
s add
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
_(Type the number or name of the session to select it)_
|
|
45
|
+
|
|
46
|
+
### 2. Viewing Sessions
|
|
47
|
+
|
|
48
|
+
**Interactive Explorer:**
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
s ls
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **↑ / ↓**: Navigate the list of sessions.
|
|
55
|
+
- **→**: View tabs inside the selected session.
|
|
56
|
+
- **Ctrl+D**: Delete the selected session or tab.
|
|
57
|
+
- **q**, **Esc**, or **Ctrl+C**: Exit.
|
|
58
|
+
|
|
59
|
+
### 3. Launching Sessions
|
|
60
|
+
|
|
61
|
+
**Launch a session:**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
s <name>
|
|
65
|
+
# Example:
|
|
66
|
+
s work
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
_Opens a new Windows Terminal window with all configured tabs._
|
|
70
|
+
|
|
71
|
+
### 4 Not implemented features
|
|
72
|
+
|
|
73
|
+
- clean scroll page,
|
|
74
|
+
- Automatically use current shell
|
|
75
|
+
- Pane capture
|
package/index.js
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import readline from 'readline';
|
|
11
|
+
|
|
12
|
+
const SESSION_FILE = path.join(os.homedir(), '.wts-sessions.json');
|
|
13
|
+
const program = new Command();
|
|
14
|
+
readline.emitKeypressEvents(process.stdin);
|
|
15
|
+
|
|
16
|
+
// --- Data Helpers ---
|
|
17
|
+
|
|
18
|
+
function loadSessions() {
|
|
19
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error(chalk.red('Error reading session file.'));
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function saveSessions(sessions) {
|
|
31
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(sessions, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// --- Logic Helpers ---
|
|
35
|
+
|
|
36
|
+
async function addToSession(sessionName, cwd) {
|
|
37
|
+
const sessions = loadSessions();
|
|
38
|
+
if (!sessions[sessionName]) {
|
|
39
|
+
console.log(chalk.red(`Session '${sessionName}' not found. Use 's create ${sessionName}' first.`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleExit = (str, key) => {
|
|
44
|
+
if (key && (key.ctrl && key.name === 'c' || key.name === 'q' || key.name === 'escape')) {
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
process.stdin.on('keypress', handleExit);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const { command } = await inquirer.prompt([
|
|
52
|
+
{
|
|
53
|
+
type: 'input',
|
|
54
|
+
name: 'command',
|
|
55
|
+
message: ' Command to run on start (optional):',
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
sessions[sessionName].push({
|
|
60
|
+
path: cwd,
|
|
61
|
+
command: command.trim() || null
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
saveSessions(sessions);
|
|
65
|
+
console.log(chalk.green(`Added current path to session '${sessionName}'.`));
|
|
66
|
+
} finally {
|
|
67
|
+
process.stdin.removeListener('keypress', handleExit);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function restoreSession(sessionName) {
|
|
72
|
+
const sessions = loadSessions();
|
|
73
|
+
if (!sessions[sessionName]) {
|
|
74
|
+
console.log(chalk.red(`Session '${sessionName}' not found.`));
|
|
75
|
+
console.log('Available sessions:', Object.keys(sessions).join(', '));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const tabs = sessions[sessionName];
|
|
80
|
+
if (tabs.length === 0) {
|
|
81
|
+
console.log(chalk.yellow(`Session '${sessionName}' is empty.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const args = [];
|
|
86
|
+
|
|
87
|
+
tabs.forEach((tab, index) => {
|
|
88
|
+
if (index > 0) {
|
|
89
|
+
args.push(';');
|
|
90
|
+
args.push('new-tab');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Force PowerShell profile
|
|
94
|
+
args.push('-p');
|
|
95
|
+
args.push('Windows PowerShell');
|
|
96
|
+
|
|
97
|
+
if (tab.path) {
|
|
98
|
+
args.push('-d');
|
|
99
|
+
args.push(tab.path);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (tab.command) {
|
|
103
|
+
// PowerShell command execution
|
|
104
|
+
args.push('powershell'); // Redundant if profile is set, but ensures command syntax works if profile is weird
|
|
105
|
+
args.push('-NoExit');
|
|
106
|
+
args.push('-Command');
|
|
107
|
+
args.push(tab.command);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log(chalk.blue(`Launching session '${sessionName}'...`));
|
|
112
|
+
const subprocess = spawn('wt', args, { detached: true, stdio: 'ignore', shell: false });
|
|
113
|
+
subprocess.unref();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async function interactiveList() {
|
|
119
|
+
const sessions = loadSessions();
|
|
120
|
+
const sessionNames = Object.keys(sessions);
|
|
121
|
+
|
|
122
|
+
if (sessionNames.length === 0) {
|
|
123
|
+
console.log("No sessions found.");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// State
|
|
128
|
+
let view = 'sessions'; // 'sessions' | 'tabs'
|
|
129
|
+
let selectedIndex = 0;
|
|
130
|
+
let activeSessionName = null;
|
|
131
|
+
let tabs = [];
|
|
132
|
+
|
|
133
|
+
// Input Handling
|
|
134
|
+
const { stdin, stdout } = process;
|
|
135
|
+
readline.emitKeypressEvents(stdin); // Required for keypress events
|
|
136
|
+
stdin.setRawMode(true);
|
|
137
|
+
stdin.resume();
|
|
138
|
+
stdin.setEncoding('utf8');
|
|
139
|
+
|
|
140
|
+
// Helper to hide cursor
|
|
141
|
+
stdout.write('\x1B[?25l');
|
|
142
|
+
|
|
143
|
+
function cleanup() {
|
|
144
|
+
stdout.write('\x1B[?25h'); // Show cursor
|
|
145
|
+
stdin.setRawMode(false);
|
|
146
|
+
stdin.pause();
|
|
147
|
+
stdin.removeListener('keypress', handleInput);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function render() {
|
|
151
|
+
// Clear screen for full-screen feel, or use clearDown if preferred.
|
|
152
|
+
// console.clear() is robust.
|
|
153
|
+
console.clear();
|
|
154
|
+
|
|
155
|
+
if (view === 'sessions') {
|
|
156
|
+
console.log(chalk.cyan.bold(" Windows Terminal Sessions"));
|
|
157
|
+
console.log(chalk.gray(" -------------------------"));
|
|
158
|
+
|
|
159
|
+
sessionNames.forEach((name, idx) => {
|
|
160
|
+
if (idx === selectedIndex) {
|
|
161
|
+
console.log(chalk.green.bold(`> ${name}`));
|
|
162
|
+
} else {
|
|
163
|
+
console.log(` ${name}`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
console.log(chalk.gray("\n (↑/↓: Move, Enter/→: Open, Ctrl+D: Delete, q/Esc: Exit)"));
|
|
168
|
+
|
|
169
|
+
} else if (view === 'tabs') {
|
|
170
|
+
console.log(chalk.cyan.bold(` Session: ${activeSessionName}`));
|
|
171
|
+
console.log(chalk.gray(" -------------------------"));
|
|
172
|
+
|
|
173
|
+
if (tabs.length === 0) {
|
|
174
|
+
console.log(" (Empty Session)");
|
|
175
|
+
} else {
|
|
176
|
+
tabs.forEach((tab, idx) => {
|
|
177
|
+
const title = tab.path + (tab.command ? ` [${tab.command}]` : '');
|
|
178
|
+
if (idx === selectedIndex) {
|
|
179
|
+
console.log(chalk.green.bold(`> ${idx + 1}. ${title}`));
|
|
180
|
+
} else {
|
|
181
|
+
console.log(` ${idx + 1}. ${title}`);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log(chalk.gray("\n (←: Back, Ctrl+D: Delete, q/Esc: Exit)"));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const handleInput = (str, key) => {
|
|
191
|
+
if (!key) return;
|
|
192
|
+
|
|
193
|
+
// Ctrl+C (End of Text) or 'q' or Esc
|
|
194
|
+
if (key.sequence === '\u0003' || key.name === 'q' || key.name === 'escape') {
|
|
195
|
+
cleanup();
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Ctrl+D (Delete)
|
|
200
|
+
if (key.ctrl && key.name === 'd') {
|
|
201
|
+
if (view === 'sessions') {
|
|
202
|
+
const nameToDelete = sessionNames[selectedIndex];
|
|
203
|
+
if (nameToDelete) {
|
|
204
|
+
delete sessions[nameToDelete];
|
|
205
|
+
saveSessions(sessions);
|
|
206
|
+
// Refresh list
|
|
207
|
+
const idx = sessionNames.indexOf(nameToDelete);
|
|
208
|
+
sessionNames.splice(idx, 1);
|
|
209
|
+
if (selectedIndex >= sessionNames.length) selectedIndex = Math.max(0, sessionNames.length - 1);
|
|
210
|
+
render();
|
|
211
|
+
}
|
|
212
|
+
} else if (view === 'tabs') {
|
|
213
|
+
const currentTabs = sessions[activeSessionName];
|
|
214
|
+
if (currentTabs && currentTabs.length > 0) {
|
|
215
|
+
currentTabs.splice(selectedIndex, 1);
|
|
216
|
+
saveSessions(sessions);
|
|
217
|
+
if (selectedIndex >= currentTabs.length) selectedIndex = Math.max(0, currentTabs.length - 1);
|
|
218
|
+
render();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Up Arrow
|
|
225
|
+
if (key.name === 'up') {
|
|
226
|
+
selectedIndex--;
|
|
227
|
+
const max = view === 'sessions' ? sessionNames.length : (sessions[activeSessionName] || []).length;
|
|
228
|
+
if (max > 0) {
|
|
229
|
+
if (selectedIndex < 0) selectedIndex = max - 1; // Wrap top
|
|
230
|
+
render();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Down Arrow
|
|
235
|
+
if (key.name === 'down') {
|
|
236
|
+
selectedIndex++;
|
|
237
|
+
const max = view === 'sessions' ? sessionNames.length : (sessions[activeSessionName] || []).length;
|
|
238
|
+
if (max > 0) {
|
|
239
|
+
if (selectedIndex >= max) selectedIndex = 0; // Wrap bottom
|
|
240
|
+
render();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Right Arrow or Enter
|
|
245
|
+
if (key.name === 'right' || key.name === 'return' || key.name === 'enter') {
|
|
246
|
+
if (view === 'sessions') {
|
|
247
|
+
activeSessionName = sessionNames[selectedIndex];
|
|
248
|
+
tabs = sessions[activeSessionName];
|
|
249
|
+
if (tabs) { // Only switch if valid
|
|
250
|
+
view = 'tabs';
|
|
251
|
+
selectedIndex = 0; // Reset index for tabs list
|
|
252
|
+
render();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Left Arrow
|
|
258
|
+
if (key.name === 'left') {
|
|
259
|
+
if (view === 'tabs') {
|
|
260
|
+
view = 'sessions';
|
|
261
|
+
// Try to restore index of previous session
|
|
262
|
+
const prevIdx = sessionNames.indexOf(activeSessionName);
|
|
263
|
+
selectedIndex = prevIdx >= 0 ? prevIdx : 0;
|
|
264
|
+
|
|
265
|
+
activeSessionName = null;
|
|
266
|
+
tabs = [];
|
|
267
|
+
render();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Initial Render
|
|
273
|
+
render();
|
|
274
|
+
stdin.on('keypress', handleInput);
|
|
275
|
+
|
|
276
|
+
// Return a promise that never resolves so the program waits for input
|
|
277
|
+
return new Promise(() => { });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
// --- Commands ---
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
program
|
|
287
|
+
.name('s')
|
|
288
|
+
.description('Windows Terminal Session Manager')
|
|
289
|
+
.version('1.0.0');
|
|
290
|
+
|
|
291
|
+
// 1. s create <name>
|
|
292
|
+
program
|
|
293
|
+
.command('create <name>')
|
|
294
|
+
.description('Create a new session config')
|
|
295
|
+
.action((name) => {
|
|
296
|
+
const sessions = loadSessions();
|
|
297
|
+
if (sessions[name]) {
|
|
298
|
+
console.log(chalk.yellow(`Session '${name}' already exists.`));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
sessions[name] = [];
|
|
302
|
+
saveSessions(sessions);
|
|
303
|
+
console.log(chalk.green(`Session '${name}' created.`));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// 2. s add [name] (Modified)
|
|
307
|
+
program
|
|
308
|
+
.command('add [name]')
|
|
309
|
+
.description('Add current path to a session')
|
|
310
|
+
.action(async (nameOrIndex) => {
|
|
311
|
+
const sessions = loadSessions();
|
|
312
|
+
const sessionNames = Object.keys(sessions);
|
|
313
|
+
|
|
314
|
+
if (sessionNames.length === 0) {
|
|
315
|
+
console.log(chalk.red("No sessions found. Create one first with 's create <name>'"));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let targetSession = nameOrIndex;
|
|
320
|
+
|
|
321
|
+
if (!targetSession) {
|
|
322
|
+
// Print numbered list
|
|
323
|
+
console.log(chalk.cyan.bold(" Available Sessions"));
|
|
324
|
+
console.log(chalk.gray(" ------------------"));
|
|
325
|
+
sessionNames.forEach((name, idx) => {
|
|
326
|
+
console.log(` ${chalk.yellow(idx + 1)}. ${chalk.white(name)}`);
|
|
327
|
+
});
|
|
328
|
+
console.log();
|
|
329
|
+
|
|
330
|
+
const handleExit = (str, key) => {
|
|
331
|
+
if (key && (key.ctrl && key.name === 'c' || key.name === 'q' || key.name === 'escape')) {
|
|
332
|
+
process.exit(0);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
process.stdin.on('keypress', handleExit);
|
|
336
|
+
|
|
337
|
+
let answer;
|
|
338
|
+
try {
|
|
339
|
+
answer = await inquirer.prompt([
|
|
340
|
+
{
|
|
341
|
+
type: 'input',
|
|
342
|
+
name: 'input',
|
|
343
|
+
message: ' Select session (number or name):',
|
|
344
|
+
validate: (input) => {
|
|
345
|
+
if (input === 'q') return true;
|
|
346
|
+
if (!input) return "Please enter a value";
|
|
347
|
+
const num = parseInt(input, 10);
|
|
348
|
+
if (!isNaN(num)) {
|
|
349
|
+
if (num < 1 || num > sessionNames.length) return "Invalid number";
|
|
350
|
+
} else {
|
|
351
|
+
if (!sessions[input]) return "Session not found";
|
|
352
|
+
}
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
]);
|
|
357
|
+
} finally {
|
|
358
|
+
process.stdin.removeListener('keypress', handleExit);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (answer.input === 'q') process.exit(0);
|
|
362
|
+
|
|
363
|
+
const num = parseInt(answer.input, 10);
|
|
364
|
+
if (!isNaN(num)) {
|
|
365
|
+
targetSession = sessionNames[num - 1];
|
|
366
|
+
} else {
|
|
367
|
+
targetSession = answer.input;
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
// Check if user provided a number directly in CLI: `s add 1`
|
|
371
|
+
const num = parseInt(nameOrIndex, 10);
|
|
372
|
+
if (!isNaN(num)) {
|
|
373
|
+
if (num >= 1 && num <= sessionNames.length) {
|
|
374
|
+
targetSession = sessionNames[num - 1];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
await addToSession(targetSession, process.cwd());
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// 5. s ls (Renamed & Interactive)
|
|
383
|
+
program
|
|
384
|
+
.command('ls [name]')
|
|
385
|
+
.alias('list')
|
|
386
|
+
.description('List sessions (interactive) or tabs in a session')
|
|
387
|
+
.action((name) => {
|
|
388
|
+
if (!name) {
|
|
389
|
+
// Interactive Mode
|
|
390
|
+
interactiveList();
|
|
391
|
+
} else {
|
|
392
|
+
// List tabs in specific session (Legacy/Scriptable mode)
|
|
393
|
+
const sessions = loadSessions();
|
|
394
|
+
if (!sessions[name]) {
|
|
395
|
+
console.log(chalk.red(`Session '${name}' not found.`));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
console.log(chalk.cyan(`Session: ${name}`));
|
|
399
|
+
const tabs = sessions[name];
|
|
400
|
+
if (tabs.length === 0) {
|
|
401
|
+
console.log(" (Empty)");
|
|
402
|
+
} else {
|
|
403
|
+
tabs.forEach((tab, idx) => {
|
|
404
|
+
console.log(` ${chalk.bold(idx + 1)}. Path: ${tab.path}`);
|
|
405
|
+
if (tab.command) console.log(` Cmd: ${chalk.gray(tab.command)}`);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
// --- Custom Dispatcher ---
|
|
413
|
+
const rawArgs = process.argv.slice(2);
|
|
414
|
+
const knownCommands = ['create', 'add', 'ls', 'list', 'help', '--help', '-h', '--version', '-V'];
|
|
415
|
+
|
|
416
|
+
if (rawArgs.length > 0 && !knownCommands.includes(rawArgs[0])) {
|
|
417
|
+
const sessionName = rawArgs[0];
|
|
418
|
+
const secondArg = rawArgs[1];
|
|
419
|
+
|
|
420
|
+
if (secondArg === 'add') {
|
|
421
|
+
addToSession(sessionName, process.cwd());
|
|
422
|
+
} else {
|
|
423
|
+
restoreSession(sessionName);
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
program.parse(process.argv);
|
|
427
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wtsm",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Windows Terminal Session Manager",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"s": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"chalk": "^5.6.2",
|
|
17
|
+
"commander": "^14.0.2",
|
|
18
|
+
"inquirer": "^13.2.1"
|
|
19
|
+
}
|
|
20
|
+
}
|