@myclaude-cli/cli 0.3.0-alpha

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.
Files changed (93) hide show
  1. package/bin/dev.js +5 -0
  2. package/bin/myclaude.js +20 -0
  3. package/dist/commands/help.d.ts +11 -0
  4. package/dist/commands/help.js +221 -0
  5. package/dist/commands/help.js.map +1 -0
  6. package/dist/commands/info.d.ts +12 -0
  7. package/dist/commands/info.js +87 -0
  8. package/dist/commands/info.js.map +1 -0
  9. package/dist/commands/install.d.ts +14 -0
  10. package/dist/commands/install.js +113 -0
  11. package/dist/commands/install.js.map +1 -0
  12. package/dist/commands/list.d.ts +9 -0
  13. package/dist/commands/list.js +41 -0
  14. package/dist/commands/list.js.map +1 -0
  15. package/dist/commands/login.d.ts +9 -0
  16. package/dist/commands/login.js +83 -0
  17. package/dist/commands/login.js.map +1 -0
  18. package/dist/commands/logout.d.ts +9 -0
  19. package/dist/commands/logout.js +25 -0
  20. package/dist/commands/logout.js.map +1 -0
  21. package/dist/commands/publish.d.ts +10 -0
  22. package/dist/commands/publish.js +229 -0
  23. package/dist/commands/publish.js.map +1 -0
  24. package/dist/commands/search.d.ts +18 -0
  25. package/dist/commands/search.js +108 -0
  26. package/dist/commands/search.js.map +1 -0
  27. package/dist/commands/status.d.ts +8 -0
  28. package/dist/commands/status.js +84 -0
  29. package/dist/commands/status.js.map +1 -0
  30. package/dist/commands/uninstall.d.ts +14 -0
  31. package/dist/commands/uninstall.js +98 -0
  32. package/dist/commands/uninstall.js.map +1 -0
  33. package/dist/commands/validate.d.ts +9 -0
  34. package/dist/commands/validate.js +144 -0
  35. package/dist/commands/validate.js.map +1 -0
  36. package/dist/commands/whoami.d.ts +9 -0
  37. package/dist/commands/whoami.js +52 -0
  38. package/dist/commands/whoami.js.map +1 -0
  39. package/dist/constants.d.ts +9 -0
  40. package/dist/constants.js +10 -0
  41. package/dist/constants.js.map +1 -0
  42. package/dist/core/api.d.ts +18 -0
  43. package/dist/core/api.js +135 -0
  44. package/dist/core/api.js.map +1 -0
  45. package/dist/core/auth.d.ts +30 -0
  46. package/dist/core/auth.js +71 -0
  47. package/dist/core/auth.js.map +1 -0
  48. package/dist/core/browser-auth.d.ts +24 -0
  49. package/dist/core/browser-auth.js +155 -0
  50. package/dist/core/browser-auth.js.map +1 -0
  51. package/dist/core/install.d.ts +24 -0
  52. package/dist/core/install.js +191 -0
  53. package/dist/core/install.js.map +1 -0
  54. package/dist/core/lockfile.d.ts +18 -0
  55. package/dist/core/lockfile.js +41 -0
  56. package/dist/core/lockfile.js.map +1 -0
  57. package/dist/core/manifest.d.ts +41 -0
  58. package/dist/core/manifest.js +203 -0
  59. package/dist/core/manifest.js.map +1 -0
  60. package/dist/core/packer.d.ts +25 -0
  61. package/dist/core/packer.js +169 -0
  62. package/dist/core/packer.js.map +1 -0
  63. package/dist/core/paths.d.ts +2 -0
  64. package/dist/core/paths.js +50 -0
  65. package/dist/core/paths.js.map +1 -0
  66. package/dist/core/secret-scan.d.ts +17 -0
  67. package/dist/core/secret-scan.js +73 -0
  68. package/dist/core/secret-scan.js.map +1 -0
  69. package/dist/ui/exit-codes.d.ts +91 -0
  70. package/dist/ui/exit-codes.js +51 -0
  71. package/dist/ui/exit-codes.js.map +1 -0
  72. package/dist/ui/format.d.ts +12 -0
  73. package/dist/ui/format.js +44 -0
  74. package/dist/ui/format.js.map +1 -0
  75. package/dist/ui/index.d.ts +13 -0
  76. package/dist/ui/index.js +11 -0
  77. package/dist/ui/index.js.map +1 -0
  78. package/dist/ui/layout.d.ts +28 -0
  79. package/dist/ui/layout.js +161 -0
  80. package/dist/ui/layout.js.map +1 -0
  81. package/dist/ui/table.d.ts +10 -0
  82. package/dist/ui/table.js +42 -0
  83. package/dist/ui/table.js.map +1 -0
  84. package/dist/ui/theme.d.ts +46 -0
  85. package/dist/ui/theme.js +180 -0
  86. package/dist/ui/theme.js.map +1 -0
  87. package/dist/utils/config.d.ts +11 -0
  88. package/dist/utils/config.js +44 -0
  89. package/dist/utils/config.js.map +1 -0
  90. package/dist/utils/keychain.d.ts +3 -0
  91. package/dist/utils/keychain.js +53 -0
  92. package/dist/utils/keychain.js.map +1 -0
  93. package/package.json +63 -0
@@ -0,0 +1,24 @@
1
+ export interface BrowserAuthResult {
2
+ idToken: string;
3
+ refreshToken: string;
4
+ uid: string;
5
+ email: string;
6
+ username: string;
7
+ displayName: string;
8
+ }
9
+ /**
10
+ * Browser-based authentication flow (localhost callback).
11
+ *
12
+ * 1. Start HTTP server on 127.0.0.1:random-port
13
+ * 2. Open browser to myclaude.sh/cli/auth?port=PORT&state=STATE
14
+ * 3. User authenticates in browser (existing Firebase Auth)
15
+ * 4. Browser POSTs tokens to localhost:PORT/callback
16
+ * 5. Return tokens to caller
17
+ *
18
+ * Security:
19
+ * - Server binds to 127.0.0.1 only (no remote access)
20
+ * - Random state param prevents CSRF
21
+ * - Server shuts down after first successful callback
22
+ * - 5 minute timeout
23
+ */
24
+ export declare function browserAuth(): Promise<BrowserAuthResult>;
@@ -0,0 +1,155 @@
1
+ import { createServer } from 'node:http';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { exec } from 'node:child_process';
4
+ import { platform } from 'node:os';
5
+ import { getBaseUrl } from './api.js';
6
+ const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
7
+ /**
8
+ * Browser-based authentication flow (localhost callback).
9
+ *
10
+ * 1. Start HTTP server on 127.0.0.1:random-port
11
+ * 2. Open browser to myclaude.sh/cli/auth?port=PORT&state=STATE
12
+ * 3. User authenticates in browser (existing Firebase Auth)
13
+ * 4. Browser POSTs tokens to localhost:PORT/callback
14
+ * 5. Return tokens to caller
15
+ *
16
+ * Security:
17
+ * - Server binds to 127.0.0.1 only (no remote access)
18
+ * - Random state param prevents CSRF
19
+ * - Server shuts down after first successful callback
20
+ * - 5 minute timeout
21
+ */
22
+ export async function browserAuth() {
23
+ const state = randomBytes(32).toString('hex');
24
+ return new Promise((resolve, reject) => {
25
+ let resolved = false;
26
+ const server = createServer((req, res) => {
27
+ // CORS preflight for browser POST
28
+ if (req.method === 'OPTIONS') {
29
+ res.setHeader('Access-Control-Allow-Origin', '*');
30
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
31
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
32
+ res.setHeader('Access-Control-Max-Age', '86400');
33
+ res.writeHead(204);
34
+ res.end();
35
+ return;
36
+ }
37
+ if (req.method === 'POST' && (req.url === '/callback' || req.url?.startsWith('/callback'))) {
38
+ let body = '';
39
+ req.on('data', (chunk) => { body += chunk.toString(); });
40
+ req.on('end', () => {
41
+ try {
42
+ // Support both JSON and form-urlencoded (form submit bypasses CORS)
43
+ const contentType = req.headers['content-type'] || '';
44
+ let data;
45
+ if (contentType.includes('application/json')) {
46
+ data = JSON.parse(body);
47
+ }
48
+ else {
49
+ // Parse URL-encoded form data
50
+ data = Object.fromEntries(new URLSearchParams(body));
51
+ }
52
+ // Verify state to prevent CSRF
53
+ if (data.state !== state) {
54
+ res.setHeader('Access-Control-Allow-Origin', '*');
55
+ res.writeHead(403, { 'Content-Type': 'application/json' });
56
+ res.end(JSON.stringify({ error: 'Invalid state parameter' }));
57
+ return;
58
+ }
59
+ if (!data.idToken || !data.refreshToken || !data.uid) {
60
+ res.setHeader('Access-Control-Allow-Origin', '*');
61
+ res.writeHead(400, { 'Content-Type': 'application/json' });
62
+ res.end(JSON.stringify({ error: 'Missing required token fields' }));
63
+ return;
64
+ }
65
+ // Send success page
66
+ res.setHeader('Access-Control-Allow-Origin', '*');
67
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
68
+ res.end(successPage());
69
+ resolved = true;
70
+ server.close();
71
+ resolve({
72
+ displayName: data.displayName || '',
73
+ email: data.email || '',
74
+ idToken: data.idToken,
75
+ refreshToken: data.refreshToken,
76
+ uid: data.uid,
77
+ username: data.username || '',
78
+ });
79
+ }
80
+ catch {
81
+ res.setHeader('Access-Control-Allow-Origin', '*');
82
+ res.writeHead(400, { 'Content-Type': 'application/json' });
83
+ res.end(JSON.stringify({ error: 'Invalid request body' }));
84
+ }
85
+ });
86
+ return;
87
+ }
88
+ res.writeHead(404);
89
+ res.end('Not found');
90
+ });
91
+ // Bind to localhost (use 'localhost' not '127.0.0.1' for browser CORS compat on Windows)
92
+ server.listen(0, 'localhost', () => {
93
+ const { port } = server.address();
94
+ const authUrl = `${getBaseUrl()}/cli/auth?port=${port}&state=${state}`;
95
+ openBrowser(authUrl);
96
+ });
97
+ // Timeout
98
+ const timer = setTimeout(() => {
99
+ if (!resolved) {
100
+ resolved = true;
101
+ server.close();
102
+ reject(new Error('Authentication timed out after 5 minutes. Run: myclaude login'));
103
+ }
104
+ }, TIMEOUT_MS);
105
+ server.on('close', () => clearTimeout(timer));
106
+ server.on('error', (err) => {
107
+ if (!resolved) {
108
+ resolved = true;
109
+ clearTimeout(timer);
110
+ reject(new Error(`Failed to start auth server: ${err.message}`));
111
+ }
112
+ });
113
+ });
114
+ }
115
+ function openBrowser(url) {
116
+ const os = platform();
117
+ let cmd;
118
+ if (os === 'win32') {
119
+ cmd = `start "" "${url}"`;
120
+ }
121
+ else if (os === 'darwin') {
122
+ cmd = `open "${url}"`;
123
+ }
124
+ else {
125
+ cmd = `xdg-open "${url}"`;
126
+ }
127
+ exec(cmd, (err) => {
128
+ if (err) {
129
+ // If browser can't open, show URL for manual copy
130
+ process.stderr.write(`\n Open this URL in your browser:\n ${url}\n\n`);
131
+ }
132
+ });
133
+ }
134
+ function successPage() {
135
+ return `<!DOCTYPE html>
136
+ <html>
137
+ <head>
138
+ <meta charset="utf-8">
139
+ <title>MyClaude CLI — Authenticated</title>
140
+ <style>
141
+ body { font-family: -apple-system, system-ui, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5; }
142
+ .card { text-align: center; padding: 3rem; border: 1px solid #333; border-radius: 12px; max-width: 400px; }
143
+ h1 { font-size: 1.5rem; margin-bottom: 0.5rem; color: #c4704b; }
144
+ p { color: #999; font-size: 0.9rem; }
145
+ </style>
146
+ </head>
147
+ <body>
148
+ <div class="card">
149
+ <h1>Authenticated</h1>
150
+ <p>You can close this tab and return to your terminal.</p>
151
+ </div>
152
+ </body>
153
+ </html>`;
154
+ }
155
+ //# sourceMappingURL=browser-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-auth.js","sourceRoot":"","sources":["../../src/core/browser-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface InstallResult {
2
+ slug: string;
3
+ version: string;
4
+ type: string;
5
+ location: string;
6
+ checksum: string;
7
+ }
8
+ export declare function validateSlug(slug: string): void;
9
+ /**
10
+ * Download and install a product from the MyClaude marketplace.
11
+ *
12
+ * Security defenses:
13
+ * - Slug validated with regex (no path traversal)
14
+ * - fileName from server validated (no path separators)
15
+ * - Download URL must be HTTPS
16
+ * - Download size capped at 50 MB
17
+ * - Product type validated against known set
18
+ * - Zip paths validated + resolved path must stay within target
19
+ * - Rollback: temp dir cleaned on failure
20
+ */
21
+ export declare function installProduct(productId: string, slug: string, type: string, price: number, options?: {
22
+ force?: boolean;
23
+ global?: boolean;
24
+ }): Promise<InstallResult>;
@@ -0,0 +1,191 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, posix, resolve, sep } from 'node:path';
4
+ import { apiPost } from './api.js';
5
+ import { addEntry } from './lockfile.js';
6
+ import { getInstallPath } from './paths.js';
7
+ // Validate slug: lowercase alphanumeric + hyphens only
8
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
9
+ // Validate fileName from server: no path separators, no traversal
10
+ const SAFE_FILENAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
11
+ // Allowed download URL prefixes (R2 + Firebase Storage)
12
+ const ALLOWED_DOWNLOAD_PREFIXES = [
13
+ 'https://', // All HTTPS origins are accepted for signed URLs
14
+ ];
15
+ // Maximum download size: 50 MB
16
+ const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
17
+ // Valid product types from the marketplace
18
+ const VALID_TYPES = new Set([
19
+ 'skills', 'squads', 'agents', 'workflows', 'design-systems',
20
+ 'claude-md', 'prompts', 'applications', 'systems',
21
+ ]);
22
+ export function validateSlug(slug) {
23
+ if (!slug || slug.length > 128) {
24
+ throw new Error(`Invalid product slug: too ${slug ? 'long' : 'short'}`);
25
+ }
26
+ if (!SLUG_RE.test(slug)) {
27
+ throw new Error(`Invalid product slug "${slug}". Slugs must be lowercase alphanumeric with hyphens.`);
28
+ }
29
+ if (slug.includes('--') || slug.includes('..')) {
30
+ throw new Error(`Invalid product slug "${slug}". Contains invalid sequence.`);
31
+ }
32
+ }
33
+ function validateFileName(name) {
34
+ if (!name || !SAFE_FILENAME_RE.test(name) || name.includes('..') || name.length > 255) {
35
+ throw new Error(`Invalid file name from server: "${name}"`);
36
+ }
37
+ }
38
+ function validateDownloadUrl(url) {
39
+ if (!url || !url.startsWith('https://')) {
40
+ throw new Error(`Security: download URL must be HTTPS. Got: ${url?.slice(0, 50)}`);
41
+ }
42
+ }
43
+ function validateType(type) {
44
+ if (!VALID_TYPES.has(type)) {
45
+ throw new Error(`Unknown product type: "${type}"`);
46
+ }
47
+ }
48
+ /**
49
+ * Download and install a product from the MyClaude marketplace.
50
+ *
51
+ * Security defenses:
52
+ * - Slug validated with regex (no path traversal)
53
+ * - fileName from server validated (no path separators)
54
+ * - Download URL must be HTTPS
55
+ * - Download size capped at 50 MB
56
+ * - Product type validated against known set
57
+ * - Zip paths validated + resolved path must stay within target
58
+ * - Rollback: temp dir cleaned on failure
59
+ */
60
+ export async function installProduct(productId, slug, type, price, options = {}) {
61
+ validateSlug(slug);
62
+ validateType(type);
63
+ // Get signed download URL
64
+ const downloadRes = await apiPost('/api/products/download', { productId }, { auth: true });
65
+ if (!downloadRes.ok) {
66
+ const err = await downloadRes.json().catch(() => ({}));
67
+ if (downloadRes.status === 403) {
68
+ throw new Error('Purchase required. Paid product install available in Beta.');
69
+ }
70
+ throw new Error(err.error || `Download failed: ${downloadRes.status}`);
71
+ }
72
+ const { url, fileName } = await downloadRes.json();
73
+ validateDownloadUrl(url);
74
+ validateFileName(fileName);
75
+ // Download the file
76
+ const fileRes = await fetch(url);
77
+ if (!fileRes.ok || !fileRes.body) {
78
+ throw new Error(`File download failed: ${fileRes.status}`);
79
+ }
80
+ // Check Content-Length before downloading
81
+ const contentLength = Number(fileRes.headers.get('content-length') || 0);
82
+ if (contentLength > MAX_DOWNLOAD_BYTES) {
83
+ throw new Error(`File too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max 50 MB)`);
84
+ }
85
+ // Determine install location
86
+ const location = getInstallPath(slug, type, options.global || false);
87
+ const isZip = fileName.endsWith('.zip');
88
+ const isClaude = type === 'claude-md';
89
+ // Download to buffer, compute checksum (with size guard)
90
+ const chunks = [];
91
+ let totalBytes = 0;
92
+ const reader = fileRes.body.getReader();
93
+ while (true) {
94
+ const { done, value } = await reader.read();
95
+ if (done)
96
+ break;
97
+ totalBytes += value.byteLength;
98
+ if (totalBytes > MAX_DOWNLOAD_BYTES) {
99
+ throw new Error(`File too large: exceeded 50 MB limit during download`);
100
+ }
101
+ chunks.push(Buffer.from(value));
102
+ }
103
+ const fileBuffer = Buffer.concat(chunks);
104
+ const checksum = 'sha256:' + createHash('sha256').update(fileBuffer).digest('hex');
105
+ // Create target directory (clean first if --force)
106
+ try {
107
+ if (isClaude) {
108
+ mkdirSync(dirname(location), { recursive: true });
109
+ }
110
+ else {
111
+ if (options.force && existsSync(location)) {
112
+ rmSync(location, { force: true, recursive: true });
113
+ }
114
+ mkdirSync(location, { recursive: true });
115
+ }
116
+ if (isZip) {
117
+ const extractTarget = isClaude ? dirname(location) : location;
118
+ await extractZip(fileBuffer, extractTarget);
119
+ }
120
+ else if (isClaude) {
121
+ writeFileSync(location, fileBuffer);
122
+ }
123
+ else {
124
+ writeFileSync(join(location, fileName), fileBuffer);
125
+ }
126
+ }
127
+ catch (error) {
128
+ // Rollback: clean up partially created directory on failure
129
+ if (!isClaude && existsSync(location)) {
130
+ try {
131
+ rmSync(location, { force: true, recursive: true });
132
+ }
133
+ catch { /* best effort */ }
134
+ }
135
+ throw error;
136
+ }
137
+ const version = '1.0.0'; // Alpha placeholder — version from product metadata in Beta
138
+ addEntry(slug, {
139
+ checksum,
140
+ installedAt: new Date().toISOString(),
141
+ location,
142
+ paid: price > 0,
143
+ productId,
144
+ type,
145
+ version,
146
+ });
147
+ return { checksum, location, slug, type, version };
148
+ }
149
+ /**
150
+ * Extract zip contents with security validation (DC23).
151
+ * Defense in depth:
152
+ * 1. Reject paths with "..", absolute paths, >255 chars
153
+ * 2. Resolve final path and verify it stays within targetDir
154
+ * 3. Reject UNC paths (Windows \\server\share)
155
+ */
156
+ async function extractZip(buffer, targetDir) {
157
+ const { default: JSZip } = await import('jszip');
158
+ const zip = await JSZip.loadAsync(buffer);
159
+ const resolvedTarget = resolve(targetDir);
160
+ for (const [entryPath, entry] of Object.entries(zip.files)) {
161
+ validateZipPath(entryPath);
162
+ // Canonical zip-slip defense: resolve and verify containment
163
+ const fullPath = resolve(join(targetDir, entryPath));
164
+ if (!fullPath.startsWith(resolvedTarget + sep) && fullPath !== resolvedTarget) {
165
+ throw new Error(`Security: resolved path escapes target directory: ${entryPath}`);
166
+ }
167
+ if (entry.dir) {
168
+ mkdirSync(fullPath, { recursive: true });
169
+ continue;
170
+ }
171
+ mkdirSync(dirname(fullPath), { recursive: true });
172
+ const content = await entry.async('nodebuffer');
173
+ writeFileSync(fullPath, content);
174
+ }
175
+ }
176
+ function validateZipPath(entryPath) {
177
+ if (!entryPath || entryPath.trim() === '') {
178
+ throw new Error('Invalid zip: empty file path');
179
+ }
180
+ if (entryPath.includes('..')) {
181
+ throw new Error(`Security: zip contains path traversal: ${entryPath}`);
182
+ }
183
+ // Reject absolute paths: Unix, Windows drive, UNC
184
+ if (posix.isAbsolute(entryPath) || /^[A-Za-z]:/.test(entryPath) || entryPath.startsWith('\\\\')) {
185
+ throw new Error(`Security: zip contains absolute path: ${entryPath}`);
186
+ }
187
+ if (entryPath.length > 255) {
188
+ throw new Error(`Security: zip path exceeds 255 characters: ${entryPath.slice(0, 50)}...`);
189
+ }
190
+ }
191
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface LockfileEntry {
2
+ version: string;
3
+ type: string;
4
+ productId: string;
5
+ location: string;
6
+ checksum: string;
7
+ installedAt: string;
8
+ paid: boolean;
9
+ }
10
+ export interface Lockfile {
11
+ lockfileVersion: number;
12
+ installed: Record<string, LockfileEntry>;
13
+ }
14
+ export declare function readLockfile(): Lockfile;
15
+ export declare function writeLockfile(lockfile: Lockfile): void;
16
+ export declare function isInstalled(slug: string): LockfileEntry | null;
17
+ export declare function addEntry(slug: string, entry: LockfileEntry): void;
18
+ export declare function removeEntry(slug: string): boolean;
@@ -0,0 +1,41 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const LOCKFILE_NAME = 'myclaude-lock.json';
4
+ function getLockfilePath() {
5
+ return join(process.cwd(), LOCKFILE_NAME);
6
+ }
7
+ export function readLockfile() {
8
+ const path = getLockfilePath();
9
+ try {
10
+ if (!existsSync(path)) {
11
+ return { installed: {}, lockfileVersion: 1 };
12
+ }
13
+ const raw = readFileSync(path, 'utf-8');
14
+ return JSON.parse(raw);
15
+ }
16
+ catch {
17
+ return { installed: {}, lockfileVersion: 1 };
18
+ }
19
+ }
20
+ export function writeLockfile(lockfile) {
21
+ const path = getLockfilePath();
22
+ writeFileSync(path, JSON.stringify(lockfile, null, 2) + '\n');
23
+ }
24
+ export function isInstalled(slug) {
25
+ const lockfile = readLockfile();
26
+ return lockfile.installed[slug] || null;
27
+ }
28
+ export function addEntry(slug, entry) {
29
+ const lockfile = readLockfile();
30
+ lockfile.installed[slug] = entry;
31
+ writeLockfile(lockfile);
32
+ }
33
+ export function removeEntry(slug) {
34
+ const lockfile = readLockfile();
35
+ if (!lockfile.installed[slug])
36
+ return false;
37
+ delete lockfile.installed[slug];
38
+ writeLockfile(lockfile);
39
+ return true;
40
+ }
41
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/core/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAiB9B,MAAM,aAAa,GAAG,oBAAoB,CAAA;AAE1C,SAAS,eAAe;IACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;IAC9B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,EAAC,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAC,CAAA;QAC5C,CAAC;QAED,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAC,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAC,CAAA;IAC5C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;IAC9B,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAoB;IACzD,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;IAChC,aAAa,CAAC,QAAQ,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3C,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC/B,aAAa,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * vault.yaml manifest parser and validator.
3
+ * Source of truth for CLI publishing (DC11).
4
+ */
5
+ declare const VALID_TYPES: readonly ["skill", "agent", "squad", "workflow", "design-system", "claude-md", "prompt", "application", "system"];
6
+ type ProductType = typeof VALID_TYPES[number];
7
+ export interface Manifest {
8
+ name: string;
9
+ version: string;
10
+ type: ProductType;
11
+ description: string;
12
+ price: number;
13
+ license: string;
14
+ tags: string[];
15
+ entry: string;
16
+ readme: string;
17
+ category: string;
18
+ displayName: string;
19
+ mcsLevel: number;
20
+ language: string;
21
+ longDescription: string;
22
+ installTarget: string;
23
+ compatibility: {
24
+ claudeCode: string;
25
+ };
26
+ dependencies: {
27
+ myclaude: string[];
28
+ };
29
+ }
30
+ export interface ValidationIssue {
31
+ field: string;
32
+ message: string;
33
+ blocker: boolean;
34
+ }
35
+ export declare function readManifest(dir?: string): {
36
+ manifest: Manifest | null;
37
+ issues: ValidationIssue[];
38
+ };
39
+ /** Read README.md contents for marketplace listing */
40
+ export declare function readReadmeContents(dir?: string, readmePath?: string): string;
41
+ export {};