@trekagent/claude 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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/bin/cli.mjs +245 -0
  4. package/package.json +37 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Trek
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,61 @@
1
+ # @trekagent/claude
2
+
3
+ One-command installer for [Trek](https://trekagent.io). It installs the **Trek Claude Code
4
+ plugin** (skill + presence hooks + remote MCP server) from the Trek marketplace and wires your
5
+ API token — so an agent starts reporting presence and working the ready-task frontier the moment
6
+ you restart Claude Code.
7
+
8
+ ## Usage
9
+
10
+ ```bash
11
+ # Install into the current project (writes ./.claude/settings.local.json)
12
+ npx @trekagent/claude init
13
+
14
+ # User-level (writes ~/.claude/settings.local.json)
15
+ npx @trekagent/claude init --user
16
+
17
+ # Reverse it
18
+ npx @trekagent/claude init --uninstall
19
+ ```
20
+
21
+ ### Flags
22
+
23
+ | Flag | Default | Description |
24
+ | --- | --- | --- |
25
+ | `--project` | (default) | Project scope — write `./.claude/settings.local.json`. |
26
+ | `--user` | | User scope — write `~/.claude/settings.local.json`. |
27
+ | `--token <trk_...>` | `$TREK_TOKEN`, else prompt | Trek API token. |
28
+ | `--api-url <url>` | `https://api.trekagent.io` | Trek API base URL. |
29
+ | `--project-id <uuid>` | `$TREK_PROJECT_ID` | Bind a default Trek project. |
30
+ | `--marketplace <owner>/<repo>` | `trekagent/trek-claude-plugin` or `$TREK_MARKETPLACE` | GitHub repo hosting the plugin marketplace. |
31
+ | `--uninstall` | | Remove the plugin + Trek env for the chosen scope. |
32
+
33
+ If no token is provided via flag or env, `init` prompts for one (TTY) and points you at the
34
+ cockpit **Settings → API tokens** page to mint one.
35
+
36
+ ## What `init` does
37
+
38
+ 1. `claude plugin marketplace add trekagent/trek-claude-plugin`
39
+ 2. `claude plugin install trek@trek`
40
+ 3. Writes the token into `.claude/settings.local.json` (gitignored), deep-merged:
41
+ ```jsonc
42
+ { "env": { "TREK_TOKEN": "trk_…", "TREK_API_URL": "https://api.trekagent.io" } }
43
+ ```
44
+
45
+ That one `env` block powers **both** the plugin's MCP auth (`Bearer ${TREK_TOKEN}`) and the
46
+ presence hooks (which read `TREK_TOKEN` / `TREK_API_URL`). The skill, hooks, and MCP server
47
+ themselves all live in the plugin — this installer just stands it up and authenticates it.
48
+
49
+ > The plugin marketplace lives at `trekagent/trek-claude-plugin`. Override it with
50
+ > `--marketplace <owner>/<repo>` or `TREK_MARKETPLACE` when testing a fork.
51
+
52
+ ## Idempotency
53
+
54
+ `init` is safe to re-run: marketplace-add / plugin-install tolerate "already present", and the
55
+ `settings.local.json` `env` block is deep-merged (never clobbers your other settings).
56
+
57
+ ## Next steps after install
58
+
59
+ 1. Restart Claude Code (in the project, for project scope) so it loads the Trek plugin.
60
+ 2. Approve the **trek** MCP server when prompted (it reads `${TREK_TOKEN}`).
61
+ 3. Update later with `claude plugin update trek@trek`.
package/bin/cli.mjs ADDED
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env node
2
+ // @trekagent/claude — install Trek into Claude Code in one command.
3
+ //
4
+ // Installs the Trek plugin (skill + presence hooks + remote MCP server) from the Trek
5
+ // marketplace, then wires your API token so the MCP server and hooks authenticate.
6
+ //
7
+ // npx @trekagent/claude init # project scope (writes ./.claude/settings.local.json)
8
+ // npx @trekagent/claude init --user # user scope (writes ~/.claude/settings.local.json)
9
+ // npx @trekagent/claude init --token trk_... --api-url https://api.trekagent.io
10
+ // npx @trekagent/claude init --marketplace owner/repo # override the marketplace source
11
+ // npx @trekagent/claude init --uninstall
12
+ //
13
+ // Pure Node, no deps. Idempotent: re-running never duplicates or clobbers your other settings.
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
15
+ import { dirname, join } from 'node:path';
16
+ import { homedir } from 'node:os';
17
+ import { createInterface } from 'node:readline';
18
+ import { spawnSync } from 'node:child_process';
19
+
20
+ // --- defaults --------------------------------------------------------------
21
+ const DEFAULT_API_URL = 'https://api.trekagent.io';
22
+ const COCKPIT_SETTINGS_URL = 'https://console.trekagent.io/settings';
23
+ const PLUGIN = 'trek';
24
+ const MARKETPLACE = 'trek'; // the `name` in the marketplace's marketplace.json
25
+ // GitHub org/repo hosting the plugin marketplace.
26
+ const DEFAULT_MARKETPLACE_REPO = process.env.TREK_MARKETPLACE || 'trekagent/trek-claude-plugin';
27
+
28
+ // --- console helpers -------------------------------------------------------
29
+ const c = {
30
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
31
+ dim: (s) => `\x1b[2m${s}\x1b[0m`,
32
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
33
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
34
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
35
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
36
+ };
37
+ const log = (s = '') => process.stdout.write(s + '\n');
38
+ const ok = (s) => log(` ${c.green('ok')} ${s}`);
39
+ const skip = (s) => log(` ${c.dim('--')} ${s}`);
40
+ const warn = (s) => log(` ${c.yellow('!!')} ${s}`);
41
+
42
+ // --- arg parsing -----------------------------------------------------------
43
+ function parseArgs(argv) {
44
+ const args = { _: [], flags: {} };
45
+ const bools = new Set(['user', 'project', 'uninstall', 'help', 'force']);
46
+ for (let i = 0; i < argv.length; i++) {
47
+ const a = argv[i];
48
+ if (a === '-h') { args.flags.help = true; continue; }
49
+ if (a.startsWith('--')) {
50
+ const key = a.slice(2);
51
+ const next = argv[i + 1];
52
+ if (bools.has(key)) args.flags[key] = true;
53
+ else if (next !== undefined && !next.startsWith('--')) { args.flags[key] = next; i++; }
54
+ else args.flags[key] = true;
55
+ } else args._.push(a);
56
+ }
57
+ return args;
58
+ }
59
+
60
+ // --- fs / json helpers -----------------------------------------------------
61
+ function readJson(path) {
62
+ if (!existsSync(path)) return null;
63
+ try { return JSON.parse(readFileSync(path, 'utf8')); }
64
+ catch { throw new Error(`Could not parse JSON at ${path} — fix or remove it and re-run.`); }
65
+ }
66
+ function writeJson(path, obj) {
67
+ mkdirSync(dirname(path), { recursive: true });
68
+ writeFileSync(path, JSON.stringify(obj, null, 2) + '\n');
69
+ }
70
+ function prompt(question) {
71
+ return new Promise((res) => {
72
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
73
+ rl.question(question, (ans) => { rl.close(); res(ans.trim()); });
74
+ });
75
+ }
76
+ const CWD = process.cwd();
77
+ const rel = (p) => p.replace(CWD + '/', './').replace(homedir(), '~');
78
+
79
+ // --- claude CLI ------------------------------------------------------------
80
+ function whichClaude() {
81
+ const r = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['claude'], { encoding: 'utf8' });
82
+ return r.status === 0 && r.stdout.trim() ? r.stdout.trim().split('\n')[0] : null;
83
+ }
84
+ function claude(bin, args) {
85
+ return spawnSync(bin, args, { encoding: 'utf8' });
86
+ }
87
+
88
+ // --- token -----------------------------------------------------------------
89
+ async function resolveToken(flags) {
90
+ if (typeof flags.token === 'string' && flags.token) return flags.token;
91
+ if (process.env.TREK_TOKEN) { skip('using TREK_TOKEN from environment'); return process.env.TREK_TOKEN; }
92
+ log();
93
+ log(`Trek needs an API token (${c.cyan('trk_...')}).`);
94
+ log(`Mint one in the cockpit: ${c.cyan(COCKPIT_SETTINGS_URL)} (Settings → API tokens).`);
95
+ log();
96
+ if (!process.stdin.isTTY) throw new Error('No token provided. Pass --token <trk_...> or set TREK_TOKEN.');
97
+ const tok = await prompt('Paste your Trek token (or press Enter to skip): ');
98
+ if (!tok) { warn('No token entered — writing config with a placeholder. Set TREK_TOKEN later.'); return 'trk_REPLACE_ME'; }
99
+ return tok;
100
+ }
101
+
102
+ // --- settings.local.json: merge env block ----------------------------------
103
+ function writeTokenEnv(claudeDir, apiUrl, token, projectId) {
104
+ const path = join(claudeDir, 'settings.local.json');
105
+ const cur = readJson(path) || {};
106
+ const env = { ...(cur.env || {}), TREK_TOKEN: token, TREK_API_URL: apiUrl };
107
+ if (projectId) env.TREK_PROJECT_ID = projectId;
108
+ const next = { ...cur, env };
109
+ if (existsSync(path) && JSON.stringify(cur) === JSON.stringify(next)) {
110
+ skip(`${rel(path)} already has TREK_TOKEN`);
111
+ return path;
112
+ }
113
+ writeJson(path, next);
114
+ ok(`${rel(path)} env wired (TREK_TOKEN, TREK_API_URL${projectId ? ', TREK_PROJECT_ID' : ''})`);
115
+ return path;
116
+ }
117
+
118
+ // --- .gitignore ------------------------------------------------------------
119
+ function ensureGitignore(dir, entries) {
120
+ const path = join(dir, '.gitignore');
121
+ let lines = existsSync(path) ? readFileSync(path, 'utf8').split('\n') : [];
122
+ const have = new Set(lines.map((l) => l.trim()));
123
+ const toAdd = entries.filter((e) => !have.has(e));
124
+ if (toAdd.length === 0) { skip('.gitignore already covers settings.local.json'); return; }
125
+ if (lines.length && lines[lines.length - 1].trim() !== '') lines.push('');
126
+ if (!have.has('# Trek')) lines.push('# Trek');
127
+ lines.push(...toAdd);
128
+ writeFileSync(path, lines.join('\n').replace(/\n+$/, '\n'));
129
+ ok(`.gitignore += ${toAdd.join(', ')}`);
130
+ }
131
+
132
+ // --- plugin install via marketplace ----------------------------------------
133
+ function installPlugin(bin, marketplaceRepo) {
134
+ // Add marketplace (tolerate "already added").
135
+ let r = claude(bin, ['plugin', 'marketplace', 'add', marketplaceRepo]);
136
+ if (r.status === 0) ok(`marketplace added: ${marketplaceRepo}`);
137
+ else if (/already|exists/i.test(`${r.stdout}${r.stderr}`)) skip(`marketplace ${marketplaceRepo} already added`);
138
+ else { warn(`\`claude plugin marketplace add ${marketplaceRepo}\` failed:`); log(c.dim((r.stderr || r.stdout || '').trim())); return false; }
139
+
140
+ // Install (tolerate "already installed").
141
+ r = claude(bin, ['plugin', 'install', `${PLUGIN}@${MARKETPLACE}`]);
142
+ if (r.status === 0) ok(`plugin installed: ${PLUGIN}@${MARKETPLACE}`);
143
+ else if (/already|installed/i.test(`${r.stdout}${r.stderr}`)) skip(`plugin ${PLUGIN}@${MARKETPLACE} already installed`);
144
+ else { warn(`\`claude plugin install ${PLUGIN}@${MARKETPLACE}\` failed:`); log(c.dim((r.stderr || r.stdout || '').trim())); return false; }
145
+ return true;
146
+ }
147
+
148
+ // --- init ------------------------------------------------------------------
149
+ async function init(flags) {
150
+ const userScope = !!flags.user;
151
+ const apiUrl = (typeof flags['api-url'] === 'string' && flags['api-url']) || DEFAULT_API_URL;
152
+ const projectId = typeof flags['project-id'] === 'string' ? flags['project-id'] : process.env.TREK_PROJECT_ID || '';
153
+ const marketplaceRepo = (typeof flags.marketplace === 'string' && flags.marketplace) || DEFAULT_MARKETPLACE_REPO;
154
+
155
+ log(c.bold(`\nTrek installer — ${userScope ? 'user' : 'project'} scope`));
156
+ log(c.dim(`api: ${apiUrl}`));
157
+
158
+ const bin = whichClaude();
159
+ if (!bin) {
160
+ warn('`claude` CLI not found. Install Claude Code first: https://docs.claude.com/claude-code');
161
+ log('Then re-run `npx @trekagent/claude init`. Or install the plugin manually:');
162
+ log(` claude plugin marketplace add ${marketplaceRepo}`);
163
+ log(` claude plugin install ${PLUGIN}@${MARKETPLACE}`);
164
+ } else {
165
+ installPlugin(bin, marketplaceRepo);
166
+ }
167
+
168
+ const token = await resolveToken(flags);
169
+ const claudeDir = userScope ? join(homedir(), '.claude') : join(CWD, '.claude');
170
+ writeTokenEnv(claudeDir, apiUrl, token, projectId);
171
+ if (!userScope) ensureGitignore(CWD, ['.claude/settings.local.json']);
172
+
173
+ log(c.bold(`\nDone (${userScope ? 'user' : 'project'} scope).`));
174
+ log('Next steps:');
175
+ log(` 1. Restart Claude Code${userScope ? '' : ' in this project'} so it loads the Trek plugin.`);
176
+ log(' 2. Approve the "trek" MCP server when prompted (it reads ${TREK_TOKEN}).');
177
+ log(` 3. Update later with: ${c.cyan(`claude plugin update ${PLUGIN}@${MARKETPLACE}`)}`);
178
+ }
179
+
180
+ // --- uninstall -------------------------------------------------------------
181
+ async function uninstall(flags) {
182
+ const userScope = !!flags.user;
183
+ log(c.bold(`\nTrek uninstall — ${userScope ? 'user' : 'project'} scope`));
184
+ const bin = whichClaude();
185
+ if (bin) {
186
+ const r = claude(bin, ['plugin', 'uninstall', `${PLUGIN}@${MARKETPLACE}`]);
187
+ if (r.status === 0) ok(`plugin uninstalled: ${PLUGIN}@${MARKETPLACE}`);
188
+ else skip('plugin not installed (or already removed)');
189
+ } else {
190
+ warn('`claude` CLI not found — remove the plugin manually with `claude plugin uninstall`.');
191
+ }
192
+
193
+ // Strip the Trek env keys from settings.local.json (leave the rest intact).
194
+ const path = join(userScope ? homedir() : CWD, '.claude', 'settings.local.json');
195
+ const cur = readJson(path);
196
+ if (cur && cur.env) {
197
+ for (const k of ['TREK_TOKEN', 'TREK_API_URL', 'TREK_PROJECT_ID']) delete cur.env[k];
198
+ if (Object.keys(cur.env).length === 0) delete cur.env;
199
+ writeJson(path, cur);
200
+ ok(`${rel(path)} Trek env removed`);
201
+ }
202
+ log(c.bold('\nUninstalled.'));
203
+ }
204
+
205
+ // --- help / main -----------------------------------------------------------
206
+ const HELP = `${c.bold('@trekagent/claude')} — install Trek into Claude Code.
207
+
208
+ ${c.bold('Usage')}
209
+ npx @trekagent/claude init [options]
210
+
211
+ ${c.bold('Options')}
212
+ --project Project scope: write ./.claude/settings.local.json (default)
213
+ --user User scope: write ~/.claude/settings.local.json
214
+ --token <trk_...> Trek API token (else $TREK_TOKEN, else prompt)
215
+ --api-url <url> Trek API base URL (default ${DEFAULT_API_URL})
216
+ --project-id <uuid> Bind a default Trek project (sets TREK_PROJECT_ID)
217
+ --marketplace <o/r> GitHub owner/repo of the plugin marketplace (else $TREK_MARKETPLACE)
218
+ --uninstall Remove the plugin + Trek env for the chosen scope
219
+ -h, --help Show this help
220
+
221
+ ${c.bold('What init does')}
222
+ 1. claude plugin marketplace add trekagent/trek-claude-plugin
223
+ 2. claude plugin install ${PLUGIN}@${MARKETPLACE}
224
+ 3. writes TREK_TOKEN / TREK_API_URL into .claude/settings.local.json (gitignored)
225
+
226
+ The plugin ships the skill, presence hooks, and the remote MCP server; the token in
227
+ settings.local.json is what makes them authenticate. Re-running is safe.
228
+ `;
229
+
230
+ async function main() {
231
+ const argv = process.argv.slice(2);
232
+ const { _, flags } = parseArgs(argv);
233
+ const cmd = _[0];
234
+ if (flags.help || (!cmd && argv.length === 0) || cmd === 'help') { log(HELP); return; }
235
+ if (cmd !== 'init') { log(c.red(`Unknown command: ${cmd ?? '(none)'}`)); log(HELP); process.exitCode = 1; return; }
236
+ try {
237
+ if (flags.uninstall) await uninstall(flags);
238
+ else await init(flags);
239
+ } catch (err) {
240
+ log('');
241
+ log(c.red(`Error: ${err.message}`));
242
+ process.exitCode = 1;
243
+ }
244
+ }
245
+ main();
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@trekagent/claude",
3
+ "version": "0.1.0",
4
+ "description": "One-command installer for Trek: installs the Trek Claude Code plugin (skill + hooks + MCP) from the Trek marketplace and wires your API token.",
5
+ "private": false,
6
+ "type": "module",
7
+ "bin": {
8
+ "trek-claude": "bin/cli.mjs"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/trekagent/trek-claude-installer.git"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public",
20
+ "registry": "https://registry.npmjs.org"
21
+ },
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "scripts": {
26
+ "init": "node bin/cli.mjs init"
27
+ },
28
+ "keywords": [
29
+ "trek",
30
+ "claude",
31
+ "claude-code",
32
+ "plugin",
33
+ "mcp",
34
+ "agent"
35
+ ],
36
+ "license": "MIT"
37
+ }