@aman_asmuei/akit 0.1.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/LICENSE +21 -0
- package/README.md +165 -0
- package/bin/akit.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +591 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aman Asmuei
|
|
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,165 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<br>
|
|
4
|
+
|
|
5
|
+
<picture>
|
|
6
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/akit-capability_layer-white?style=for-the-badge&labelColor=0d1117&color=58a6ff">
|
|
7
|
+
<img alt="akit" src="https://img.shields.io/badge/akit-capability_layer-black?style=for-the-badge&labelColor=f6f8fa&color=24292f">
|
|
8
|
+
</picture>
|
|
9
|
+
|
|
10
|
+
### The portable capability layer for AI companions.
|
|
11
|
+
|
|
12
|
+
Give any AI tools that work everywhere — MCP on dev platforms, manual fallback everywhere else.
|
|
13
|
+
|
|
14
|
+
<br>
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@aman_asmuei/akit)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://github.com/amanasmuei/acore)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## The Problem
|
|
25
|
+
|
|
26
|
+
AI tools are platform-locked. MCP tools only work on MCP platforms. Function calling schemas differ between OpenAI, Anthropic, and Google. You can't take your AI's capabilities with you.
|
|
27
|
+
|
|
28
|
+
## The Solution
|
|
29
|
+
|
|
30
|
+
**akit** gives any AI a portable toolkit. One command to add tools. Works everywhere.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx @aman_asmuei/akit add github
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
On **Claude Code / Cursor / Windsurf**: installs the real MCP server. Your AI gets actual tool access.
|
|
37
|
+
|
|
38
|
+
On **ChatGPT / Gemini / other**: updates your `kit.md` capability manifest. Your AI knows what tools exist and guides you through manual steps.
|
|
39
|
+
|
|
40
|
+
Same command. Same file. Different capabilities based on platform.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## The Ecosystem
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
acore (who it IS) + amem (what it KNOWS) + akit (what it CAN DO)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Layer | Package | What it does |
|
|
51
|
+
|:------|:--------|:-------------|
|
|
52
|
+
| Identity | [acore](https://github.com/amanasmuei/acore) | Personality, values, relationship memory |
|
|
53
|
+
| Memory | [amem](https://github.com/amanasmuei/amem) | Automated knowledge storage |
|
|
54
|
+
| Tools | **akit** | Portable AI capabilities |
|
|
55
|
+
|
|
56
|
+
Each works independently. Together they're a complete portable AI agent.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Search for tools
|
|
64
|
+
npx @aman_asmuei/akit search database
|
|
65
|
+
|
|
66
|
+
# Add a tool
|
|
67
|
+
npx @aman_asmuei/akit add github
|
|
68
|
+
|
|
69
|
+
# See your toolkit
|
|
70
|
+
npx @aman_asmuei/akit show
|
|
71
|
+
|
|
72
|
+
# Health check
|
|
73
|
+
npx @aman_asmuei/akit doctor
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
| Command | What it does |
|
|
79
|
+
|:--------|:------------|
|
|
80
|
+
| `akit add <tool>` | Add a tool (auto-configures MCP if available) |
|
|
81
|
+
| `akit remove <tool>` | Remove a tool |
|
|
82
|
+
| `akit list` | List installed tools |
|
|
83
|
+
| `akit search <query>` | Search the tool registry |
|
|
84
|
+
| `akit show` | View your kit.md |
|
|
85
|
+
| `akit doctor` | Health check your toolkit |
|
|
86
|
+
|
|
87
|
+
## Available Tools
|
|
88
|
+
|
|
89
|
+
| Tool | Category | What it does |
|
|
90
|
+
|:-----|:---------|:-------------|
|
|
91
|
+
| `web-search` | Search | Search the web for current information |
|
|
92
|
+
| `brave-search` | Search | Private web search via Brave |
|
|
93
|
+
| `github` | Development | PRs, issues, repos, code review |
|
|
94
|
+
| `git` | Development | Log, diff, blame, branch management |
|
|
95
|
+
| `filesystem` | Development | Read, write, search project files |
|
|
96
|
+
| `memory` | Memory | Persistent AI memory via amem |
|
|
97
|
+
| `postgres` | Data | Query PostgreSQL databases |
|
|
98
|
+
| `sqlite` | Data | Query local SQLite databases |
|
|
99
|
+
| `fetch` | Automation | HTTP requests to APIs |
|
|
100
|
+
| `puppeteer` | Automation | Browser automation |
|
|
101
|
+
| `slack` | Communication | Send and read Slack messages |
|
|
102
|
+
| `notion` | Communication | Notion pages and databases |
|
|
103
|
+
| `linear` | Development | Linear issues and projects |
|
|
104
|
+
| `sentry` | Development | Error monitoring and triage |
|
|
105
|
+
| `docker` | Automation | Container management |
|
|
106
|
+
|
|
107
|
+
## How It Works
|
|
108
|
+
|
|
109
|
+
### kit.md — The Capability Manifest
|
|
110
|
+
|
|
111
|
+
Every tool you add gets recorded in `~/.akit/kit.md`:
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+
# My AI Toolkit
|
|
115
|
+
|
|
116
|
+
## github
|
|
117
|
+
- Do: Manage GitHub repos, PRs, issues, and code review
|
|
118
|
+
- When: PRs, issues, repos, code review
|
|
119
|
+
- How: MCP -> @modelcontextprotocol/server-github
|
|
120
|
+
|
|
121
|
+
## filesystem
|
|
122
|
+
- Do: Read, write, and search project files
|
|
123
|
+
- When: coding, documentation
|
|
124
|
+
- How: native (built into Claude Code, Cursor)
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Permissions
|
|
129
|
+
- Never delete data without confirmation
|
|
130
|
+
- Never push to main without approval
|
|
131
|
+
- Ask before making external API calls that modify state
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Your AI reads this file and knows what it can do.
|
|
135
|
+
|
|
136
|
+
### Platform Behavior
|
|
137
|
+
|
|
138
|
+
| Platform | What happens when you `akit add` |
|
|
139
|
+
|:---------|:--------------------------------|
|
|
140
|
+
| Claude Code | Writes MCP config to `~/.claude/settings.json` |
|
|
141
|
+
| Cursor | Writes MCP config to `.cursor/mcp.json` |
|
|
142
|
+
| Windsurf | Writes MCP config to `~/.windsurf/mcp.json` |
|
|
143
|
+
| ChatGPT / Other | Updates kit.md with manual instructions |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Privacy
|
|
148
|
+
|
|
149
|
+
All data stays local. `~/.akit/` contains your toolkit config. No telemetry. No accounts. No cloud.
|
|
150
|
+
|
|
151
|
+
## Contributing
|
|
152
|
+
|
|
153
|
+
Contributions welcome! Add tools to the registry, improve platform support, or suggest features.
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
[MIT](LICENSE)
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
<div align="center">
|
|
162
|
+
|
|
163
|
+
**One command. Any tool. Every AI.**
|
|
164
|
+
|
|
165
|
+
</div>
|
package/bin/akit.js
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
// src/commands/add.ts
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
|
|
8
|
+
// src/lib/registry.ts
|
|
9
|
+
var REGISTRY = [
|
|
10
|
+
{
|
|
11
|
+
name: "web-search",
|
|
12
|
+
description: "Search the web for current information",
|
|
13
|
+
category: "search",
|
|
14
|
+
mcp: {
|
|
15
|
+
package: "@anthropic/web-search",
|
|
16
|
+
command: "npx",
|
|
17
|
+
args: ["-y", "@anthropic/web-search"]
|
|
18
|
+
},
|
|
19
|
+
manual: "Ask user to search and paste results",
|
|
20
|
+
capabilities: ["research", "fact-checking", "current events"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "brave-search",
|
|
24
|
+
description: "Private web search via Brave",
|
|
25
|
+
category: "search",
|
|
26
|
+
mcp: {
|
|
27
|
+
package: "@modelcontextprotocol/server-brave-search",
|
|
28
|
+
command: "npx",
|
|
29
|
+
args: ["-y", "@modelcontextprotocol/server-brave-search"],
|
|
30
|
+
env: { BRAVE_API_KEY: "" }
|
|
31
|
+
},
|
|
32
|
+
manual: "Ask user to search Brave and paste results",
|
|
33
|
+
capabilities: ["research", "private search"],
|
|
34
|
+
envHint: "Set BRAVE_API_KEY from https://brave.com/search/api/"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "github",
|
|
38
|
+
description: "Manage GitHub repos, PRs, issues, and code review",
|
|
39
|
+
category: "development",
|
|
40
|
+
mcp: {
|
|
41
|
+
package: "@modelcontextprotocol/server-github",
|
|
42
|
+
command: "npx",
|
|
43
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
44
|
+
env: { GITHUB_TOKEN: "" }
|
|
45
|
+
},
|
|
46
|
+
manual: "Suggest gh CLI commands for user to run",
|
|
47
|
+
capabilities: ["PRs", "issues", "repos", "code review"],
|
|
48
|
+
envHint: "Set GITHUB_TOKEN from https://github.com/settings/tokens"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "git",
|
|
52
|
+
description: "Git operations \u2014 log, diff, blame, branch management",
|
|
53
|
+
category: "development",
|
|
54
|
+
mcp: {
|
|
55
|
+
package: "@modelcontextprotocol/server-git",
|
|
56
|
+
command: "npx",
|
|
57
|
+
args: ["-y", "@modelcontextprotocol/server-git"]
|
|
58
|
+
},
|
|
59
|
+
manual: "Suggest git commands for user to run",
|
|
60
|
+
capabilities: ["log", "diff", "blame", "branch", "commit history"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "filesystem",
|
|
64
|
+
description: "Read, write, and search project files",
|
|
65
|
+
category: "development",
|
|
66
|
+
mcp: {
|
|
67
|
+
package: "@modelcontextprotocol/server-filesystem",
|
|
68
|
+
command: "npx",
|
|
69
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
70
|
+
},
|
|
71
|
+
manual: "Ask user to share file contents or run commands",
|
|
72
|
+
capabilities: ["read files", "write files", "search", "directory listing"]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "memory",
|
|
76
|
+
description: "Persistent AI memory via amem",
|
|
77
|
+
category: "memory",
|
|
78
|
+
mcp: {
|
|
79
|
+
package: "@aman_asmuei/amem",
|
|
80
|
+
command: "npx",
|
|
81
|
+
args: ["-y", "@aman_asmuei/amem"]
|
|
82
|
+
},
|
|
83
|
+
manual: "Use acore's built-in memory lifecycle rules",
|
|
84
|
+
capabilities: ["store observations", "recall context", "semantic search"]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "postgres",
|
|
88
|
+
description: "Query and manage PostgreSQL databases",
|
|
89
|
+
category: "data",
|
|
90
|
+
mcp: {
|
|
91
|
+
package: "@modelcontextprotocol/server-postgres",
|
|
92
|
+
command: "npx",
|
|
93
|
+
args: ["-y", "@modelcontextprotocol/server-postgres"],
|
|
94
|
+
env: { DATABASE_URL: "" }
|
|
95
|
+
},
|
|
96
|
+
manual: "Ask user to run SQL queries and paste results",
|
|
97
|
+
capabilities: ["query", "schema inspection", "data analysis"],
|
|
98
|
+
envHint: "Set DATABASE_URL (e.g., postgresql://user:pass@localhost/db)"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "sqlite",
|
|
102
|
+
description: "Query local SQLite databases",
|
|
103
|
+
category: "data",
|
|
104
|
+
mcp: {
|
|
105
|
+
package: "@modelcontextprotocol/server-sqlite",
|
|
106
|
+
command: "npx",
|
|
107
|
+
args: ["-y", "@modelcontextprotocol/server-sqlite"]
|
|
108
|
+
},
|
|
109
|
+
manual: "Ask user to run sqlite3 commands and paste results",
|
|
110
|
+
capabilities: ["query", "schema inspection", "local data"]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "fetch",
|
|
114
|
+
description: "Make HTTP requests to APIs and websites",
|
|
115
|
+
category: "automation",
|
|
116
|
+
mcp: {
|
|
117
|
+
package: "@modelcontextprotocol/server-fetch",
|
|
118
|
+
command: "npx",
|
|
119
|
+
args: ["-y", "@modelcontextprotocol/server-fetch"]
|
|
120
|
+
},
|
|
121
|
+
manual: "Ask user to run curl commands and paste results",
|
|
122
|
+
capabilities: ["GET", "POST", "API calls", "web scraping"]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "puppeteer",
|
|
126
|
+
description: "Browser automation \u2014 screenshots, scraping, testing",
|
|
127
|
+
category: "automation",
|
|
128
|
+
mcp: {
|
|
129
|
+
package: "@modelcontextprotocol/server-puppeteer",
|
|
130
|
+
command: "npx",
|
|
131
|
+
args: ["-y", "@modelcontextprotocol/server-puppeteer"]
|
|
132
|
+
},
|
|
133
|
+
manual: "Ask user to take screenshots or run browser scripts",
|
|
134
|
+
capabilities: ["screenshots", "form filling", "scraping", "testing"]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "slack",
|
|
138
|
+
description: "Send and read Slack messages",
|
|
139
|
+
category: "communication",
|
|
140
|
+
mcp: {
|
|
141
|
+
package: "@modelcontextprotocol/server-slack",
|
|
142
|
+
command: "npx",
|
|
143
|
+
args: ["-y", "@modelcontextprotocol/server-slack"],
|
|
144
|
+
env: { SLACK_BOT_TOKEN: "" }
|
|
145
|
+
},
|
|
146
|
+
manual: "Ask user to check Slack and paste relevant messages",
|
|
147
|
+
capabilities: ["send messages", "read channels", "search"],
|
|
148
|
+
envHint: "Set SLACK_BOT_TOKEN from your Slack app settings"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "notion",
|
|
152
|
+
description: "Read and write Notion pages and databases",
|
|
153
|
+
category: "communication",
|
|
154
|
+
mcp: {
|
|
155
|
+
package: "@notionhq/notion-mcp-server",
|
|
156
|
+
command: "npx",
|
|
157
|
+
args: ["-y", "@notionhq/notion-mcp-server"],
|
|
158
|
+
env: { NOTION_API_KEY: "" }
|
|
159
|
+
},
|
|
160
|
+
manual: "Ask user to paste Notion content or share links",
|
|
161
|
+
capabilities: ["read pages", "create pages", "query databases"],
|
|
162
|
+
envHint: "Set NOTION_API_KEY from https://www.notion.so/my-integrations"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "linear",
|
|
166
|
+
description: "Manage Linear issues, projects, and cycles",
|
|
167
|
+
category: "development",
|
|
168
|
+
mcp: {
|
|
169
|
+
package: "@linear/mcp-server",
|
|
170
|
+
command: "npx",
|
|
171
|
+
args: ["-y", "@linear/mcp-server"],
|
|
172
|
+
env: { LINEAR_API_KEY: "" }
|
|
173
|
+
},
|
|
174
|
+
manual: "Ask user to check Linear and paste issue details",
|
|
175
|
+
capabilities: ["issues", "projects", "cycles", "search"],
|
|
176
|
+
envHint: "Set LINEAR_API_KEY from Linear settings \u2192 API"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "sentry",
|
|
180
|
+
description: "Monitor and triage application errors",
|
|
181
|
+
category: "development",
|
|
182
|
+
mcp: {
|
|
183
|
+
package: "@sentry/mcp-server",
|
|
184
|
+
command: "npx",
|
|
185
|
+
args: ["-y", "@sentry/mcp-server"],
|
|
186
|
+
env: { SENTRY_AUTH_TOKEN: "" }
|
|
187
|
+
},
|
|
188
|
+
manual: "Ask user to paste Sentry error details",
|
|
189
|
+
capabilities: ["error monitoring", "issue triage", "stack traces"],
|
|
190
|
+
envHint: "Set SENTRY_AUTH_TOKEN from Sentry settings \u2192 API keys"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: "docker",
|
|
194
|
+
description: "Manage Docker containers and images",
|
|
195
|
+
category: "automation",
|
|
196
|
+
mcp: {
|
|
197
|
+
package: "@modelcontextprotocol/server-docker",
|
|
198
|
+
command: "npx",
|
|
199
|
+
args: ["-y", "@modelcontextprotocol/server-docker"]
|
|
200
|
+
},
|
|
201
|
+
manual: "Suggest docker commands for user to run",
|
|
202
|
+
capabilities: ["containers", "images", "compose", "logs"]
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
function findTool(name) {
|
|
206
|
+
return REGISTRY.find((t) => t.name === name);
|
|
207
|
+
}
|
|
208
|
+
function searchTools(query) {
|
|
209
|
+
const q = query.toLowerCase();
|
|
210
|
+
return REGISTRY.filter(
|
|
211
|
+
(t) => t.name.includes(q) || t.description.toLowerCase().includes(q) || t.category.includes(q) || t.capabilities.some((c) => c.toLowerCase().includes(q))
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/lib/platform.ts
|
|
216
|
+
import fs from "fs";
|
|
217
|
+
import path from "path";
|
|
218
|
+
import os from "os";
|
|
219
|
+
function detectPlatform() {
|
|
220
|
+
const cwd = process.cwd();
|
|
221
|
+
const acoreConfig = path.join(cwd, ".acore", "config.json");
|
|
222
|
+
if (fs.existsSync(acoreConfig)) {
|
|
223
|
+
try {
|
|
224
|
+
const raw = fs.readFileSync(acoreConfig, "utf-8");
|
|
225
|
+
const parsed = JSON.parse(raw);
|
|
226
|
+
if (typeof parsed.platform === "string") {
|
|
227
|
+
const p7 = parsed.platform;
|
|
228
|
+
if (p7 === "claude-code" || p7 === "cursor" || p7 === "windsurf") return p7;
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (fs.existsSync(path.join(cwd, "CLAUDE.md"))) return "claude-code";
|
|
234
|
+
if (fs.existsSync(path.join(cwd, ".cursorrules"))) return "cursor";
|
|
235
|
+
if (fs.existsSync(path.join(cwd, ".windsurfrules"))) return "windsurf";
|
|
236
|
+
return "other";
|
|
237
|
+
}
|
|
238
|
+
function isMcpPlatform(platform) {
|
|
239
|
+
return platform === "claude-code" || platform === "cursor" || platform === "windsurf";
|
|
240
|
+
}
|
|
241
|
+
function getMcpConfigPath(platform) {
|
|
242
|
+
switch (platform) {
|
|
243
|
+
case "claude-code":
|
|
244
|
+
return path.join(os.homedir(), ".claude", "settings.json");
|
|
245
|
+
case "cursor":
|
|
246
|
+
return path.join(process.cwd(), ".cursor", "mcp.json");
|
|
247
|
+
case "windsurf":
|
|
248
|
+
return path.join(os.homedir(), ".windsurf", "mcp.json");
|
|
249
|
+
default:
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function addMcpServer(platform, name, config) {
|
|
254
|
+
const configPath = getMcpConfigPath(platform);
|
|
255
|
+
if (!configPath) return;
|
|
256
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
257
|
+
let existing = {};
|
|
258
|
+
if (fs.existsSync(configPath)) {
|
|
259
|
+
try {
|
|
260
|
+
existing = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (platform === "claude-code") {
|
|
265
|
+
const mcpServers = existing.mcpServers ?? {};
|
|
266
|
+
mcpServers[name] = config;
|
|
267
|
+
existing.mcpServers = mcpServers;
|
|
268
|
+
} else {
|
|
269
|
+
const mcpServers = existing.mcpServers ?? {};
|
|
270
|
+
mcpServers[name] = config;
|
|
271
|
+
existing.mcpServers = mcpServers;
|
|
272
|
+
}
|
|
273
|
+
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
274
|
+
}
|
|
275
|
+
function removeMcpServer(platform, name) {
|
|
276
|
+
const configPath = getMcpConfigPath(platform);
|
|
277
|
+
if (!configPath || !fs.existsSync(configPath)) return;
|
|
278
|
+
try {
|
|
279
|
+
const existing = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
280
|
+
const mcpServers = existing.mcpServers ?? {};
|
|
281
|
+
delete mcpServers[name];
|
|
282
|
+
existing.mcpServers = mcpServers;
|
|
283
|
+
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/lib/kit.ts
|
|
289
|
+
import fs3 from "fs";
|
|
290
|
+
|
|
291
|
+
// src/lib/paths.ts
|
|
292
|
+
import fs2 from "fs";
|
|
293
|
+
import path2 from "path";
|
|
294
|
+
import os2 from "os";
|
|
295
|
+
function getGlobalDir() {
|
|
296
|
+
return path2.join(os2.homedir(), ".akit");
|
|
297
|
+
}
|
|
298
|
+
function globalConfigExists() {
|
|
299
|
+
return fs2.existsSync(path2.join(getGlobalDir(), "kit.md"));
|
|
300
|
+
}
|
|
301
|
+
function getKitPath() {
|
|
302
|
+
return path2.join(getGlobalDir(), "kit.md");
|
|
303
|
+
}
|
|
304
|
+
function getInstalledToolsPath() {
|
|
305
|
+
return path2.join(getGlobalDir(), "installed.json");
|
|
306
|
+
}
|
|
307
|
+
function acoreExists() {
|
|
308
|
+
const acoreDir = path2.join(os2.homedir(), ".acore");
|
|
309
|
+
return fs2.existsSync(path2.join(acoreDir, "core.md"));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/lib/kit.ts
|
|
313
|
+
function loadInstalledTools() {
|
|
314
|
+
const filePath = getInstalledToolsPath();
|
|
315
|
+
if (!fs3.existsSync(filePath)) return [];
|
|
316
|
+
try {
|
|
317
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
318
|
+
} catch {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function saveInstalledTools(tools) {
|
|
323
|
+
const dir = getGlobalDir();
|
|
324
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
325
|
+
fs3.writeFileSync(getInstalledToolsPath(), JSON.stringify(tools, null, 2) + "\n", "utf-8");
|
|
326
|
+
}
|
|
327
|
+
function addInstalledTool(name, mcpConfigured) {
|
|
328
|
+
const tools = loadInstalledTools();
|
|
329
|
+
const existing = tools.findIndex((t) => t.name === name);
|
|
330
|
+
const entry = {
|
|
331
|
+
name,
|
|
332
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
333
|
+
mcpConfigured
|
|
334
|
+
};
|
|
335
|
+
if (existing >= 0) {
|
|
336
|
+
tools[existing] = entry;
|
|
337
|
+
} else {
|
|
338
|
+
tools.push(entry);
|
|
339
|
+
}
|
|
340
|
+
saveInstalledTools(tools);
|
|
341
|
+
}
|
|
342
|
+
function removeInstalledTool(name) {
|
|
343
|
+
const tools = loadInstalledTools().filter((t) => t.name !== name);
|
|
344
|
+
saveInstalledTools(tools);
|
|
345
|
+
}
|
|
346
|
+
function isToolInstalled(name) {
|
|
347
|
+
return loadInstalledTools().some((t) => t.name === name);
|
|
348
|
+
}
|
|
349
|
+
function generateKitMd(tools, entries) {
|
|
350
|
+
const lines = ["# My AI Toolkit", ""];
|
|
351
|
+
if (tools.length === 0) {
|
|
352
|
+
lines.push("No tools installed yet. Run `akit add <tool>` to get started.");
|
|
353
|
+
lines.push("");
|
|
354
|
+
return lines.join("\n");
|
|
355
|
+
}
|
|
356
|
+
for (const tool of tools) {
|
|
357
|
+
const entry = entries.find((e) => e.name === tool.name);
|
|
358
|
+
if (!entry) continue;
|
|
359
|
+
lines.push(`## ${entry.name}`);
|
|
360
|
+
lines.push(`- Do: ${entry.description}`);
|
|
361
|
+
lines.push(`- When: ${entry.capabilities.join(", ")}`);
|
|
362
|
+
if (tool.mcpConfigured && entry.mcp) {
|
|
363
|
+
lines.push(`- How: MCP \u2192 ${entry.mcp.package}`);
|
|
364
|
+
} else {
|
|
365
|
+
lines.push(`- How: ${entry.manual}`);
|
|
366
|
+
}
|
|
367
|
+
lines.push("");
|
|
368
|
+
}
|
|
369
|
+
lines.push("---");
|
|
370
|
+
lines.push("");
|
|
371
|
+
lines.push("## Permissions");
|
|
372
|
+
lines.push("- Never delete data without confirmation");
|
|
373
|
+
lines.push("- Never push to main without approval");
|
|
374
|
+
lines.push("- Ask before making external API calls that modify state");
|
|
375
|
+
lines.push("");
|
|
376
|
+
return lines.join("\n");
|
|
377
|
+
}
|
|
378
|
+
function writeKitMd(content) {
|
|
379
|
+
const dir = getGlobalDir();
|
|
380
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
381
|
+
fs3.writeFileSync(getKitPath(), content, "utf-8");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/commands/add.ts
|
|
385
|
+
async function addCommand(toolName) {
|
|
386
|
+
p.intro(pc.bold("akit") + " \u2014 adding " + pc.cyan(toolName));
|
|
387
|
+
const tool = findTool(toolName);
|
|
388
|
+
if (!tool) {
|
|
389
|
+
p.log.error(`Tool "${toolName}" not found in registry.`);
|
|
390
|
+
p.log.info(`Run ${pc.bold("akit search " + toolName)} to find similar tools.`);
|
|
391
|
+
p.outro("");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (isToolInstalled(toolName)) {
|
|
395
|
+
p.log.warning(`${pc.bold(toolName)} is already installed.`);
|
|
396
|
+
p.outro("");
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const platform = detectPlatform();
|
|
400
|
+
const hasMcp = isMcpPlatform(platform) && tool.mcp !== null;
|
|
401
|
+
p.log.info(`Tool: ${pc.bold(tool.description)}`);
|
|
402
|
+
p.log.info(`Capabilities: ${tool.capabilities.join(", ")}`);
|
|
403
|
+
if (hasMcp) {
|
|
404
|
+
p.log.info(`Platform: ${pc.green(platform)} (MCP supported)`);
|
|
405
|
+
const mcp = tool.mcp;
|
|
406
|
+
addMcpServer(platform, toolName, {
|
|
407
|
+
command: mcp.command || "npx",
|
|
408
|
+
args: mcp.args || ["-y", mcp.package],
|
|
409
|
+
...mcp.env && Object.keys(mcp.env).length > 0 ? { env: mcp.env } : {}
|
|
410
|
+
});
|
|
411
|
+
p.log.success(`Added MCP config for ${pc.bold(toolName)}`);
|
|
412
|
+
if (tool.envHint) {
|
|
413
|
+
p.log.warning(tool.envHint);
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
p.log.info(`Platform: ${pc.dim(platform)} (manual mode)`);
|
|
417
|
+
p.log.info(`Fallback: ${tool.manual}`);
|
|
418
|
+
}
|
|
419
|
+
addInstalledTool(toolName, hasMcp);
|
|
420
|
+
const installed = loadInstalledTools();
|
|
421
|
+
const entries = installed.map((t) => findTool(t.name)).filter(Boolean);
|
|
422
|
+
const kitContent = generateKitMd(installed, entries);
|
|
423
|
+
writeKitMd(kitContent);
|
|
424
|
+
p.log.success(`Updated ${pc.dim("~/.akit/kit.md")}`);
|
|
425
|
+
p.outro("Done.");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/commands/remove.ts
|
|
429
|
+
import * as p2 from "@clack/prompts";
|
|
430
|
+
import pc2 from "picocolors";
|
|
431
|
+
async function removeCommand(toolName) {
|
|
432
|
+
p2.intro(pc2.bold("akit") + " \u2014 removing " + pc2.cyan(toolName));
|
|
433
|
+
if (!isToolInstalled(toolName)) {
|
|
434
|
+
p2.log.error(`Tool "${toolName}" is not installed.`);
|
|
435
|
+
p2.outro("");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const platform = detectPlatform();
|
|
439
|
+
if (isMcpPlatform(platform)) {
|
|
440
|
+
removeMcpServer(platform, toolName);
|
|
441
|
+
p2.log.success(`Removed MCP config for ${pc2.bold(toolName)}`);
|
|
442
|
+
}
|
|
443
|
+
removeInstalledTool(toolName);
|
|
444
|
+
const installed = loadInstalledTools();
|
|
445
|
+
const entries = installed.map((t) => findTool(t.name)).filter(Boolean);
|
|
446
|
+
const kitContent = generateKitMd(installed, entries);
|
|
447
|
+
writeKitMd(kitContent);
|
|
448
|
+
p2.log.success(`Updated ${pc2.dim("~/.akit/kit.md")}`);
|
|
449
|
+
p2.outro("Done.");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/commands/list.ts
|
|
453
|
+
import * as p3 from "@clack/prompts";
|
|
454
|
+
import pc3 from "picocolors";
|
|
455
|
+
function listCommand() {
|
|
456
|
+
const installed = loadInstalledTools();
|
|
457
|
+
if (installed.length === 0) {
|
|
458
|
+
p3.intro(pc3.bold("akit") + " \u2014 your AI toolkit");
|
|
459
|
+
p3.log.info("No tools installed yet.");
|
|
460
|
+
p3.log.info(`Run ${pc3.bold("akit search <query>")} to find tools.`);
|
|
461
|
+
p3.log.info(`Run ${pc3.bold("akit add <tool>")} to install one.`);
|
|
462
|
+
p3.outro("");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
p3.intro(pc3.bold("akit") + " \u2014 " + installed.length + " tools installed");
|
|
466
|
+
for (const tool of installed) {
|
|
467
|
+
const entry = findTool(tool.name);
|
|
468
|
+
const desc = entry ? entry.description : "Unknown tool";
|
|
469
|
+
const mode = tool.mcpConfigured ? pc3.green("MCP") : pc3.dim("manual");
|
|
470
|
+
p3.log.info(`${pc3.bold(tool.name)} \u2014 ${desc} [${mode}]`);
|
|
471
|
+
}
|
|
472
|
+
p3.outro("");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// src/commands/search.ts
|
|
476
|
+
import * as p4 from "@clack/prompts";
|
|
477
|
+
import pc4 from "picocolors";
|
|
478
|
+
function searchCommand(query) {
|
|
479
|
+
const results = searchTools(query);
|
|
480
|
+
if (results.length === 0) {
|
|
481
|
+
p4.intro(pc4.bold("akit search") + " \u2014 no results for " + pc4.cyan(query));
|
|
482
|
+
p4.log.info(`${REGISTRY.length} tools in registry, none matched "${query}".`);
|
|
483
|
+
p4.outro("");
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
p4.intro(pc4.bold("akit search") + " \u2014 " + results.length + " results for " + pc4.cyan(query));
|
|
487
|
+
for (const tool of results) {
|
|
488
|
+
const installed = isToolInstalled(tool.name);
|
|
489
|
+
const status = installed ? pc4.green(" [installed]") : "";
|
|
490
|
+
const mcp = tool.mcp ? pc4.dim(" (MCP)") : "";
|
|
491
|
+
p4.log.info(
|
|
492
|
+
`${pc4.bold(tool.name)}${status}${mcp} \u2014 ${tool.description}`
|
|
493
|
+
);
|
|
494
|
+
p4.log.message(` ${pc4.dim(tool.capabilities.join(", "))}`);
|
|
495
|
+
}
|
|
496
|
+
p4.outro("");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/commands/show.ts
|
|
500
|
+
import * as p5 from "@clack/prompts";
|
|
501
|
+
import pc5 from "picocolors";
|
|
502
|
+
import fs4 from "fs";
|
|
503
|
+
function showCommand() {
|
|
504
|
+
if (!globalConfigExists()) {
|
|
505
|
+
p5.intro(pc5.bold("akit show"));
|
|
506
|
+
p5.log.info("No toolkit configured yet.");
|
|
507
|
+
p5.log.info(`Run ${pc5.bold("akit add <tool>")} to get started.`);
|
|
508
|
+
p5.outro("");
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const content = fs4.readFileSync(getKitPath(), "utf-8");
|
|
512
|
+
p5.intro(pc5.bold("akit") + " \u2014 your toolkit");
|
|
513
|
+
console.log(content);
|
|
514
|
+
p5.outro("");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/commands/doctor.ts
|
|
518
|
+
import * as p6 from "@clack/prompts";
|
|
519
|
+
import pc6 from "picocolors";
|
|
520
|
+
import fs5 from "fs";
|
|
521
|
+
function doctorCommand() {
|
|
522
|
+
p6.intro(pc6.bold("akit doctor") + " \u2014 health check");
|
|
523
|
+
let score = 10;
|
|
524
|
+
const installed = loadInstalledTools();
|
|
525
|
+
const platform = detectPlatform();
|
|
526
|
+
if (installed.length === 0) {
|
|
527
|
+
p6.log.warning("No tools installed \u2014 run " + pc6.bold("akit add <tool>"));
|
|
528
|
+
score -= 2;
|
|
529
|
+
} else {
|
|
530
|
+
p6.log.success(`${installed.length} tools installed`);
|
|
531
|
+
}
|
|
532
|
+
if (globalConfigExists()) {
|
|
533
|
+
p6.log.success("kit.md exists");
|
|
534
|
+
} else {
|
|
535
|
+
p6.log.error("kit.md not found \u2014 run " + pc6.bold("akit add <tool>") + " to create it");
|
|
536
|
+
score -= 2;
|
|
537
|
+
}
|
|
538
|
+
if (isMcpPlatform(platform)) {
|
|
539
|
+
p6.log.success(`Platform: ${pc6.bold(platform)} (MCP supported)`);
|
|
540
|
+
} else {
|
|
541
|
+
p6.log.warning(`Platform: ${pc6.dim(platform)} \u2014 tools will run in manual mode`);
|
|
542
|
+
score -= 1;
|
|
543
|
+
}
|
|
544
|
+
if (isMcpPlatform(platform)) {
|
|
545
|
+
const configPath = getMcpConfigPath(platform);
|
|
546
|
+
if (configPath && fs5.existsSync(configPath)) {
|
|
547
|
+
p6.log.success("MCP config file exists");
|
|
548
|
+
} else {
|
|
549
|
+
p6.log.warning("MCP config file not found \u2014 tools may not be connected");
|
|
550
|
+
score -= 1;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (const tool of installed) {
|
|
554
|
+
const entry = findTool(tool.name);
|
|
555
|
+
if (entry?.mcp?.env) {
|
|
556
|
+
for (const envVar of Object.keys(entry.mcp.env)) {
|
|
557
|
+
if (!process.env[envVar]) {
|
|
558
|
+
p6.log.warning(`${pc6.bold(tool.name)}: ${envVar} not set \u2014 ${entry.envHint || "set this environment variable"}`);
|
|
559
|
+
score -= 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (acoreExists()) {
|
|
565
|
+
p6.log.success("acore detected \u2014 identity layer connected");
|
|
566
|
+
} else {
|
|
567
|
+
p6.log.info("acore not found \u2014 run " + pc6.bold("npx @aman_asmuei/acore") + " for AI identity");
|
|
568
|
+
}
|
|
569
|
+
score = Math.max(0, score);
|
|
570
|
+
const scoreColor = score >= 8 ? pc6.green : score >= 5 ? pc6.yellow : pc6.red;
|
|
571
|
+
p6.log.message("");
|
|
572
|
+
p6.log.info(`Score: ${scoreColor(`${score}/10`)}`);
|
|
573
|
+
p6.outro("");
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/index.ts
|
|
577
|
+
var program = new Command();
|
|
578
|
+
program.name("akit").description("The portable capability layer for AI companions").version("0.1.0").action(() => {
|
|
579
|
+
if (globalConfigExists()) {
|
|
580
|
+
showCommand();
|
|
581
|
+
} else {
|
|
582
|
+
listCommand();
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
program.command("add <tool>").description("Add a tool to your AI toolkit").action((tool) => addCommand(tool));
|
|
586
|
+
program.command("remove <tool>").description("Remove a tool from your toolkit").action((tool) => removeCommand(tool));
|
|
587
|
+
program.command("list").description("List installed tools").action(() => listCommand());
|
|
588
|
+
program.command("search <query>").description("Search available tools in the registry").action((query) => searchCommand(query));
|
|
589
|
+
program.command("show").description("View your current kit.md").action(() => showCommand());
|
|
590
|
+
program.command("doctor").description("Health check your toolkit configuration").action(() => doctorCommand());
|
|
591
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aman_asmuei/akit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The portable capability layer for AI companions — give any AI tools that work everywhere",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"akit": "./bin/akit.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"lint": "tsc --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ai",
|
|
27
|
+
"llm",
|
|
28
|
+
"tools",
|
|
29
|
+
"mcp",
|
|
30
|
+
"capabilities",
|
|
31
|
+
"cli",
|
|
32
|
+
"acore"
|
|
33
|
+
],
|
|
34
|
+
"author": "Aman Asmuei",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/amanasmuei/akit.git"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@clack/prompts": "^0.9.1",
|
|
42
|
+
"commander": "^13.1.0",
|
|
43
|
+
"picocolors": "^1.1.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.0.0",
|
|
47
|
+
"tsup": "^8.4.0",
|
|
48
|
+
"typescript": "^5.7.0",
|
|
49
|
+
"vitest": "^3.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|