@telemetryos/cli 1.7.5 → 1.8.1
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/CHANGELOG.md +22 -0
- package/dist/commands/init.js +11 -0
- package/dist/services/generate-application.d.ts +1 -0
- package/dist/services/generate-application.js +127 -4
- package/dist/utils/validate-project-name.d.ts +19 -0
- package/dist/utils/validate-project-name.js +44 -0
- package/package.json +2 -2
- package/templates/vite-react-typescript/CLAUDE.md +68 -1244
- package/templates/vite-react-typescript/_claude/settings.local.json +17 -0
- package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +313 -0
- package/templates/vite-react-typescript/_claude/skills/tos-debugging/SKILL.md +299 -0
- package/templates/vite-react-typescript/_claude/skills/tos-media-api/SKILL.md +335 -0
- package/templates/vite-react-typescript/_claude/skills/tos-proxy-fetch/SKILL.md +319 -0
- package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +332 -0
- package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +252 -0
- package/templates/vite-react-typescript/_claude/skills/tos-settings-ui/SKILL.md +636 -0
- package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +359 -0
- package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +384 -0
- package/templates/vite-react-typescript/src/views/Render.css +1 -1
- package/templates/vite-react-typescript/src/views/Render.tsx +1 -1
- package/templates/vite-react-typescript/src/views/Settings.tsx +2 -2
- package/templates/vite-react-typescript/AGENTS.md +0 -7
- /package/templates/vite-react-typescript/{gitignore → _gitignore} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @telemetryos/cli
|
|
2
2
|
|
|
3
|
+
## 1.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix uncaught exception that broke SDK bridge messages
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @telemetryos/development-application-host-ui@1.8.1
|
|
10
|
+
|
|
11
|
+
## 1.8.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- Adds currency API, claude code skills, initializes git repos
|
|
16
|
+
- Adds currency API to root-sdk and sdk
|
|
17
|
+
- Adds Claude Code skills to tos init apps
|
|
18
|
+
- tos init initializes a git repo for your project
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- @telemetryos/development-application-host-ui@1.8.0
|
|
24
|
+
|
|
3
25
|
## 1.7.5
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/commands/init.js
CHANGED
|
@@ -85,5 +85,16 @@ async function handleInitCommand(projectName, options) {
|
|
|
85
85
|
progressFn: (createdFilePath) => {
|
|
86
86
|
console.log(`.${path.sep}${path.relative(process.cwd(), createdFilePath)}`);
|
|
87
87
|
},
|
|
88
|
+
confirmOverwrite: async () => {
|
|
89
|
+
const { proceed } = await inquirer.prompt([
|
|
90
|
+
{
|
|
91
|
+
type: 'confirm',
|
|
92
|
+
name: 'proceed',
|
|
93
|
+
message: 'Do you want to continue and overwrite these files?',
|
|
94
|
+
default: false,
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
97
|
+
return proceed;
|
|
98
|
+
},
|
|
88
99
|
});
|
|
89
100
|
}
|
|
@@ -6,5 +6,6 @@ export type GenerateApplicationOptions = {
|
|
|
6
6
|
template: string;
|
|
7
7
|
projectPath: string;
|
|
8
8
|
progressFn: (createdFilePath: string) => void;
|
|
9
|
+
confirmOverwrite?: () => Promise<boolean>;
|
|
9
10
|
};
|
|
10
11
|
export declare function generateApplication(options: GenerateApplicationOptions): Promise<void>;
|
|
@@ -1,12 +1,52 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
1
|
+
import { spawn, execSync } from 'child_process';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
const ignoredTemplateFiles = ['.DS_Store', 'thumbs.db', 'node_modules', '.git', 'dist'];
|
|
5
5
|
const templatesDir = path.join(import.meta.dirname, '../../templates');
|
|
6
|
-
const dotfileNames = ['
|
|
6
|
+
const dotfileNames = ['_gitignore', '_claude'];
|
|
7
|
+
// ANSI color codes for terminal output
|
|
8
|
+
const ansiGreen = '\u001b[32m';
|
|
9
|
+
const ansiCyan = '\u001b[36m';
|
|
10
|
+
const ansiBold = '\u001b[1m';
|
|
11
|
+
const ansiReset = '\u001b[0m';
|
|
12
|
+
// Files that can exist in a directory without being considered conflicts
|
|
13
|
+
const safeExistingFiles = [
|
|
14
|
+
'.DS_Store',
|
|
15
|
+
'.git',
|
|
16
|
+
'.gitignore',
|
|
17
|
+
'.gitattributes',
|
|
18
|
+
'.idea',
|
|
19
|
+
'.vscode',
|
|
20
|
+
'Thumbs.db',
|
|
21
|
+
'LICENSE',
|
|
22
|
+
'README.md',
|
|
23
|
+
];
|
|
7
24
|
export async function generateApplication(options) {
|
|
8
|
-
const { name, description, author, version, template, projectPath, progressFn } = options;
|
|
25
|
+
const { name, description, author, version, template, projectPath, progressFn, confirmOverwrite, } = options;
|
|
26
|
+
// Check for directory conflicts before proceeding
|
|
27
|
+
const conflicts = await checkDirectoryConflicts(projectPath);
|
|
28
|
+
if (conflicts.length > 0) {
|
|
29
|
+
console.log(`\nThe directory ${name} contains files that could conflict:\n`);
|
|
30
|
+
conflicts.forEach((file) => console.log(` ${file}`));
|
|
31
|
+
console.log();
|
|
32
|
+
if (confirmOverwrite) {
|
|
33
|
+
const proceed = await confirmOverwrite();
|
|
34
|
+
if (!proceed) {
|
|
35
|
+
console.log('Aborting installation');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log('Aborting installation');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
9
44
|
await fs.mkdir(projectPath, { recursive: true });
|
|
45
|
+
// Initialize git repo early (before template dependencies that may have git hooks)
|
|
46
|
+
const gitInitialized = tryGitInit(projectPath);
|
|
47
|
+
if (gitInitialized) {
|
|
48
|
+
console.log('\nInitialized a git repository');
|
|
49
|
+
}
|
|
10
50
|
await copyDir(path.join(templatesDir, template), projectPath, {
|
|
11
51
|
name,
|
|
12
52
|
version,
|
|
@@ -16,6 +56,11 @@ export async function generateApplication(options) {
|
|
|
16
56
|
// Install latest versions of @telemetryos/sdk and @telemetryos/cli
|
|
17
57
|
console.log('\nInstalling dependencies...');
|
|
18
58
|
await installPackages(projectPath);
|
|
59
|
+
// Create initial commit after all files are in place
|
|
60
|
+
if (gitInitialized) {
|
|
61
|
+
await tryGitCommit(projectPath);
|
|
62
|
+
}
|
|
63
|
+
printSuccessMessage(name, projectPath);
|
|
19
64
|
}
|
|
20
65
|
async function installPackages(projectPath) {
|
|
21
66
|
// Install SDK as a regular dependency
|
|
@@ -55,13 +100,91 @@ async function installPackages(projectPath) {
|
|
|
55
100
|
});
|
|
56
101
|
});
|
|
57
102
|
}
|
|
103
|
+
function isInGitRepository(cwd) {
|
|
104
|
+
try {
|
|
105
|
+
execSync('git rev-parse --is-inside-work-tree', { cwd, stdio: 'ignore' });
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function tryGitInit(projectPath) {
|
|
113
|
+
try {
|
|
114
|
+
execSync('git --version', { stdio: 'ignore' });
|
|
115
|
+
if (isInGitRepository(projectPath)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
execSync('git init', { cwd: projectPath, stdio: 'ignore' });
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function tryGitCommit(projectPath) {
|
|
126
|
+
try {
|
|
127
|
+
execSync('git add -A', { cwd: projectPath, stdio: 'ignore' });
|
|
128
|
+
execSync('git commit -m "Initialize project using TelemetryOS CLI"', {
|
|
129
|
+
cwd: projectPath,
|
|
130
|
+
stdio: 'ignore',
|
|
131
|
+
});
|
|
132
|
+
console.log('Created initial commit');
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Commit failed (e.g., git user not configured)
|
|
137
|
+
// Remove .git directory to avoid half-initialized state
|
|
138
|
+
console.log('Git commit not created');
|
|
139
|
+
console.log('Removing .git directory...');
|
|
140
|
+
try {
|
|
141
|
+
await fs.rm(path.join(projectPath, '.git'), { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Ignore cleanup errors
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function checkDirectoryConflicts(projectPath) {
|
|
150
|
+
try {
|
|
151
|
+
const entries = await fs.readdir(projectPath);
|
|
152
|
+
return entries.filter((file) => !safeExistingFiles.includes(file));
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
// Directory doesn't exist, no conflicts
|
|
156
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function printSuccessMessage(name, projectPath) {
|
|
163
|
+
console.log(`
|
|
164
|
+
${ansiGreen}Success!${ansiReset} Created ${ansiBold}${name}${ansiReset} at ${ansiCyan}${projectPath}${ansiReset}
|
|
165
|
+
|
|
166
|
+
Inside that directory, you can run:
|
|
167
|
+
|
|
168
|
+
${ansiCyan}npm run dev${ansiReset}
|
|
169
|
+
Starts the development server
|
|
170
|
+
|
|
171
|
+
${ansiCyan}npm run build${ansiReset}
|
|
172
|
+
Builds the app for production
|
|
173
|
+
|
|
174
|
+
You may begin with:
|
|
175
|
+
|
|
176
|
+
${ansiCyan}cd ${name}${ansiReset}
|
|
177
|
+
${ansiCyan}npm run dev${ansiReset}
|
|
178
|
+
|
|
179
|
+
`);
|
|
180
|
+
}
|
|
58
181
|
async function copyDir(source, destination, replacements, progressFn) {
|
|
59
182
|
const dirListing = await fs.readdir(source);
|
|
60
183
|
for (const dirEntry of dirListing) {
|
|
61
184
|
if (ignoredTemplateFiles.includes(dirEntry))
|
|
62
185
|
continue;
|
|
63
186
|
const sourcePath = path.join(source, dirEntry);
|
|
64
|
-
const destinationPath = path.join(destination, dotfileNames.includes(dirEntry) ? `.${dirEntry}` : dirEntry);
|
|
187
|
+
const destinationPath = path.join(destination, dotfileNames.includes(dirEntry) ? `.${dirEntry.slice(1)}` : dirEntry);
|
|
65
188
|
const stats = await fs.stat(sourcePath);
|
|
66
189
|
if (stats.isDirectory()) {
|
|
67
190
|
await fs.mkdir(destinationPath, { recursive: true });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ValidationResult = {
|
|
2
|
+
valid: true;
|
|
3
|
+
} | {
|
|
4
|
+
valid: false;
|
|
5
|
+
errors: string[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Validates a project name against npm naming conventions.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateProjectName(name: string): ValidationResult;
|
|
11
|
+
/**
|
|
12
|
+
* Formats validation errors for CLI display.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatValidationErrors(errors: string[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* Validator for Inquirer prompts.
|
|
17
|
+
* Returns true if valid, or an error message string if invalid.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateProjectNameForPrompt(input: string): string | true;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import validateNpmPackageName from 'validate-npm-package-name';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a project name against npm naming conventions.
|
|
4
|
+
*/
|
|
5
|
+
export function validateProjectName(name) {
|
|
6
|
+
const result = validateNpmPackageName(name);
|
|
7
|
+
if (result.validForNewPackages) {
|
|
8
|
+
return { valid: true };
|
|
9
|
+
}
|
|
10
|
+
const problems = [];
|
|
11
|
+
if (result.errors) {
|
|
12
|
+
problems.push(...result.errors);
|
|
13
|
+
}
|
|
14
|
+
if (result.warnings) {
|
|
15
|
+
problems.push(...result.warnings);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
errors: problems.length > 0 ? problems : ['Invalid package name'],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Formats validation errors for CLI display.
|
|
24
|
+
*/
|
|
25
|
+
export function formatValidationErrors(errors) {
|
|
26
|
+
if (errors.length === 1) {
|
|
27
|
+
return `Invalid project name: ${errors[0]}`;
|
|
28
|
+
}
|
|
29
|
+
return `Invalid project name:\n${errors.map((e) => ` - ${e}`).join('\n')}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validator for Inquirer prompts.
|
|
33
|
+
* Returns true if valid, or an error message string if invalid.
|
|
34
|
+
*/
|
|
35
|
+
export function validateProjectNameForPrompt(input) {
|
|
36
|
+
if (!input || input.trim().length === 0) {
|
|
37
|
+
return 'Project name cannot be empty';
|
|
38
|
+
}
|
|
39
|
+
const result = validateProjectName(input.trim());
|
|
40
|
+
if (result.valid) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return formatValidationErrors(result.errors);
|
|
44
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telemetryos/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "The official TelemetryOS application CLI package. Use it to build applications that run on the TelemetryOS platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "",
|
|
26
26
|
"repository": "github:TelemetryTV/Application-API",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@telemetryos/development-application-host-ui": "^1.
|
|
28
|
+
"@telemetryos/development-application-host-ui": "^1.8.1",
|
|
29
29
|
"@types/serve-handler": "^6.1.4",
|
|
30
30
|
"commander": "^14.0.0",
|
|
31
31
|
"inquirer": "^12.9.6",
|