@untitled-devs/wasla 0.1.2 → 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 +34 -30
- package/dist/{utils → apps/cli/src}/cli-output.d.ts +4 -0
- package/dist/apps/cli/src/cli-output.js +98 -0
- package/dist/{cli → apps/cli/src}/commands/config.d.ts +1 -1
- package/dist/apps/cli/src/commands/config.js +62 -0
- package/dist/{cli → apps/cli/src}/commands/install.js +11 -6
- package/dist/{cli → apps/cli/src}/commands/register.js +8 -6
- package/dist/apps/cli/src/commands/status.d.ts +1 -0
- package/dist/apps/cli/src/commands/status.js +39 -0
- package/dist/{cli → apps/cli/src}/commands/sync-to.d.ts +0 -1
- package/dist/{cli → apps/cli/src}/commands/sync-to.js +7 -6
- package/dist/apps/cli/src/commands/sync.d.ts +5 -0
- package/dist/apps/cli/src/commands/sync.js +49 -0
- package/dist/apps/cli/src/commands/watch.d.ts +1 -0
- package/dist/{cli → apps/cli/src}/commands/watch.js +8 -7
- package/dist/{cli → apps/cli/src}/index.js +26 -16
- package/dist/{cli/commands/visualizer.d.ts → apps/cli/src/server/visualizer-server.d.ts} +0 -2
- package/dist/{cli/commands/visualizer.js → apps/cli/src/server/visualizer-server.js} +86 -31
- package/dist/{adapters → packages/adapters/src}/base.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/claude.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/claude.js +2 -2
- package/dist/{adapters → packages/adapters/src}/cursor.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/cursor.js +2 -2
- package/dist/{adapters → packages/adapters/src}/factory.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/gemini.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/gemini.js +2 -2
- package/dist/{adapters → packages/adapters/src}/github-copilot-cli.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/github-copilot-cli.js +2 -2
- package/dist/{adapters → packages/adapters/src}/github-copilot.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/github-copilot.js +2 -2
- package/dist/{adapters → packages/adapters/src}/openclaw.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/openclaw.js +2 -2
- package/dist/{adapters → packages/adapters/src}/opencode.d.ts +1 -1
- package/dist/{adapters → packages/adapters/src}/opencode.js +2 -2
- package/dist/{core → packages/core/src}/registry.d.ts +1 -1
- package/dist/{core → packages/core/src}/registry.js +2 -2
- package/dist/{core → packages/core/src}/types.d.ts +0 -1
- package/dist/{core → packages/core/src}/visualizer-types.d.ts +8 -0
- package/dist/packages/shared/src/config.d.ts +6 -0
- package/dist/packages/shared/src/config.js +34 -0
- package/dist/{utils → packages/shared/src}/fs.js +13 -7
- package/dist/{syncer → packages/sync/src}/index.d.ts +3 -3
- package/dist/{syncer → packages/sync/src}/index.js +4 -4
- package/dist/{core → packages/sync/src}/scanner.d.ts +1 -1
- package/dist/{core → packages/sync/src}/scanner.js +11 -6
- package/dist/visualizer/assets/index-cU_xphSj.js +144 -0
- package/{src/visualizer/dist → dist/visualizer}/index.html +1 -1
- package/package.json +76 -62
- package/dist/cli/commands/config.js +0 -65
- package/dist/cli/commands/status.d.ts +0 -5
- package/dist/cli/commands/status.js +0 -36
- package/dist/cli/commands/sync.d.ts +0 -5
- package/dist/cli/commands/sync.js +0 -26
- package/dist/cli/commands/watch.d.ts +0 -5
- package/dist/utils/cli-output.js +0 -44
- package/src/visualizer/dist/assets/index-C6aJB2Yl.js +0 -144
- /package/dist/{cli → apps/cli/src}/commands/install.d.ts +0 -0
- /package/dist/{cli → apps/cli/src}/commands/register.d.ts +0 -0
- /package/dist/{cli → apps/cli/src}/index.d.ts +0 -0
- /package/dist/{adapters → packages/adapters/src}/base.js +0 -0
- /package/dist/{adapters → packages/adapters/src}/factory.js +0 -0
- /package/dist/{core → packages/core/src}/types.js +0 -0
- /package/dist/{core → packages/core/src}/visualizer-types.js +0 -0
- /package/dist/{utils → packages/shared/src}/fs.d.ts +0 -0
- /package/dist/{utils → packages/shared/src}/paths.d.ts +0 -0
- /package/dist/{utils → packages/shared/src}/paths.js +0 -0
- /package/{src/visualizer/dist → dist/visualizer}/logo.png +0 -0
package/README.md
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
[](LICENSE)
|
|
15
15
|
[](https://github.com/The-Untitled-Org/wasla-genie)
|
|
16
|
+
[](https://www.npmjs.com/package/@untitled-devs/wasla)
|
|
17
|
+
[](https://www.npmjs.com/package/@untitled-devs/wasla)
|
|
18
|
+
[](https://github.com/The-Untitled-Org/wasla-genie/releases)
|
|
16
19
|
[](https://github.com/The-Untitled-Org/wasla-genie/actions/workflows/ci-docs.yml)
|
|
17
20
|
[](https://codecov.io/gh/The-Untitled-Org/wasla-genie)
|
|
18
21
|
[]()
|
|
@@ -121,15 +124,18 @@ The same pattern applies across every asset type:
|
|
|
121
124
|
WaslaGenie is cross-platform via `npx` — no global install required:
|
|
122
125
|
|
|
123
126
|
```bash
|
|
127
|
+
npx @untitled-devs/wasla config --scope workspace
|
|
124
128
|
npx @untitled-devs/wasla sync
|
|
125
129
|
```
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
Choose `workspace` or `user` once before running operational commands. This runs the CLI directly.
|
|
132
|
+
It does not register helper skills inside Claude, Gemini, or other tools.
|
|
128
133
|
|
|
129
134
|
**Or install globally:**
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
137
|
npm install -g @untitled-devs/wasla
|
|
138
|
+
waslagenie config --scope workspace
|
|
133
139
|
waslagenie sync
|
|
134
140
|
```
|
|
135
141
|
|
|
@@ -178,10 +184,10 @@ npx @untitled-devs/wasla visualizer
|
|
|
178
184
|
### You (developing this repo)
|
|
179
185
|
|
|
180
186
|
```bash
|
|
181
|
-
# Build + run sync
|
|
187
|
+
# Build + run sync using your configured scope
|
|
182
188
|
npm run sync
|
|
183
189
|
|
|
184
|
-
# Build + run watch
|
|
190
|
+
# Build + run watch using your configured scope
|
|
185
191
|
npm run watch
|
|
186
192
|
```
|
|
187
193
|
|
|
@@ -257,14 +263,18 @@ No restart. No manual trigger. The moment something changes — it's everywhere.
|
|
|
257
263
|
|
|
258
264
|
### Scope — workspace or user level
|
|
259
265
|
|
|
266
|
+
Choose the active scope before running sync, watch, status, or the visualizer:
|
|
267
|
+
|
|
260
268
|
```bash
|
|
261
|
-
#
|
|
262
|
-
waslagenie
|
|
269
|
+
# Use the current project workspace registry
|
|
270
|
+
waslagenie config --scope workspace
|
|
263
271
|
|
|
264
|
-
#
|
|
265
|
-
waslagenie
|
|
272
|
+
# Use the user-level registry across projects
|
|
273
|
+
waslagenie config --scope user
|
|
266
274
|
```
|
|
267
275
|
|
|
276
|
+
All other commands use the saved scope automatically. They do not accept `--scope`.
|
|
277
|
+
|
|
268
278
|
---
|
|
269
279
|
|
|
270
280
|
### Status — see everything and where it lives
|
|
@@ -312,22 +322,19 @@ review-pr command openclaw claude ✔ gemini ✔ codex ✔ her
|
|
|
312
322
|
|
|
313
323
|
## 🗃️ Registry Storage
|
|
314
324
|
|
|
315
|
-
WaslaGenie keeps its own state separately from all orchestrators. You choose the scope
|
|
325
|
+
WaslaGenie keeps its own state separately from all orchestrators. You choose the active scope explicitly before the first sync:
|
|
316
326
|
|
|
317
|
-
**User-level** (
|
|
327
|
+
**User-level** (available across all your projects):
|
|
318
328
|
```
|
|
319
329
|
~/.waslagenie/
|
|
320
|
-
├── registry.json ←
|
|
321
|
-
|
|
322
|
-
└── config.json ← your scope and preferences
|
|
330
|
+
├── registry.json ← user-scope assets and stub locations
|
|
331
|
+
└── config.json ← active scope preference
|
|
323
332
|
```
|
|
324
333
|
|
|
325
334
|
**Workspace-level** (scoped to current project only):
|
|
326
335
|
```
|
|
327
336
|
.waslagenie/
|
|
328
|
-
|
|
329
|
-
├── stubs/
|
|
330
|
-
└── config.json
|
|
337
|
+
└── registry.json ← workspace-scope assets and stub locations
|
|
331
338
|
```
|
|
332
339
|
|
|
333
340
|
Switch anytime:
|
|
@@ -374,22 +381,17 @@ Nothing is forced. Centralization is a convenience, not a requirement.
|
|
|
374
381
|
|
|
375
382
|
```
|
|
376
383
|
wasla-genie/
|
|
377
|
-
├──
|
|
378
|
-
│ ├── cli/
|
|
379
|
-
│
|
|
380
|
-
|
|
381
|
-
│ ├──
|
|
382
|
-
│ ├──
|
|
383
|
-
│
|
|
384
|
-
│
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
│ ├── openclaw.js
|
|
388
|
-
│ └── hermes.js
|
|
384
|
+
├── apps/
|
|
385
|
+
│ ├── cli/src/ # CLI commands and visualizer server
|
|
386
|
+
│ └── visualizer/src/ # React visualizer
|
|
387
|
+
├── packages/
|
|
388
|
+
│ ├── adapters/src/ # Per-tool directory knowledge + stub format
|
|
389
|
+
│ ├── core/src/ # Registry, scanner, and shared types
|
|
390
|
+
│ ├── shared/src/ # Shared config, filesystem, and path helpers
|
|
391
|
+
│ └── sync/src/ # Sync orchestration and filesystem watcher
|
|
392
|
+
├── tests/
|
|
393
|
+
├── scripts/
|
|
389
394
|
├── docs/
|
|
390
|
-
│ ├── how-stubs-work.md
|
|
391
|
-
│ ├── adapters.md
|
|
392
|
-
│ └── roadmap.md
|
|
393
395
|
├── package.json
|
|
394
396
|
└── README.md
|
|
395
397
|
```
|
|
@@ -414,11 +416,13 @@ Nothing is ever duplicated.
|
|
|
414
416
|
git clone https://github.com/The-Untitled-Org/wasla-genie
|
|
415
417
|
cd wasla-genie
|
|
416
418
|
npm install
|
|
419
|
+
npm run visualizer:install
|
|
417
420
|
npm run dev
|
|
418
421
|
```
|
|
419
422
|
|
|
420
423
|
- [Contributing Guide](CONTRIBUTING.md)
|
|
421
424
|
- [Architecture Docs](docs/docs/architecture/index.md)
|
|
425
|
+
- [Release Guide](RELEASING.md)
|
|
422
426
|
|
|
423
427
|
---
|
|
424
428
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { Asset } from '#core/types.js';
|
|
2
|
+
export declare function banner(): void;
|
|
1
3
|
export declare function success(message: string): void;
|
|
2
4
|
export declare function error(message: string): void;
|
|
3
5
|
export declare function info(message: string): void;
|
|
4
6
|
export declare function warning(message: string): void;
|
|
5
7
|
export declare function highlight(message: string): void;
|
|
8
|
+
export declare function metric(label: string, value: string | number): void;
|
|
9
|
+
export declare function assetList(assets: Asset[], includeModifiedAt?: boolean, activeProviders?: string[]): void;
|
|
6
10
|
export declare function step(title: string): void;
|
|
7
11
|
export declare function section(title: string): void;
|
|
8
12
|
export declare function table(rows: string[][], columnWidths?: number[]): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const ansi = {
|
|
2
|
+
reset: '\u001b[0m',
|
|
3
|
+
bold: '\u001b[1m',
|
|
4
|
+
cyan: '\u001b[36m',
|
|
5
|
+
blue: '\u001b[34m',
|
|
6
|
+
green: '\u001b[32m',
|
|
7
|
+
red: '\u001b[31m',
|
|
8
|
+
yellow: '\u001b[33m',
|
|
9
|
+
magenta: '\u001b[35m',
|
|
10
|
+
};
|
|
11
|
+
function color(text, ...codes) {
|
|
12
|
+
return `${codes.join('')}${text}${ansi.reset}`;
|
|
13
|
+
}
|
|
14
|
+
export function banner() {
|
|
15
|
+
console.log(color(`
|
|
16
|
+
__ __ _ ____ _
|
|
17
|
+
\\ \\ / /_ _ ___| | __ _ / ___| ___ _ __ (_) ___
|
|
18
|
+
\\ \\ /\\ / / _\` / __| |/ _\` | | _ / _ \\ '_ \\| |/ _ \\
|
|
19
|
+
\\ V V / (_| \\__ \\ | (_| | |_| | __/ | | | | __/
|
|
20
|
+
\\_/\\_/ \\__,_|___/_|\\__,_|\\____|\\___|_| |_|_|\\___|
|
|
21
|
+
`, ansi.bold, ansi.cyan));
|
|
22
|
+
}
|
|
23
|
+
export function success(message) {
|
|
24
|
+
console.log(color(`✔ ${message}`, ansi.green));
|
|
25
|
+
}
|
|
26
|
+
export function error(message) {
|
|
27
|
+
console.error(color(`✗ ${message}`, ansi.red));
|
|
28
|
+
}
|
|
29
|
+
export function info(message) {
|
|
30
|
+
console.log(color(`ℹ ${message}`, ansi.blue));
|
|
31
|
+
}
|
|
32
|
+
export function warning(message) {
|
|
33
|
+
console.log(color(`⚠ ${message}`, ansi.yellow));
|
|
34
|
+
}
|
|
35
|
+
export function highlight(message) {
|
|
36
|
+
console.log(color(`✨ ${message}`, ansi.magenta, ansi.bold));
|
|
37
|
+
}
|
|
38
|
+
export function metric(label, value) {
|
|
39
|
+
console.log(` ${color(label.padEnd(20), ansi.blue)} ${color(String(value), ansi.bold)}`);
|
|
40
|
+
}
|
|
41
|
+
export function assetList(assets, includeModifiedAt = false, activeProviders) {
|
|
42
|
+
const assetTypes = ['agent', 'skill', 'mcp', 'context'];
|
|
43
|
+
const headings = {
|
|
44
|
+
agent: 'AGENTS',
|
|
45
|
+
skill: 'SKILLS',
|
|
46
|
+
mcp: 'MCP SERVERS',
|
|
47
|
+
context: 'CONTEXT FILES',
|
|
48
|
+
};
|
|
49
|
+
const activeProviderSet = activeProviders ? new Set(activeProviders) : null;
|
|
50
|
+
for (const type of assetTypes) {
|
|
51
|
+
const typedAssets = assets
|
|
52
|
+
.filter((asset) => asset.type === type)
|
|
53
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
54
|
+
if (typedAssets.length === 0)
|
|
55
|
+
continue;
|
|
56
|
+
console.log(color(` ${headings[type]} (${typedAssets.length})`, ansi.cyan, ansi.bold));
|
|
57
|
+
for (const asset of typedAssets) {
|
|
58
|
+
const providers = [...new Set(asset.stubs.map((stub) => stub.tool))]
|
|
59
|
+
.sort()
|
|
60
|
+
.filter((provider) => !activeProviderSet || activeProviderSet.has(provider));
|
|
61
|
+
console.log(` ${color('•', ansi.green)} ${color(asset.name, ansi.bold)}`);
|
|
62
|
+
console.log(` ${color('Mirrors:', ansi.blue)} ${providers.join(', ') || 'none'}`);
|
|
63
|
+
if (includeModifiedAt) {
|
|
64
|
+
console.log(` ${color('Updated:', ansi.blue)} ${new Date(asset.last_modified_at).toLocaleString()}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
spacer();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function step(title) {
|
|
71
|
+
console.log(`\n${title}`);
|
|
72
|
+
}
|
|
73
|
+
export function section(title) {
|
|
74
|
+
console.log(color(`\n🔍 ${title}`, ansi.blue, ansi.bold));
|
|
75
|
+
}
|
|
76
|
+
export function table(rows, columnWidths) {
|
|
77
|
+
if (rows.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
// Calculate column widths if not provided
|
|
80
|
+
const widths = columnWidths ||
|
|
81
|
+
rows[0].map((_, i) => {
|
|
82
|
+
return Math.max(...rows.map((row) => (row[i] || '').length));
|
|
83
|
+
});
|
|
84
|
+
rows.forEach((row) => {
|
|
85
|
+
const line = row.map((cell, i) => (cell || '').padEnd(widths[i] || 10)).join(' ');
|
|
86
|
+
console.log(line);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
export function spacer() {
|
|
90
|
+
console.log('');
|
|
91
|
+
}
|
|
92
|
+
export function bulletPoint(text, indent = 0) {
|
|
93
|
+
const spaces = ' '.repeat(indent);
|
|
94
|
+
console.log(`${spaces}• ${text}`);
|
|
95
|
+
}
|
|
96
|
+
export function code(text) {
|
|
97
|
+
return `\`${text}\``;
|
|
98
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { section, success, error, spacer, info } from '../cli-output.js';
|
|
2
|
+
import prompts from 'prompts';
|
|
3
|
+
import { getConfigPath, getConfiguredRegistryPath, readConfiguredScope, writeConfiguredScope, } from '#shared/config.js';
|
|
4
|
+
export async function configCommand(options) {
|
|
5
|
+
try {
|
|
6
|
+
const currentScope = await readConfiguredScope();
|
|
7
|
+
// Show current config
|
|
8
|
+
if (options.show) {
|
|
9
|
+
section('Current Configuration');
|
|
10
|
+
spacer();
|
|
11
|
+
showConfig(currentScope);
|
|
12
|
+
spacer();
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
// Change scope if requested
|
|
16
|
+
if (options.scope) {
|
|
17
|
+
const newScope = options.scope;
|
|
18
|
+
if (newScope !== 'user' && newScope !== 'workspace') {
|
|
19
|
+
error('Invalid scope. Use: user or workspace');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
await saveScope(newScope);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
section('Configure Scope');
|
|
26
|
+
spacer();
|
|
27
|
+
const response = await prompts({
|
|
28
|
+
type: 'select',
|
|
29
|
+
name: 'scope',
|
|
30
|
+
message: 'Where should WaslaGenie store and sync assets?',
|
|
31
|
+
choices: [
|
|
32
|
+
{ title: 'Workspace - current project only', value: 'workspace' },
|
|
33
|
+
{ title: 'User - available across all projects', value: 'user' },
|
|
34
|
+
],
|
|
35
|
+
initial: currentScope === 'user' ? 1 : 0,
|
|
36
|
+
});
|
|
37
|
+
const scope = response.scope;
|
|
38
|
+
if (!scope) {
|
|
39
|
+
info('Configuration cancelled');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
await saveScope(scope);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
error(`Config failed: ${err}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function saveScope(scope) {
|
|
51
|
+
await writeConfiguredScope(scope);
|
|
52
|
+
success(`Scope changed to: ${scope}`);
|
|
53
|
+
info(`Config: ${getConfigPath()}`);
|
|
54
|
+
info(`Registry: ${getConfiguredRegistryPath(scope)}`);
|
|
55
|
+
spacer();
|
|
56
|
+
}
|
|
57
|
+
function showConfig(scope) {
|
|
58
|
+
info(`Scope: ${scope ?? 'not configured'}`);
|
|
59
|
+
info(`Config: ${getConfigPath()}`);
|
|
60
|
+
if (scope)
|
|
61
|
+
info(`Registry: ${getConfiguredRegistryPath(scope)}`);
|
|
62
|
+
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { section, success, error, highlight, spacer } from '
|
|
2
|
-
import {
|
|
3
|
-
import { ensureDir } from '../../utils/fs.js';
|
|
1
|
+
import { section, success, error, highlight, spacer } from '../cli-output.js';
|
|
2
|
+
import { readConfiguredScope } from '#shared/config.js';
|
|
4
3
|
export async function installCommand() {
|
|
5
4
|
try {
|
|
6
5
|
section('Preparing WaslaGenie CLI...');
|
|
7
6
|
spacer();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const scope = await readConfiguredScope();
|
|
8
|
+
if (scope) {
|
|
9
|
+
success(`Scope configured: ${scope}`);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
console.log('Choose a scope before running sync:');
|
|
13
|
+
console.log(' waslagenie config --scope user');
|
|
14
|
+
console.log(' waslagenie config --scope workspace');
|
|
15
|
+
}
|
|
11
16
|
spacer();
|
|
12
17
|
highlight('CLI setup complete!');
|
|
13
18
|
console.log('');
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { getInstalledAdapters } from '
|
|
2
|
-
import { section, success, error, warning, highlight, spacer } from '
|
|
3
|
-
import { getRegistryDir } from '
|
|
4
|
-
import { ensureDir } from '
|
|
1
|
+
import { getInstalledAdapters } from '#adapters/factory.js';
|
|
2
|
+
import { section, success, error, warning, highlight, spacer } from '../cli-output.js';
|
|
3
|
+
import { getRegistryDir } from '#shared/paths.js';
|
|
4
|
+
import { ensureDir } from '#shared/fs.js';
|
|
5
|
+
import { requireConfiguredScope } from '#shared/config.js';
|
|
5
6
|
export async function registerCommand(options = {}) {
|
|
6
7
|
try {
|
|
7
8
|
section('Detecting installed orchestrators...');
|
|
8
9
|
spacer();
|
|
9
|
-
const
|
|
10
|
+
const scope = await requireConfiguredScope();
|
|
11
|
+
const adapters = await getInstalledAdapters(scope);
|
|
10
12
|
if (adapters.length === 0) {
|
|
11
13
|
error('No supported orchestrators found');
|
|
12
14
|
warning('Please install Claude Code, Gemini CLI, or OpenCode first');
|
|
@@ -35,7 +37,7 @@ export async function registerCommand(options = {}) {
|
|
|
35
37
|
spacer();
|
|
36
38
|
section('Registering WaslaGenie helper skills...');
|
|
37
39
|
spacer();
|
|
38
|
-
await ensureDir(getRegistryDir(
|
|
40
|
+
await ensureDir(getRegistryDir(scope));
|
|
39
41
|
for (const adapter of targets) {
|
|
40
42
|
try {
|
|
41
43
|
await adapter.installSkill();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statusCommand(): Promise<void>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RegistryManager } from '#core/registry.js';
|
|
2
|
+
import { Scanner } from '#sync/scanner.js';
|
|
3
|
+
import { assetList, section, error, info, metric, spacer } from '../cli-output.js';
|
|
4
|
+
import { fileExists } from '#shared/fs.js';
|
|
5
|
+
import { getRegistryPath } from '#shared/paths.js';
|
|
6
|
+
import { requireConfiguredScope } from '#shared/config.js';
|
|
7
|
+
export async function statusCommand() {
|
|
8
|
+
try {
|
|
9
|
+
const scope = await requireConfiguredScope();
|
|
10
|
+
const registryPath = getRegistryPath(scope);
|
|
11
|
+
if (!(await fileExists(registryPath))) {
|
|
12
|
+
error('Registry not found. Run: waslagenie sync');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const registry = new RegistryManager(scope);
|
|
16
|
+
await registry.load();
|
|
17
|
+
const registryData = registry.get();
|
|
18
|
+
const installedTools = await new Scanner(scope).detectInstalledTools();
|
|
19
|
+
section('Registry status');
|
|
20
|
+
info(`Scope: ${scope}`);
|
|
21
|
+
info(`Registry: ${registryPath}`);
|
|
22
|
+
spacer();
|
|
23
|
+
metric('Assets', registryData.assets.length);
|
|
24
|
+
metric('Conflicts', registryData.conflicts.length);
|
|
25
|
+
if (registryData.assets.length === 0) {
|
|
26
|
+
spacer();
|
|
27
|
+
section('No assets synced yet');
|
|
28
|
+
spacer();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
section('Synced assets');
|
|
32
|
+
spacer();
|
|
33
|
+
assetList(registryData.assets, true, installedTools);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
error(`Status check failed: ${err}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { RegistryManager } from '
|
|
2
|
-
import { Scanner } from '
|
|
3
|
-
import { Syncer } from '
|
|
4
|
-
import { section, error, highlight, spacer } from '
|
|
1
|
+
import { RegistryManager } from '#core/registry.js';
|
|
2
|
+
import { Scanner } from '#sync/scanner.js';
|
|
3
|
+
import { Syncer } from '#sync/index.js';
|
|
4
|
+
import { section, error, highlight, spacer } from '../cli-output.js';
|
|
5
|
+
import { requireConfiguredScope } from '#shared/config.js';
|
|
5
6
|
export async function syncToCommand(options) {
|
|
6
7
|
try {
|
|
7
|
-
const scope = (
|
|
8
|
+
const scope = await requireConfiguredScope();
|
|
8
9
|
const from = options.from;
|
|
9
10
|
const to = options.to;
|
|
10
11
|
if (!from || !to) {
|
|
11
12
|
error('Error: --from and --to are required');
|
|
12
|
-
console.log('Usage: waslagenie sync-to --from <source> --to <target>
|
|
13
|
+
console.log('Usage: waslagenie sync-to --from <source> --to <target>');
|
|
13
14
|
console.log('Example: waslagenie sync-to --from gemini --to claude');
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RegistryManager } from '#core/registry.js';
|
|
2
|
+
import { Scanner } from '#sync/scanner.js';
|
|
3
|
+
import { Syncer } from '#sync/index.js';
|
|
4
|
+
import { assetList, bulletPoint, section, error, highlight, info, metric, spacer, } from '../cli-output.js';
|
|
5
|
+
import { getConfiguredRegistryPath, requireConfiguredScope } from '#shared/config.js';
|
|
6
|
+
import { configCommand } from './config.js';
|
|
7
|
+
export async function syncCommand(options = {}) {
|
|
8
|
+
try {
|
|
9
|
+
if (options.promptForScope !== false && !(await configCommand({}))) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const scope = await requireConfiguredScope();
|
|
13
|
+
section('Syncing assets');
|
|
14
|
+
info(`Scope: ${scope}`);
|
|
15
|
+
info(`Registry: ${getConfiguredRegistryPath(scope)}`);
|
|
16
|
+
spacer();
|
|
17
|
+
section('Scanning providers');
|
|
18
|
+
const registry = new RegistryManager(scope);
|
|
19
|
+
await registry.load();
|
|
20
|
+
const scanner = new Scanner(scope);
|
|
21
|
+
const installedTools = await scanner.detectInstalledTools();
|
|
22
|
+
info(`${installedTools.length} providers detected`);
|
|
23
|
+
for (const tool of installedTools) {
|
|
24
|
+
bulletPoint(tool, 1);
|
|
25
|
+
}
|
|
26
|
+
spacer();
|
|
27
|
+
const syncer = new Syncer(registry, scanner, scope);
|
|
28
|
+
const result = await syncer.sync(false);
|
|
29
|
+
spacer();
|
|
30
|
+
highlight('Sync complete');
|
|
31
|
+
spacer();
|
|
32
|
+
metric('Assets discovered', result.assetsDiscovered);
|
|
33
|
+
metric('Stubs written', result.stubsWritten);
|
|
34
|
+
metric('Stubs deleted', result.stubsDeleted);
|
|
35
|
+
spacer();
|
|
36
|
+
if (registry.get().assets.length === 0) {
|
|
37
|
+
info('No assets discovered');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
section('Discovered assets');
|
|
41
|
+
spacer();
|
|
42
|
+
assetList(registry.get().assets, false, installedTools);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
error(`Sync failed: ${err}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function watchCommand(): Promise<void>;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import chokidar from 'chokidar';
|
|
2
|
-
import { RegistryManager } from '
|
|
3
|
-
import { Scanner } from '
|
|
4
|
-
import { Syncer } from '
|
|
5
|
-
import { getAllAdapters } from '
|
|
6
|
-
import { section, success, error, info, spacer } from '
|
|
7
|
-
|
|
2
|
+
import { RegistryManager } from '#core/registry.js';
|
|
3
|
+
import { Scanner } from '#sync/scanner.js';
|
|
4
|
+
import { Syncer } from '#sync/index.js';
|
|
5
|
+
import { getAllAdapters } from '#adapters/factory.js';
|
|
6
|
+
import { section, success, error, info, spacer } from '../cli-output.js';
|
|
7
|
+
import { requireConfiguredScope } from '#shared/config.js';
|
|
8
|
+
export async function watchCommand() {
|
|
8
9
|
try {
|
|
9
|
-
const scope = (
|
|
10
|
+
const scope = await requireConfiguredScope();
|
|
10
11
|
section('Watching for changes...');
|
|
11
12
|
spacer();
|
|
12
13
|
const registry = new RegistryManager(scope);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
3
6
|
import { installCommand } from './commands/install.js';
|
|
4
7
|
import { registerCommand } from './commands/register.js';
|
|
5
8
|
import { syncCommand } from './commands/sync.js';
|
|
@@ -7,52 +10,59 @@ import { syncToCommand } from './commands/sync-to.js';
|
|
|
7
10
|
import { statusCommand } from './commands/status.js';
|
|
8
11
|
import { configCommand } from './commands/config.js';
|
|
9
12
|
import { watchCommand } from './commands/watch.js';
|
|
10
|
-
import { visualizerCommand } from './
|
|
13
|
+
import { visualizerCommand } from './server/visualizer-server.js';
|
|
14
|
+
import { banner } from './cli-output.js';
|
|
11
15
|
const program = new Command();
|
|
16
|
+
function readPackageVersion(moduleUrl) {
|
|
17
|
+
let directory = dirname(fileURLToPath(moduleUrl));
|
|
18
|
+
while (true) {
|
|
19
|
+
const packagePath = join(directory, 'package.json');
|
|
20
|
+
if (existsSync(packagePath)) {
|
|
21
|
+
return JSON.parse(readFileSync(packagePath, 'utf-8')).version;
|
|
22
|
+
}
|
|
23
|
+
const parent = dirname(directory);
|
|
24
|
+
if (parent === directory)
|
|
25
|
+
throw new Error('Cannot find package.json');
|
|
26
|
+
directory = parent;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
12
29
|
program
|
|
13
30
|
.name('waslagenie')
|
|
14
31
|
.description('Universal synchronization layer for AI agent orchestrators')
|
|
15
|
-
.version(
|
|
32
|
+
.version(readPackageVersion(import.meta.url));
|
|
16
33
|
program.addCommand(new Command('install').description('Prepare WaslaGenie CLI state').action(installCommand));
|
|
17
34
|
program.addCommand(new Command('register')
|
|
18
35
|
.option('--to <targets>', 'Target provider(s), comma-separated. Example: claude,gemini')
|
|
19
36
|
.description('Register WaslaGenie helper skills inside installed AI tools')
|
|
20
37
|
.action((options) => registerCommand(options)));
|
|
21
38
|
program.addCommand(new Command('sync')
|
|
22
|
-
.option('--scope <scope>', 'user or workspace', 'workspace')
|
|
23
39
|
.description('Scan and sync agents/MCPs across tools')
|
|
24
|
-
.action((
|
|
40
|
+
.action(() => syncCommand()));
|
|
25
41
|
program.addCommand(new Command('sync-to')
|
|
26
42
|
.option('--from <source>', 'Source tool (gemini, claude, etc.)')
|
|
27
43
|
.option('--to <targets>', 'Target tool(s), comma-separated')
|
|
28
|
-
.option('--scope <scope>', 'user or workspace', 'workspace')
|
|
29
44
|
.description('Sync agents/MCPs from one tool to specific target(s)')
|
|
30
45
|
.action((options) => syncToCommand(options)));
|
|
31
46
|
program.addCommand(new Command('status')
|
|
32
|
-
.option('--scope <scope>', 'user or workspace', 'workspace')
|
|
33
47
|
.description('Show all discovered assets and their sync state')
|
|
34
|
-
.action((
|
|
48
|
+
.action(() => statusCommand()));
|
|
35
49
|
program.addCommand(new Command('config')
|
|
36
50
|
.option('--scope <scope>', 'Set scope to user or workspace')
|
|
37
51
|
.option('--show', 'Show current config')
|
|
38
52
|
.description('Configure WaslaGenie settings')
|
|
39
|
-
.action((options) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.action((options) => watchCommand(options)));
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
await configCommand(options);
|
|
55
|
+
}));
|
|
56
|
+
program.addCommand(new Command('watch').description('Watch for changes and auto-sync').action(() => watchCommand()));
|
|
44
57
|
program.addCommand(new Command('visualizer')
|
|
45
|
-
.option('--scope <scope>', 'user or workspace', 'workspace')
|
|
46
|
-
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
47
58
|
.option('--port <port>', 'Port to bind', '4072')
|
|
48
59
|
.option('--no-open', 'Do not open browser automatically')
|
|
49
60
|
.description('Open interactive sync visualizer with built-in backend')
|
|
50
61
|
.action((options) => visualizerCommand(options)));
|
|
51
62
|
program.addCommand(new Command('ui')
|
|
52
|
-
.option('--scope <scope>', 'user or workspace', 'workspace')
|
|
53
|
-
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
54
63
|
.option('--port <port>', 'Port to bind', '4072')
|
|
55
64
|
.option('--no-open', 'Do not open browser automatically')
|
|
56
65
|
.description('Alias for `visualizer`')
|
|
57
66
|
.action((options) => visualizerCommand(options)));
|
|
67
|
+
banner();
|
|
58
68
|
program.parse(process.argv);
|