@ouro.bot/cli 0.1.0-alpha.60 → 0.1.0-alpha.61
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/README.md +1 -1
- package/changelog.json +9 -0
- package/dist/heart/daemon/daemon-cli.js +16 -19
- package/dist/heart/daemon/skill-management-installer.js +73 -0
- package/dist/repertoire/skills.js +3 -26
- package/package.json +1 -1
- package/subagents/README.md +4 -83
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ Other important top-level paths:
|
|
|
48
48
|
- `AdoptionSpecialist.ouro/`
|
|
49
49
|
Packaged specialist bundle used by `ouro hatch`.
|
|
50
50
|
- `subagents/`
|
|
51
|
-
|
|
51
|
+
Workflow skills have moved to [github.com/ouroborosbot/ouroboros-skills](https://github.com/ouroborosbot/ouroboros-skills). Use the skill-management skill for installation and updates.
|
|
52
52
|
- `scripts/teams-sense/`
|
|
53
53
|
Operator scripts for the Teams deployment path.
|
|
54
54
|
- `docs/`
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.61",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Workflow skills (work-planner, work-doer, work-merger) migrated to the shared ouroboros-skills repo at github.com/ouroborosbot/ouroboros-skills.",
|
|
8
|
+
"Removed the subagent-installer. Skills are now installed via the skill-management bootstrap skill or manual download.",
|
|
9
|
+
"Skills loading simplified from 3-source to 2-source model. Canonical-protocol fallback to subagents/ removed.",
|
|
10
|
+
"New ensureSkillManagement() auto-installs the skill-management bootstrap skill from the shared repo on ouro up."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.60",
|
|
6
15
|
"changes": [
|
|
@@ -49,7 +49,7 @@ const store_file_1 = require("../../mind/friends/store-file");
|
|
|
49
49
|
const types_1 = require("../../mind/friends/types");
|
|
50
50
|
const ouro_uti_1 = require("./ouro-uti");
|
|
51
51
|
const ouro_path_installer_1 = require("./ouro-path-installer");
|
|
52
|
-
const
|
|
52
|
+
const skill_management_installer_1 = require("./skill-management-installer");
|
|
53
53
|
const hatch_flow_1 = require("./hatch-flow");
|
|
54
54
|
const specialist_orchestrator_1 = require("./specialist-orchestrator");
|
|
55
55
|
const specialist_prompt_1 = require("./specialist-prompt");
|
|
@@ -799,11 +799,6 @@ function defaultEnsureDaemonBootPersistence(socketPath) {
|
|
|
799
799
|
envPath: process.env.PATH,
|
|
800
800
|
});
|
|
801
801
|
}
|
|
802
|
-
async function defaultInstallSubagents() {
|
|
803
|
-
return (0, subagent_installer_1.installSubagentsForAvailableCli)({
|
|
804
|
-
repoRoot: (0, identity_1.getRepoRoot)(),
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
802
|
async function defaultPromptInput(question) {
|
|
808
803
|
const readline = await Promise.resolve().then(() => __importStar(require("readline/promises")));
|
|
809
804
|
const rl = readline.createInterface({
|
|
@@ -1075,7 +1070,6 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
|
|
|
1075
1070
|
checkSocketAlive: socket_client_1.checkDaemonSocketAlive,
|
|
1076
1071
|
cleanupStaleSocket: defaultCleanupStaleSocket,
|
|
1077
1072
|
fallbackPendingMessage: defaultFallbackPendingMessage,
|
|
1078
|
-
installSubagents: defaultInstallSubagents,
|
|
1079
1073
|
listDiscoveredAgents: defaultListDiscoveredAgents,
|
|
1080
1074
|
runHatchFlow: hatch_flow_1.runHatchFlow,
|
|
1081
1075
|
promptInput: defaultPromptInput,
|
|
@@ -1084,6 +1078,7 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
|
|
|
1084
1078
|
registerOuroBundleType: ouro_uti_1.registerOuroBundleUti,
|
|
1085
1079
|
installOuroCommand: ouro_path_installer_1.installOuroCommand,
|
|
1086
1080
|
syncGlobalOuroBotWrapper: ouro_bot_global_installer_1.syncGlobalOuroBotWrapper,
|
|
1081
|
+
ensureSkillManagement: skill_management_installer_1.ensureSkillManagement,
|
|
1087
1082
|
ensureDaemonBootPersistence: defaultEnsureDaemonBootPersistence,
|
|
1088
1083
|
/* v8 ignore next 3 -- integration: launches interactive CLI session @preserve */
|
|
1089
1084
|
startChat: async (agentName) => {
|
|
@@ -1179,18 +1174,20 @@ async function performSystemSetup(deps) {
|
|
|
1179
1174
|
});
|
|
1180
1175
|
}
|
|
1181
1176
|
}
|
|
1182
|
-
//
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
(
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1177
|
+
// Ensure skill-management skill is available
|
|
1178
|
+
if (deps.ensureSkillManagement) {
|
|
1179
|
+
try {
|
|
1180
|
+
await deps.ensureSkillManagement();
|
|
1181
|
+
}
|
|
1182
|
+
catch (error) {
|
|
1183
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1184
|
+
level: "warn",
|
|
1185
|
+
component: "daemon",
|
|
1186
|
+
event: "daemon.system_setup_skill_management_error",
|
|
1187
|
+
message: "failed to ensure skill-management skill",
|
|
1188
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1194
1191
|
}
|
|
1195
1192
|
// Register .ouro bundle type (UTI on macOS)
|
|
1196
1193
|
await registerOuroBundleTypeNonBlocking(deps);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ensureSkillManagement = ensureSkillManagement;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const SKILL_MANAGEMENT_URL = "https://raw.githubusercontent.com/ouroborosbot/ouroboros-skills/main/skills/skill-management/SKILL.md";
|
|
42
|
+
async function ensureSkillManagement() {
|
|
43
|
+
const skillsDir = path.join((0, identity_1.getAgentRoot)(), "skills");
|
|
44
|
+
const targetPath = path.join(skillsDir, "skill-management.md");
|
|
45
|
+
if (fs.existsSync(targetPath)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(SKILL_MANAGEMENT_URL);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
(0, runtime_1.emitNervesEvent)({
|
|
52
|
+
level: "warn",
|
|
53
|
+
component: "daemon",
|
|
54
|
+
event: "daemon.skill_management_install_error",
|
|
55
|
+
message: "failed to fetch skill-management from GitHub",
|
|
56
|
+
meta: { status: response.status, url: SKILL_MANAGEMENT_URL },
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const content = await response.text();
|
|
61
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
62
|
+
fs.writeFileSync(targetPath, content, "utf-8");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
(0, runtime_1.emitNervesEvent)({
|
|
66
|
+
level: "warn",
|
|
67
|
+
component: "daemon",
|
|
68
|
+
event: "daemon.skill_management_install_error",
|
|
69
|
+
message: "failed to install skill-management skill",
|
|
70
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -50,10 +50,6 @@ function getSkillsDir() {
|
|
|
50
50
|
function getProtocolMirrorDir() {
|
|
51
51
|
return path.join(getSkillsDir(), "protocols");
|
|
52
52
|
}
|
|
53
|
-
// Canonical protocol source lives in {repoRoot}/subagents/.
|
|
54
|
-
function getCanonicalProtocolsDir() {
|
|
55
|
-
return path.join((0, identity_1.getRepoRoot)(), "subagents");
|
|
56
|
-
}
|
|
57
53
|
function listMarkdownBasenames(dir) {
|
|
58
54
|
if (!fs.existsSync(dir))
|
|
59
55
|
return [];
|
|
@@ -74,8 +70,7 @@ function listSkills() {
|
|
|
74
70
|
});
|
|
75
71
|
const baseSkills = listMarkdownBasenames(getSkillsDir());
|
|
76
72
|
const protocolMirrors = listMarkdownBasenames(getProtocolMirrorDir());
|
|
77
|
-
const
|
|
78
|
-
const skills = [...new Set([...baseSkills, ...protocolMirrors, ...canonicalProtocols])].sort();
|
|
73
|
+
const skills = [...new Set([...baseSkills, ...protocolMirrors])].sort();
|
|
79
74
|
(0, runtime_1.emitNervesEvent)({
|
|
80
75
|
event: "repertoire.load_end",
|
|
81
76
|
component: "repertoire",
|
|
@@ -93,7 +88,6 @@ function loadSkill(skillName) {
|
|
|
93
88
|
});
|
|
94
89
|
const directSkillPath = path.join(getSkillsDir(), `${skillName}.md`);
|
|
95
90
|
const protocolMirrorPath = path.join(getProtocolMirrorDir(), `${skillName}.md`);
|
|
96
|
-
const canonicalProtocolPath = path.join(getCanonicalProtocolsDir(), `${skillName}.md`);
|
|
97
91
|
let resolvedPath = null;
|
|
98
92
|
// 1) Direct agent skill.
|
|
99
93
|
if (fs.existsSync(directSkillPath)) {
|
|
@@ -103,22 +97,6 @@ function loadSkill(skillName) {
|
|
|
103
97
|
else if (fs.existsSync(protocolMirrorPath)) {
|
|
104
98
|
resolvedPath = protocolMirrorPath;
|
|
105
99
|
}
|
|
106
|
-
// 3) Canonical protocol fallback.
|
|
107
|
-
else if (fs.existsSync(canonicalProtocolPath)) {
|
|
108
|
-
(0, runtime_1.emitNervesEvent)({
|
|
109
|
-
level: "warn",
|
|
110
|
-
event: "repertoire.error",
|
|
111
|
-
component: "repertoire",
|
|
112
|
-
message: "protocol mirror missing; using canonical fallback",
|
|
113
|
-
meta: {
|
|
114
|
-
operation: "loadSkill",
|
|
115
|
-
skill: skillName,
|
|
116
|
-
mirrorPath: protocolMirrorPath,
|
|
117
|
-
canonicalPath: canonicalProtocolPath,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
resolvedPath = canonicalProtocolPath;
|
|
121
|
-
}
|
|
122
100
|
if (!resolvedPath) {
|
|
123
101
|
(0, runtime_1.emitNervesEvent)({
|
|
124
102
|
level: "error",
|
|
@@ -128,13 +106,12 @@ function loadSkill(skillName) {
|
|
|
128
106
|
meta: {
|
|
129
107
|
operation: "loadSkill",
|
|
130
108
|
skill: skillName,
|
|
131
|
-
checkedPaths: [directSkillPath, protocolMirrorPath
|
|
109
|
+
checkedPaths: [directSkillPath, protocolMirrorPath],
|
|
132
110
|
},
|
|
133
111
|
});
|
|
134
112
|
throw new Error(`skill '${skillName}' not found in:\n` +
|
|
135
113
|
`- ${directSkillPath}\n` +
|
|
136
|
-
`- ${protocolMirrorPath}
|
|
137
|
-
`- ${canonicalProtocolPath}`);
|
|
114
|
+
`- ${protocolMirrorPath}`);
|
|
138
115
|
}
|
|
139
116
|
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
140
117
|
if (!loadedSkills.includes(skillName)) {
|
package/package.json
CHANGED
package/subagents/README.md
CHANGED
|
@@ -1,86 +1,7 @@
|
|
|
1
|
-
# Workflow
|
|
1
|
+
# Workflow Skills (Moved)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The workflow skills (`work-planner`, `work-doer`, `work-merger`) have moved to the shared skills repository:
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
- `work-doer`
|
|
7
|
-
- `work-merger`
|
|
5
|
+
**https://github.com/ouroborosbot/ouroboros-skills**
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## What They Do
|
|
12
|
-
|
|
13
|
-
- `work-planner.md`
|
|
14
|
-
Creates and refines planning docs, then converts approved plans into doing docs.
|
|
15
|
-
- `work-doer.md`
|
|
16
|
-
Executes approved doing docs unit by unit with strict validation discipline.
|
|
17
|
-
- `work-merger.md`
|
|
18
|
-
Syncs with `main`, resolves conflicts, creates the PR, handles CI, and merges.
|
|
19
|
-
|
|
20
|
-
## Important Repo-Specific Truth
|
|
21
|
-
|
|
22
|
-
These helpers do not hardcode task-doc paths. They are expected to read project instructions to discover them.
|
|
23
|
-
|
|
24
|
-
In this repo, that means:
|
|
25
|
-
|
|
26
|
-
- task docs live in `~/AgentBundles/<agent>.ouro/tasks/one-shots/`
|
|
27
|
-
- not inside the repo
|
|
28
|
-
|
|
29
|
-
## Installing For Claude Code
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
mkdir -p ~/.claude/agents
|
|
33
|
-
cp subagents/*.md ~/.claude/agents/
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Installing For Codex-Style Skills
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
mkdir -p ~/.agents/skills/work-planner ~/.agents/skills/work-doer ~/.agents/skills/work-merger
|
|
40
|
-
|
|
41
|
-
# Hard-link to keep one source of truth
|
|
42
|
-
ln -f "$(pwd)/subagents/work-planner.md" ~/.agents/skills/work-planner/SKILL.md
|
|
43
|
-
ln -f "$(pwd)/subagents/work-doer.md" ~/.agents/skills/work-doer/SKILL.md
|
|
44
|
-
ln -f "$(pwd)/subagents/work-merger.md" ~/.agents/skills/work-merger/SKILL.md
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Important:** For Codex/OpenAI skill installs, use the generic `~/.agents/skills` root and use hard links (`ln`, not `ln -s`). Installing the same skill into both `~/.agents/skills` and `~/.codex/skills` can produce duplicate entries in Codex. Symlinked `SKILL.md` files may load but are not advertised reliably by Codex surfaces. Hard-links break when editors save by replacing the file (new inode). After editing any `subagents/*.md` file, re-run the `ln -f` command for that file to restore the link. You can verify with `stat -f '%i'` — both files should share the same inode.
|
|
48
|
-
|
|
49
|
-
Optional UI metadata:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
mkdir -p ~/.agents/skills/work-planner/agents ~/.agents/skills/work-doer/agents ~/.agents/skills/work-merger/agents
|
|
53
|
-
cat > ~/.agents/skills/work-planner/agents/openai.yaml << 'EOF'
|
|
54
|
-
interface:
|
|
55
|
-
display_name: "Work Planner"
|
|
56
|
-
short_description: "Create and gate planning/doing task docs"
|
|
57
|
-
default_prompt: "Use $work-planner to create or update a planning doc, then stop at NEEDS_REVIEW."
|
|
58
|
-
EOF
|
|
59
|
-
cat > ~/.agents/skills/work-doer/agents/openai.yaml << 'EOF'
|
|
60
|
-
interface:
|
|
61
|
-
display_name: "Work Doer"
|
|
62
|
-
short_description: "Execute approved doing docs with strict TDD"
|
|
63
|
-
default_prompt: "Use $work-doer to execute an approved doing doc unit by unit."
|
|
64
|
-
EOF
|
|
65
|
-
cat > ~/.agents/skills/work-merger/agents/openai.yaml << 'EOF'
|
|
66
|
-
interface:
|
|
67
|
-
display_name: "Work Merger"
|
|
68
|
-
short_description: "Merge feature branch into main via PR after work-doer completes"
|
|
69
|
-
default_prompt: "Use $work-merger to merge the current feature branch into main."
|
|
70
|
-
EOF
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Keeping Local Skill Copies Fresh
|
|
74
|
-
|
|
75
|
-
After editing any `subagents/*.md` file, resync your local installed copies.
|
|
76
|
-
|
|
77
|
-
The repo workflow usually checks this with diffs like:
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
diff -q ~/.agents/skills/work-planner/SKILL.md subagents/work-planner.md
|
|
81
|
-
diff -q ~/.agents/skills/work-doer/SKILL.md subagents/work-doer.md
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Restart Behavior
|
|
85
|
-
|
|
86
|
-
Some tools only discover new skills on startup. If a shell/app does not see updates immediately, restart that shell/app after syncing.
|
|
7
|
+
To install or update these skills, use the **skill-management** skill from that repo. It covers browsing available skills, installing them into your agent's local skills directory, and keeping them up to date.
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.installSubagentsForAvailableCli = installSubagentsForAvailableCli;
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const child_process_1 = require("child_process");
|
|
41
|
-
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
-
function detectCliBinary(binary) {
|
|
43
|
-
const result = (0, child_process_1.spawnSync)("which", [binary], { encoding: "utf-8" });
|
|
44
|
-
if (result.status !== 0)
|
|
45
|
-
return null;
|
|
46
|
-
const resolved = result.stdout.trim();
|
|
47
|
-
return resolved.length > 0 ? resolved : null;
|
|
48
|
-
}
|
|
49
|
-
function listSubagentSources(subagentsDir) {
|
|
50
|
-
if (!fs.existsSync(subagentsDir))
|
|
51
|
-
return [];
|
|
52
|
-
return fs.readdirSync(subagentsDir)
|
|
53
|
-
.filter((name) => name.endsWith(".md"))
|
|
54
|
-
.filter((name) => name !== "README.md")
|
|
55
|
-
.map((name) => path.join(subagentsDir, name))
|
|
56
|
-
.sort((a, b) => a.localeCompare(b));
|
|
57
|
-
}
|
|
58
|
-
function pathExists(target) {
|
|
59
|
-
try {
|
|
60
|
-
fs.lstatSync(target);
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function isSameFile(source, target) {
|
|
68
|
-
try {
|
|
69
|
-
const sourceStats = fs.statSync(source);
|
|
70
|
-
const targetStats = fs.statSync(target);
|
|
71
|
-
return sourceStats.dev === targetStats.dev && sourceStats.ino === targetStats.ino;
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function ensureSymlink(source, target) {
|
|
78
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
79
|
-
if (pathExists(target)) {
|
|
80
|
-
const stats = fs.lstatSync(target);
|
|
81
|
-
if (stats.isSymbolicLink()) {
|
|
82
|
-
const linkedPath = fs.readlinkSync(target);
|
|
83
|
-
if (linkedPath === source)
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
fs.unlinkSync(target);
|
|
87
|
-
}
|
|
88
|
-
fs.symlinkSync(source, target);
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
function ensureHardLink(source, target) {
|
|
92
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
93
|
-
if (pathExists(target)) {
|
|
94
|
-
const stats = fs.lstatSync(target);
|
|
95
|
-
if (!stats.isSymbolicLink() && isSameFile(source, target)) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
fs.unlinkSync(target);
|
|
99
|
-
}
|
|
100
|
-
fs.linkSync(source, target);
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
function hasOpenAiSkillHome(homeDir) {
|
|
104
|
-
return pathExists(path.join(homeDir, ".codex")) || pathExists(path.join(homeDir, ".agents"));
|
|
105
|
-
}
|
|
106
|
-
function openAiSkillTargets(homeDir, source) {
|
|
107
|
-
const skillName = path.basename(source, ".md");
|
|
108
|
-
return [path.join(homeDir, ".agents", "skills", skillName, "SKILL.md")];
|
|
109
|
-
}
|
|
110
|
-
async function installSubagentsForAvailableCli(options = {}) {
|
|
111
|
-
const repoRoot = options.repoRoot ?? path.resolve(__dirname, "..", "..", "..");
|
|
112
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
113
|
-
const which = options.which ?? detectCliBinary;
|
|
114
|
-
const subagentsDir = path.join(repoRoot, "subagents");
|
|
115
|
-
const sources = listSubagentSources(subagentsDir);
|
|
116
|
-
const notes = [];
|
|
117
|
-
(0, runtime_1.emitNervesEvent)({
|
|
118
|
-
component: "daemon",
|
|
119
|
-
event: "daemon.subagent_install_start",
|
|
120
|
-
message: "starting subagent auto-install",
|
|
121
|
-
meta: { sources: sources.length },
|
|
122
|
-
});
|
|
123
|
-
if (sources.length === 0) {
|
|
124
|
-
notes.push(`no subagent files found at ${subagentsDir}`);
|
|
125
|
-
return { claudeInstalled: 0, codexInstalled: 0, notes };
|
|
126
|
-
}
|
|
127
|
-
let claudeInstalled = 0;
|
|
128
|
-
let codexInstalled = 0;
|
|
129
|
-
const claudePath = which("claude");
|
|
130
|
-
if (!claudePath) {
|
|
131
|
-
notes.push("claude CLI not found; skipping subagent install");
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
const claudeAgentsDir = path.join(homeDir, ".claude", "agents");
|
|
135
|
-
for (const source of sources) {
|
|
136
|
-
const target = path.join(claudeAgentsDir, path.basename(source));
|
|
137
|
-
if (ensureSymlink(source, target)) {
|
|
138
|
-
claudeInstalled += 1;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const codexPath = which("codex");
|
|
143
|
-
if (!codexPath && !hasOpenAiSkillHome(homeDir)) {
|
|
144
|
-
notes.push("codex CLI/config not found; skipping subagent install");
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
for (const source of sources) {
|
|
148
|
-
let installedForSkill = false;
|
|
149
|
-
for (const target of openAiSkillTargets(homeDir, source)) {
|
|
150
|
-
if (ensureHardLink(source, target)) {
|
|
151
|
-
installedForSkill = true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (installedForSkill) {
|
|
155
|
-
codexInstalled += 1;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
(0, runtime_1.emitNervesEvent)({
|
|
160
|
-
component: "daemon",
|
|
161
|
-
event: "daemon.subagent_install_end",
|
|
162
|
-
message: "completed subagent auto-install",
|
|
163
|
-
meta: { claudeInstalled, codexInstalled, notes: notes.length },
|
|
164
|
-
});
|
|
165
|
-
return { claudeInstalled, codexInstalled, notes };
|
|
166
|
-
}
|