@mootup/moot-templates 0.1.0-rc.0 → 0.2.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/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@mootup/moot-templates",
3
- "version": "0.1.0-rc.0",
3
+ "version": "0.2.0",
4
4
  "description": "Canonical project templates (skills, teams, devcontainer, Claude hooks) for @mootup/moot-cli, synced from the mootup Python CLI source.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/mootup-io/moot-cli-js.git",
9
+ "directory": "packages/moot-templates"
10
+ },
6
11
  "type": "module",
7
12
  "main": "./dist/index.js",
8
13
  "types": "./dist/index.d.ts",
@@ -18,7 +23,7 @@
18
23
  "README.md"
19
24
  ],
20
25
  "engines": {
21
- "node": ">=20.0.0"
26
+ "node": ">=18.0.0"
22
27
  },
23
28
  "scripts": {
24
29
  "sync:templates": "node scripts/sync-templates.mjs",
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "moot-agent-team-cursor",
3
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22",
4
+ "features": {
5
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {
6
+ "moby": false
7
+ },
8
+ "ghcr.io/devcontainers/features/python:1": {
9
+ "version": "3.11"
10
+ }
11
+ },
12
+ "postCreateCommand": "bash .devcontainer/post-create.sh",
13
+ "runArgs": [
14
+ "--name", "moot-${localWorkspaceFolderBasename}"
15
+ ],
16
+ "customizations": {
17
+ "cursor": {
18
+ "extensions": [
19
+ "ms-python.python",
20
+ "ms-pyright.pyright"
21
+ ]
22
+ }
23
+ },
24
+ "remoteUser": "node"
25
+ }
@@ -0,0 +1,68 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # System packages
5
+ sudo apt-get update && sudo apt-get install -y tmux
6
+
7
+ # Claude Code CLI — install from npm first (puts `claude` on PATH
8
+ # via /usr/local/share/npm-global/bin), use it to register MCP servers,
9
+ # then call `claude install` to migrate to the native build at
10
+ # ~/.local/bin. The native build is the officially-supported path
11
+ # going forward; the TUI nags on first run when it's still npm-based.
12
+ npm install -g @anthropic-ai/claude-code
13
+
14
+ # Python tooling
15
+ pip install uv
16
+
17
+ # Install moot package
18
+ pip install mootup
19
+
20
+ # Register MCP servers for Claude Code at user scope so claude finds
21
+ # them regardless of cwd (agents launch in worktrees under .worktrees/,
22
+ # not the project root). Use absolute paths to the wrapper scripts so
23
+ # they resolve from any cwd. The wrappers read CONVO_ROLE at runtime
24
+ # to look up the per-role API key from .moot/actors.json.
25
+ DEVCONTAINER_DIR="$(realpath .devcontainer)"
26
+ claude mcp add convo "$DEVCONTAINER_DIR/run-moot-mcp.sh" -s user
27
+ claude mcp add convo-channel "$DEVCONTAINER_DIR/run-moot-channel.sh" -s user
28
+
29
+ # Migrate from the npm-installed claude to the native build. This runs
30
+ # LAST (after `claude mcp add`) because `claude install` deletes the
31
+ # npm symlink — anything calling `claude` after this point must rely on
32
+ # ~/.local/bin/claude, which `bash -lc` picks up via ~/.profile's
33
+ # standard "$HOME/.local/bin" snippet. Agent tmux sessions launch with
34
+ # `bash -lc`, so they find the native binary automatically.
35
+ claude install
36
+
37
+ # Rebind tmux prefix to Ctrl-Space. Claude Code intercepts Ctrl-B (the
38
+ # default prefix), so the usual `Ctrl-B d` detach never reaches tmux.
39
+ # Ctrl-Space is rarely claimed by TUIs and leaves readline-style editing
40
+ # bindings (Ctrl-A/E/etc.) untouched inside claude's input line.
41
+ cat > /home/node/.tmux.conf <<'TMUX_CONF'
42
+ unbind C-b
43
+ set -g prefix C-Space
44
+ bind C-Space send-prefix
45
+
46
+ # Mouse on: scroll-wheel scrolls the pane, click selects a pane/window,
47
+ # drag copies. Without this, scrollback is only reachable via the
48
+ # copy-mode keybind (<prefix> [) which is a tmux-literacy tax users
49
+ # shouldn't have to pay just to read recent output.
50
+ set -g mouse on
51
+ TMUX_CONF
52
+
53
+ # Register a /detach slash command for claude so the user can leave a
54
+ # tmux session without having to fight for the prefix key. The command
55
+ # calls `tmux detach-client`, which disconnects the terminal but leaves
56
+ # claude running in the session so `moot attach` picks up where it left
57
+ # off. User-scope so every worktree sees it.
58
+ mkdir -p /home/node/.claude/commands
59
+ cat > /home/node/.claude/commands/detach.md <<'DETACH_MD'
60
+ ---
61
+ description: Detach from the tmux session (leaves claude running in the background)
62
+ allowed-tools: Bash(bash:*)
63
+ ---
64
+
65
+ !bash -c 'SOCK=$(find /tmp /run -maxdepth 3 -name default -type s 2>/dev/null | head -1); if [ -n "$SOCK" ]; then tmux -S "$SOCK" detach-client; else echo "tmux socket not found"; fi'
66
+ DETACH_MD
67
+
68
+ echo "Container ready."
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+ # Channel adapter wrapper — reads CONVO_ROLE and looks up API key
3
+ # from .moot/actors.json. Same logic as run-moot-mcp.sh.
4
+
5
+ ROLE="${CONVO_ROLE:-implementation}"
6
+ ACTORS_FILE=".moot/actors.json"
7
+
8
+ # Find project root (walk up to moot.toml)
9
+ PROJECT_ROOT="$(pwd)"
10
+ while [ "$PROJECT_ROOT" != "/" ]; do
11
+ [ -f "$PROJECT_ROOT/moot.toml" ] && break
12
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
13
+ done
14
+
15
+ # Read per-role actor identity from .moot/actors.json. See run-moot-mcp.sh
16
+ # for the full rationale — same rule applies to the channel adapter.
17
+ if [ -f "$PROJECT_ROOT/$ACTORS_FILE" ]; then
18
+ eval $(python3 -c "
19
+ import json, shlex
20
+ with open('$PROJECT_ROOT/$ACTORS_FILE') as f:
21
+ data = json.load(f)
22
+ entry = data.get('actors', {}).get('$ROLE', {})
23
+ print('KEY=' + shlex.quote(entry.get('api_key', '')))
24
+ print('AID=' + shlex.quote(entry.get('actor_id', '')))
25
+ print('ANAME=' + shlex.quote(entry.get('display_name', '$ROLE')))
26
+ " 2>/dev/null)
27
+ if [ -n "$KEY" ]; then
28
+ export CONVO_API_KEY="$KEY"
29
+ else
30
+ echo "WARNING: No API key for role '$ROLE' in $ACTORS_FILE" >&2
31
+ fi
32
+ [ -n "$AID" ] && export CONVO_AGENT_ID="$AID"
33
+ [ -n "$ANAME" ] && export CONVO_AGENT_NAME="$ANAME"
34
+ fi
35
+
36
+ # Read API URL from moot.toml
37
+ if [ -z "$CONVO_API_URL" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
38
+ URL=$(python3 -c "
39
+ import tomllib
40
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
41
+ data = tomllib.load(f)
42
+ print(data.get('convo', {}).get('api_url', ''))
43
+ " 2>/dev/null)
44
+ if [ -n "$URL" ]; then
45
+ export CONVO_API_URL="$URL"
46
+ fi
47
+ fi
48
+
49
+ # Read space ID from moot.toml
50
+ if [ -z "$CONVO_SPACE_ID" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
51
+ SID=$(python3 -c "
52
+ import tomllib
53
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
54
+ data = tomllib.load(f)
55
+ print(data.get('convo', {}).get('space_id', ''))
56
+ " 2>/dev/null)
57
+ if [ -n "$SID" ]; then
58
+ export CONVO_SPACE_ID="$SID"
59
+ fi
60
+ fi
61
+
62
+ # Alpha-grade diagnostics: DEBUG-level logs to a per-role file under
63
+ # .moot/logs/ in the project root. Bind-mounted to the host so users
64
+ # and support can grep and share the logs without docker exec.
65
+ # Override with MOOT_LOG_LEVEL=INFO (or higher) once alpha stabilizes.
66
+ LOG_DIR="$PROJECT_ROOT/.moot/logs"
67
+ mkdir -p "$LOG_DIR"
68
+ LOG_FILE="$LOG_DIR/channel-${ROLE}.log"
69
+ export MOOT_LOG_LEVEL="${MOOT_LOG_LEVEL:-DEBUG}"
70
+ exec python -u -m moot.adapters.channel_runner "$@" 2>> "$LOG_FILE"
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+ # MCP adapter wrapper — reads CONVO_ROLE and looks up API key
3
+ # from .moot/actors.json. Set CONVO_ROLE before launching Claude Code
4
+ # to pick a different identity.
5
+
6
+ ROLE="${CONVO_ROLE:-implementation}"
7
+ ACTORS_FILE=".moot/actors.json"
8
+
9
+ # Find project root (walk up to moot.toml)
10
+ PROJECT_ROOT="$(pwd)"
11
+ while [ "$PROJECT_ROOT" != "/" ]; do
12
+ [ -f "$PROJECT_ROOT/moot.toml" ] && break
13
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
14
+ done
15
+
16
+ # Read per-role actor identity from .moot/actors.json. We MUST export
17
+ # CONVO_AGENT_ID and CONVO_AGENT_NAME — the mcp_runner defaults them to
18
+ # "unknown-agent", and the backend rejects any post whose agent_id in the
19
+ # body doesn't match the authenticated actor (HTTP 400, which the adapter
20
+ # surfaces as an empty event_id).
21
+ if [ -f "$PROJECT_ROOT/$ACTORS_FILE" ]; then
22
+ eval $(python3 -c "
23
+ import json, shlex
24
+ with open('$PROJECT_ROOT/$ACTORS_FILE') as f:
25
+ data = json.load(f)
26
+ entry = data.get('actors', {}).get('$ROLE', {})
27
+ print('KEY=' + shlex.quote(entry.get('api_key', '')))
28
+ print('AID=' + shlex.quote(entry.get('actor_id', '')))
29
+ print('ANAME=' + shlex.quote(entry.get('display_name', '$ROLE')))
30
+ " 2>/dev/null)
31
+ if [ -n "$KEY" ]; then
32
+ export CONVO_API_KEY="$KEY"
33
+ else
34
+ echo "WARNING: No API key for role '$ROLE' in $ACTORS_FILE" >&2
35
+ fi
36
+ [ -n "$AID" ] && export CONVO_AGENT_ID="$AID"
37
+ [ -n "$ANAME" ] && export CONVO_AGENT_NAME="$ANAME"
38
+ fi
39
+
40
+ # Read API URL from moot.toml
41
+ if [ -z "$CONVO_API_URL" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
42
+ URL=$(python3 -c "
43
+ import tomllib
44
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
45
+ data = tomllib.load(f)
46
+ print(data.get('convo', {}).get('api_url', ''))
47
+ " 2>/dev/null)
48
+ if [ -n "$URL" ]; then
49
+ export CONVO_API_URL="$URL"
50
+ fi
51
+ fi
52
+
53
+ # Read space ID from moot.toml
54
+ if [ -z "$CONVO_SPACE_ID" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
55
+ SID=$(python3 -c "
56
+ import tomllib
57
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
58
+ data = tomllib.load(f)
59
+ print(data.get('convo', {}).get('space_id', ''))
60
+ " 2>/dev/null)
61
+ if [ -n "$SID" ]; then
62
+ export CONVO_SPACE_ID="$SID"
63
+ fi
64
+ fi
65
+
66
+ # Alpha-grade diagnostics: DEBUG-level logs to a per-role file under
67
+ # .moot/logs/ in the project root. Bind-mounted to the host so users
68
+ # and support can grep and share the logs without docker exec.
69
+ # Override with MOOT_LOG_LEVEL=INFO (or higher) once alpha stabilizes.
70
+ LOG_DIR="$PROJECT_ROOT/.moot/logs"
71
+ mkdir -p "$LOG_DIR"
72
+ LOG_FILE="$LOG_DIR/mcp-${ROLE}.log"
73
+ export MOOT_LOG_LEVEL="${MOOT_LOG_LEVEL:-DEBUG}"
74
+ exec python -u -m moot.adapters.mcp_runner "$@" 2>> "$LOG_FILE"
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+ # Tmux notification daemon wrapper -- reads CONVO_ROLE and looks up
3
+ # API key from .moot/actors.json. Same pattern as run-moot-channel.sh.
4
+
5
+ ROLE="${CONVO_ROLE:-implementation}"
6
+ ACTORS_FILE=".moot/actors.json"
7
+
8
+ # Find project root (walk up to moot.toml)
9
+ PROJECT_ROOT="$(pwd)"
10
+ while [ "$PROJECT_ROOT" != "/" ]; do
11
+ [ -f "$PROJECT_ROOT/moot.toml" ] && break
12
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
13
+ done
14
+
15
+ # Read API key from .moot/actors.json (nested schema)
16
+ if [ -f "$PROJECT_ROOT/$ACTORS_FILE" ]; then
17
+ KEY=$(python3 -c "
18
+ import json
19
+ with open('$PROJECT_ROOT/$ACTORS_FILE') as f:
20
+ data = json.load(f)
21
+ entry = data.get('actors', {}).get('$ROLE', {})
22
+ print(entry.get('api_key', ''))
23
+ " 2>/dev/null)
24
+ if [ -n "$KEY" ]; then
25
+ export CONVO_API_KEY="$KEY"
26
+ else
27
+ echo "WARNING: No API key for role '$ROLE' in $ACTORS_FILE" >&2
28
+ fi
29
+ fi
30
+
31
+ # Read API URL from moot.toml
32
+ if [ -z "$CONVO_API_URL" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
33
+ URL=$(python3 -c "
34
+ import tomllib
35
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
36
+ data = tomllib.load(f)
37
+ print(data.get('convo', {}).get('api_url', ''))
38
+ " 2>/dev/null)
39
+ if [ -n "$URL" ]; then
40
+ export CONVO_API_URL="$URL"
41
+ fi
42
+ fi
43
+
44
+ # Read space ID from moot.toml
45
+ if [ -z "$CONVO_SPACE_ID" ] && [ -f "$PROJECT_ROOT/moot.toml" ]; then
46
+ SID=$(python3 -c "
47
+ import tomllib
48
+ with open('$PROJECT_ROOT/moot.toml', 'rb') as f:
49
+ data = tomllib.load(f)
50
+ print(data.get('convo', {}).get('space_id', ''))
51
+ " 2>/dev/null)
52
+ if [ -n "$SID" ]; then
53
+ export CONVO_SPACE_ID="$SID"
54
+ fi
55
+ fi
56
+
57
+ # Log daemon output for diagnostics
58
+ LOG_FILE="/tmp/moot-notify-${ROLE}.log"
59
+ exec python -m moot.adapters.notify_runner "$@" 2>"$LOG_FILE"