@orlan-maker/cli 0.2.1 → 0.3.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/README.md +111 -95
- package/dist/agents.js +229 -214
- package/dist/commands.js +468 -669
- package/dist/http.js +93 -16
- package/dist/main.js +17 -3
- package/dist/skills.js +20 -4
- package/package.json +5 -6
- package/skills/orlan/SKILL.md +77 -12
- package/skills/orlan-review/SKILL.md +14 -28
- package/.claude-plugin/marketplace.json +0 -15
- package/.claude-plugin/plugin.json +0 -14
- package/bin/orlan +0 -8
- package/hooks/hooks.json +0 -68
- package/hooks/orlan-intro.txt +0 -1
- package/hooks/session-start +0 -14
- package/opencode/orlan.js +0 -121
package/hooks/session-start
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# SessionStart hook of the Orlan plugin (F044). The board shows the session as working, and the brief
|
|
3
|
-
# goes into the session's context (the output of a SessionStart hook does). An error of the brief goes
|
|
4
|
-
# there too, for example "No agent is connected", so the agent can tell the person what to do.
|
|
5
|
-
orlan="$CLAUDE_PLUGIN_ROOT/bin/orlan"
|
|
6
|
-
"$orlan" hook working --agent claude-code
|
|
7
|
-
if "$orlan" brief --agent claude-code 2>&1; then
|
|
8
|
-
# This line tells the session what Orlan is and when to load the orlan skill (D088, F086). Codex,
|
|
9
|
-
# Cursor and OpenCode read the same file (F090).
|
|
10
|
-
cat "$CLAUDE_PLUGIN_ROOT/hooks/orlan-intro.txt"
|
|
11
|
-
echo "orlan inbox lists the open requests. orlan wait waits for the next request and blocks the turn: run it only when the person asks for that."
|
|
12
|
-
echo "Answer a request with the orlan command in few steps: orlan files pull <file id> --out <path> && cat <path>; edit the file; orlan respond <request id> --file <path> [--to <file id>] --message \"<what changed>\"."
|
|
13
|
-
fi
|
|
14
|
-
exit 0
|
package/opencode/orlan.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// The Orlan plugin for OpenCode (F045). `orlan connect --agent opencode` writes it to OpenCode's
|
|
2
|
-
// plugins folder, and `orlan disconnect --agent opencode` takes it out.
|
|
3
|
-
//
|
|
4
|
-
// OpenCode has no stop hook and no monitor: a turn that ends raises `session.idle`, and a plugin may
|
|
5
|
-
// answer it with a new prompt. So when a session goes idle, this plugin runs `orlan hook wait`. When a
|
|
6
|
-
// person mentions the agent on Orlan, the wait prints the request, and the plugin sends it to the
|
|
7
|
-
// session with prompt_async: a new turn starts. When the session works again before a request
|
|
8
|
-
// comes, the plugin ends the wait, and an untaken request stays in the inbox.
|
|
9
|
-
// The board shows the session's state from OpenCode's events through `orlan hook <state>`.
|
|
10
|
-
// OpenCode has no session start hook that adds context: the plugin adds the Orlan intro line (what
|
|
11
|
-
// Orlan is, F090) to the system prompt of each session's model call, with experimental.chat.system.transform.
|
|
12
|
-
import { spawn } from "node:child_process";
|
|
13
|
-
|
|
14
|
-
// The orlan command. `orlan connect` writes this Node and this CLI by full path here, because
|
|
15
|
-
// OpenCode started from the desktop often has no `orlan` on its PATH.
|
|
16
|
-
const ORLAN = ["orlan"];
|
|
17
|
-
const AGENT = ["--agent", "opencode"];
|
|
18
|
-
// The Orlan intro line, hooks/orlan-intro.txt of the orlan package. `orlan connect` writes it here.
|
|
19
|
-
const INTRO = "";
|
|
20
|
-
|
|
21
|
-
/** Runs `orlan <args>` with the JSON the hook reads on standard input. */
|
|
22
|
-
function orlan(args, input) {
|
|
23
|
-
const child = spawn(ORLAN[0], [...ORLAN.slice(1), ...args, ...AGENT], { stdio: ["pipe", "pipe", "pipe"] });
|
|
24
|
-
child.on("error", () => {});
|
|
25
|
-
if (input !== undefined) child.stdin.end(JSON.stringify(input));
|
|
26
|
-
return child;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const OrlanPlugin = async ({ client }) => {
|
|
30
|
-
/** The open wait: the session it wakes, and the `orlan hook wait` process. */
|
|
31
|
-
let wait;
|
|
32
|
-
/** The parent of each subagent session: the parent answers for the board. */
|
|
33
|
-
const parents = new Map();
|
|
34
|
-
const root = (sessionID) => parents.get(sessionID) ?? sessionID;
|
|
35
|
-
/** The last state each session reported, so a state goes to the board once. */
|
|
36
|
-
const states = new Map();
|
|
37
|
-
/** A person stopped the agent on the board: no wait until the session works again. */
|
|
38
|
-
let stopped = false;
|
|
39
|
-
|
|
40
|
-
const report = (sessionID, state, more = []) => {
|
|
41
|
-
const id = root(sessionID);
|
|
42
|
-
if (!id || states.get(id) === state) return;
|
|
43
|
-
states.set(id, state);
|
|
44
|
-
orlan(["hook", state, ...more], { sessionID: id });
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const endWait = () => {
|
|
48
|
-
wait?.child.kill();
|
|
49
|
-
wait = undefined;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const startWait = (sessionID) => {
|
|
53
|
-
if (stopped || wait?.sessionID === sessionID) return;
|
|
54
|
-
endWait();
|
|
55
|
-
// Standard input stays open: when OpenCode exits, it closes, and the wait ends with it.
|
|
56
|
-
const child = orlan(["hook", "wait"]);
|
|
57
|
-
wait = { sessionID, child };
|
|
58
|
-
let out = "";
|
|
59
|
-
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
60
|
-
out += chunk;
|
|
61
|
-
});
|
|
62
|
-
child.on("close", async (code) => {
|
|
63
|
-
if (wait?.child === child) wait = undefined;
|
|
64
|
-
// A wait that printed and exited 0 took the request: it goes to the session even when the
|
|
65
|
-
// session works again now. OpenCode queues a prompt for a busy session.
|
|
66
|
-
if (code !== 0 || !out.trim()) return;
|
|
67
|
-
const answer = JSON.parse(out);
|
|
68
|
-
if (answer.prompt) {
|
|
69
|
-
await client.session.promptAsync({
|
|
70
|
-
path: { id: sessionID },
|
|
71
|
-
body: { parts: [{ type: "text", text: answer.prompt }] },
|
|
72
|
-
});
|
|
73
|
-
} else if (answer.stopped) {
|
|
74
|
-
stopped = true;
|
|
75
|
-
await client.tui.showToast({ body: { message: answer.stopped, variant: "warning" } }).catch(() => {});
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
// What Orlan is, in the system prompt of a session (a title or other call with no session gets nothing).
|
|
82
|
-
"experimental.chat.system.transform": async (input, output) => {
|
|
83
|
-
if (input.sessionID && INTRO) output.system.push(INTRO);
|
|
84
|
-
},
|
|
85
|
-
// The orlan commands of the agent's shell name its session (F085): the parent of a subagent's session.
|
|
86
|
-
"shell.env": async (input, output) => {
|
|
87
|
-
if (input.sessionID) output.env.ORLAN_SESSION = root(input.sessionID);
|
|
88
|
-
},
|
|
89
|
-
event: async ({ event }) => {
|
|
90
|
-
const properties = event.properties ?? {};
|
|
91
|
-
switch (event.type) {
|
|
92
|
-
case "session.created":
|
|
93
|
-
case "session.updated":
|
|
94
|
-
if (properties.info?.parentID) parents.set(properties.info.id, root(properties.info.parentID));
|
|
95
|
-
return;
|
|
96
|
-
case "session.status":
|
|
97
|
-
if (properties.status?.type !== "busy" || parents.has(properties.sessionID)) return;
|
|
98
|
-
stopped = false;
|
|
99
|
-
if (wait?.sessionID === properties.sessionID) endWait();
|
|
100
|
-
report(properties.sessionID, "working");
|
|
101
|
-
return;
|
|
102
|
-
case "permission.asked":
|
|
103
|
-
report(properties.sessionID, "needs-input", ["--prompt", "permission"]);
|
|
104
|
-
return;
|
|
105
|
-
case "permission.replied":
|
|
106
|
-
report(properties.sessionID, "working");
|
|
107
|
-
return;
|
|
108
|
-
case "session.idle":
|
|
109
|
-
if (parents.has(properties.sessionID)) return;
|
|
110
|
-
report(properties.sessionID, "idle");
|
|
111
|
-
startWait(properties.sessionID);
|
|
112
|
-
return;
|
|
113
|
-
case "session.deleted":
|
|
114
|
-
if (parents.has(properties.info?.id)) return;
|
|
115
|
-
if (wait?.sessionID === properties.info?.id) endWait();
|
|
116
|
-
report(properties.info?.id, "done");
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
};
|