@sheepbun/yips 0.1.1 → 0.1.46
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/LICENSE +21 -0
- package/README.md +52 -0
- package/bin/yips.js +15 -0
- package/package.json +21 -128
- package/postinstall.js +50 -0
- package/dist/agent/commands/command-catalog.js +0 -243
- package/dist/agent/commands/commands.js +0 -418
- package/dist/agent/conductor.js +0 -118
- package/dist/agent/context/code-context.js +0 -68
- package/dist/agent/context/memory-store.js +0 -159
- package/dist/agent/context/session-store.js +0 -211
- package/dist/agent/protocol/tool-protocol.js +0 -160
- package/dist/agent/skills/skills.js +0 -327
- package/dist/agent/tools/tool-executor.js +0 -415
- package/dist/agent/tools/tool-safety.js +0 -52
- package/dist/app/index.js +0 -35
- package/dist/app/repl.js +0 -105
- package/dist/app/update-check.js +0 -132
- package/dist/app/version.js +0 -51
- package/dist/code-context.js +0 -68
- package/dist/colors.js +0 -204
- package/dist/command-catalog.js +0 -242
- package/dist/commands.js +0 -350
- package/dist/conductor.js +0 -94
- package/dist/config/config.js +0 -335
- package/dist/config/hooks.js +0 -187
- package/dist/config.js +0 -335
- package/dist/downloader-state.js +0 -302
- package/dist/downloader-ui.js +0 -289
- package/dist/gateway/adapters/discord.js +0 -108
- package/dist/gateway/adapters/formatting.js +0 -96
- package/dist/gateway/adapters/telegram.js +0 -106
- package/dist/gateway/adapters/types.js +0 -2
- package/dist/gateway/adapters/whatsapp.js +0 -124
- package/dist/gateway/auth-policy.js +0 -66
- package/dist/gateway/core.js +0 -87
- package/dist/gateway/headless-conductor.js +0 -328
- package/dist/gateway/message-router.js +0 -23
- package/dist/gateway/rate-limiter.js +0 -48
- package/dist/gateway/runtime/backend-policy.js +0 -18
- package/dist/gateway/runtime/discord-bot.js +0 -104
- package/dist/gateway/runtime/discord-main.js +0 -69
- package/dist/gateway/session-manager.js +0 -77
- package/dist/gateway/types.js +0 -2
- package/dist/hardware.js +0 -92
- package/dist/hooks.js +0 -187
- package/dist/index.js +0 -34
- package/dist/input-engine.js +0 -250
- package/dist/llama-client.js +0 -227
- package/dist/llama-server.js +0 -620
- package/dist/llm/llama-client.js +0 -227
- package/dist/llm/llama-server.js +0 -620
- package/dist/llm/token-counter.js +0 -47
- package/dist/memory-store.js +0 -159
- package/dist/messages.js +0 -59
- package/dist/model-downloader.js +0 -382
- package/dist/model-manager-state.js +0 -118
- package/dist/model-manager-ui.js +0 -194
- package/dist/model-manager.js +0 -190
- package/dist/models/hardware.js +0 -92
- package/dist/models/model-downloader.js +0 -382
- package/dist/models/model-manager.js +0 -190
- package/dist/prompt-box.js +0 -78
- package/dist/prompt-composer.js +0 -498
- package/dist/repl.js +0 -105
- package/dist/session-store.js +0 -211
- package/dist/spinner.js +0 -76
- package/dist/title-box.js +0 -388
- package/dist/token-counter.js +0 -47
- package/dist/tool-executor.js +0 -415
- package/dist/tool-protocol.js +0 -121
- package/dist/tool-safety.js +0 -52
- package/dist/tui/app.js +0 -2553
- package/dist/tui/startup.js +0 -56
- package/dist/tui-input-routing.js +0 -53
- package/dist/tui.js +0 -51
- package/dist/types/app-types.js +0 -2
- package/dist/types.js +0 -2
- package/dist/ui/colors.js +0 -204
- package/dist/ui/downloader/downloader-state.js +0 -302
- package/dist/ui/downloader/downloader-ui.js +0 -289
- package/dist/ui/input/input-engine.js +0 -250
- package/dist/ui/input/tui-input-routing.js +0 -53
- package/dist/ui/input/vt-session.js +0 -168
- package/dist/ui/messages.js +0 -59
- package/dist/ui/model-manager/model-manager-state.js +0 -118
- package/dist/ui/model-manager/model-manager-ui.js +0 -194
- package/dist/ui/prompt/prompt-box.js +0 -78
- package/dist/ui/prompt/prompt-composer.js +0 -498
- package/dist/ui/spinner.js +0 -76
- package/dist/ui/title-box.js +0 -388
- package/dist/ui/tui/app.js +0 -6
- package/dist/ui/tui/autocomplete.js +0 -85
- package/dist/ui/tui/constants.js +0 -18
- package/dist/ui/tui/history.js +0 -29
- package/dist/ui/tui/layout.js +0 -341
- package/dist/ui/tui/runtime-core.js +0 -2584
- package/dist/ui/tui/runtime-utils.js +0 -53
- package/dist/ui/tui/start-tui.js +0 -54
- package/dist/ui/tui/startup.js +0 -56
- package/dist/version.js +0 -51
- package/dist/vt-session.js +0 -168
- package/install.sh +0 -457
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Katherine North
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Automatic Git Versioning
|
|
2
|
+
|
|
3
|
+
- **Format**: `vmajor.minor.patch` where each commit adds `+0.0.1`, patch rolls into minor after `v0.0.99 -> v0.1.00`, minor rolls into major after `v0.99.99 -> v1.0.00`.
|
|
4
|
+
- **Example**: 121 commits → `v0.1.21`
|
|
5
|
+
- **Usage**: Run `python version.py` to see the current version
|
|
6
|
+
|
|
7
|
+
The version is recalculated from the commit count so it always reflects the repository history.
|
|
8
|
+
|
|
9
|
+
## 🚀 Quick Start
|
|
10
|
+
|
|
11
|
+
Yips is designed to be zero-config. The included startup script handles dependency checks, virtual environments, and configuration automatically.
|
|
12
|
+
|
|
13
|
+
### 1. Clone the repository
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/sheepbun/yips-cli.git
|
|
16
|
+
cd Yips
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Run the startup script
|
|
20
|
+
```bash
|
|
21
|
+
chmod +x startup.sh
|
|
22
|
+
./startup.sh
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The script will:
|
|
26
|
+
* Verify Python 3 installation.
|
|
27
|
+
* Create a virtual environment (`.venv`) if missing.
|
|
28
|
+
* Install/Update all dependencies from `requirements.txt`.
|
|
29
|
+
* Clone/update and build `llama.cpp` automatically, enabling CUDA when an NVIDIA CUDA environment is detected.
|
|
30
|
+
* Initialize your `.yips_config.json` on the first run.
|
|
31
|
+
* Launch the Yips CLI.
|
|
32
|
+
|
|
33
|
+
## 🛠 Backends
|
|
34
|
+
|
|
35
|
+
Yips supports multiple backends for maximum flexibility:
|
|
36
|
+
1. **llama.cpp** (Local, High Performance) - *Recommended*
|
|
37
|
+
2. **LM Studio** (Local, GUI-based)
|
|
38
|
+
3. **Claude CLI** (Cloud-based fallback)
|
|
39
|
+
|
|
40
|
+
Use the `/backend` command within Yips to switch between them.
|
|
41
|
+
|
|
42
|
+
## 🧠 Model Management
|
|
43
|
+
|
|
44
|
+
Yips features a hardware-aware Model Manager. Run:
|
|
45
|
+
```bash
|
|
46
|
+
/model
|
|
47
|
+
```
|
|
48
|
+
within the CLI to manage your local models, switch between backends, or jump to the interactive downloader.
|
|
49
|
+
|
|
50
|
+
## Developer documentation
|
|
51
|
+
|
|
52
|
+
For persistence layout, how to add slash-command plugins, model tool tags, and setup/backend operations, see [docs/DEVELOPER.md](docs/DEVELOPER.md).
|
package/bin/yips.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const isWin = os.platform() === 'win32';
|
|
7
|
+
const ext = isWin ? '.exe' : '';
|
|
8
|
+
const binPath = path.join(__dirname, `yips${ext}`);
|
|
9
|
+
|
|
10
|
+
const result = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
11
|
+
if (result.error) {
|
|
12
|
+
console.error(`Failed to execute Yips binary at ${binPath}:`, result.error.message);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
process.exit(result.status || 0);
|
package/package.json
CHANGED
|
@@ -1,128 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@sheepbun/yips",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"imports": {
|
|
23
|
-
"#app/*": {
|
|
24
|
-
"development": "./src/app/*.ts",
|
|
25
|
-
"default": "./dist/app/*.js"
|
|
26
|
-
},
|
|
27
|
-
"#agent/conductor": {
|
|
28
|
-
"development": "./src/agent/conductor.ts",
|
|
29
|
-
"default": "./dist/agent/conductor.js"
|
|
30
|
-
},
|
|
31
|
-
"#agent/commands/*": {
|
|
32
|
-
"development": "./src/agent/commands/*.ts",
|
|
33
|
-
"default": "./dist/agent/commands/*.js"
|
|
34
|
-
},
|
|
35
|
-
"#agent/context/*": {
|
|
36
|
-
"development": "./src/agent/context/*.ts",
|
|
37
|
-
"default": "./dist/agent/context/*.js"
|
|
38
|
-
},
|
|
39
|
-
"#agent/protocol/*": {
|
|
40
|
-
"development": "./src/agent/protocol/*.ts",
|
|
41
|
-
"default": "./dist/agent/protocol/*.js"
|
|
42
|
-
},
|
|
43
|
-
"#agent/tools/*": {
|
|
44
|
-
"development": "./src/agent/tools/*.ts",
|
|
45
|
-
"default": "./dist/agent/tools/*.js"
|
|
46
|
-
},
|
|
47
|
-
"#agent/skills/*": {
|
|
48
|
-
"development": "./src/agent/skills/*.ts",
|
|
49
|
-
"default": "./dist/agent/skills/*.js"
|
|
50
|
-
},
|
|
51
|
-
"#config/*": {
|
|
52
|
-
"development": "./src/config/*.ts",
|
|
53
|
-
"default": "./dist/config/*.js"
|
|
54
|
-
},
|
|
55
|
-
"#gateway/*": {
|
|
56
|
-
"development": "./src/gateway/*.ts",
|
|
57
|
-
"default": "./dist/gateway/*.js"
|
|
58
|
-
},
|
|
59
|
-
"#llm/*": {
|
|
60
|
-
"development": "./src/llm/*.ts",
|
|
61
|
-
"default": "./dist/llm/*.js"
|
|
62
|
-
},
|
|
63
|
-
"#models/*": {
|
|
64
|
-
"development": "./src/models/*.ts",
|
|
65
|
-
"default": "./dist/models/*.js"
|
|
66
|
-
},
|
|
67
|
-
"#types/*": {
|
|
68
|
-
"development": "./src/types/*.ts",
|
|
69
|
-
"default": "./dist/types/*.js"
|
|
70
|
-
},
|
|
71
|
-
"#ui/*": {
|
|
72
|
-
"development": "./src/ui/*.ts",
|
|
73
|
-
"default": "./dist/ui/*.js"
|
|
74
|
-
},
|
|
75
|
-
"#ui/prompt/*": {
|
|
76
|
-
"development": "./src/ui/prompt/*.ts",
|
|
77
|
-
"default": "./dist/ui/prompt/*.js"
|
|
78
|
-
},
|
|
79
|
-
"#ui/input/*": {
|
|
80
|
-
"development": "./src/ui/input/*.ts",
|
|
81
|
-
"default": "./dist/ui/input/*.js"
|
|
82
|
-
},
|
|
83
|
-
"#ui/downloader/*": {
|
|
84
|
-
"development": "./src/ui/downloader/*.ts",
|
|
85
|
-
"default": "./dist/ui/downloader/*.js"
|
|
86
|
-
},
|
|
87
|
-
"#ui/model-manager/*": {
|
|
88
|
-
"development": "./src/ui/model-manager/*.ts",
|
|
89
|
-
"default": "./dist/ui/model-manager/*.js"
|
|
90
|
-
},
|
|
91
|
-
"#ui/tui/*": {
|
|
92
|
-
"development": "./src/ui/tui/*.ts",
|
|
93
|
-
"default": "./dist/ui/tui/*.js"
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
"engines": {
|
|
97
|
-
"node": ">=20.0.0"
|
|
98
|
-
},
|
|
99
|
-
"scripts": {
|
|
100
|
-
"build": "tsc -p tsconfig.build.json",
|
|
101
|
-
"dev": "tsx --conditions=development src/app/index.ts",
|
|
102
|
-
"gateway:discord": "tsx --conditions=development src/gateway/runtime/discord-main.ts",
|
|
103
|
-
"start": "node dist/app/index.js",
|
|
104
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
105
|
-
"test": "vitest run",
|
|
106
|
-
"test:watch": "vitest",
|
|
107
|
-
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
108
|
-
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \".github/**/*.yml\" \"*.json\"",
|
|
109
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \".github/**/*.yml\" \"*.json\""
|
|
110
|
-
},
|
|
111
|
-
"devDependencies": {
|
|
112
|
-
"@types/node": "^22.13.4",
|
|
113
|
-
"@types/react": "^18.3.26",
|
|
114
|
-
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
115
|
-
"@typescript-eslint/parser": "^7.18.0",
|
|
116
|
-
"eslint": "^8.57.1",
|
|
117
|
-
"prettier": "^3.5.3",
|
|
118
|
-
"tsx": "^4.19.3",
|
|
119
|
-
"typescript": "^5.8.2",
|
|
120
|
-
"vitest": "^3.0.7"
|
|
121
|
-
},
|
|
122
|
-
"dependencies": {
|
|
123
|
-
"discord.js": "^14.22.1",
|
|
124
|
-
"ink": "^4.4.1",
|
|
125
|
-
"node-pty": "^1.1.0",
|
|
126
|
-
"react": "^18.3.1"
|
|
127
|
-
}
|
|
128
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@sheepbun/yips",
|
|
3
|
+
"version": "0.1.46",
|
|
4
|
+
"description": "Yips Personal Desktop Agent",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"postinstall": "node postinstall.js"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"yips": "bin/yips.js"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/sheepbun/yips.git"
|
|
14
|
+
},
|
|
15
|
+
"author": "Sheepbun",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"files": [
|
|
18
|
+
"postinstall.js",
|
|
19
|
+
"bin/yips.js"
|
|
20
|
+
]
|
|
21
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const https = require('https');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const isWin = os.platform() === 'win32';
|
|
7
|
+
const ext = isWin ? '.exe' : '';
|
|
8
|
+
const binaryName = isWin ? 'yips-windows.exe' : 'yips-linux';
|
|
9
|
+
const url = `https://github.com/sheepbun/yips/releases/latest/download/${binaryName}`;
|
|
10
|
+
const binDir = path.join(__dirname, 'bin');
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binDir)) {
|
|
13
|
+
fs.mkdirSync(binDir);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const dest = path.join(binDir, `yips${ext}`);
|
|
17
|
+
|
|
18
|
+
console.log(`Downloading Yips binary from ${url}...`);
|
|
19
|
+
|
|
20
|
+
https.get(url, (res) => {
|
|
21
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
22
|
+
// Follow redirect
|
|
23
|
+
https.get(res.headers.location, (redirectRes) => {
|
|
24
|
+
downloadStream(redirectRes);
|
|
25
|
+
}).on('error', handleError);
|
|
26
|
+
} else {
|
|
27
|
+
downloadStream(res);
|
|
28
|
+
}
|
|
29
|
+
}).on('error', handleError);
|
|
30
|
+
|
|
31
|
+
function downloadStream(res) {
|
|
32
|
+
if (res.statusCode !== 200) {
|
|
33
|
+
console.error(`Failed to download: HTTP ${res.statusCode}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
const file = fs.createWriteStream(dest);
|
|
37
|
+
res.pipe(file);
|
|
38
|
+
file.on('finish', () => {
|
|
39
|
+
file.close();
|
|
40
|
+
if (!isWin) {
|
|
41
|
+
fs.chmodSync(dest, 0o755);
|
|
42
|
+
}
|
|
43
|
+
console.log('Downloaded Yips binary successfully.');
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function handleError(err) {
|
|
48
|
+
console.error('Error downloading Yips:', err.message);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadCommandCatalog = loadCommandCatalog;
|
|
4
|
-
const node_fs_1 = require("node:fs");
|
|
5
|
-
const node_path_1 = require("node:path");
|
|
6
|
-
const GENERIC_DESCRIPTIONS = new Set(["Tool command", "Markdown skill", "Command"]);
|
|
7
|
-
const RESTORED_COMMAND_DEFAULTS = [
|
|
8
|
-
{ name: "backend", description: "Switch AI backends (llamacpp, claude)", kind: "builtin" },
|
|
9
|
-
{ name: "clear", description: "Clear context and start a new session", kind: "builtin" },
|
|
10
|
-
{ name: "dl", description: "Alias for /download", kind: "builtin" },
|
|
11
|
-
{ name: "download", description: "Open the interactive model downloader", kind: "builtin" },
|
|
12
|
-
{ name: "exit", description: "Exit Yips", kind: "builtin" },
|
|
13
|
-
{ name: "fetch", description: "Retrieve and display content from a URL", kind: "tool" },
|
|
14
|
-
{ name: "grab", description: "Read a file's content into context", kind: "tool" },
|
|
15
|
-
{ name: "help", description: "Show available commands and tips", kind: "skill" },
|
|
16
|
-
{ name: "memorize", description: "Save a fact to long-term memory", kind: "tool" },
|
|
17
|
-
{ name: "model", description: "Open the Model Manager or switch to a specific model", kind: "builtin" },
|
|
18
|
-
{ name: "new", description: "Start a new session", kind: "builtin" },
|
|
19
|
-
{ name: "nick", description: "Set a custom nickname for a model", kind: "builtin" },
|
|
20
|
-
{ name: "quit", description: "Exit Yips", kind: "builtin" },
|
|
21
|
-
{ name: "search", description: "Search the web (DuckDuckGo)", kind: "tool" },
|
|
22
|
-
{ name: "sessions", description: "Interactively select and load a session", kind: "builtin" },
|
|
23
|
-
{ name: "stream", description: "Toggle streaming responses", kind: "builtin" },
|
|
24
|
-
{ name: "tokens", description: "Show or set token counter mode and max", kind: "builtin" },
|
|
25
|
-
{ name: "update", description: "Check for newer Yips versions and upgrade guidance", kind: "builtin" },
|
|
26
|
-
{ name: "verbose", description: "Toggle verbose output", kind: "builtin" },
|
|
27
|
-
{ name: "vt", description: "Toggle the Virtual Terminal", kind: "tool" }
|
|
28
|
-
];
|
|
29
|
-
function normalizeCommandName(name) {
|
|
30
|
-
return name.trim().replace(/^\/+/, "").toLowerCase();
|
|
31
|
-
}
|
|
32
|
-
function isGenericDescription(description) {
|
|
33
|
-
const trimmed = description.trim();
|
|
34
|
-
if (trimmed.length === 0) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return GENERIC_DESCRIPTIONS.has(trimmed);
|
|
38
|
-
}
|
|
39
|
-
function normalizeDescription(description) {
|
|
40
|
-
const trimmed = description.trim();
|
|
41
|
-
return trimmed.length > 0 ? trimmed : "Command";
|
|
42
|
-
}
|
|
43
|
-
function sortedDirectoryEntries(path) {
|
|
44
|
-
return (0, node_fs_1.readdirSync)(path, { withFileTypes: true })
|
|
45
|
-
.filter((entry) => entry.isDirectory())
|
|
46
|
-
.map((entry) => entry.name)
|
|
47
|
-
.sort((left, right) => left.localeCompare(right));
|
|
48
|
-
}
|
|
49
|
-
function fileNames(path) {
|
|
50
|
-
return (0, node_fs_1.readdirSync)(path, { withFileTypes: true })
|
|
51
|
-
.filter((entry) => entry.isFile())
|
|
52
|
-
.map((entry) => entry.name)
|
|
53
|
-
.sort((left, right) => left.localeCompare(right));
|
|
54
|
-
}
|
|
55
|
-
function fileBasename(filename) {
|
|
56
|
-
const dotIndex = filename.lastIndexOf(".");
|
|
57
|
-
return dotIndex >= 0 ? filename.slice(0, dotIndex) : filename;
|
|
58
|
-
}
|
|
59
|
-
function pickCommandFile(commandDir, commandDirName, extension) {
|
|
60
|
-
if (!(0, node_fs_1.existsSync)(commandDir)) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
const names = fileNames(commandDir);
|
|
64
|
-
const extensionLower = extension.toLowerCase();
|
|
65
|
-
const targetBase = commandDirName.toLowerCase();
|
|
66
|
-
const exact = names.find((name) => {
|
|
67
|
-
if (!name.toLowerCase().endsWith(extensionLower)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
return fileBasename(name).toLowerCase() === targetBase;
|
|
71
|
-
});
|
|
72
|
-
if (exact) {
|
|
73
|
-
return (0, node_path_1.join)(commandDir, exact);
|
|
74
|
-
}
|
|
75
|
-
const fallback = names.find((name) => name.toLowerCase().endsWith(extensionLower));
|
|
76
|
-
return fallback ? (0, node_path_1.join)(commandDir, fallback) : null;
|
|
77
|
-
}
|
|
78
|
-
function toNonEmptyTrimmedLines(text) {
|
|
79
|
-
return text
|
|
80
|
-
.split(/\r?\n/u)
|
|
81
|
-
.map((line) => line.trim())
|
|
82
|
-
.filter((line) => line.length > 0);
|
|
83
|
-
}
|
|
84
|
-
function descriptionFromLines(lines) {
|
|
85
|
-
for (const line of lines) {
|
|
86
|
-
if (/^description\s*:/iu.test(line)) {
|
|
87
|
-
const extracted = line.replace(/^description\s*:\s*/iu, "").trim();
|
|
88
|
-
if (extracted.length > 0) {
|
|
89
|
-
return extracted;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const first = lines[0];
|
|
94
|
-
if (!first) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const split = first.split(" - ");
|
|
98
|
-
if (split.length > 1) {
|
|
99
|
-
const remainder = split.slice(1).join(" - ").trim();
|
|
100
|
-
if (remainder.length > 0) {
|
|
101
|
-
return remainder;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return first;
|
|
105
|
-
}
|
|
106
|
-
function extractPythonDescription(contents) {
|
|
107
|
-
const docstringMatch = contents.match(/^\s*(?:#.*\r?\n|\s)*(?:"""([\s\S]*?)"""|'''([\s\S]*?)''')/u);
|
|
108
|
-
const docstring = docstringMatch?.[1] ?? docstringMatch?.[2];
|
|
109
|
-
if (!docstring) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
return descriptionFromLines(toNonEmptyTrimmedLines(docstring));
|
|
113
|
-
}
|
|
114
|
-
function extractMarkdownDescription(contents) {
|
|
115
|
-
const lines = toNonEmptyTrimmedLines(contents);
|
|
116
|
-
if (lines.length === 0) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
const first = lines[0];
|
|
120
|
-
if (!first) {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
if (!first.startsWith("#")) {
|
|
124
|
-
return first;
|
|
125
|
-
}
|
|
126
|
-
for (const line of lines.slice(1)) {
|
|
127
|
-
if (line.startsWith("#") || line.startsWith("!") || line.startsWith("```")) {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
return line;
|
|
131
|
-
}
|
|
132
|
-
const heading = first.replace(/^#+\s*/u, "").trim();
|
|
133
|
-
return heading.length > 0 ? heading : null;
|
|
134
|
-
}
|
|
135
|
-
function readTextSafely(path) {
|
|
136
|
-
try {
|
|
137
|
-
return (0, node_fs_1.readFileSync)(path, "utf8");
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function discoverCommands(parentDir, parentKind) {
|
|
144
|
-
if (!(0, node_fs_1.existsSync)(parentDir)) {
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
const discovered = [];
|
|
148
|
-
for (const commandDirName of sortedDirectoryEntries(parentDir)) {
|
|
149
|
-
const commandName = normalizeCommandName(commandDirName);
|
|
150
|
-
if (commandName.length === 0) {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
const commandDir = (0, node_path_1.join)(parentDir, commandDirName);
|
|
154
|
-
const pythonPath = pickCommandFile(commandDir, commandDirName, ".py");
|
|
155
|
-
const markdownPath = pickCommandFile(commandDir, commandDirName, ".md");
|
|
156
|
-
let kind = parentKind;
|
|
157
|
-
let description = null;
|
|
158
|
-
let priority = 0;
|
|
159
|
-
if (pythonPath) {
|
|
160
|
-
const contents = readTextSafely(pythonPath);
|
|
161
|
-
if (contents) {
|
|
162
|
-
description = extractPythonDescription(contents);
|
|
163
|
-
}
|
|
164
|
-
kind = "tool";
|
|
165
|
-
priority = description ? 2 : 0;
|
|
166
|
-
}
|
|
167
|
-
else if (markdownPath) {
|
|
168
|
-
const contents = readTextSafely(markdownPath);
|
|
169
|
-
if (contents) {
|
|
170
|
-
description = extractMarkdownDescription(contents);
|
|
171
|
-
}
|
|
172
|
-
kind = "skill";
|
|
173
|
-
priority = description ? 2 : 0;
|
|
174
|
-
}
|
|
175
|
-
discovered.push({
|
|
176
|
-
name: commandName,
|
|
177
|
-
description: normalizeDescription(description ?? "Command"),
|
|
178
|
-
kind,
|
|
179
|
-
implemented: false,
|
|
180
|
-
priority
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
return discovered;
|
|
184
|
-
}
|
|
185
|
-
function upsertCatalogEntry(catalog, candidate) {
|
|
186
|
-
const normalizedName = normalizeCommandName(candidate.name);
|
|
187
|
-
if (normalizedName.length === 0) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
const normalizedCandidate = {
|
|
191
|
-
...candidate,
|
|
192
|
-
name: normalizedName,
|
|
193
|
-
description: normalizeDescription(candidate.description)
|
|
194
|
-
};
|
|
195
|
-
const existing = catalog.get(normalizedName);
|
|
196
|
-
if (!existing) {
|
|
197
|
-
catalog.set(normalizedName, normalizedCandidate);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const candidateGeneric = isGenericDescription(normalizedCandidate.description);
|
|
201
|
-
const existingGeneric = isGenericDescription(existing.description);
|
|
202
|
-
const shouldReplace = normalizedCandidate.priority > existing.priority ||
|
|
203
|
-
(normalizedCandidate.priority === existing.priority && !candidateGeneric && existingGeneric);
|
|
204
|
-
if (shouldReplace) {
|
|
205
|
-
catalog.set(normalizedName, {
|
|
206
|
-
...normalizedCandidate,
|
|
207
|
-
implemented: existing.implemented || normalizedCandidate.implemented
|
|
208
|
-
});
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
catalog.set(normalizedName, {
|
|
212
|
-
...existing,
|
|
213
|
-
implemented: existing.implemented || normalizedCandidate.implemented
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
function loadCommandCatalog(options = {}) {
|
|
217
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
218
|
-
const catalog = new Map();
|
|
219
|
-
for (const descriptor of RESTORED_COMMAND_DEFAULTS) {
|
|
220
|
-
upsertCatalogEntry(catalog, {
|
|
221
|
-
...descriptor,
|
|
222
|
-
name: normalizeCommandName(descriptor.name),
|
|
223
|
-
implemented: false,
|
|
224
|
-
priority: 1
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
const toolsDir = (0, node_path_1.join)(projectRoot, "commands", "tools");
|
|
228
|
-
const skillsDir = (0, node_path_1.join)(projectRoot, "commands", "skills");
|
|
229
|
-
for (const descriptor of discoverCommands(toolsDir, "tool")) {
|
|
230
|
-
upsertCatalogEntry(catalog, descriptor);
|
|
231
|
-
}
|
|
232
|
-
for (const descriptor of discoverCommands(skillsDir, "skill")) {
|
|
233
|
-
upsertCatalogEntry(catalog, descriptor);
|
|
234
|
-
}
|
|
235
|
-
return [...catalog.values()]
|
|
236
|
-
.sort((left, right) => left.name.localeCompare(right.name))
|
|
237
|
-
.map((descriptor) => ({
|
|
238
|
-
name: descriptor.name,
|
|
239
|
-
description: descriptor.description,
|
|
240
|
-
kind: descriptor.kind,
|
|
241
|
-
implemented: descriptor.implemented
|
|
242
|
-
}));
|
|
243
|
-
}
|