@monoes/monomindcli 2.16.3 → 2.16.4
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/.claude/commands/mastermind/createorg.md +2 -2
- package/.claude/commands/mastermind/help.md +5 -11
- package/.claude/commands/mastermind/master.md +1 -2
- package/.claude/commands/mastermind/orgs.md +1 -1
- package/.claude/commands/mastermind/orgstatus.md +1 -1
- package/.claude/commands/mastermind/runorg.md +2 -2
- package/.claude/commands/mastermind/stoporg.md +7 -11
- package/.claude/helpers/agent-registry.cjs +434 -0
- package/.claude/helpers/handlers/pick-core.cjs +20 -2
- package/.claude/helpers/handlers/session-restore-handler.cjs +4 -1
- package/.claude/helpers/jev-catalog.cjs +7 -3
- package/.claude/helpers/jev-picker.cjs +3 -1
- package/.claude/helpers/pick-rank.cjs +90 -4
- package/.claude/skills/mastermind-agents/SKILL.md +31 -38
- package/.claude/skills/mastermind-createorg/SKILL.md +3 -3
- package/.claude/skills/mastermind-env/SKILL.md +4 -18
- package/.claude/skills/mastermind-inbox/SKILL.md +31 -52
- package/.claude/skills/mastermind-instance/SKILL.md +7 -5
- package/.claude/skills/mastermind-new-agent/SKILL.md +3 -3
- package/.claude/skills/mastermind-org-settings/SKILL.md +3 -4
- package/.claude/skills/mastermind-orgs/SKILL.md +26 -32
- package/.claude/skills/mastermind-orgstatus/SKILL.md +49 -140
- package/.claude/skills/mastermind-runorg/SKILL.md +13 -13
- package/.claude/skills/mastermind-stoporg/SKILL.md +7 -62
- package/.claude/skills/mastermind-tasks/SKILL.md +6 -8
- package/README.md +28 -43
- package/dist/src/agents/registry-builder.d.ts +57 -21
- package/dist/src/agents/registry-builder.d.ts.map +1 -1
- package/dist/src/agents/registry-builder.js +41 -341
- package/dist/src/agents/registry-builder.js.map +1 -1
- package/dist/src/agents/registry-freshness.d.ts +4 -4
- package/dist/src/agents/registry-freshness.d.ts.map +1 -1
- package/dist/src/agents/registry-freshness.js +11 -60
- package/dist/src/agents/registry-freshness.js.map +1 -1
- package/dist/src/commands/doc.d.ts.map +1 -1
- package/dist/src/commands/doc.js +6 -15
- package/dist/src/commands/doc.js.map +1 -1
- package/dist/src/commands/doctor-pick-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-pick-checks.js +9 -41
- package/dist/src/commands/doctor-pick-checks.js.map +1 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +7 -5
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/hooks-routing-commands.d.ts.map +1 -1
- package/dist/src/commands/hooks-routing-commands.js +4 -3
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/init-upgrade.d.ts.map +1 -1
- package/dist/src/commands/init-upgrade.js +4 -0
- package/dist/src/commands/init-upgrade.js.map +1 -1
- package/dist/src/commands/init-wizard.d.ts.map +1 -1
- package/dist/src/commands/init-wizard.js +7 -2
- package/dist/src/commands/init-wizard.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +16 -27
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/monograph.d.ts.map +1 -1
- package/dist/src/commands/monograph.js +16 -21
- package/dist/src/commands/monograph.js.map +1 -1
- package/dist/src/commands/org.js +1 -1
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/decision/jev.d.ts +4 -0
- package/dist/src/decision/jev.d.ts.map +1 -1
- package/dist/src/decision/jev.js.map +1 -1
- package/dist/src/decision/pick-real.d.ts +93 -0
- package/dist/src/decision/pick-real.d.ts.map +1 -0
- package/dist/src/decision/pick-real.js +253 -0
- package/dist/src/decision/pick-real.js.map +1 -0
- package/dist/src/decision/picks.d.ts +1 -0
- package/dist/src/decision/picks.d.ts.map +1 -1
- package/dist/src/decision/picks.js +1 -0
- package/dist/src/decision/picks.js.map +1 -1
- package/dist/src/init/embeddings-step.d.ts +23 -0
- package/dist/src/init/embeddings-step.d.ts.map +1 -0
- package/dist/src/init/embeddings-step.js +53 -0
- package/dist/src/init/embeddings-step.js.map +1 -0
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +9 -1
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/helpers-generator.d.ts.map +1 -1
- package/dist/src/init/helpers-generator.js +4 -0
- package/dist/src/init/helpers-generator.js.map +1 -1
- package/dist/src/init/project-indexes.d.ts +19 -0
- package/dist/src/init/project-indexes.d.ts.map +1 -0
- package/dist/src/init/project-indexes.js +44 -0
- package/dist/src/init/project-indexes.js.map +1 -0
- package/dist/src/init/shared.d.ts +7 -2
- package/dist/src/init/shared.d.ts.map +1 -1
- package/dist/src/init/shared.js +7 -6
- package/dist/src/init/shared.js.map +1 -1
- package/dist/src/init/types.d.ts +13 -2
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js +11 -3
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/init/upgrade.d.ts +3 -0
- package/dist/src/init/upgrade.d.ts.map +1 -1
- package/dist/src/init/upgrade.js +21 -6
- package/dist/src/init/upgrade.js.map +1 -1
- package/dist/src/init/write-antigravity.d.ts +1 -0
- package/dist/src/init/write-antigravity.d.ts.map +1 -1
- package/dist/src/init/write-antigravity.js +19 -1
- package/dist/src/init/write-antigravity.js.map +1 -1
- package/dist/src/init/write-claude.d.ts.map +1 -1
- package/dist/src/init/write-claude.js +8 -5
- package/dist/src/init/write-claude.js.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.js +14 -5
- package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +4 -3
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/monograph/build-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph/build-tools.js +16 -9
- package/dist/src/mcp-tools/monograph/build-tools.js.map +1 -1
- package/dist/src/memory/embedding-operations.d.ts +0 -4
- package/dist/src/memory/embedding-operations.d.ts.map +1 -1
- package/dist/src/memory/embedding-operations.js +11 -85
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +7 -0
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +15 -0
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/orgrt/bash-timeout.d.ts +17 -0
- package/dist/src/orgrt/bash-timeout.d.ts.map +1 -1
- package/dist/src/orgrt/bash-timeout.js +15 -0
- package/dist/src/orgrt/bash-timeout.js.map +1 -1
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +5 -2
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-match.d.ts +3 -1
- package/dist/src/orgrt/task-match.d.ts.map +1 -1
- package/dist/src/orgrt/task-match.js +12 -4
- package/dist/src/orgrt/task-match.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Define and save an autonomous agent organization (Org Runtime
|
|
1
|
+
<!-- Define and save an autonomous agent organization (Org Runtime) — roles and hierarchy. Suggest or confirm roles, then persist the org config for `monomind org run`/`serve`. -->
|
|
2
2
|
|
|
3
3
|
**If $ARGUMENTS is empty:** Output the following and wait.
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**MASTERMIND: CREATE ORG**
|
|
8
8
|
|
|
9
|
-
An org is a named, persistent agent team, run by the Org Runtime
|
|
9
|
+
An org is a named, persistent agent team, run by the Org Runtime daemon — not a one-shot Mastermind run and not a Task-tool-spawned boss. Once created, every role in the config starts as its own live agent session the moment you run the org; roles message each other directly (no shared task board) and the org keeps running until stopped.
|
|
10
10
|
|
|
11
11
|
Use orgs when the work is ongoing, not single-shot. A content team that ships 10 posts a month. A research squad that runs competitive scans weekly. A dev team with a permanent backlog.
|
|
12
12
|
|
|
@@ -18,23 +18,17 @@ Quick reference for all Mastermind skills and CLI commands available in this pro
|
|
|
18
18
|
| `mastermind:swarm` | Swarm coordination reference — topologies, strategies, init patterns |
|
|
19
19
|
| `mastermind:memory` | Memory CLI quick reference |
|
|
20
20
|
|
|
21
|
-
## Org Runtime
|
|
21
|
+
## Org Runtime
|
|
22
22
|
|
|
23
23
|
| Command | Description |
|
|
24
24
|
|-------|-------------|
|
|
25
|
-
| `/mastermind:createorg` | Define and save an org
|
|
26
|
-
| `/mastermind:runorg` | Start a saved org via the Org Runtime
|
|
27
|
-
| `/mastermind:stoporg` | Stop a running
|
|
25
|
+
| `/mastermind:createorg` | Define and save an org |
|
|
26
|
+
| `/mastermind:runorg` | Start a saved org via the Org Runtime daemon (`monomind org run`/`serve`) — converts legacy-format config files first |
|
|
27
|
+
| `/mastermind:stoporg` | Stop a running org (`monomind org stop`) |
|
|
28
28
|
| `/mastermind:orgstatus` | Detailed status for a single org |
|
|
29
29
|
| `/mastermind:orgs` | List all saved orgs |
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| Command | Description |
|
|
34
|
-
|-------|-------------|
|
|
35
|
-
| `/mastermind:runorgv1` | Legacy prompt-orchestrated org runner — superseded by `/mastermind:runorg` |
|
|
36
|
-
| `/mastermind:approvev1` | Legacy approval queue for v1 orgs — v2 approvals arrive as dashboard question events instead |
|
|
37
|
-
| `/mastermind:heartbeatv1` | Legacy manual agent wake-up for v1 orgs — v2 roles are live SDK sessions with no wake cycle |
|
|
31
|
+
Pending tool approvals from a running org: `monomind org approvals <org>`, then `monomind org approve` / `monomind org deny` (or the dashboard's Human Input tab).
|
|
38
32
|
|
|
39
33
|
## Core CLI Commands
|
|
40
34
|
|
|
@@ -249,8 +249,7 @@ Describe your goal. Mastermind identifies the relevant domains, spawns specialis
|
|
|
249
249
|
|
|
250
250
|
**Persistent agent orgs** — named teams that coordinate across sessions
|
|
251
251
|
`/mastermind:createorg` — define an org: roles, hierarchy, goal
|
|
252
|
-
`/mastermind:runorg` — start a saved org via the Org Runtime
|
|
253
|
-
`/mastermind:approvev1` — review and action pending approval requests from running org agents (v1 orgs only — v2 approvals arrive in the dashboard Human Input tab)
|
|
252
|
+
`/mastermind:runorg` — start a saved org via the Org Runtime daemon (converts legacy-format config files first); pending tool approvals: `monomind org approvals <org>` or the dashboard Human Input tab
|
|
254
253
|
|
|
255
254
|
**Autonomous & advanced**
|
|
256
255
|
`/mastermind:repeat` — wrap any mastermind command in a loop (`--tillend` supported)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- List all saved orgs with their status, schedule
|
|
1
|
+
<!-- List all saved orgs with their runtime status, schedule, and last run time. Flags crashed runs and legacy-format config files. -->
|
|
2
2
|
|
|
3
3
|
**If $ARGUMENTS is empty:** Execute the listing below directly.
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Show detailed status for a single org —
|
|
1
|
+
<!-- Show detailed status for a single org — runtime state, schedule, config health, pending approvals, recent activity, and roles. -->
|
|
2
2
|
|
|
3
3
|
**If $ARGUMENTS is empty:** Output the following and wait.
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Start a saved org via the Org Runtime
|
|
1
|
+
<!-- Start a saved org via the Org Runtime daemon (monomind org run/serve). Validates the config, converts legacy-format config files, starts the daemon, and confirms liveness. -->
|
|
2
2
|
|
|
3
3
|
**If $ARGUMENTS is empty:** Output the following and wait.
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**MASTERMIND: RUN ORG**
|
|
8
8
|
|
|
9
|
-
Running an org starts it through the Org Runtime
|
|
9
|
+
Running an org starts it through the Org Runtime daemon. Every role becomes
|
|
10
10
|
a live SDK session; the daemon forwards dashboard events itself — no boss
|
|
11
11
|
agent, no monotask board, no manual curl emissions.
|
|
12
12
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Stop a running
|
|
1
|
+
<!-- Stop a running org. Wraps `monomind org stop`, which writes the stop file the org daemon polls — the daemon exits within about 2s. -->
|
|
2
2
|
|
|
3
3
|
**If $ARGUMENTS is empty:** Output the following and wait.
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**MASTERMIND: STOP ORG**
|
|
8
8
|
|
|
9
|
-
Stops a
|
|
9
|
+
Stops a running org cleanly. Runs `monomind org stop <name>`, which writes `.monomind/orgs/<name>/stop`; the org daemon polls for that file and exits within about 2 seconds.
|
|
10
10
|
|
|
11
11
|
**Usage:**
|
|
12
12
|
|
|
@@ -23,16 +23,12 @@ Stops a scheduled org loop cleanly. The org's `status` is set to `"stopped"` —
|
|
|
23
23
|
|
|
24
24
|
**Lifecycle:**
|
|
25
25
|
```
|
|
26
|
-
stopped → (runorg)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
paused → (
|
|
26
|
+
stopped → (runorg / monomind org run) → running
|
|
27
|
+
running → (stoporg / monomind org stop) → stopped
|
|
28
|
+
running → (monomind org pause) → paused
|
|
29
|
+
paused → (monomind org resume) → running
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
**Note:** v2 orgs (the default) are stopped via `monomind org stop <name>` — this
|
|
33
|
-
command handles both: it routes v2 orgs to the CLI and legacy `.loop` orgs
|
|
34
|
-
through the v1 status-flip path.
|
|
35
|
-
|
|
36
32
|
Your orgs (with live runtime status):
|
|
37
33
|
|
|
38
34
|
```bash
|
|
@@ -48,7 +44,7 @@ npx -y monomind@latest org list 2>/dev/null || echo "(none — run /mastermind:c
|
|
|
48
44
|
Parse `$ARGUMENTS` for:
|
|
49
45
|
- `--org <name>` → org_name = <name>
|
|
50
46
|
|
|
51
|
-
If `--org` is not provided, list orgs
|
|
47
|
+
If `--org` is not provided, list orgs and ask which to stop.
|
|
52
48
|
|
|
53
49
|
Verify the org file exists:
|
|
54
50
|
```bash
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* The agent registry (.monomind/registry.json) — ONE implementation shared by
|
|
5
|
+
* the CLI (src/agents/registry-builder.ts and registry-freshness.ts load this
|
|
6
|
+
* file) and the SessionStart hook (handlers/pick-core.cjs), so an agent added
|
|
7
|
+
* to ~/.claude/agents or .claude/agents is indexed at the next session start
|
|
8
|
+
* without running the CLI, and both sides parse frontmatter the same way.
|
|
9
|
+
*
|
|
10
|
+
* Roots, in precedence order (the first definition of a slug wins):
|
|
11
|
+
* extra $MONOMIND_EXTRA_AGENT_PATHS (':'-separated), else a sibling
|
|
12
|
+
* ../agency-agents directory — canonical, wins over the project
|
|
13
|
+
* project <root>/.claude/agents
|
|
14
|
+
* user ~/.claude/agents — Claude Code loads these in every project, so
|
|
15
|
+
* they are valid Task subagent_types. As in Claude Code, a project
|
|
16
|
+
* (or extra) agent wins: a user agent whose slug or frontmatter
|
|
17
|
+
* `name` is already defined is left out (listed under `shadowed`).
|
|
18
|
+
*
|
|
19
|
+
* User entries record `~/.claude/agents/...` as filePath, never the absolute
|
|
20
|
+
* home path. The registry is rebuilt when missing or older than any agent file
|
|
21
|
+
* in those roots (an mtime scan, no parsing) or this builder.
|
|
22
|
+
*
|
|
23
|
+
* Usage: node .claude/helpers/agent-registry.cjs [projectRoot]
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
var fs = require('fs');
|
|
27
|
+
var os = require('os');
|
|
28
|
+
var path = require('path');
|
|
29
|
+
|
|
30
|
+
/** Directories to skip during the scan. `reengineer-squad` is a repo-only
|
|
31
|
+
* squad the package does not ship, and its `tester` would shadow the core
|
|
32
|
+
* tester. */
|
|
33
|
+
var SKIP_DIRS = new Set(['schemas', 'ephemeral', 'reengineer-squad']);
|
|
34
|
+
var MAX_AGENT_BYTES = 512 * 1024;
|
|
35
|
+
var USER_LABEL = '~/.claude/agents';
|
|
36
|
+
|
|
37
|
+
function homeDir(opts) {
|
|
38
|
+
return (opts && opts.home) || os.homedir();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Every `.md` under `root` (recursively), skipping SKIP_DIRS and `._` forks. */
|
|
42
|
+
function collectMdFiles(root) {
|
|
43
|
+
var results = [];
|
|
44
|
+
var entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = fs.readdirSync(root);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
for (var i = 0; i < entries.length; i++) {
|
|
51
|
+
var entry = entries[i];
|
|
52
|
+
var full = path.join(root, entry);
|
|
53
|
+
var stat;
|
|
54
|
+
try {
|
|
55
|
+
stat = fs.statSync(full);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (stat.isDirectory()) {
|
|
60
|
+
if (SKIP_DIRS.has(entry)) continue;
|
|
61
|
+
results.push.apply(results, collectMdFiles(full));
|
|
62
|
+
} else if (stat.isFile() && path.extname(entry) === '.md' && !entry.startsWith('._')) {
|
|
63
|
+
results.push(full);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function unquote(v) {
|
|
70
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) return v.slice(1, -1);
|
|
71
|
+
return v;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Nested YAML list items under a key whose value was empty
|
|
76
|
+
* (`triggers:\n - pattern: ...`): a string array when every item is a plain
|
|
77
|
+
* scalar, an array of objects when items hold `key: value` pairs, null when
|
|
78
|
+
* there are no list items.
|
|
79
|
+
*/
|
|
80
|
+
function parseNestedYamlList(lines, startIdx, parentIndent) {
|
|
81
|
+
var objects = [];
|
|
82
|
+
var simpleItems = [];
|
|
83
|
+
var current = null;
|
|
84
|
+
var consumed = 0;
|
|
85
|
+
var hasObjects = false;
|
|
86
|
+
for (var i = startIdx; i < lines.length; i++) {
|
|
87
|
+
var line = lines[i];
|
|
88
|
+
var trimmed = line.trim();
|
|
89
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
90
|
+
consumed++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
var indent = line.length - line.trimStart().length;
|
|
94
|
+
if (indent <= parentIndent) break;
|
|
95
|
+
if (trimmed.startsWith('- ')) {
|
|
96
|
+
var itemContent = trimmed.slice(2);
|
|
97
|
+
var colon = itemContent.indexOf(': ');
|
|
98
|
+
if (colon !== -1) {
|
|
99
|
+
hasObjects = true;
|
|
100
|
+
current = {};
|
|
101
|
+
current[itemContent.slice(0, colon).trim()] = unquote(itemContent.slice(colon + 2).trim());
|
|
102
|
+
objects.push(current);
|
|
103
|
+
} else {
|
|
104
|
+
simpleItems.push(unquote(itemContent.trim()));
|
|
105
|
+
}
|
|
106
|
+
consumed++;
|
|
107
|
+
} else if (current && hasObjects) {
|
|
108
|
+
var c = trimmed.indexOf(': ');
|
|
109
|
+
if (c !== -1) current[trimmed.slice(0, c).trim()] = unquote(trimmed.slice(c + 2).trim());
|
|
110
|
+
consumed++;
|
|
111
|
+
} else {
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (hasObjects && objects.length > 0) return { value: objects, consumed: consumed };
|
|
116
|
+
if (simpleItems.length > 0) return { value: simpleItems, consumed: consumed };
|
|
117
|
+
return { value: null, consumed: consumed };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The leading `---` frontmatter block as a flat key → value map (scalars,
|
|
121
|
+
* booleans, `[a, b]` and nested `- ` lists, `|`/`>` block scalars). */
|
|
122
|
+
function parseFrontmatter(content) {
|
|
123
|
+
var match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
124
|
+
if (!match) return {};
|
|
125
|
+
var result = {};
|
|
126
|
+
var lines = match[1].split('\n');
|
|
127
|
+
for (var i = 0; i < lines.length; i++) {
|
|
128
|
+
var line = lines[i];
|
|
129
|
+
var trimmed = line.trim();
|
|
130
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
131
|
+
var colonIdx = trimmed.indexOf(':');
|
|
132
|
+
if (colonIdx === -1) continue;
|
|
133
|
+
var key = trimmed.slice(0, colonIdx).trim();
|
|
134
|
+
var value = trimmed.slice(colonIdx + 1).trim();
|
|
135
|
+
|
|
136
|
+
var blockScalar = /^([|>])[+-]?$/.exec(value);
|
|
137
|
+
if (blockScalar) {
|
|
138
|
+
var blockIndent = line.length - line.trimStart().length;
|
|
139
|
+
var body = [];
|
|
140
|
+
while (i + 1 < lines.length) {
|
|
141
|
+
var next = lines[i + 1];
|
|
142
|
+
if (next.trim() && next.length - next.trimStart().length <= blockIndent) break;
|
|
143
|
+
body.push(next.trim());
|
|
144
|
+
i++;
|
|
145
|
+
}
|
|
146
|
+
result[key] = body.join(blockScalar[1] === '|' ? '\n' : ' ').trim();
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (value === '') {
|
|
151
|
+
var nested = parseNestedYamlList(lines, i + 1, line.length - line.trimStart().length);
|
|
152
|
+
if (nested.value !== null) {
|
|
153
|
+
result[key] = nested.value;
|
|
154
|
+
i += nested.consumed;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
160
|
+
value = value
|
|
161
|
+
.slice(1, -1)
|
|
162
|
+
.split(',')
|
|
163
|
+
.map(function (s) { return s.trim().replace(/^["']|["']$/g, ''); })
|
|
164
|
+
.filter(Boolean);
|
|
165
|
+
} else if (value === 'true') value = true;
|
|
166
|
+
else if (value === 'false') value = false;
|
|
167
|
+
else value = unquote(value);
|
|
168
|
+
result[key] = value;
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The first argument that is not undefined/null (a `??` chain). */
|
|
174
|
+
function first() {
|
|
175
|
+
for (var i = 0; i < arguments.length; i++) if (arguments[i] !== undefined && arguments[i] !== null) return arguments[i];
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function toStringArray(val) {
|
|
180
|
+
if (Array.isArray(val)) return val.map(String);
|
|
181
|
+
if (typeof val === 'string' && val.length > 0) return [val];
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parseTriggers(val) {
|
|
186
|
+
if (!val) return [];
|
|
187
|
+
var arr = Array.isArray(val) ? val : [val];
|
|
188
|
+
return arr.map(function (t) {
|
|
189
|
+
if (typeof t === 'object' && t !== null && 'pattern' in t) {
|
|
190
|
+
return { pattern: String(t.pattern), mode: String(first(t.mode, 'glob')) };
|
|
191
|
+
}
|
|
192
|
+
return { pattern: String(t), mode: 'glob' };
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function slugFromFilename(file) {
|
|
197
|
+
return path.basename(file, path.extname(file))
|
|
198
|
+
.toLowerCase()
|
|
199
|
+
.replace(/\s+/g, '-')
|
|
200
|
+
.replace(/[^a-z0-9-]/g, '');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function categoryFromPath(file, root) {
|
|
204
|
+
var parts = path.relative(root, file).split(path.sep);
|
|
205
|
+
return parts.length > 1 ? parts[0] : 'default';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function isDir(p) {
|
|
209
|
+
try { return fs.statSync(p).isDirectory(); } catch (e) { return false; }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The agent roots for project `cwd`, in precedence order: extras, the
|
|
214
|
+
* project's .claude/agents, then ~/.claude/agents (opts.user === false leaves
|
|
215
|
+
* it out; opts.home overrides the home directory). When cwd is the home
|
|
216
|
+
* directory the two .claude/agents are one directory, listed once as `user`.
|
|
217
|
+
*/
|
|
218
|
+
function computeAgentRoots(cwd, opts) {
|
|
219
|
+
var env = (opts && opts.env) || process.env;
|
|
220
|
+
var extras = env.MONOMIND_EXTRA_AGENT_PATHS ? env.MONOMIND_EXTRA_AGENT_PATHS.split(':').filter(Boolean) : [];
|
|
221
|
+
var sibling = path.join(cwd, '..', 'agency-agents');
|
|
222
|
+
if (extras.length === 0 && isDir(sibling)) extras.push(sibling);
|
|
223
|
+
var roots = extras.map(function (dir) { return { dir: dir, origin: 'extra' }; });
|
|
224
|
+
var projectDir = path.join(cwd, '.claude', 'agents');
|
|
225
|
+
var userDir = path.join(homeDir(opts), '.claude', 'agents');
|
|
226
|
+
var withUser = !(opts && opts.user === false);
|
|
227
|
+
if (!withUser || path.resolve(projectDir) !== path.resolve(userDir)) roots.push({ dir: projectDir, origin: 'project' });
|
|
228
|
+
if (withUser) roots.push({ dir: userDir, origin: 'user', label: USER_LABEL });
|
|
229
|
+
return roots;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** A root given as a plain path is a project-local definition directory. */
|
|
233
|
+
function asRoot(r) {
|
|
234
|
+
return typeof r === 'string' ? { dir: r, origin: 'project' } : r;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Builds the registry from `roots` (first root wins a slug; a `user` agent
|
|
239
|
+
* also loses to any earlier agent with the same frontmatter `name` or slug),
|
|
240
|
+
* writing it to `outputPath` when given. opts.base: the directory project and
|
|
241
|
+
* extra filePaths are relative to (default cwd).
|
|
242
|
+
*/
|
|
243
|
+
function buildUnifiedRegistry(roots, outputPath, opts) {
|
|
244
|
+
var now = new Date().toISOString();
|
|
245
|
+
var base = (opts && opts.base) || process.cwd();
|
|
246
|
+
var seen = new Map();
|
|
247
|
+
var names = new Set();
|
|
248
|
+
var dupes = new Map();
|
|
249
|
+
var shadowed = [];
|
|
250
|
+
var counts = { project: 0, user: 0, extra: 0 };
|
|
251
|
+
(roots || []).map(asRoot).forEach(function (root) {
|
|
252
|
+
var files = collectMdFiles(root.dir);
|
|
253
|
+
files.forEach(function (file) {
|
|
254
|
+
var content;
|
|
255
|
+
try {
|
|
256
|
+
if (fs.statSync(file).size > MAX_AGENT_BYTES) return;
|
|
257
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
258
|
+
} catch (e) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
var fm = parseFrontmatter(content);
|
|
262
|
+
var slug = (typeof fm.slug === 'string' && fm.slug) || slugFromFilename(file);
|
|
263
|
+
var name = (typeof fm.name === 'string' && fm.name) || slug;
|
|
264
|
+
var filePath = root.label
|
|
265
|
+
? root.label + '/' + path.relative(root.dir, file).split(path.sep).join('/')
|
|
266
|
+
: path.isAbsolute(file) ? path.relative(base, file) : file;
|
|
267
|
+
var prior = seen.get(slug);
|
|
268
|
+
if (root.origin === 'user' && (prior ? prior.origin !== 'user' : names.has(name) || names.has(slug))) {
|
|
269
|
+
shadowed.push({ slug: slug, name: name, filePath: filePath });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (prior) {
|
|
273
|
+
var d = dupes.get(slug) || { slug: slug, kept: prior.filePath, dropped: [] };
|
|
274
|
+
d.dropped.push(filePath);
|
|
275
|
+
dupes.set(slug, d);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
var str = function (v) { return typeof v === 'string' ? v : undefined; };
|
|
279
|
+
seen.set(slug, {
|
|
280
|
+
slug: slug,
|
|
281
|
+
name: name,
|
|
282
|
+
version: str(fm.version) || '0.0.0',
|
|
283
|
+
category: str(fm.category) || categoryFromPath(file, root.dir),
|
|
284
|
+
description: str(fm.description) || '',
|
|
285
|
+
whenToUse: str(first(fm.when_to_use, fm.whenToUse)),
|
|
286
|
+
tags: toStringArray(fm.tags),
|
|
287
|
+
vibe: str(fm.vibe),
|
|
288
|
+
// A `capability:` block is read flattened, so its `expertise:` list lands top-level
|
|
289
|
+
capabilities: toStringArray(first(fm.capabilities, fm.expertise)),
|
|
290
|
+
taskTypes: toStringArray(first(fm.taskTypes, fm['task-types'], fm.task_types)),
|
|
291
|
+
tools: toStringArray(fm.tools),
|
|
292
|
+
triggers: parseTriggers(fm.triggers),
|
|
293
|
+
deprecated: fm.deprecated === true,
|
|
294
|
+
deprecatedBy: str(fm.deprecatedBy),
|
|
295
|
+
dependencies: toStringArray(fm.dependencies),
|
|
296
|
+
origin: root.origin,
|
|
297
|
+
filePath: filePath,
|
|
298
|
+
registeredAt: now,
|
|
299
|
+
lastUpdated: now,
|
|
300
|
+
});
|
|
301
|
+
names.add(name);
|
|
302
|
+
names.add(slug);
|
|
303
|
+
counts[root.origin] = (counts[root.origin] || 0) + 1;
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
var agents = Array.from(seen.values());
|
|
307
|
+
var registry = {
|
|
308
|
+
version: '1.0.0',
|
|
309
|
+
generatedAt: now,
|
|
310
|
+
totalAgents: agents.length,
|
|
311
|
+
counts: counts,
|
|
312
|
+
agents: agents,
|
|
313
|
+
duplicates: Array.from(dupes.values()),
|
|
314
|
+
shadowed: shadowed,
|
|
315
|
+
};
|
|
316
|
+
if (outputPath) writeRegistryFile(outputPath, registry);
|
|
317
|
+
return registry;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function agentCountOnDisk(file) {
|
|
321
|
+
try {
|
|
322
|
+
var reg = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
323
|
+
return reg && Array.isArray(reg.agents) ? reg.agents.length : 0;
|
|
324
|
+
} catch (e) {
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Writes `registry` atomically — except an empty registry never replaces a
|
|
330
|
+
* non-empty one. Returns whether the file was written. */
|
|
331
|
+
function writeRegistryFile(file, registry) {
|
|
332
|
+
if (registry.agents.length === 0 && agentCountOnDisk(file) > 0) return false;
|
|
333
|
+
var tmp = file + '.' + process.pid + '.tmp';
|
|
334
|
+
fs.writeFileSync(tmp, JSON.stringify(registry, null, 2), 'utf-8');
|
|
335
|
+
fs.renameSync(tmp, file);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Newest mtime (ms) of any agent `.md` under `dirs`; directories count too,
|
|
340
|
+
* so a deleted file also marks the registry stale. 0 when there are none. */
|
|
341
|
+
function newestAgentMtime(dirs) {
|
|
342
|
+
var newest = 0;
|
|
343
|
+
function walk(dir) {
|
|
344
|
+
var entries;
|
|
345
|
+
try {
|
|
346
|
+
newest = Math.max(newest, fs.statSync(dir).mtimeMs);
|
|
347
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
348
|
+
} catch (e) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
entries.forEach(function (e) {
|
|
352
|
+
var full = path.join(dir, e.name);
|
|
353
|
+
if (e.isDirectory()) walk(full);
|
|
354
|
+
else if (e.isFile() && path.extname(e.name) === '.md') {
|
|
355
|
+
try { newest = Math.max(newest, fs.statSync(full).mtimeMs); } catch (err) { /* vanished */ }
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
(dirs || []).forEach(function (d) { walk(typeof d === 'string' ? d : d.dir); });
|
|
360
|
+
return newest;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function registryPath(root) {
|
|
364
|
+
return path.join(root, '.monomind', 'registry.json');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function mtime(p) {
|
|
368
|
+
try { return fs.statSync(p).mtimeMs; } catch (e) { return 0; }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** True when `root`'s registry is missing or older than an agent definition
|
|
372
|
+
* in any root (project, user, extra) or than this builder. */
|
|
373
|
+
function isStale(root, opts) {
|
|
374
|
+
var at = mtime(registryPath(root));
|
|
375
|
+
if (at === 0) return true;
|
|
376
|
+
return Math.max(newestAgentMtime(computeAgentRoots(root, opts)), mtime(__filename)) > at;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Rebuilds `root`'s registry when stale; the built registry, or null when it
|
|
380
|
+
* was fresh or the build failed. Never throws. */
|
|
381
|
+
function ensure(root, opts) {
|
|
382
|
+
try {
|
|
383
|
+
if (!isStale(root, opts)) return null;
|
|
384
|
+
fs.mkdirSync(path.join(root, '.monomind'), { recursive: true });
|
|
385
|
+
return buildUnifiedRegistry(computeAgentRoots(root, opts), registryPath(root), { base: root });
|
|
386
|
+
} catch (e) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* The nearest directory at or above `cwd` holding `.claude/agents` or
|
|
393
|
+
* `.monomind`. The walk stops at the git root (inclusive) and never returns
|
|
394
|
+
* the home directory itself, whose ~/.monomind is the user's global state.
|
|
395
|
+
* Null when no project is found.
|
|
396
|
+
*/
|
|
397
|
+
function findProjectRoot(cwd, home) {
|
|
398
|
+
var stopAt = path.resolve(home || os.homedir());
|
|
399
|
+
var dir = path.resolve(cwd);
|
|
400
|
+
for (;;) {
|
|
401
|
+
if (dir === stopAt) return null;
|
|
402
|
+
if (fs.existsSync(path.join(dir, '.claude', 'agents')) || fs.existsSync(path.join(dir, '.monomind'))) return dir;
|
|
403
|
+
if (fs.existsSync(path.join(dir, '.git'))) return null;
|
|
404
|
+
var parent = path.dirname(dir);
|
|
405
|
+
if (parent === dir) return null;
|
|
406
|
+
dir = parent;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function main() {
|
|
411
|
+
var root = process.argv[2] || process.env.CLAUDE_PROJECT_DIR || findProjectRoot(process.cwd());
|
|
412
|
+
if (!root) {
|
|
413
|
+
process.stderr.write('registry.json: no project found at or above ' + process.cwd() + '; nothing written\n');
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
fs.mkdirSync(path.join(root, '.monomind'), { recursive: true });
|
|
417
|
+
var reg = buildUnifiedRegistry(computeAgentRoots(root), registryPath(root), { base: root });
|
|
418
|
+
process.stdout.write('registry.json: ' + reg.totalAgents + ' agents (' + reg.counts.user + ' from ' + USER_LABEL + ')\n');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (require.main === module) main();
|
|
422
|
+
|
|
423
|
+
module.exports = {
|
|
424
|
+
USER_LABEL: USER_LABEL,
|
|
425
|
+
parseFrontmatter: parseFrontmatter,
|
|
426
|
+
computeAgentRoots: computeAgentRoots,
|
|
427
|
+
buildUnifiedRegistry: buildUnifiedRegistry,
|
|
428
|
+
writeRegistryFile: writeRegistryFile,
|
|
429
|
+
newestAgentMtime: newestAgentMtime,
|
|
430
|
+
registryPath: registryPath,
|
|
431
|
+
isStale: isStale,
|
|
432
|
+
ensure: ensure,
|
|
433
|
+
findProjectRoot: findProjectRoot,
|
|
434
|
+
};
|
|
@@ -62,11 +62,12 @@ function isSystemPrompt(prompt) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** True for a prompt with fewer than MIN_PICK_TOKENS content words
|
|
65
|
-
* (pick-rank
|
|
65
|
+
* (pick-rank queryTokens: stopwords and words the prompt rules out dropped,
|
|
66
|
+
* any script), so "anything else pending rather than release" is a reply. */
|
|
66
67
|
function isTrivialPrompt(prompt) {
|
|
67
68
|
if (!pickRank || typeof prompt !== 'string') return false;
|
|
68
69
|
var text = prompt.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, ' ').slice(0, 2000);
|
|
69
|
-
return pickRank.
|
|
70
|
+
return pickRank.queryTokens(text).length < MIN_PICK_TOKENS;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
function promptHash(prompt) {
|
|
@@ -422,6 +423,22 @@ function ensureSkillRegistryFresh(root, builder) {
|
|
|
422
423
|
return 'rebuilt';
|
|
423
424
|
}
|
|
424
425
|
|
|
426
|
+
/** Rebuild .monomind/registry.json when agent-registry.cjs says it is stale
|
|
427
|
+
* (missing, or older than an agent in .claude/agents, ~/.claude/agents or an
|
|
428
|
+
* extra root), so a new user agent is routed from the next session on. Only
|
|
429
|
+
* inside a project (findProjectRoot: never the home directory, never a
|
|
430
|
+
* folder without .monomind or .claude/agents). opts.home overrides the home
|
|
431
|
+
* directory. Returns what it did. */
|
|
432
|
+
function ensureAgentRegistryFresh(root, builder, opts) {
|
|
433
|
+
if (builder === undefined) {
|
|
434
|
+
try { builder = require('../agent-registry.cjs'); } catch (e) { builder = null; }
|
|
435
|
+
}
|
|
436
|
+
if (!builder || typeof builder.isStale !== 'function' || typeof builder.ensure !== 'function') return 'no-builder';
|
|
437
|
+
if (builder.findProjectRoot(root, opts && opts.home) !== path.resolve(root)) return 'not-a-project';
|
|
438
|
+
if (!builder.isStale(root, opts)) return 'fresh';
|
|
439
|
+
return builder.ensure(root, opts) ? 'rebuilt' : 'failed';
|
|
440
|
+
}
|
|
441
|
+
|
|
425
442
|
module.exports = {
|
|
426
443
|
KEYWORD_MIN_AGENT_SCORE: KEYWORD_MIN_AGENT_SCORE,
|
|
427
444
|
KEYWORD_AGENT_LEAD: KEYWORD_AGENT_LEAD,
|
|
@@ -442,4 +459,5 @@ module.exports = {
|
|
|
442
459
|
joinOutcome: joinOutcome,
|
|
443
460
|
recordAdherence: recordAdherence,
|
|
444
461
|
ensureSkillRegistryFresh: ensureSkillRegistryFresh,
|
|
462
|
+
ensureAgentRegistryFresh: ensureAgentRegistryFresh,
|
|
445
463
|
};
|
|
@@ -39,6 +39,9 @@ module.exports = {
|
|
|
39
39
|
// Keep skill-registry.json (the skill catalog routing and Jev read) in
|
|
40
40
|
// step with .claude/skills: rebuilt when missing or older than any skill.
|
|
41
41
|
try { require('./pick-core.cjs').ensureSkillRegistryFresh(CWD); } catch (e) { /* non-fatal */ }
|
|
42
|
+
// Same for .monomind/registry.json (the agent catalog): rebuilt when an
|
|
43
|
+
// agent in .claude/agents or ~/.claude/agents is newer.
|
|
44
|
+
try { require('./pick-core.cjs').ensureAgentRegistryFresh(CWD); } catch (e) { /* non-fatal */ }
|
|
42
45
|
|
|
43
46
|
// ── Non-blocking security scan via @monoes/hooks worker ─────────────
|
|
44
47
|
// The hooks package ships a security worker (worker-security.ts) that scans
|
|
@@ -135,7 +138,7 @@ module.exports = {
|
|
|
135
138
|
if (bundledDir) {
|
|
136
139
|
var healed = [];
|
|
137
140
|
// Top-level critical files — mirrors executor.ts's `criticalHelpers` list.
|
|
138
|
-
var helpersToCheck = ['hook-handler.cjs', 'statusline.cjs', 'router.cjs', 'monograph-freshen.cjs', 'control-start.cjs', 'intelligence.cjs', 'auto-memory-hook.mjs', 'build-skill-registry.cjs', 'org-skill-index.cjs', 'jev-picker.cjs', 'jev-catalog.cjs', 'redact-secrets.cjs', 'pick-rank.cjs', 'pick-stats.cjs'];
|
|
141
|
+
var helpersToCheck = ['hook-handler.cjs', 'statusline.cjs', 'router.cjs', 'monograph-freshen.cjs', 'control-start.cjs', 'intelligence.cjs', 'auto-memory-hook.mjs', 'build-skill-registry.cjs', 'agent-registry.cjs', 'org-skill-index.cjs', 'jev-picker.cjs', 'jev-catalog.cjs', 'redact-secrets.cjs', 'pick-rank.cjs', 'pick-stats.cjs'];
|
|
139
142
|
for (var hi = 0; hi < helpersToCheck.length; hi++) {
|
|
140
143
|
var hName = helpersToCheck[hi];
|
|
141
144
|
var healedName = _healIfStale(
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* prompt hook (via jev-picker.cjs) and the CLI (`monomind pick`, MCP pick via
|
|
5
5
|
* src/decision/catalogs.ts), so both rank exactly the same agents and skills.
|
|
6
6
|
*
|
|
7
|
-
* agents .monomind/registry.json (
|
|
7
|
+
* agents .monomind/registry.json (agent-registry.cjs: project,
|
|
8
|
+
* ~/.claude/agents and extra roots)
|
|
8
9
|
* skills .claude/helpers/skill-registry.json (build-skill-registry.cjs):
|
|
9
10
|
* `skills` = platform commands/skills, `orgSkills` = Org library
|
|
10
11
|
*/
|
|
@@ -60,7 +61,7 @@ function loadAgentCatalog(root, opts) {
|
|
|
60
61
|
var category = typeof a.category === 'string' ? a.category : '';
|
|
61
62
|
var description = typeof a.description === 'string' ? a.description : '';
|
|
62
63
|
var when = typeof a.whenToUse === 'string' ? a.whenToUse.trim() : '';
|
|
63
|
-
|
|
64
|
+
var item = {
|
|
64
65
|
id: a.slug,
|
|
65
66
|
name: typeof a.name === 'string' ? a.name : a.slug,
|
|
66
67
|
category: category,
|
|
@@ -69,7 +70,10 @@ function loadAgentCatalog(root, opts) {
|
|
|
69
70
|
.concat(strings(a.tags), strings(a.capabilities), strings(a.taskTypes), typeof a.vibe === 'string' ? [a.vibe] : [])
|
|
70
71
|
.filter(Boolean)
|
|
71
72
|
.join(' '),
|
|
72
|
-
}
|
|
73
|
+
};
|
|
74
|
+
// 'project' | 'user' (~/.claude/agents) | 'extra'; shown by `pick --json`, never sent to the model.
|
|
75
|
+
if (typeof a.origin === 'string') item.origin = a.origin;
|
|
76
|
+
out.push(item);
|
|
73
77
|
});
|
|
74
78
|
return out;
|
|
75
79
|
}
|