@agentuity/cli 0.0.11 → 0.0.12
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/cli.ts +43 -2
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +1 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/sound.d.ts +1 -1
- package/dist/sound.d.ts.map +1 -1
- package/dist/tui.d.ts +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cmd/dev/index.ts +0 -2
- package/src/cmd/example/sound.ts +2 -2
- package/src/cmd/project/create.ts +1 -0
- package/src/cmd/project/download.ts +37 -52
- package/src/cmd/project/template-flow.ts +26 -11
- package/src/download.ts +2 -2
- package/src/sound.ts +27 -13
- package/src/tui.ts +5 -3
package/bin/cli.ts
CHANGED
|
@@ -8,7 +8,27 @@ import { detectColorScheme } from '../src/terminal';
|
|
|
8
8
|
import { setColorScheme } from '../src/tui';
|
|
9
9
|
import { getVersion } from '../src/version';
|
|
10
10
|
import { checkLegacyCLI } from '../src/legacy-check';
|
|
11
|
-
import type { LogLevel } from '../src/types';
|
|
11
|
+
import type { CommandContext, LogLevel } from '../src/types';
|
|
12
|
+
|
|
13
|
+
// Cleanup TTY state before exit
|
|
14
|
+
function cleanupAndExit() {
|
|
15
|
+
if (process.stdin.isTTY) {
|
|
16
|
+
process.stdin.setRawMode(false);
|
|
17
|
+
process.stdout.write('\x1B[?25h'); // Restore cursor
|
|
18
|
+
}
|
|
19
|
+
process.exitCode = 0;
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handle Ctrl+C gracefully
|
|
24
|
+
process.once('SIGINT', () => {
|
|
25
|
+
console.log('\n');
|
|
26
|
+
cleanupAndExit();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
process.once('SIGTERM', () => {
|
|
30
|
+
cleanupAndExit();
|
|
31
|
+
});
|
|
12
32
|
|
|
13
33
|
validateRuntime();
|
|
14
34
|
|
|
@@ -61,11 +81,32 @@ const ctx = {
|
|
|
61
81
|
};
|
|
62
82
|
|
|
63
83
|
const commands = await discoverCommands();
|
|
64
|
-
await registerCommands(program, commands, ctx);
|
|
84
|
+
await registerCommands(program, commands, ctx as unknown as CommandContext);
|
|
65
85
|
|
|
66
86
|
try {
|
|
67
87
|
await program.parseAsync(process.argv);
|
|
68
88
|
} catch (error) {
|
|
89
|
+
// Don't log error if it's from Ctrl+C, user cancellation, or signal termination
|
|
90
|
+
if (error instanceof Error) {
|
|
91
|
+
const msg = error.message.toLowerCase();
|
|
92
|
+
if (
|
|
93
|
+
msg.includes('sigint') ||
|
|
94
|
+
msg.includes('sigterm') ||
|
|
95
|
+
msg.includes('user force closed') ||
|
|
96
|
+
msg.includes('cancelled') || // UK
|
|
97
|
+
msg.includes('canceled') || // US
|
|
98
|
+
msg === ''
|
|
99
|
+
) {
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
if ('name' in error && error.name === 'AbortError') {
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Also exit cleanly if error is empty/undefined (user cancellation)
|
|
107
|
+
if (!error) {
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
69
110
|
logger.error('CLI error:', error);
|
|
70
111
|
process.exit(1);
|
|
71
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmd/dev/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmd/dev/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO,qCAyElB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/create.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/create.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,wCA+ClC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/download.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/download.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,UAAU,eAAe;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,YAAY;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf;AAsBD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD9E;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-flow.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/template-flow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template-flow.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/template-flow.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMvC,UAAU,iBAAiB;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0J7E"}
|
package/dist/sound.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function playSound():
|
|
1
|
+
export declare function playSound(): void;
|
|
2
2
|
//# sourceMappingURL=sound.d.ts.map
|
package/dist/sound.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sound.d.ts","sourceRoot":"","sources":["../src/sound.ts"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"sound.d.ts","sourceRoot":"","sources":["../src/sound.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,IAAI,IAAI,CAoChC"}
|
package/dist/tui.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare function error(message: string): void;
|
|
|
17
17
|
/**
|
|
18
18
|
* Print a warning message with a yellow warning icon
|
|
19
19
|
*/
|
|
20
|
-
export declare function warning(message: string): void;
|
|
20
|
+
export declare function warning(message: string, asError?: boolean): void;
|
|
21
21
|
/**
|
|
22
22
|
* Print an info message with a cyan info icon
|
|
23
23
|
*/
|
package/dist/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA+C9C,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAExD;AAUD;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI3C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cmd/dev/index.ts
CHANGED
|
@@ -35,9 +35,7 @@ export const command = createCommand({
|
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
tui.newline();
|
|
39
38
|
tui.info('Starting development server...');
|
|
40
|
-
tui.newline();
|
|
41
39
|
|
|
42
40
|
// Use shell to run in a process group for proper cleanup
|
|
43
41
|
// The 'exec' ensures the shell is replaced by the actual process
|
package/src/cmd/example/sound.ts
CHANGED
|
@@ -6,9 +6,9 @@ export const soundSubcommand: SubcommandDefinition = {
|
|
|
6
6
|
name: 'sound',
|
|
7
7
|
description: 'Test completion sound',
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
handler() {
|
|
10
10
|
tui.info('Playing completion sound...');
|
|
11
|
-
|
|
11
|
+
playSound();
|
|
12
12
|
tui.success('Sound played!');
|
|
13
13
|
},
|
|
14
14
|
};
|
|
@@ -39,6 +39,7 @@ export const createProjectSubcommand = createSubcommand({
|
|
|
39
39
|
const { logger, opts } = ctx;
|
|
40
40
|
await runCreateFlow({
|
|
41
41
|
projectName: opts.name,
|
|
42
|
+
dir: opts.dir,
|
|
42
43
|
template: opts.template,
|
|
43
44
|
templateDir: opts.templateDir,
|
|
44
45
|
templateBranch: opts.templateBranch,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
2
|
import { existsSync, mkdirSync, renameSync, readdirSync, cpSync, rmSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
3
4
|
import { pipeline } from 'node:stream/promises';
|
|
4
5
|
import { createGunzip } from 'node:zlib';
|
|
5
|
-
import { extract } from 'tar-fs';
|
|
6
|
+
import { extract, type Headers } from 'tar-fs';
|
|
6
7
|
import type { Logger } from '@/logger';
|
|
7
8
|
import * as tui from '@/tui';
|
|
8
9
|
import { downloadWithSpinner } from '@/download';
|
|
@@ -27,22 +28,12 @@ interface SetupOptions {
|
|
|
27
28
|
logger: Logger;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// Copy from local directory if provided
|
|
36
|
-
if (templateDir) {
|
|
37
|
-
const { resolve } = await import('node:path');
|
|
38
|
-
const sourceDir = resolve(join(templateDir, template.directory));
|
|
39
|
-
|
|
40
|
-
if (!existsSync(sourceDir)) {
|
|
41
|
-
throw new Error(`Template directory not found: ${sourceDir}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
tui.info(`📦 Copying template from ${sourceDir}...`);
|
|
31
|
+
async function cleanup(sourceDir: string, dest: string) {
|
|
32
|
+
if (!existsSync(sourceDir)) {
|
|
33
|
+
throw new Error(`Template directory not found: ${sourceDir}`);
|
|
34
|
+
}
|
|
45
35
|
|
|
36
|
+
tui.spinner(`📦 Copying template from ${sourceDir}...`, async () => {
|
|
46
37
|
// Copy all files from source to dest
|
|
47
38
|
const files = readdirSync(sourceDir);
|
|
48
39
|
for (const file of files) {
|
|
@@ -54,8 +45,23 @@ export async function downloadTemplate(options: DownloadOptions): Promise<void>
|
|
|
54
45
|
if (existsSync(gi)) {
|
|
55
46
|
renameSync(gi, join(dest, '.gitignore'));
|
|
56
47
|
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function downloadTemplate(options: DownloadOptions): Promise<void> {
|
|
52
|
+
const { dest, template, templateDir, templateBranch } = options;
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
mkdirSync(dest, { recursive: true });
|
|
55
|
+
|
|
56
|
+
// Copy from local directory if provided
|
|
57
|
+
if (templateDir) {
|
|
58
|
+
const sourceDir = resolve(join(templateDir, template.directory));
|
|
59
|
+
|
|
60
|
+
if (!existsSync(sourceDir)) {
|
|
61
|
+
throw new Error(`Template directory not found: ${sourceDir}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return cleanup(sourceDir, dest);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
// Download from GitHub
|
|
@@ -74,65 +80,43 @@ export async function downloadTemplate(options: DownloadOptions): Promise<void>
|
|
|
74
80
|
},
|
|
75
81
|
async (stream) => {
|
|
76
82
|
// Extract only the template directory from tarball
|
|
83
|
+
const prefix = `sdk-${branch}/${templatePath}/`;
|
|
77
84
|
await pipeline(
|
|
78
85
|
stream,
|
|
79
86
|
createGunzip(),
|
|
80
87
|
extract(tempDir, {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return header;
|
|
86
|
-
}
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
-
return null as any;
|
|
88
|
+
filter: (name: string) => name.startsWith(prefix),
|
|
89
|
+
map: (header: Headers) => {
|
|
90
|
+
header.name = header.name.substring(prefix.length);
|
|
91
|
+
return header;
|
|
89
92
|
},
|
|
90
93
|
})
|
|
91
94
|
);
|
|
92
95
|
}
|
|
93
96
|
);
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
const files = readdirSync(tempDir);
|
|
97
|
-
for (const file of files) {
|
|
98
|
-
cpSync(join(tempDir, file), join(dest, file), { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
98
|
+
await cleanup(tempDir, dest);
|
|
101
99
|
|
|
102
|
-
//
|
|
103
|
-
const
|
|
104
|
-
if (
|
|
105
|
-
|
|
100
|
+
// Extra safety: refuse to delete root or home directories
|
|
101
|
+
const home = homedir();
|
|
102
|
+
if (tempDir === '/' || tempDir === home) {
|
|
103
|
+
throw new Error(`Refusing to delete protected path: ${tempDir}`);
|
|
106
104
|
}
|
|
105
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
export async function setupProject(options: SetupOptions): Promise<void> {
|
|
110
109
|
const { dest, projectName, dirName, noInstall, noBuild, logger } = options;
|
|
111
110
|
|
|
112
|
-
process.chdir(dest);
|
|
113
|
-
|
|
114
111
|
// Replace {{PROJECT_NAME}} in files
|
|
115
112
|
tui.info(`🔧 Setting up ${projectName}...`);
|
|
116
113
|
await replaceInFiles(dest, projectName, dirName);
|
|
117
114
|
|
|
118
|
-
// Run setup.ts if it exists (legacy)
|
|
119
|
-
if (await Bun.file('./setup.ts').exists()) {
|
|
120
|
-
await tui.spinner({
|
|
121
|
-
message: 'Running setup script...',
|
|
122
|
-
callback: async () => {
|
|
123
|
-
const proc = Bun.spawn(['bun', './setup.ts'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
124
|
-
const exitCode = await proc.exited;
|
|
125
|
-
if (exitCode !== 0) {
|
|
126
|
-
logger.error('Setup script failed');
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
115
|
// Install dependencies
|
|
133
116
|
if (!noInstall) {
|
|
134
117
|
const exitCode = await tui.runCommand({
|
|
135
118
|
command: 'bun install',
|
|
119
|
+
cwd: dest,
|
|
136
120
|
cmd: ['bun', 'install'],
|
|
137
121
|
clearOnSuccess: true,
|
|
138
122
|
});
|
|
@@ -145,6 +129,7 @@ export async function setupProject(options: SetupOptions): Promise<void> {
|
|
|
145
129
|
if (!noBuild) {
|
|
146
130
|
const exitCode = await tui.runCommand({
|
|
147
131
|
command: 'bun run build',
|
|
132
|
+
cwd: dest,
|
|
148
133
|
cmd: ['bun', 'run', 'build'],
|
|
149
134
|
clearOnSuccess: true,
|
|
150
135
|
});
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { basename, resolve } from 'node:path';
|
|
2
2
|
import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
3
|
+
import { cwd } from 'node:process';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
3
5
|
import enquirer from 'enquirer';
|
|
4
6
|
import type { Logger } from '@/logger';
|
|
5
7
|
import * as tui from '@/tui';
|
|
8
|
+
import { playSound } from '@/sound';
|
|
6
9
|
import { fetchTemplates, type TemplateInfo } from './templates';
|
|
7
10
|
import { downloadTemplate, setupProject } from './download';
|
|
8
11
|
|
|
9
12
|
interface CreateFlowOptions {
|
|
10
13
|
projectName?: string;
|
|
14
|
+
dir?: string;
|
|
11
15
|
template?: string;
|
|
12
16
|
templateDir?: string;
|
|
13
17
|
templateBranch?: string;
|
|
@@ -20,6 +24,7 @@ interface CreateFlowOptions {
|
|
|
20
24
|
export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
21
25
|
const {
|
|
22
26
|
projectName: initialProjectName,
|
|
27
|
+
dir: targetDir,
|
|
23
28
|
template: initialTemplate,
|
|
24
29
|
templateDir,
|
|
25
30
|
templateBranch,
|
|
@@ -64,14 +69,20 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
64
69
|
const dirName = projectName === '.' ? '.' : sanitizeDirectoryName(projectName);
|
|
65
70
|
|
|
66
71
|
// Step 4: Determine destination directory
|
|
67
|
-
|
|
72
|
+
// Expand ~ to home directory
|
|
73
|
+
let expandedTargetDir = targetDir;
|
|
74
|
+
if (expandedTargetDir && expandedTargetDir.startsWith('~')) {
|
|
75
|
+
expandedTargetDir = expandedTargetDir.replace(/^~/, homedir());
|
|
76
|
+
}
|
|
77
|
+
const baseDir = expandedTargetDir ? resolve(expandedTargetDir) : process.cwd();
|
|
78
|
+
const dest = dirName === '.' ? baseDir : resolve(baseDir, dirName);
|
|
68
79
|
const destExists = existsSync(dest);
|
|
69
80
|
const destEmpty = destExists ? readdirSync(dest).length === 0 : true;
|
|
70
81
|
|
|
71
82
|
if (destExists && !destEmpty && dirName !== '.') {
|
|
72
83
|
// In TTY mode, ask if they want to overwrite
|
|
73
84
|
if (process.stdin.isTTY && !skipPrompts) {
|
|
74
|
-
tui.warning(`Directory ${
|
|
85
|
+
tui.warning(`Directory ${dest} already exists and is not empty.`, true);
|
|
75
86
|
const response = await enquirer.prompt<{ overwrite: boolean }>({
|
|
76
87
|
type: 'confirm',
|
|
77
88
|
name: 'overwrite',
|
|
@@ -84,19 +95,23 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
84
95
|
process.exit(0);
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
//
|
|
98
|
+
// Extra safety: refuse to delete root or home directories
|
|
99
|
+
const home = homedir();
|
|
100
|
+
if (dest === '/' || dest === home) {
|
|
101
|
+
logger.fatal(`Refusing to delete protected path: ${dest}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
88
104
|
rmSync(dest, { recursive: true, force: true });
|
|
89
|
-
tui.success(`Deleted ${
|
|
105
|
+
tui.success(`Deleted ${dest}\n`);
|
|
90
106
|
} else {
|
|
91
|
-
logger.fatal(`Directory ${
|
|
107
|
+
logger.fatal(`Directory ${dest} already exists and is not empty.`, true);
|
|
92
108
|
}
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
// Show directory and name confirmation
|
|
96
112
|
if (!skipPrompts) {
|
|
97
|
-
const displayPath = dirName === '.' ? basename(dest) : dirName;
|
|
98
113
|
tui.info(`📁 Project: ${tui.bold(projectName)}`);
|
|
99
|
-
tui.info(`📂 Directory: ${tui.bold(
|
|
114
|
+
tui.info(`📂 Directory: ${tui.bold(dest)}\n`);
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
// Step 5: Select template
|
|
@@ -152,14 +167,14 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
152
167
|
tui.success('✨ Project created successfully!\n');
|
|
153
168
|
tui.info('Next steps:');
|
|
154
169
|
if (dirName !== '.') {
|
|
170
|
+
const dirDisplay = cwd() == targetDir ? basename(dirName) : dest;
|
|
155
171
|
tui.newline();
|
|
156
|
-
console.log(` 1. ${tui.bold(`cd ${
|
|
172
|
+
console.log(` 1. ${tui.bold(`cd ${dirDisplay}`)}`);
|
|
157
173
|
console.log(` 2. ${tui.bold('bun run dev')}`);
|
|
158
174
|
} else {
|
|
159
|
-
console.log(`
|
|
175
|
+
console.log(` ${tui.bold('bun run dev')}`);
|
|
160
176
|
}
|
|
161
|
-
|
|
162
|
-
console.log(`Your agents will be running at ${tui.link('http://localhost:3000')}`);
|
|
177
|
+
playSound();
|
|
163
178
|
}
|
|
164
179
|
|
|
165
180
|
/**
|
package/src/download.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Transform } from 'node:stream';
|
|
1
|
+
import { Transform, Readable } from 'node:stream';
|
|
2
2
|
import * as tui from './tui';
|
|
3
3
|
|
|
4
4
|
export interface DownloadOptions {
|
|
@@ -48,7 +48,7 @@ export async function downloadWithProgress(
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
// Pipe the response through the progress tracker
|
|
51
|
-
const responseStream = response.body as unknown as
|
|
51
|
+
const responseStream = Readable.fromWeb(response.body as unknown as ReadableStream);
|
|
52
52
|
responseStream.pipe(progressStream);
|
|
53
53
|
|
|
54
54
|
return progressStream;
|
package/src/sound.ts
CHANGED
|
@@ -1,25 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { join } from 'node:path';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function playSound(): void {
|
|
4
4
|
const platform = process.platform;
|
|
5
5
|
|
|
6
|
-
let
|
|
6
|
+
let command: string[];
|
|
7
7
|
switch (platform) {
|
|
8
|
-
case 'darwin':
|
|
9
|
-
|
|
8
|
+
case 'darwin': {
|
|
9
|
+
const items = [
|
|
10
|
+
'Blow.aiff',
|
|
11
|
+
'Bottle.aiff',
|
|
12
|
+
'Frog.aiff',
|
|
13
|
+
'Funk.aiff',
|
|
14
|
+
'Glass.aiff',
|
|
15
|
+
'Hero.aiff',
|
|
16
|
+
'Morse.aiff',
|
|
17
|
+
'Ping.aiff',
|
|
18
|
+
'Pop.aiff',
|
|
19
|
+
'Purr.aiff',
|
|
20
|
+
'Sosumi.aiff',
|
|
21
|
+
] as const;
|
|
22
|
+
const file = items[Math.floor(Math.random() * items.length)];
|
|
23
|
+
command = ['afplay', join('/System/Library/Sounds', file)];
|
|
10
24
|
break;
|
|
25
|
+
}
|
|
11
26
|
case 'linux':
|
|
12
|
-
|
|
13
|
-
.quiet()
|
|
14
|
-
.nothrow();
|
|
27
|
+
command = ['paplay', '/usr/share/sounds/freedesktop/stereo/complete.oga'];
|
|
15
28
|
break;
|
|
16
29
|
case 'win32':
|
|
17
|
-
|
|
30
|
+
command = ['rundll32', 'user32.dll,MessageBeep', '0x00000040'];
|
|
18
31
|
break;
|
|
32
|
+
default:
|
|
33
|
+
return;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
36
|
+
Bun.spawn(command, {
|
|
37
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
38
|
+
}).unref();
|
|
25
39
|
}
|
package/src/tui.ts
CHANGED
|
@@ -85,8 +85,8 @@ export function error(message: string): void {
|
|
|
85
85
|
/**
|
|
86
86
|
* Print a warning message with a yellow warning icon
|
|
87
87
|
*/
|
|
88
|
-
export function warning(message: string): void {
|
|
89
|
-
const color = getColor('warning');
|
|
88
|
+
export function warning(message: string, asError = false): void {
|
|
89
|
+
const color = asError ? getColor('error') : getColor('warning');
|
|
90
90
|
const reset = COLORS.reset;
|
|
91
91
|
console.log(`${color}${ICONS.warning} ${message}${reset}`);
|
|
92
92
|
}
|
|
@@ -805,7 +805,9 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
|
|
|
805
805
|
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
806
806
|
|
|
807
807
|
// Show compact success: ✓ command
|
|
808
|
-
process.stdout.write(
|
|
808
|
+
process.stdout.write(
|
|
809
|
+
`\r\x1b[K${green}${ICONS.success}${reset} ${cmdColor}${displayCmd}${reset}\n`
|
|
810
|
+
);
|
|
809
811
|
} else {
|
|
810
812
|
// Determine how many lines to show in final output
|
|
811
813
|
const finalLinesToShow = exitCode === 0 ? 3 : 10;
|