cmdr-mcp 0.1.0 → 0.1.1

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.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ // Kept outside dist so a damaged runtime can still be inspected with Node alone.
3
+ import { readFileSync, existsSync, lstatSync } from 'node:fs';
4
+ import { resolve, join } from 'node:path';
5
+ import { createHash } from 'node:crypto';
6
+ const root = resolve(process.argv[2]);
7
+ const errors = [];
8
+ const read = (path) => JSON.parse(readFileSync(join(root, path), 'utf8'));
9
+ let manifest;
10
+ try {
11
+ manifest = read('dist/integrity.json');
12
+ } catch {
13
+ errors.push(
14
+ 'Missing or invalid dist/integrity.json; reinstall this plugin from the built npm package.',
15
+ );
16
+ }
17
+ const files = {};
18
+ const required = [
19
+ 'dist/cli.mjs',
20
+ 'dist/mcp.mjs',
21
+ 'dist/hook.mjs',
22
+ 'dist/daemon.mjs',
23
+ 'bin/cmdr',
24
+ 'bin/cmdr-node',
25
+ 'bin/cmdr-mcp',
26
+ 'bin/cmdr-hook',
27
+ 'bin/cmdr-daemon',
28
+ 'bin/cmdr-check.mjs',
29
+ 'hooks/hooks.json',
30
+ '.mcp.json',
31
+ '.claude-plugin/plugin.json',
32
+ '.codex-plugin/plugin.json',
33
+ '.zcode-plugin/plugin.json',
34
+ ];
35
+ for (const path of new Set([...required, ...Object.keys(manifest?.files || {})])) {
36
+ if (
37
+ !required.includes(path) &&
38
+ !path.startsWith('skills/') &&
39
+ !path.startsWith('commands/') &&
40
+ path !== 'THIRD_PARTY_NOTICES.txt'
41
+ ) {
42
+ errors.push(`Unexpected manifest path: ${path}`);
43
+ continue;
44
+ }
45
+ if (path.split('/').some((part) => part === '..' || part === '')) {
46
+ errors.push('Invalid manifest path');
47
+ continue;
48
+ }
49
+ try {
50
+ const full = join(root, path);
51
+ const linked = path
52
+ .split('/')
53
+ .some((_, i, parts) => lstatSync(join(root, ...parts.slice(0, i + 1))).isSymbolicLink());
54
+ const digest = createHash('sha256').update(readFileSync(full)).digest('hex');
55
+ files[path] = !linked && manifest?.files?.[path] === digest;
56
+ } catch {
57
+ files[path] = false;
58
+ }
59
+ if (!files[path]) errors.push(`Missing, modified or linked asset: ${path}`);
60
+ }
61
+ const versions = {};
62
+ for (const host of ['claude', 'codex', 'zcode']) {
63
+ try {
64
+ versions[host] = read(`.${host}-plugin/plugin.json`).version;
65
+ if (versions[host] !== manifest?.version) errors.push(`${host} version differs from build`);
66
+ } catch {
67
+ errors.push(`Invalid ${host} manifest`);
68
+ }
69
+ }
70
+ let hooks = {};
71
+ try {
72
+ hooks = read('hooks/hooks.json').hooks;
73
+ } catch {
74
+ errors.push('Invalid hooks configuration');
75
+ }
76
+ console.log(
77
+ JSON.stringify(
78
+ {
79
+ ok: errors.length === 0,
80
+ plugin_root: root,
81
+ version: manifest?.version,
82
+ node: process.version,
83
+ files,
84
+ versions,
85
+ hooks: {
86
+ configured: Object.keys(hooks || {}),
87
+ observed: 'unknown; configuration alone does not prove execution',
88
+ },
89
+ errors,
90
+ repair:
91
+ 'npm install -g cmdr-mcp; register its package root, then refresh/reinstall the host plugin cache and start a new session. For a source checkout run npm ci && npm run build at repository root. Do not link dist from another installation.',
92
+ },
93
+ null,
94
+ 2,
95
+ ),
96
+ );
97
+ process.exitCode = errors.length ? 1 : 0;
@@ -1,4 +1,12 @@
1
1
  #!/bin/sh
2
- CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
2
+ CMDR_SCRIPT=$0
3
+ while [ -L "$CMDR_SCRIPT" ]; do
4
+ CMDR_LINK=$(readlink "$CMDR_SCRIPT") || exit 1
5
+ case "$CMDR_LINK" in
6
+ /*) CMDR_SCRIPT=$CMDR_LINK ;;
7
+ *) CMDR_SCRIPT=$(dirname -- "$CMDR_SCRIPT")/$CMDR_LINK ;;
8
+ esac
9
+ done
10
+ CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$CMDR_SCRIPT")" && pwd)
3
11
  export CMDR_BIN_DIR
4
12
  exec "$CMDR_BIN_DIR/cmdr-node" daemon "$@"
@@ -1,4 +1,12 @@
1
1
  #!/bin/sh
2
- CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
2
+ CMDR_SCRIPT=$0
3
+ while [ -L "$CMDR_SCRIPT" ]; do
4
+ CMDR_LINK=$(readlink "$CMDR_SCRIPT") || exit 1
5
+ case "$CMDR_LINK" in
6
+ /*) CMDR_SCRIPT=$CMDR_LINK ;;
7
+ *) CMDR_SCRIPT=$(dirname -- "$CMDR_SCRIPT")/$CMDR_LINK ;;
8
+ esac
9
+ done
10
+ CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$CMDR_SCRIPT")" && pwd)
3
11
  export CMDR_BIN_DIR
4
12
  exec "$CMDR_BIN_DIR/cmdr-node" hook "$@"
@@ -1,4 +1,12 @@
1
1
  #!/bin/sh
2
- CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
2
+ CMDR_SCRIPT=$0
3
+ while [ -L "$CMDR_SCRIPT" ]; do
4
+ CMDR_LINK=$(readlink "$CMDR_SCRIPT") || exit 1
5
+ case "$CMDR_LINK" in
6
+ /*) CMDR_SCRIPT=$CMDR_LINK ;;
7
+ *) CMDR_SCRIPT=$(dirname -- "$CMDR_SCRIPT")/$CMDR_LINK ;;
8
+ esac
9
+ done
10
+ CMDR_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "$CMDR_SCRIPT")" && pwd)
3
11
  export CMDR_BIN_DIR
4
12
  exec "$CMDR_BIN_DIR/cmdr-node" mcp "$@"
@@ -3,6 +3,20 @@
3
3
  CMDR_ENTRY=$1
4
4
  shift
5
5
  CMDR_RUNTIME_HOME=${CMDR_HOME:-"$HOME/.cmdr"}
6
+ cmdr_failure() {
7
+ # One fixed-size record per failure type, rate limited across hook processes.
8
+ (umask 077
9
+ CMDR_LOG_DIR="$CMDR_RUNTIME_HOME/logs/diagnostics"
10
+ mkdir -p "$CMDR_LOG_DIR" || exit 0
11
+ CMDR_LOG_FILE="$CMDR_LOG_DIR/bootstrap-$1.json"
12
+ CMDR_NOW=$(date +%s)
13
+ CMDR_LAST=$(cat "$CMDR_LOG_DIR/bootstrap-$1.time" 2>/dev/null || true)
14
+ case "$CMDR_LAST" in ''|*[!0-9]*) CMDR_LAST=0 ;; esac
15
+ [ $((CMDR_NOW - CMDR_LAST)) -lt 60 ] && exit 0
16
+ printf '%s\n' "$CMDR_NOW" > "$CMDR_LOG_DIR/bootstrap-$1.time"
17
+ printf '{"code":"bootstrap-%s","at":%s000}\n' "$1" "$CMDR_NOW" > "$CMDR_LOG_FILE"
18
+ ) 2>/dev/null || true
19
+ }
6
20
  CMDR_NODE=''
7
21
  if [ -r "$CMDR_RUNTIME_HOME/node-path" ]; then
8
22
  IFS= read -r CMDR_NODE < "$CMDR_RUNTIME_HOME/node-path"
@@ -22,13 +36,22 @@ if [ -z "$CMDR_NODE" ]; then
22
36
  fi
23
37
  fi
24
38
  if [ -z "$CMDR_NODE" ]; then
39
+ cmdr_failure node
25
40
  [ "$CMDR_ENTRY" = hook ] && exit 0
26
41
  echo 'cmdr requires Node.js >=22.5 (24 recommended). Install Node and retry.' >&2
27
42
  exit 1
28
43
  fi
29
44
  if [ ! -f "$CMDR_BIN_DIR/../dist/$CMDR_ENTRY.mjs" ]; then
45
+ cmdr_failure runtime
30
46
  [ "$CMDR_ENTRY" = hook ] && exit 0
31
- echo 'cmdr runtime is not built. Run npm ci && npm run build in the repository, or install the built npm package.' >&2
47
+ if [ "$CMDR_ENTRY" = cli ] && [ "$1" = doctor ] && [ -f "$CMDR_BIN_DIR/cmdr-check.mjs" ]; then
48
+ exec "$CMDR_NODE" "$CMDR_BIN_DIR/cmdr-check.mjs" "$CMDR_BIN_DIR/.."
49
+ fi
50
+ echo 'cmdr runtime is not built. Run npm ci && npm run build in the repository, or install cmdr-mcp from npm, refresh/reinstall the host plugin cache and start a new session. Do not symlink another installation. Inspect this plugin with a working cmdr doctor --plugin-root PATH.' >&2
32
51
  exit 1
33
52
  fi
53
+ if [ "$CMDR_ENTRY" = hook ]; then
54
+ "$CMDR_NODE" --experimental-sqlite --disable-warning=ExperimentalWarning "$CMDR_BIN_DIR/../dist/hook.mjs" "$@" 2>/dev/null || cmdr_failure runtime
55
+ exit 0
56
+ fi
34
57
  exec "$CMDR_NODE" --experimental-sqlite --disable-warning=ExperimentalWarning "$CMDR_BIN_DIR/../dist/$CMDR_ENTRY.mjs" "$@"
@@ -5,3 +5,5 @@ argument-hint: <name>
5
5
  Call the cmdr join tool with squad_name="$ARGUMENTS". This is an atomic find-or-create operation; do not list then create. If no name was provided, ask for one. Do not invent a name.
6
6
  If the squad is orphaned, ask whether to take over as commander or join as executor and then use explicit role and squad ID.
7
7
  Follow the returned protocol_hint. An executor reports ready with cwd, capabilities and context before replying. Translate user_reply into the user's language, keep the join line unchanged, and reply with only that text. Then use read(wait=me.recommended_wait) to participate.
8
+
9
+ If cmdr tools are unavailable, follow the using-cmdr installation diagnostics and member CLI fallback. Do not invent native session IDs or access raw sockets.