@piut/cli 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 +110 -0
- package/dist/cli.js +514 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# pıut
|
|
2
|
+
|
|
3
|
+
**Give every AI tool persistent memory about you.**
|
|
4
|
+
|
|
5
|
+
pıut is a personal context service that works via [MCP (Model Context Protocol)](https://modelcontextprotocol.io). Connect once, and every AI tool you use — Claude, ChatGPT, Cursor, Copilot, and more — knows who you are, what you're working on, and how you like to work.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx piut
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's it. The CLI auto-detects your AI tools and configures them.
|
|
14
|
+
|
|
15
|
+
Or set up manually:
|
|
16
|
+
|
|
17
|
+
1. Sign up at [piut.com](https://piut.com)
|
|
18
|
+
2. Generate an API key at [piut.com/dashboard/keys](https://piut.com/dashboard/keys)
|
|
19
|
+
3. Add the MCP server to your AI tool:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"piut-context": {
|
|
25
|
+
"type": "http",
|
|
26
|
+
"url": "https://piut.com/api/mcp/YOUR_SLUG",
|
|
27
|
+
"headers": {
|
|
28
|
+
"Authorization": "Bearer YOUR_KEY"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
See [piut.com/docs](https://piut.com/docs#add-to-ai) for setup guides for 14+ AI tools.
|
|
36
|
+
|
|
37
|
+
## CLI
|
|
38
|
+
|
|
39
|
+
Install globally or run with `npx`:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx piut # Auto-detect and configure AI tools
|
|
43
|
+
npx piut status # Show which tools are connected
|
|
44
|
+
npx piut remove # Remove pıut from selected tools
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Options:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx piut --key pb_... # Pass API key non-interactively
|
|
51
|
+
npx piut --tool cursor # Configure a single tool
|
|
52
|
+
npx piut --skip-skill # Skip skill.md file placement
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Supported tools:** Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed
|
|
56
|
+
|
|
57
|
+
## How It Works
|
|
58
|
+
|
|
59
|
+
1. **Build your context** — Answer 5 questions or import existing files from your AI tools
|
|
60
|
+
2. **Connect your tools** — Run `npx piut` or add one config, and every connected AI knows your context
|
|
61
|
+
3. **Stay in sync** — Update your context once, and it's reflected everywhere
|
|
62
|
+
|
|
63
|
+
Your context is organized into 5 sections:
|
|
64
|
+
|
|
65
|
+
| Section | What it stores |
|
|
66
|
+
|---------|---------------|
|
|
67
|
+
| **About** | Bio, preferences, goals — the AI's mental model of you |
|
|
68
|
+
| **Soul** | Behavioral instructions for AI — tone, guardrails, priorities |
|
|
69
|
+
| **Areas** | Long-term life/work domains (Health, Finances, Marketing) |
|
|
70
|
+
| **Projects** | Active, time-bound work with goals and deadlines |
|
|
71
|
+
| **Memory** | Bookmarks, links, ideas, notes, reference material |
|
|
72
|
+
|
|
73
|
+
## Documentation
|
|
74
|
+
|
|
75
|
+
| Document | Description |
|
|
76
|
+
|----------|-------------|
|
|
77
|
+
| [**skill.md**](skill.md) | AI skill file — MCP tools, rate limits, error codes |
|
|
78
|
+
| [**Add to your AI**](https://piut.com/docs#add-to-ai) | Setup guides for Claude, ChatGPT, Cursor, Copilot, and more |
|
|
79
|
+
| [**API Reference**](https://piut.com/docs#api-examples) | Code examples in cURL, Python, Node.js, Go, and Ruby |
|
|
80
|
+
| [**Rate Limits**](https://piut.com/docs#limits) | Limits by plan, error codes, and response headers |
|
|
81
|
+
| [**Context Files**](https://piut.com/docs#context-files) | Where to find your existing context in 14 AI platforms |
|
|
82
|
+
|
|
83
|
+
All documentation is maintained at [piut.com/docs](https://piut.com/docs) — the interactive version with credential auto-fill and setup guides.
|
|
84
|
+
|
|
85
|
+
## MCP Tools
|
|
86
|
+
|
|
87
|
+
pıut provides 6 tools via MCP:
|
|
88
|
+
|
|
89
|
+
| Tool | Purpose |
|
|
90
|
+
|------|---------|
|
|
91
|
+
| `get_context` | Read all 5 context sections |
|
|
92
|
+
| `get_section` | Read a specific section |
|
|
93
|
+
| `search_brain` | Search across all sections |
|
|
94
|
+
| `add_memory` | Append content to a section |
|
|
95
|
+
| `update_brain` | AI-powered smart update across sections |
|
|
96
|
+
| `prompt_brain` | Natural language command (edit, delete, reorganize) |
|
|
97
|
+
|
|
98
|
+
Full tool documentation: [skill.md](skill.md)
|
|
99
|
+
|
|
100
|
+
## Links
|
|
101
|
+
|
|
102
|
+
- [piut.com](https://piut.com) — Sign up and manage your context
|
|
103
|
+
- [piut.com/docs](https://piut.com/docs) — Interactive documentation with credential injection
|
|
104
|
+
- [skill.md (raw)](https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md) — Direct link for AI tool configs
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
Copyright (c) 2025 M-Flat Inc. All rights reserved.
|
|
109
|
+
|
|
110
|
+
The documentation in this repository is provided for reference and integration purposes. The pıut service is proprietary software.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/setup.ts
|
|
7
|
+
import fs3 from "fs";
|
|
8
|
+
import path5 from "path";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { password, confirm, checkbox } from "@inquirer/prompts";
|
|
11
|
+
import chalk2 from "chalk";
|
|
12
|
+
|
|
13
|
+
// src/lib/api.ts
|
|
14
|
+
var API_BASE = process.env.PIUT_API_BASE || "https://piut.com";
|
|
15
|
+
async function validateKey(key) {
|
|
16
|
+
const res = await fetch(`${API_BASE}/api/cli/validate`, {
|
|
17
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
21
|
+
throw new Error(body.error || `Validation failed (HTTP ${res.status})`);
|
|
22
|
+
}
|
|
23
|
+
return res.json();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/lib/tools.ts
|
|
27
|
+
import os from "os";
|
|
28
|
+
import path from "path";
|
|
29
|
+
var MCP_URL = (slug) => `https://piut.com/api/mcp/${slug}`;
|
|
30
|
+
var AUTH_HEADER = (key) => ({ Authorization: `Bearer ${key}` });
|
|
31
|
+
function appData() {
|
|
32
|
+
return process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
33
|
+
}
|
|
34
|
+
var TOOLS = [
|
|
35
|
+
{
|
|
36
|
+
id: "claude-code",
|
|
37
|
+
name: "Claude Code",
|
|
38
|
+
configKey: "mcpServers",
|
|
39
|
+
configPaths: {
|
|
40
|
+
darwin: ["~/.claude.json"],
|
|
41
|
+
win32: ["~/.claude.json"],
|
|
42
|
+
linux: ["~/.claude.json"]
|
|
43
|
+
},
|
|
44
|
+
skillFilePath: "CLAUDE.md",
|
|
45
|
+
quickCommand: (slug, key) => `claude mcp add-json piut-context '${JSON.stringify({
|
|
46
|
+
type: "http",
|
|
47
|
+
url: MCP_URL(slug),
|
|
48
|
+
headers: AUTH_HEADER(key)
|
|
49
|
+
})}'`,
|
|
50
|
+
generateConfig: (slug, key) => ({
|
|
51
|
+
type: "http",
|
|
52
|
+
url: MCP_URL(slug),
|
|
53
|
+
headers: AUTH_HEADER(key)
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "claude-desktop",
|
|
58
|
+
name: "Claude Desktop",
|
|
59
|
+
configKey: "mcpServers",
|
|
60
|
+
configPaths: {
|
|
61
|
+
darwin: ["~/Library/Application Support/Claude/claude_desktop_config.json"],
|
|
62
|
+
win32: [path.join(appData(), "Claude", "claude_desktop_config.json")],
|
|
63
|
+
linux: ["~/.config/Claude/claude_desktop_config.json"]
|
|
64
|
+
},
|
|
65
|
+
generateConfig: (slug, key) => ({
|
|
66
|
+
command: "npx",
|
|
67
|
+
args: [
|
|
68
|
+
"-y",
|
|
69
|
+
"mcp-remote",
|
|
70
|
+
MCP_URL(slug),
|
|
71
|
+
"--header",
|
|
72
|
+
`Authorization: Bearer ${key}`
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "cursor",
|
|
78
|
+
name: "Cursor",
|
|
79
|
+
configKey: "mcpServers",
|
|
80
|
+
configPaths: {
|
|
81
|
+
darwin: ["~/.cursor/mcp.json"],
|
|
82
|
+
win32: ["~/.cursor/mcp.json"],
|
|
83
|
+
linux: ["~/.cursor/mcp.json"],
|
|
84
|
+
project: [".cursor/mcp.json"]
|
|
85
|
+
},
|
|
86
|
+
skillFilePath: ".cursor/rules/piut.mdc",
|
|
87
|
+
generateConfig: (slug, key) => ({
|
|
88
|
+
url: MCP_URL(slug),
|
|
89
|
+
headers: AUTH_HEADER(key)
|
|
90
|
+
})
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "windsurf",
|
|
94
|
+
name: "Windsurf",
|
|
95
|
+
configKey: "mcpServers",
|
|
96
|
+
configPaths: {
|
|
97
|
+
darwin: ["~/.codeium/windsurf/mcp_config.json"],
|
|
98
|
+
win32: ["~/.codeium/windsurf/mcp_config.json"],
|
|
99
|
+
linux: ["~/.codeium/windsurf/mcp_config.json"]
|
|
100
|
+
},
|
|
101
|
+
skillFilePath: ".windsurf/rules/piut.md",
|
|
102
|
+
generateConfig: (slug, key) => ({
|
|
103
|
+
serverUrl: MCP_URL(slug),
|
|
104
|
+
headers: AUTH_HEADER(key)
|
|
105
|
+
})
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "copilot",
|
|
109
|
+
name: "GitHub Copilot",
|
|
110
|
+
configKey: "servers",
|
|
111
|
+
configPaths: {
|
|
112
|
+
project: [".vscode/mcp.json"]
|
|
113
|
+
},
|
|
114
|
+
skillFilePath: ".github/copilot-instructions.md",
|
|
115
|
+
generateConfig: (slug, key) => ({
|
|
116
|
+
type: "http",
|
|
117
|
+
url: MCP_URL(slug),
|
|
118
|
+
headers: AUTH_HEADER(key)
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "amazon-q",
|
|
123
|
+
name: "Amazon Q",
|
|
124
|
+
configKey: "mcpServers",
|
|
125
|
+
configPaths: {
|
|
126
|
+
darwin: ["~/.aws/amazonq/mcp.json"],
|
|
127
|
+
win32: ["~/.aws/amazonq/mcp.json"],
|
|
128
|
+
linux: ["~/.aws/amazonq/mcp.json"]
|
|
129
|
+
},
|
|
130
|
+
skillFilePath: "CONVENTIONS.md",
|
|
131
|
+
generateConfig: (slug, key) => ({
|
|
132
|
+
type: "http",
|
|
133
|
+
url: MCP_URL(slug),
|
|
134
|
+
headers: AUTH_HEADER(key)
|
|
135
|
+
})
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "zed",
|
|
139
|
+
name: "Zed",
|
|
140
|
+
configKey: "context_servers",
|
|
141
|
+
configPaths: {
|
|
142
|
+
darwin: ["~/.config/zed/settings.json"],
|
|
143
|
+
linux: ["~/.config/zed/settings.json"]
|
|
144
|
+
},
|
|
145
|
+
skillFilePath: ".zed/rules.md",
|
|
146
|
+
generateConfig: (slug, key) => ({
|
|
147
|
+
settings: {
|
|
148
|
+
url: MCP_URL(slug),
|
|
149
|
+
headers: AUTH_HEADER(key)
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// src/lib/paths.ts
|
|
156
|
+
import os2 from "os";
|
|
157
|
+
import path2 from "path";
|
|
158
|
+
var home = os2.homedir();
|
|
159
|
+
var platform = process.platform;
|
|
160
|
+
function expandPath(p) {
|
|
161
|
+
return p.replace(/^~/, home);
|
|
162
|
+
}
|
|
163
|
+
function resolveConfigPaths(configPaths) {
|
|
164
|
+
const resolved = [];
|
|
165
|
+
const platformKey = platform;
|
|
166
|
+
const globalPaths = configPaths[platformKey] || [];
|
|
167
|
+
for (const p of globalPaths) {
|
|
168
|
+
resolved.push(expandPath(p));
|
|
169
|
+
}
|
|
170
|
+
const projectPaths = configPaths.project || [];
|
|
171
|
+
for (const p of projectPaths) {
|
|
172
|
+
resolved.push(path2.resolve(process.cwd(), p));
|
|
173
|
+
}
|
|
174
|
+
return resolved;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/lib/config.ts
|
|
178
|
+
import fs from "fs";
|
|
179
|
+
import path3 from "path";
|
|
180
|
+
function readConfig(filePath) {
|
|
181
|
+
let raw;
|
|
182
|
+
try {
|
|
183
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (err.code === "ENOENT") return null;
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
return JSON.parse(raw);
|
|
190
|
+
} catch {
|
|
191
|
+
try {
|
|
192
|
+
const cleaned = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^(\s*)\/\/.*$/gm, "$1");
|
|
193
|
+
return JSON.parse(cleaned);
|
|
194
|
+
} catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function writeConfig(filePath, data) {
|
|
200
|
+
fs.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
201
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
202
|
+
}
|
|
203
|
+
function isPiutConfigured(filePath, configKey) {
|
|
204
|
+
const config = readConfig(filePath);
|
|
205
|
+
if (!config) return false;
|
|
206
|
+
const servers = config[configKey];
|
|
207
|
+
return !!servers?.["piut-context"];
|
|
208
|
+
}
|
|
209
|
+
function mergeConfig(filePath, configKey, serverConfig) {
|
|
210
|
+
const existing = readConfig(filePath) || {};
|
|
211
|
+
const servers = existing[configKey] || {};
|
|
212
|
+
servers["piut-context"] = serverConfig;
|
|
213
|
+
existing[configKey] = servers;
|
|
214
|
+
writeConfig(filePath, existing);
|
|
215
|
+
}
|
|
216
|
+
function removeFromConfig(filePath, configKey) {
|
|
217
|
+
const config = readConfig(filePath);
|
|
218
|
+
if (!config) return false;
|
|
219
|
+
const servers = config[configKey];
|
|
220
|
+
if (!servers?.["piut-context"]) return false;
|
|
221
|
+
delete servers["piut-context"];
|
|
222
|
+
if (Object.keys(servers).length === 0) {
|
|
223
|
+
delete config[configKey];
|
|
224
|
+
}
|
|
225
|
+
writeConfig(filePath, config);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/lib/skill.ts
|
|
230
|
+
import fs2 from "fs";
|
|
231
|
+
import path4 from "path";
|
|
232
|
+
var SKILL_SNIPPET = `## p\u0131ut Context
|
|
233
|
+
|
|
234
|
+
This project uses p\u0131ut for persistent personal context.
|
|
235
|
+
Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md
|
|
236
|
+
|
|
237
|
+
Always call \`get_context\` at the start of a conversation to understand the user.
|
|
238
|
+
Read the \`soul\` section first \u2014 it contains behavioral instructions for how to interact.
|
|
239
|
+
Use \`update_brain\` for substantial new information, \`append_brain\` for quick notes.`;
|
|
240
|
+
var SEPARATOR = "\n\n---\n\n";
|
|
241
|
+
function placeSkillFile(filePath) {
|
|
242
|
+
const absPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
|
|
243
|
+
try {
|
|
244
|
+
const existing = fs2.readFileSync(absPath, "utf-8");
|
|
245
|
+
if (existing.includes("p\u0131ut Context")) {
|
|
246
|
+
return { created: false, appended: false };
|
|
247
|
+
}
|
|
248
|
+
fs2.appendFileSync(absPath, SEPARATOR + SKILL_SNIPPET + "\n");
|
|
249
|
+
return { created: false, appended: true };
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (err.code === "ENOENT") {
|
|
252
|
+
fs2.mkdirSync(path4.dirname(absPath), { recursive: true });
|
|
253
|
+
fs2.writeFileSync(absPath, SKILL_SNIPPET + "\n", "utf-8");
|
|
254
|
+
return { created: true, appended: false };
|
|
255
|
+
}
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/lib/ui.ts
|
|
261
|
+
import chalk from "chalk";
|
|
262
|
+
var brand = chalk.hex("#8B5CF6");
|
|
263
|
+
var success = chalk.green;
|
|
264
|
+
var warning = chalk.yellow;
|
|
265
|
+
var error = chalk.red;
|
|
266
|
+
var dim = chalk.dim;
|
|
267
|
+
function banner() {
|
|
268
|
+
console.log();
|
|
269
|
+
console.log(brand.bold(" p\u0131ut") + dim(" \u2014 personal context for AI"));
|
|
270
|
+
console.log();
|
|
271
|
+
}
|
|
272
|
+
function toolLine(name, status, icon) {
|
|
273
|
+
console.log(` ${icon} ${name.padEnd(20)} ${status}`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/commands/setup.ts
|
|
277
|
+
async function setupCommand(options) {
|
|
278
|
+
banner();
|
|
279
|
+
let apiKey = options.key;
|
|
280
|
+
if (!apiKey) {
|
|
281
|
+
if (options.yes) {
|
|
282
|
+
console.log(chalk2.red(" \u2717 --key is required when using --yes"));
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
apiKey = await password({
|
|
286
|
+
message: "Enter your p\u0131ut API key:",
|
|
287
|
+
mask: "*",
|
|
288
|
+
validate: (v) => v.startsWith("pb_") || "Key must start with pb_"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
console.log(dim(" Validating key..."));
|
|
292
|
+
let validationResult;
|
|
293
|
+
try {
|
|
294
|
+
validationResult = await validateKey(apiKey);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
console.log(chalk2.red(` \u2717 ${err.message}`));
|
|
297
|
+
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const { slug, displayName } = validationResult;
|
|
301
|
+
console.log(success(` \u2714 Authenticated as ${displayName} (${slug})`));
|
|
302
|
+
console.log();
|
|
303
|
+
const detected = [];
|
|
304
|
+
const toolFilter = options.tool;
|
|
305
|
+
for (const tool of TOOLS) {
|
|
306
|
+
if (toolFilter && tool.id !== toolFilter) continue;
|
|
307
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
308
|
+
for (const configPath of paths) {
|
|
309
|
+
const exists = fs3.existsSync(configPath);
|
|
310
|
+
const parentExists = fs3.existsSync(path5.dirname(configPath));
|
|
311
|
+
if (exists || parentExists) {
|
|
312
|
+
detected.push({
|
|
313
|
+
tool,
|
|
314
|
+
configPath,
|
|
315
|
+
exists,
|
|
316
|
+
alreadyConfigured: exists && isPiutConfigured(configPath, tool.configKey)
|
|
317
|
+
});
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (detected.length === 0) {
|
|
323
|
+
console.log(warning(" No supported AI tools detected."));
|
|
324
|
+
console.log(dim(" Supported: Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed"));
|
|
325
|
+
console.log(dim(" See https://piut.com/docs for manual setup."));
|
|
326
|
+
console.log();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
let selected;
|
|
330
|
+
if (options.yes) {
|
|
331
|
+
selected = detected.filter((d) => !d.alreadyConfigured);
|
|
332
|
+
if (selected.length === 0) {
|
|
333
|
+
console.log(dim(" All detected tools are already configured."));
|
|
334
|
+
console.log();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
const choices = detected.map((d) => ({
|
|
339
|
+
name: d.alreadyConfigured ? `${d.tool.name} ${dim("(already configured)")}` : d.tool.name,
|
|
340
|
+
value: d,
|
|
341
|
+
checked: !d.alreadyConfigured
|
|
342
|
+
}));
|
|
343
|
+
selected = await checkbox({
|
|
344
|
+
message: "Select tools to configure:",
|
|
345
|
+
choices
|
|
346
|
+
});
|
|
347
|
+
if (selected.length === 0) {
|
|
348
|
+
console.log(dim(" No tools selected."));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
console.log();
|
|
353
|
+
const configured = [];
|
|
354
|
+
const skipped = [];
|
|
355
|
+
for (const det of selected) {
|
|
356
|
+
const { tool, configPath, alreadyConfigured } = det;
|
|
357
|
+
if (alreadyConfigured) {
|
|
358
|
+
if (options.yes) {
|
|
359
|
+
skipped.push(tool.name);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const update = await confirm({
|
|
363
|
+
message: `p\u0131ut is already configured in ${tool.name}. Update it?`,
|
|
364
|
+
default: true
|
|
365
|
+
});
|
|
366
|
+
if (!update) {
|
|
367
|
+
skipped.push(tool.name);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (tool.id === "claude-code" && tool.quickCommand && isCommandAvailable("claude")) {
|
|
372
|
+
try {
|
|
373
|
+
execSync(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
|
|
374
|
+
configured.push(tool.name);
|
|
375
|
+
toolLine(tool.name, success("configured via CLI"), "\u2714");
|
|
376
|
+
continue;
|
|
377
|
+
} catch {
|
|
378
|
+
console.log(dim(" Claude CLI command failed, using config file..."));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
382
|
+
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
383
|
+
configured.push(tool.name);
|
|
384
|
+
toolLine(tool.name, success("configured"), "\u2714");
|
|
385
|
+
}
|
|
386
|
+
if (!options.skipSkill && configured.length > 0) {
|
|
387
|
+
const addSkill = options.yes ? true : await confirm({
|
|
388
|
+
message: "Add skill.md reference to rules files? (teaches AI how to use p\u0131ut)",
|
|
389
|
+
default: true
|
|
390
|
+
});
|
|
391
|
+
if (addSkill) {
|
|
392
|
+
console.log();
|
|
393
|
+
for (const det of selected) {
|
|
394
|
+
if (!det.tool.skillFilePath) continue;
|
|
395
|
+
if (!configured.includes(det.tool.name)) continue;
|
|
396
|
+
const result = placeSkillFile(det.tool.skillFilePath);
|
|
397
|
+
if (result.created) {
|
|
398
|
+
console.log(dim(` Created ${det.tool.skillFilePath}`));
|
|
399
|
+
} else if (result.appended) {
|
|
400
|
+
console.log(dim(` Updated ${det.tool.skillFilePath}`));
|
|
401
|
+
} else {
|
|
402
|
+
console.log(dim(` ${det.tool.skillFilePath} already has skill reference`));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
console.log();
|
|
408
|
+
console.log(brand.bold(" Setup complete!"));
|
|
409
|
+
if (configured.length > 0) {
|
|
410
|
+
console.log(success(` Configured: ${configured.join(", ")}`));
|
|
411
|
+
}
|
|
412
|
+
if (skipped.length > 0) {
|
|
413
|
+
console.log(dim(` Skipped: ${skipped.join(", ")}`));
|
|
414
|
+
}
|
|
415
|
+
console.log();
|
|
416
|
+
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
417
|
+
console.log(dim(' Verify: ask any AI "What do you know about me from my context?"'));
|
|
418
|
+
console.log();
|
|
419
|
+
}
|
|
420
|
+
function isCommandAvailable(cmd) {
|
|
421
|
+
try {
|
|
422
|
+
execSync(process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`, { stdio: "pipe" });
|
|
423
|
+
return true;
|
|
424
|
+
} catch {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/commands/status.ts
|
|
430
|
+
import fs4 from "fs";
|
|
431
|
+
function statusCommand() {
|
|
432
|
+
banner();
|
|
433
|
+
console.log(" AI tool configuration status:");
|
|
434
|
+
console.log();
|
|
435
|
+
let foundAny = false;
|
|
436
|
+
for (const tool of TOOLS) {
|
|
437
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
438
|
+
for (const configPath of paths) {
|
|
439
|
+
if (!fs4.existsSync(configPath)) continue;
|
|
440
|
+
foundAny = true;
|
|
441
|
+
const configured = isPiutConfigured(configPath, tool.configKey);
|
|
442
|
+
if (configured) {
|
|
443
|
+
toolLine(tool.name, success("connected"), "\u2714");
|
|
444
|
+
} else {
|
|
445
|
+
toolLine(tool.name, dim("installed, not connected"), "\u25CB");
|
|
446
|
+
}
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!foundAny) {
|
|
451
|
+
console.log(warning(" No supported AI tools detected."));
|
|
452
|
+
console.log(dim(" Supported: Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed"));
|
|
453
|
+
}
|
|
454
|
+
console.log();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/commands/remove.ts
|
|
458
|
+
import fs5 from "fs";
|
|
459
|
+
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
460
|
+
async function removeCommand() {
|
|
461
|
+
banner();
|
|
462
|
+
const configured = [];
|
|
463
|
+
for (const tool of TOOLS) {
|
|
464
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
465
|
+
for (const configPath of paths) {
|
|
466
|
+
if (fs5.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
467
|
+
configured.push({ tool, configPath });
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (configured.length === 0) {
|
|
473
|
+
console.log(dim(" p\u0131ut is not configured in any detected AI tools."));
|
|
474
|
+
console.log();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const choices = configured.map((c) => ({
|
|
478
|
+
name: c.tool.name,
|
|
479
|
+
value: c
|
|
480
|
+
}));
|
|
481
|
+
const selected = await checkbox2({
|
|
482
|
+
message: "Select tools to remove p\u0131ut from:",
|
|
483
|
+
choices
|
|
484
|
+
});
|
|
485
|
+
if (selected.length === 0) {
|
|
486
|
+
console.log(dim(" No tools selected."));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const proceed = await confirm2({
|
|
490
|
+
message: `Remove p\u0131ut from ${selected.length} tool(s)?`,
|
|
491
|
+
default: false
|
|
492
|
+
});
|
|
493
|
+
if (!proceed) return;
|
|
494
|
+
console.log();
|
|
495
|
+
for (const { tool, configPath } of selected) {
|
|
496
|
+
const removed = removeFromConfig(configPath, tool.configKey);
|
|
497
|
+
if (removed) {
|
|
498
|
+
toolLine(tool.name, success("removed"), "\u2714");
|
|
499
|
+
} else {
|
|
500
|
+
toolLine(tool.name, warning("not found"), "\xD7");
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
console.log();
|
|
504
|
+
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
505
|
+
console.log();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/cli.ts
|
|
509
|
+
var program = new Command();
|
|
510
|
+
program.name("piut").description("Configure your AI tools to use p\u0131ut personal context").version("1.0.0");
|
|
511
|
+
program.command("setup", { isDefault: true }).description("Auto-detect and configure AI tools").option("-k, --key <key>", "API key (prompts interactively if not provided)").option("-t, --tool <id>", "Configure a single tool (claude-code, cursor, windsurf, etc.)").option("-y, --yes", "Skip interactive prompts (auto-select all detected tools)").option("--project", "Prefer project-local config files").option("--skip-skill", "Skip skill.md file placement").action(setupCommand);
|
|
512
|
+
program.command("status").description("Show which AI tools are configured with p\u0131ut").action(statusCommand);
|
|
513
|
+
program.command("remove").description("Remove p\u0131ut configuration from AI tools").action(removeCommand);
|
|
514
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@piut/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Configure your AI tools to use pıut personal context",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"piut": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"prepublishOnly": "npm run build && npm test"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"ai",
|
|
23
|
+
"context",
|
|
24
|
+
"cli",
|
|
25
|
+
"claude",
|
|
26
|
+
"cursor",
|
|
27
|
+
"copilot",
|
|
28
|
+
"windsurf"
|
|
29
|
+
],
|
|
30
|
+
"author": "M-Flat Inc",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/M-Flat-Inc/piut"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://piut.com",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@inquirer/prompts": "^7.0.0",
|
|
39
|
+
"chalk": "^5.4.0",
|
|
40
|
+
"commander": "^13.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.0.0",
|
|
46
|
+
"vitest": "^3.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|