agent-takkub 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/LICENSE +21 -0
- package/README.md +71 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/dist/agent_takkub-1.0.0-py3-none-any.whl +0 -0
- package/npm/bin/agent-takkub.js +13 -0
- package/npm/bin/takkub.js +13 -0
- package/npm/scripts/lib.js +43 -0
- package/npm/scripts/postinstall.js +114 -0
- package/npm/scripts/preflight.js +57 -0
- package/npm/scripts/shortcut.js +110 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 takkub
|
|
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,71 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🛩️ agent-takkub
|
|
4
|
+
|
|
5
|
+
**A desktop cockpit for running a whole team of Claude Code agents — together, in one window.**
|
|
6
|
+
|
|
7
|
+
You direct a **Lead**; the Lead orchestrates specialist teammates
|
|
8
|
+
(frontend · backend · qa · reviewer · devops · …) as live panes.
|
|
9
|
+
|
|
10
|
+
`Windows` · `macOS` · runs entirely on your machine
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g agent-takkub # isolated Python runtime + a Desktop icon
|
|
20
|
+
claude login # one-time auth — your Claude account
|
|
21
|
+
takkub provision # recommended plugins + browser tooling
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then **double-click “Takkub Cockpit” on your Desktop** — or run `agent-takkub`.
|
|
25
|
+
|
|
26
|
+
> **Prerequisites:** Node ≥ 18 and Python ≥ 3.11 — *detected, never reinstalled.*
|
|
27
|
+
> No clone, no manual wiring. Everything lands in an isolated `~/.agent-takkub`;
|
|
28
|
+
> your existing `claude` CLI, plugins, and config are left untouched.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why
|
|
33
|
+
|
|
34
|
+
- **🧠 One Lead, many specialists** — talk to the Lead; it spawns the right roles on demand.
|
|
35
|
+
- **🔀 Real parallelism** — independent features fan out across panes; QA always runs last.
|
|
36
|
+
- **🖥️ Live, steerable panes** — every teammate is a real `claude` process you can watch.
|
|
37
|
+
- **🗂️ Multi-project tabs** — one isolated Lead per project, no cross-talk.
|
|
38
|
+
- **🧩 Batteries included** — Playwright browser automation, curated skill plugins, session memory, decision logs.
|
|
39
|
+
- **🔒 Local-first** — no SaaS, nothing leaves your machine; runs on your logged-in `claude`.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## The flow
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
you → Lead → assign → spawn claude pane → work → done → verify (QA) → ship
|
|
47
|
+
↑__________________ fan out across roles __________________↑
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Say what you want. The Lead plans it, dispatches the right specialists, gathers
|
|
51
|
+
their results, runs verification, and proposes the ship — you stay in control.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Everyday commands
|
|
56
|
+
|
|
57
|
+
| Command | |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `takkub assign --role backend "…"` | spawn + task a teammate |
|
|
60
|
+
| `takkub send --to qa "…"` | message a peer (Lead CC’d) |
|
|
61
|
+
| `takkub provision` | install / repair plugins + browser tools |
|
|
62
|
+
| `takkub doctor` | diagnose the environment |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## More
|
|
67
|
+
|
|
68
|
+
- **Architecture & flow diagrams** — [`docs/system-overview/`](docs/system-overview/)
|
|
69
|
+
- **From source / one-shot installer** (Chrome, gh, codex, rtk, …) — [`docs/INSTALL.md`](docs/INSTALL.md)
|
|
70
|
+
|
|
71
|
+
<div align="center"><sub>Windows + macOS · built on PyQt6 · powered by the Claude Code CLI</sub></div>
|
package/assets/icon.ico
ADDED
|
Binary file
|
package/assets/icon.png
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Launches the cockpit GUI from the provisioned (isolated) venv.
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
const { venvPythonIfExists } = require('../scripts/lib');
|
|
6
|
+
|
|
7
|
+
const py = venvPythonIfExists();
|
|
8
|
+
if (!py) {
|
|
9
|
+
console.error('agent-takkub is not provisioned. Run: npm install -g agent-takkub');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const r = spawnSync(py, ['-m', 'agent_takkub', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
13
|
+
process.exit(r.status == null ? 1 : r.status);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// The `takkub` CLI, run from the provisioned (isolated) venv.
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
const { venvPythonIfExists } = require('../scripts/lib');
|
|
6
|
+
|
|
7
|
+
const py = venvPythonIfExists();
|
|
8
|
+
if (!py) {
|
|
9
|
+
console.error('takkub is not provisioned. Run: npm install -g agent-takkub');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const r = spawnSync(py, ['-m', 'agent_takkub.cli', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
13
|
+
process.exit(r.status == null ? 1 : r.status);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Shared helpers for the npm wrapper. Pure path logic — importing this file
|
|
3
|
+
// runs NOTHING and touches nothing on disk.
|
|
4
|
+
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
// The cockpit's per-install home. Deliberately SEPARATE from a git dev
|
|
10
|
+
// checkout's `.venv`, so `npm install -g agent-takkub` never collides with a
|
|
11
|
+
// from-source setup. Overridable via AGENT_TAKKUB_HOME so an isolated smoke
|
|
12
|
+
// test can point at a throwaway dir and leave ~/.agent-takkub untouched.
|
|
13
|
+
function agentTakkubHome() {
|
|
14
|
+
return process.env.AGENT_TAKKUB_HOME || path.join(os.homedir(), '.agent-takkub');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function venvDir() {
|
|
18
|
+
return path.join(agentTakkubHome(), 'venv');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The venv's python, per-platform (Windows: Scripts\python.exe, else bin/python).
|
|
22
|
+
function venvPython() {
|
|
23
|
+
const dir = venvDir();
|
|
24
|
+
return process.platform === 'win32'
|
|
25
|
+
? path.join(dir, 'Scripts', 'python.exe')
|
|
26
|
+
: path.join(dir, 'bin', 'python');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function venvPythonIfExists() {
|
|
30
|
+
const p = venvPython();
|
|
31
|
+
return fs.existsSync(p) ? p : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Windowless python for GUI launchers (Windows: pythonw.exe = no console
|
|
35
|
+
// window pops up behind the cockpit). macOS has no separate pythonw.
|
|
36
|
+
function venvPythonw() {
|
|
37
|
+
const dir = venvDir();
|
|
38
|
+
return process.platform === 'win32'
|
|
39
|
+
? path.join(dir, 'Scripts', 'pythonw.exe')
|
|
40
|
+
: path.join(dir, 'bin', 'python');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { agentTakkubHome, venvDir, venvPython, venvPythonIfExists, venvPythonw };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// npm postinstall bootstrap for `npm install -g agent-takkub`.
|
|
3
|
+
//
|
|
4
|
+
// SAFETY MODEL — check first, never clobber:
|
|
5
|
+
// • Detects what's already present (preflight.js, non-mutating) and REPORTS it.
|
|
6
|
+
// • Provisions ONLY into the isolated AGENT_TAKKUB_HOME (default
|
|
7
|
+
// ~/.agent-takkub) — never a repo `.venv`, never shared user state.
|
|
8
|
+
// • REUSES an existing cockpit venv (upgrade in place) instead of wiping it.
|
|
9
|
+
// • Does NOT touch the global claude CLI, ~/.claude plugins, or ~/.takkub
|
|
10
|
+
// config. Anything shared/missing is left for an explicit, detect-first
|
|
11
|
+
// `takkub doctor --fix` the user runs consciously.
|
|
12
|
+
// Cross-platform (win32 + darwin); idempotent; fails loudly on missing Python
|
|
13
|
+
// or a missing bundled wheel.
|
|
14
|
+
|
|
15
|
+
const { spawnSync } = require('child_process');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { agentTakkubHome, venvDir, venvPython } = require('./lib');
|
|
19
|
+
const preflight = require('./preflight');
|
|
20
|
+
|
|
21
|
+
function findWheel() {
|
|
22
|
+
// Shipped alongside this package under dist/ (see package.json `files`).
|
|
23
|
+
const dist = path.join(__dirname, '..', '..', 'dist');
|
|
24
|
+
if (!fs.existsSync(dist)) return null;
|
|
25
|
+
const whl = fs.readdirSync(dist).filter((f) => f.endsWith('.whl')).sort();
|
|
26
|
+
return whl.length ? path.join(dist, whl[whl.length - 1]) : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function run(cmd, args) {
|
|
30
|
+
return spawnSync(cmd, args, { stdio: 'inherit' }).status === 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Install the claude CLI only when it's missing — never overwrite an existing
|
|
34
|
+
// (possibly version-pinned) global install. Returns true if claude is available
|
|
35
|
+
// afterward. Best-effort: a failure just falls back to a "install it yourself"
|
|
36
|
+
// hint in the next-steps.
|
|
37
|
+
function ensureClaudeCli(present) {
|
|
38
|
+
if (present) return true;
|
|
39
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
40
|
+
console.log('[agent-takkub] claude CLI not found — installing @anthropic-ai/claude-code…');
|
|
41
|
+
return run(npm, ['install', '-g', '@anthropic-ai/claude-code']);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function main() {
|
|
45
|
+
const env = preflight.detect();
|
|
46
|
+
preflight.report(env);
|
|
47
|
+
|
|
48
|
+
if (!env.python.present) {
|
|
49
|
+
console.error(
|
|
50
|
+
'\n[agent-takkub] Python >=3.11 not found. Install it, then re-run ' +
|
|
51
|
+
'`npm install -g agent-takkub`.'
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const wheel = findWheel();
|
|
56
|
+
if (!wheel) {
|
|
57
|
+
console.error('\n[agent-takkub] bundled wheel missing under dist/ — package build is incomplete.');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fs.mkdirSync(agentTakkubHome(), { recursive: true });
|
|
62
|
+
|
|
63
|
+
// Reuse an existing venv (upgrade in place); create only when absent — never wipe.
|
|
64
|
+
if (env.venv.present) {
|
|
65
|
+
console.log('\n[agent-takkub] existing cockpit venv found — upgrading in place (not recreated).');
|
|
66
|
+
} else {
|
|
67
|
+
console.log(`\n[agent-takkub] creating venv at ${venvDir()}`);
|
|
68
|
+
if (!run(env.python.cmd, [...env.python.args, '-m', 'venv', venvDir()])) {
|
|
69
|
+
console.error('[agent-takkub] failed to create venv.');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const vpy = venvPython();
|
|
75
|
+
run(vpy, ['-m', 'pip', 'install', '--upgrade', 'pip', '--quiet']);
|
|
76
|
+
console.log('[agent-takkub] installing/upgrading cockpit + PyQt6 (may take a few minutes)…');
|
|
77
|
+
// Step 1: install/upgrade the cockpit + its deps (PyQt etc.) normally.
|
|
78
|
+
if (!run(vpy, ['-m', 'pip', 'install', '--upgrade', wheel])) {
|
|
79
|
+
console.error('[agent-takkub] pip install failed.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
// Step 2: force-refresh the app package itself. pip's --upgrade treats an
|
|
83
|
+
// equal version as "already satisfied" and SKIPS it, so a same-version
|
|
84
|
+
// reinstall (a repair, or a rebuilt wheel whose version didn't bump) would
|
|
85
|
+
// otherwise keep stale code. --no-deps scopes this to the tiny app package —
|
|
86
|
+
// PyQt was already handled in step 1, so this stays fast. Best-effort: step 1
|
|
87
|
+
// already succeeded, so a hiccup here shouldn't fail the whole install.
|
|
88
|
+
run(vpy, ['-m', 'pip', 'install', '--force-reinstall', '--no-deps', wheel]);
|
|
89
|
+
|
|
90
|
+
const claudeOk = ensureClaudeCli(env.claudeCli.present);
|
|
91
|
+
|
|
92
|
+
console.log(`\n[agent-takkub] ✓ cockpit ready (isolated in ${agentTakkubHome()}).`);
|
|
93
|
+
try {
|
|
94
|
+
const sc = require('./shortcut').create();
|
|
95
|
+
if (sc) console.log(`[agent-takkub] ✓ Desktop launcher created: ${sc}`);
|
|
96
|
+
} catch (_e) {
|
|
97
|
+
/* best-effort — a missing shortcut never fails the install */
|
|
98
|
+
}
|
|
99
|
+
console.log(' Left untouched: ~/.claude plugins, ~/.takkub config (nothing overwritten).');
|
|
100
|
+
console.log('\n Next steps:');
|
|
101
|
+
if (!claudeOk) {
|
|
102
|
+
console.log(' • install the claude CLI: npm i -g @anthropic-ai/claude-code');
|
|
103
|
+
}
|
|
104
|
+
console.log(' 1) claude login # authenticate (one-time, your account)');
|
|
105
|
+
console.log(' 2) takkub provision # install recommended plugins + browser MCPs (idempotent)');
|
|
106
|
+
console.log(' 3) double-click "Takkub Cockpit" on the Desktop (or run: agent-takkub)\n');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
main();
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error('[agent-takkub] postinstall error:', e && e.message);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Non-mutating environment detection. Runs ONLY `--version`-style probes and
|
|
3
|
+
// filesystem existence checks — nothing here installs, writes, or overwrites.
|
|
4
|
+
// This is what lets the installer "check first": report what's already present
|
|
5
|
+
// and provision ONLY the gaps, so a machine that already has Python / claude /
|
|
6
|
+
// a cockpit venv is never clobbered.
|
|
7
|
+
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const { venvPython, agentTakkubHome } = require('./lib');
|
|
11
|
+
|
|
12
|
+
function probe(cmd, args) {
|
|
13
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8' });
|
|
14
|
+
if (r.status !== 0) return null;
|
|
15
|
+
return ((r.stdout || '') + (r.stderr || '')).trim();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function detectPython() {
|
|
19
|
+
const cands = process.platform === 'win32'
|
|
20
|
+
? [['py', ['-3.11', '--version']], ['py', ['-3', '--version']], ['python', ['--version']]]
|
|
21
|
+
: [['python3.11', ['--version']], ['python3', ['--version']], ['python', ['--version']]];
|
|
22
|
+
for (const [cmd, args] of cands) {
|
|
23
|
+
const out = probe(cmd, args);
|
|
24
|
+
const m = out && out.match(/Python (\d+)\.(\d+)\.\d+/);
|
|
25
|
+
if (m) {
|
|
26
|
+
const maj = parseInt(m[1], 10);
|
|
27
|
+
const min = parseInt(m[2], 10);
|
|
28
|
+
if (maj > 3 || (maj === 3 && min >= 11)) {
|
|
29
|
+
// Drop the trailing '--version' so callers can reuse (cmd, args) to run python.
|
|
30
|
+
return { present: true, version: `${maj}.${min}`, cmd, args: args.slice(0, -1) };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { present: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Detect the whole environment without mutating anything.
|
|
38
|
+
function detect() {
|
|
39
|
+
const claude = probe('claude', ['--version']);
|
|
40
|
+
return {
|
|
41
|
+
python: detectPython(),
|
|
42
|
+
node: probe('node', ['--version']),
|
|
43
|
+
claudeCli: claude ? { present: true, version: claude } : { present: false },
|
|
44
|
+
venv: { present: fs.existsSync(venvPython()), path: venvPython() },
|
|
45
|
+
home: agentTakkubHome(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function report(d) {
|
|
50
|
+
console.log('[agent-takkub] environment check (nothing below is overwritten):');
|
|
51
|
+
console.log(` Python >=3.11 : ${d.python.present ? '✓ ' + d.python.version + ' (reuse existing)' : '· missing'}`);
|
|
52
|
+
console.log(` Node : ${d.node ? '✓ ' + d.node : '· missing'}`);
|
|
53
|
+
console.log(` claude CLI : ${d.claudeCli.present ? '✓ ' + d.claudeCli.version + ' (kept as-is)' : '· not found'}`);
|
|
54
|
+
console.log(` cockpit venv : ${d.venv.present ? '✓ exists (reuse/upgrade)' : '· will create'} — ${d.home}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { detect, report };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Best-effort Desktop launcher creation (win32 + darwin). NEVER throws — a
|
|
3
|
+
// failure just means no icon; the `agent-takkub` command still works. The
|
|
4
|
+
// Desktop location is overridable via AGENT_TAKKUB_DESKTOP so an isolated test
|
|
5
|
+
// (or a user who wants it elsewhere) doesn't touch the real Desktop.
|
|
6
|
+
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { venvPythonw, venvPython } = require('./lib');
|
|
12
|
+
|
|
13
|
+
function desktopDir() {
|
|
14
|
+
return process.env.AGENT_TAKKUB_DESKTOP || path.join(os.homedir(), 'Desktop');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function iconAsset(name) {
|
|
18
|
+
return path.join(__dirname, '..', '..', 'assets', name);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Single-quote a string for a PowerShell literal (double any embedded quote).
|
|
22
|
+
function psStr(s) {
|
|
23
|
+
return "'" + String(s).replace(/'/g, "''") + "'";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createWindows() {
|
|
27
|
+
const lnk = path.join(desktopDir(), 'Takkub Cockpit.lnk');
|
|
28
|
+
const target = venvPythonw();
|
|
29
|
+
const ico = iconAsset('icon.ico');
|
|
30
|
+
// WorkingDirectory = the venv root (…/venv), two levels up from Scripts\pythonw.exe.
|
|
31
|
+
const workdir = path.dirname(path.dirname(target));
|
|
32
|
+
const lines = [
|
|
33
|
+
'$ws = New-Object -ComObject WScript.Shell',
|
|
34
|
+
`$sc = $ws.CreateShortcut(${psStr(lnk)})`,
|
|
35
|
+
`$sc.TargetPath = ${psStr(target)}`,
|
|
36
|
+
"$sc.Arguments = '-m agent_takkub'",
|
|
37
|
+
`$sc.WorkingDirectory = ${psStr(workdir)}`,
|
|
38
|
+
];
|
|
39
|
+
if (fs.existsSync(ico)) lines.push(`$sc.IconLocation = ${psStr(ico)}`);
|
|
40
|
+
lines.push("$sc.Description = 'agent-takkub dev team cockpit'");
|
|
41
|
+
lines.push('$sc.Save()');
|
|
42
|
+
|
|
43
|
+
fs.mkdirSync(desktopDir(), { recursive: true });
|
|
44
|
+
// Run from a temp .ps1 FILE, not an inline -Command: passing a long quoted
|
|
45
|
+
// command through Node→Windows arg-escaping mangles the quotes and produces
|
|
46
|
+
// an empty shortcut. A -File avoids all of that.
|
|
47
|
+
const ps1 = path.join(os.tmpdir(), `att-shortcut-${process.pid}.ps1`);
|
|
48
|
+
fs.writeFileSync(ps1, lines.join('\r\n') + '\r\n', 'utf8');
|
|
49
|
+
try {
|
|
50
|
+
const r = spawnSync(
|
|
51
|
+
'powershell',
|
|
52
|
+
['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', ps1],
|
|
53
|
+
{ stdio: 'ignore' }
|
|
54
|
+
);
|
|
55
|
+
return r.status === 0 && fs.existsSync(lnk) ? lnk : null;
|
|
56
|
+
} finally {
|
|
57
|
+
try {
|
|
58
|
+
fs.unlinkSync(ps1);
|
|
59
|
+
} catch (_e) {
|
|
60
|
+
/* ignore */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createMac() {
|
|
66
|
+
const appDir = path.join(desktopDir(), 'Takkub Cockpit.app');
|
|
67
|
+
const macos = path.join(appDir, 'Contents', 'MacOS');
|
|
68
|
+
const res = path.join(appDir, 'Contents', 'Resources');
|
|
69
|
+
fs.mkdirSync(macos, { recursive: true });
|
|
70
|
+
fs.mkdirSync(res, { recursive: true });
|
|
71
|
+
|
|
72
|
+
const launch = `#!/bin/sh\nexec ${JSON.stringify(venvPython())} -m agent_takkub "$@"\n`;
|
|
73
|
+
fs.writeFileSync(path.join(macos, 'launch'), launch);
|
|
74
|
+
fs.chmodSync(path.join(macos, 'launch'), 0o755);
|
|
75
|
+
|
|
76
|
+
const plist =
|
|
77
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
78
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ' +
|
|
79
|
+
'"http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' +
|
|
80
|
+
'<plist version="1.0"><dict>\n' +
|
|
81
|
+
' <key>CFBundleName</key><string>Takkub Cockpit</string>\n' +
|
|
82
|
+
' <key>CFBundleExecutable</key><string>launch</string>\n' +
|
|
83
|
+
' <key>CFBundleIdentifier</key><string>com.takkub.cockpit</string>\n' +
|
|
84
|
+
' <key>CFBundleIconFile</key><string>icon</string>\n' +
|
|
85
|
+
' <key>CFBundlePackageType</key><string>APPL</string>\n' +
|
|
86
|
+
'</dict></plist>\n';
|
|
87
|
+
fs.writeFileSync(path.join(appDir, 'Contents', 'Info.plist'), plist);
|
|
88
|
+
|
|
89
|
+
// Convert the bundled png → icns with sips (macOS built-in). Best-effort.
|
|
90
|
+
const png = iconAsset('icon.png');
|
|
91
|
+
if (fs.existsSync(png)) {
|
|
92
|
+
spawnSync('sips', ['-s', 'format', 'icns', png, '--out', path.join(res, 'icon.icns')], {
|
|
93
|
+
stdio: 'ignore',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return fs.existsSync(path.join(macos, 'launch')) ? appDir : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Returns the created launcher path, or null if unsupported/failed.
|
|
100
|
+
function create() {
|
|
101
|
+
try {
|
|
102
|
+
if (process.platform === 'win32') return createWindows();
|
|
103
|
+
if (process.platform === 'darwin') return createMac();
|
|
104
|
+
} catch (_e) {
|
|
105
|
+
/* best-effort — never block the install over a shortcut */
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = { create };
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-takkub",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Desktop cockpit for orchestrating a team of Claude Code agents (Windows + macOS)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"ai-agents",
|
|
9
|
+
"agent-orchestration",
|
|
10
|
+
"cockpit",
|
|
11
|
+
"desktop",
|
|
12
|
+
"cli",
|
|
13
|
+
"developer-tools"
|
|
14
|
+
],
|
|
15
|
+
"author": "takkub",
|
|
16
|
+
"bin": {
|
|
17
|
+
"agent-takkub": "npm/bin/agent-takkub.js",
|
|
18
|
+
"takkub": "npm/bin/takkub.js"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"postinstall": "node npm/scripts/postinstall.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"npm/",
|
|
25
|
+
"dist/*.whl",
|
|
26
|
+
"assets/icon.ico",
|
|
27
|
+
"assets/icon.png"
|
|
28
|
+
],
|
|
29
|
+
"os": [
|
|
30
|
+
"win32",
|
|
31
|
+
"darwin"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT"
|
|
37
|
+
}
|