@orlan-maker/cli 0.3.0 → 0.4.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 +19 -11
- package/dist/commands.js +99 -42
- package/package.json +1 -1
- package/skills/orlan/SKILL.md +43 -23
package/README.md
CHANGED
|
@@ -17,20 +17,21 @@ orlan connect --agent claude-code
|
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
- `orlan auth login` shows a code and opens the approval page in your browser. Sign in, check the
|
|
20
|
-
code, choose the organisation, and
|
|
20
|
+
code, choose the organisation, and what your agents get: one topic, or all topics of the organisation
|
|
21
|
+
(also the topics made later). Details has a checkbox for each topic and the agent role. The command waits for the
|
|
21
22
|
approval (codes work for 10 minutes). The CLI token goes to the OS keychain (macOS keychain, or the
|
|
22
23
|
Secret Service on Linux), else to `credentials.json` in the config folder, readable by you only.
|
|
23
24
|
`--server <url>` signs in to another Orlan server; `--no-browser` only prints the address.
|
|
24
25
|
- `orlan auth login --pair <id>` is the sign-in that the prompt of the Connect dialog in the Orlan web
|
|
25
26
|
app runs. It shows the code and opens no page: the code waits in the Connect dialog. Check that the
|
|
26
|
-
dialog shows the same code, choose
|
|
27
|
+
dialog shows the same code, choose this topic or all topics for your agents, then approve it there. A
|
|
27
28
|
`--pair` id works for one sign-in, for 10 minutes. A second CLI with the same id stops with
|
|
28
29
|
`pair_used`, and the dialog warns. A denied sign-in stops the CLI with `login_denied`.
|
|
29
30
|
- `orlan connect` makes an agent token and keeps it in the keychain. The agent then works on Orlan
|
|
30
31
|
with the `orlan` command. Only an admin of the organisation connects agents. The command writes the
|
|
31
32
|
skills where the agent reads skills (table below), and tests the token with one plain HTTP call.
|
|
32
33
|
It writes nothing else: no plugin, no hook, no MCP entry, and no instruction file such as
|
|
33
|
-
`CLAUDE.md` or `AGENTS.md`. `--topic`
|
|
34
|
+
`CLAUDE.md` or `AGENTS.md`. `--topic` limits the agent to the named topics, and `--role` changes its role.
|
|
34
35
|
- The skills are `orlan`, `orlan-review` and `orlan-versions`. `orlan` tells the agent what Orlan is:
|
|
35
36
|
"the canvas", "the board" and "the topic" mean the Orlan topic, and a file the person asks for goes
|
|
36
37
|
on the topic. A diagram is board shapes with labeled arrows, never an HTML file or notes: the agent
|
|
@@ -73,8 +74,8 @@ orlan comment <thread> "<text>" | --file <file> --page <n> [--region <x,y,w,h>]
|
|
|
73
74
|
orlan comments resolve <thread> [--topic <topic>]
|
|
74
75
|
orlan board [--topic <topic>]
|
|
75
76
|
orlan board view --out <file.png> [--frame <frame>] [--topic <topic>]
|
|
76
|
-
orlan board add note|text|frame|shape ["<text>"] --at <x,y> [--size <w,h>] [--kind <kind>] [--color <color>] [--frame <frame>|none] | arrow --from <item> --to <item> [--text "<label>"] [--id <id>]
|
|
77
|
-
orlan board change <item> [--text "<text>"] [--at <x,y>] [--size <w,h>] [--kind <kind>] [--color <color>] [--from <item>|none] [--to <item>|none] [--frame <frame>|none]
|
|
77
|
+
orlan board add note|text|frame|shape ["<text>"] --at <x,y> [--size <w,h>] [--text-color <color>] [--kind <kind>] [--color <color>] [--frame <frame>|none] | arrow --from <item> --to <item> [--text "<label>"] [--id <id>]
|
|
78
|
+
orlan board change <item> [--text "<text>"] [--at <x,y>] [--size <w,h>] [--text-color <color>] [--kind <kind>] [--color <color>] [--from <item>|none] [--to <item>|none] [--frame <frame>|none]
|
|
78
79
|
orlan board delete <item>...
|
|
79
80
|
orlan board edit '<edits as JSON>'
|
|
80
81
|
orlan board mermaid <file.mmd> [--frame <frame>] [--topic <topic>]
|
|
@@ -105,8 +106,8 @@ topic is the one the agent used last, else its only topic. `orlan files add` pos
|
|
|
105
106
|
topic and puts it on the board, in one request. Both are plain HTTP calls with the agent token.
|
|
106
107
|
|
|
107
108
|
`orlan board` prints the board of the topic. Each item shows its id, type, place and size in board
|
|
108
|
-
coordinates, and its text. A note, a text and a shape also show a text size other than M
|
|
109
|
-
color, a file its name and pages, and an arrow the items it links and its label. An item in a frame
|
|
109
|
+
coordinates, and its text. A note, a text and a shape also show a text size other than M and a text color other
|
|
110
|
+
than ink (`text color red`), a shape its kind and color, a file its name and pages, and an arrow the items it links and its label. An item in a frame
|
|
110
111
|
shows `in frame <id>`. Then come the timer, the open vote and the last vote with its result, and the
|
|
111
112
|
open tasks. People vote; an agent reads the vote, it does not vote. `orlan board add` adds a note, a
|
|
112
113
|
text, a shape, a frame or an arrow, `orlan board change` changes an item's text, place, size, a shape's
|
|
@@ -119,7 +120,10 @@ command is one step that a person can undo on the board. `orlan board edit` take
|
|
|
119
120
|
and makes them in one step, all or none. A create can give its item an id of its own (`"id":"api"`, also
|
|
120
121
|
`orlan board add --id api`), so the arrow of the same step, and a later command, name it. Without
|
|
121
122
|
`--size <w,h>` or `w`, `h`, a note is 190x120, a text 320x40, a shape 160x100 and a frame 480x320. A note, a
|
|
122
|
-
text and a shape take a text size: `--size S`, `M`, `L` or `XL` (`"size"` in the JSON; default M)
|
|
123
|
+
text and a shape take a text size: `--size S`, `M`, `L` or `XL` (`"size"` in the JSON; default M), and a
|
|
124
|
+
text color for their words: `--text-color` ink (default), white, gray, blue, teal, green, yellow, orange or
|
|
125
|
+
red (`"textColor"` in the JSON). The names are the shape colors; the words draw in a strong shade of each
|
|
126
|
+
that reads on a note, a shape and the board (white reads only on a dark background). Their
|
|
123
127
|
box grows to fit their words, so no word is cut and no word spills out: a note and a shape grow in
|
|
124
128
|
height, and a text is as high as its lines. With no width, a text is as wide as its words (at most 20
|
|
125
129
|
em), and a shape grows wider for its words (at most twice its default width, 320). `orlan board add`, `orlan board
|
|
@@ -143,13 +147,17 @@ rounded, `A((text))` an ellipse, `A{text}` a diamond, `A[(text)]` a cylinder and
|
|
|
143
147
|
`A[[text]]`, `A>text]`, `A{{text}}`, `A[\text\]` and `A(((text)))` take the nearest of these. A label goes as
|
|
144
148
|
`A -->|label| B` or `A -- label --> B`; `-->`, `---`, `-.->` and `==>` all become arrows, and `A & B --> C` and
|
|
145
149
|
chains work. `subgraph id [title] ... end` is a frame, also in another frame. A fill from `style`, `classDef`,
|
|
146
|
-
`class` or `:::` takes the nearest shape color. `
|
|
150
|
+
`class` or `:::` takes the nearest shape color. `A ~~~ B` places B after A and draws no arrow. A `direction` in a
|
|
151
|
+
subgraph, and a link to a subgraph id, work when no link goes out of that subgraph, as in Mermaid. `linkStyle`,
|
|
152
|
+
`click` and a `direction` in a subgraph that a link goes out of are left
|
|
147
153
|
out, and the answer says so. The layout is in layers (dagre), each box as large as its words need, with no
|
|
148
154
|
overlap and no arrow across a box for the usual diagrams; the answer lists drawing warnings when it could not
|
|
149
|
-
avoid one.
|
|
155
|
+
avoid one. Subgraphs that are stacked or side by side line up (one left edge and width, or one top and height)
|
|
156
|
+
with even gaps, and subgraphs that no link joins keep the order of the file. The diagram goes in free space right of the items on the board, and nothing on the board moves.
|
|
150
157
|
Each node and subgraph keeps its Mermaid id as its item id, so `orlan board change <id>` changes it later; an
|
|
151
158
|
id that is on the board already gets a prefix (`m2-`, `m3-`), and the answer gives it. With
|
|
152
|
-
`--frame <frame>`, the diagram goes in that frame, which
|
|
159
|
+
`--frame <frame>`, the diagram goes in that frame, which then fits it with 24 px of room (it grows and shrinks;
|
|
160
|
+
a person's items in it stay in it), and it replaces the items this agent
|
|
153
161
|
drew in that frame before: run it again after a change to the file, and the diagram changes in place, at the
|
|
154
162
|
same place and with the same ids. A person's items in the frame stay where they are. Another Mermaid diagram (`sequenceDiagram`,
|
|
155
163
|
`classDiagram` and others) is refused with `mermaid_unsupported`, and a syntax error with `mermaid_invalid` and
|
package/dist/commands.js
CHANGED
|
@@ -107,6 +107,8 @@ export const SHAPE_KINDS_HELP = "rectangle, rounded, ellipse, diamond, cylinder,
|
|
|
107
107
|
export const SHAPE_COLORS_HELP = "white, gray, blue, teal, green, yellow, orange, red";
|
|
108
108
|
/** The text sizes of a note, a text and a shape (F095): textSizes of @orlan/board. A test keeps them equal. */
|
|
109
109
|
export const TEXT_SIZES_HELP = "S, M, L, XL";
|
|
110
|
+
/** The text colors of a note, a text and a shape (F112): textColorNames of @orlan/board. A test keeps them equal. */
|
|
111
|
+
export const TEXT_COLORS_HELP = "ink, white, gray, blue, teal, green, yellow, orange, red";
|
|
110
112
|
/** How the boxes grow (F095), as applyEdits of @orlan/board makes them. */
|
|
111
113
|
const GROW_HELP = [
|
|
112
114
|
"A note, a text and a shape grow to fit their words, so no word is cut: a note and a shape grow in",
|
|
@@ -118,8 +120,10 @@ const GROW_HELP = [
|
|
|
118
120
|
const WARNINGS_HELP = [
|
|
119
121
|
"After each change, the answer lists warnings about the items it made or changed: two items overlap,",
|
|
120
122
|
"an item lies partly or all outside the frame it is in, an arrow crosses an item that is not one of",
|
|
121
|
-
"its ends, an end of an arrow points at no item, an arrow label covers an item,
|
|
122
|
-
"wider than its frame
|
|
123
|
+
"its ends, an end of an arrow points at no item, an arrow label covers an item, two arrows draw on",
|
|
124
|
+
"one line or their labels cover each other, a frame title is wider than its frame, frames are almost",
|
|
125
|
+
"but not quite lined up, stacked frames have uneven gaps, a frame is much larger than the items in it,",
|
|
126
|
+
"or a text has words much larger than the words of the shapes in its frame. Each",
|
|
123
127
|
"warning names the items and says what to change. A warning never stops a change. With --json they",
|
|
124
128
|
'are "warnings": [{"kind","ids","text"}], and [] when the drawing is clean.',
|
|
125
129
|
];
|
|
@@ -194,6 +198,28 @@ function numbersOption(values, option, count) {
|
|
|
194
198
|
}
|
|
195
199
|
return parts;
|
|
196
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* An arrow end of --from or --to: <item>, or <item>@x,y for an exact spot on the item (F111), x and y
|
|
203
|
+
* from 0 to 1 of its box. "none" (board change) is no item.
|
|
204
|
+
*/
|
|
205
|
+
function arrowEndOption(values, option) {
|
|
206
|
+
const raw = values[option];
|
|
207
|
+
if (typeof raw !== "string")
|
|
208
|
+
return {};
|
|
209
|
+
if (raw === "none")
|
|
210
|
+
return { [option]: null };
|
|
211
|
+
const at = raw.lastIndexOf("@");
|
|
212
|
+
if (at < 0)
|
|
213
|
+
return { [option]: raw };
|
|
214
|
+
const parts = raw
|
|
215
|
+
.slice(at + 1)
|
|
216
|
+
.split(",")
|
|
217
|
+
.map((part) => (part.trim() === "" ? Number.NaN : Number(part)));
|
|
218
|
+
if (parts.length !== 2 || parts.some((part) => !Number.isFinite(part) || part < 0 || part > 1)) {
|
|
219
|
+
throw new OrlanError(`--${option} takes <item> or <item>@x,y: x and y are from 0 to 1 of the item's box, 0,0 its top left corner.`, undefined, `run the command again with --${option} ${raw.slice(0, at) || "<item>"}@0.5,0.5`);
|
|
220
|
+
}
|
|
221
|
+
return { [option]: raw.slice(0, at), [`${option}At`]: { x: parts[0], y: parts[1] } };
|
|
222
|
+
}
|
|
197
223
|
const baseOption = (values) => {
|
|
198
224
|
const number = versionNumber(values, "base");
|
|
199
225
|
return number === undefined ? {} : { baseVersion: number };
|
|
@@ -259,7 +285,7 @@ async function connect(agent, values, io) {
|
|
|
259
285
|
await writeConfig(config);
|
|
260
286
|
const role = made.token.role[0]?.toUpperCase() + made.token.role.slice(1);
|
|
261
287
|
io.out(`Made an agent token for ${agentLabels[agent]}: ${made.token.prefix}..., ${role} on ` +
|
|
262
|
-
`${made.token.topics.map((topic) => quote(topic.name)).join(", ")}. It is in the ${where === "keychain" ? "keychain" : "credentials file"}.`);
|
|
288
|
+
`${made.token.allTopics ? "all topics" : made.token.topics.map((topic) => quote(topic.name)).join(", ")}. It is in the ${where === "keychain" ? "keychain" : "credentials file"}.`);
|
|
263
289
|
await writeSkills(agent);
|
|
264
290
|
io.out(`Installed the Orlan skills orlan, orlan-review and orlan-versions in ${skillsDir(agent)}.`);
|
|
265
291
|
// The test is the person's, not a call of the agent: it names no agent session.
|
|
@@ -317,8 +343,10 @@ export const commands = {
|
|
|
317
343
|
summary: "Sign in to Orlan (approve in the Connect dialog with --pair, else in the browser) and keep a CLI token.",
|
|
318
344
|
details: [
|
|
319
345
|
"Shows a code and opens the Orlan page that approves it. Sign in there, check that the code is",
|
|
320
|
-
"the same, choose the organisation, and
|
|
321
|
-
"
|
|
346
|
+
"the same, choose the organisation, and what your agents get: one topic, or all topics of the",
|
|
347
|
+
"organisation (also the topics made later; restricted topics only when you tick them in Details).",
|
|
348
|
+
"Details also has the agent role. The CLI token goes to the OS keychain, or to a file only you can",
|
|
349
|
+
"read when there is no keychain.",
|
|
322
350
|
"",
|
|
323
351
|
"With --pair, the code waits in the Connect dialog of the Orlan web app, and no page opens.",
|
|
324
352
|
"Check the code there, then approve it. The prompt that the dialog copies has the --pair id.",
|
|
@@ -465,7 +493,9 @@ export const commands = {
|
|
|
465
493
|
io.out(`Server: ${config.server}`);
|
|
466
494
|
io.out(`Signed in as: ${quote(status.user.name)} (${status.user.email ?? "guest account"})`);
|
|
467
495
|
io.out(`Organisation: ${quote(status.org.name)} (${status.org.role})`);
|
|
468
|
-
io.out(`New agents get: ${status.agentDefaults.role} on ${status.agentDefaults.
|
|
496
|
+
io.out(`New agents get: ${status.agentDefaults.role} on ${status.agentDefaults.allTopics
|
|
497
|
+
? "all topics, also new ones"
|
|
498
|
+
: status.agentDefaults.topics.map((topic) => quote(topic.name)).join(", ") || "no topics (use --topic)"}`);
|
|
469
499
|
if (agents.length === 0)
|
|
470
500
|
io.out("Agents: none connected.");
|
|
471
501
|
for (const { agent, token: local, role, check } of agents) {
|
|
@@ -505,7 +535,8 @@ export const commands = {
|
|
|
505
535
|
" other .agents/skills in the folder you are in",
|
|
506
536
|
"",
|
|
507
537
|
AGENT_HELP,
|
|
508
|
-
"--topic <id> A topic the agent can use. Repeat it for more.
|
|
538
|
+
"--topic <id> A topic the agent can use. Repeat it for more. The agent then gets only these topics.",
|
|
539
|
+
" Default: what you chose at sign-in (one topic, the ticked topics, or all topics).",
|
|
509
540
|
"--role <role> editor, commenter or viewer. Default: the role you chose at sign-in.",
|
|
510
541
|
],
|
|
511
542
|
options: CONNECT_OPTIONS,
|
|
@@ -947,8 +978,10 @@ export const commands = {
|
|
|
947
978
|
summary: "Print the board: its items with their ids and places, the timer, the votes and the open tasks.",
|
|
948
979
|
details: [
|
|
949
980
|
"Each item is one line: its id, its type, its place (x,y) and size (w x h) in board coordinates, and",
|
|
950
|
-
"its text. A note, a text and a shape also show a text size other than M
|
|
951
|
-
"
|
|
981
|
+
"its text. A note, a text and a shape also show a text size other than M and a text color other than ink,",
|
|
982
|
+
"and a shape its kind and color, a",
|
|
983
|
+
"file its name and pages, an arrow the items it links and its label. An arrow end at an exact spot of",
|
|
984
|
+
"its item shows <item>@x,y: x and y are from 0 to 1 of the item's box, 0,0 its top left corner. An item in a frame shows",
|
|
952
985
|
'"in frame <id>". Each box is the box after it grew to fit its words. People vote; an agent reads',
|
|
953
986
|
"the vote and its result, it does not vote.",
|
|
954
987
|
"",
|
|
@@ -969,7 +1002,7 @@ export const commands = {
|
|
|
969
1002
|
const items = board.items.map((item) => item.type === "file" ? { ...item, name: names.get(item.artifactId ?? "") ?? "" } : item);
|
|
970
1003
|
const { timer, vote, lastVote, tasks = [] } = board.session ?? {};
|
|
971
1004
|
const lines = [
|
|
972
|
-
`Board of ${quote(board.topic.name)} ${board.topic.id}: ${items.length} item${items.length === 1 ? "" : "s"}, text size M unless named.`,
|
|
1005
|
+
`Board of ${quote(board.topic.name)} ${board.topic.id}: ${items.length} item${items.length === 1 ? "" : "s"}, text size M and text color ink unless named.`,
|
|
973
1006
|
];
|
|
974
1007
|
if (items.some((item) => item.text) || vote || tasks.length > 0)
|
|
975
1008
|
lines.push(QUOTED_TEXT);
|
|
@@ -977,14 +1010,15 @@ export const commands = {
|
|
|
977
1010
|
for (const item of items) {
|
|
978
1011
|
const place = `at ${round(item.x)},${round(item.y)}, ${round(item.w)}x${round(item.h)}` +
|
|
979
1012
|
(item.frame ? ` in frame ${item.frame}` : "");
|
|
980
|
-
const end = (id, x, y) => id
|
|
1013
|
+
const end = (id, at, x, y) => (id ? (at ? `${id}@${at.x},${at.y}` : id) : `${round(x)},${round(y)}`);
|
|
981
1014
|
const words = item.text === undefined ? "" : `: ${quote(item.text)}`;
|
|
982
|
-
// The text size M
|
|
983
|
-
const size = item.size && item.size !== "M" ? ` size ${item.size}` : ""
|
|
1015
|
+
// The text size M and the text color ink are the defaults: only another one shows.
|
|
1016
|
+
const size = (item.size && item.size !== "M" ? ` size ${item.size}` : "") +
|
|
1017
|
+
(item.textColor && item.textColor !== "ink" ? ` text color ${item.textColor}` : "");
|
|
984
1018
|
lines.push(item.type === "shape"
|
|
985
1019
|
? `- ${item.id} shape ${item.kind} ${item.color}${size} ${place}${words}`
|
|
986
1020
|
: item.type === "arrow"
|
|
987
|
-
? `- ${item.id} arrow from ${end(item.from, item.x, item.y)} to ${end(item.to, item.x + item.w, item.y + item.h)}${item.text ? words : ""}`
|
|
1021
|
+
? `- ${item.id} arrow from ${end(item.from, item.fromAt, item.x, item.y)} to ${end(item.to, item.toAt, item.x + item.w, item.y + item.h)}${item.text ? words : ""}`
|
|
988
1022
|
: item.type === "file"
|
|
989
1023
|
? `- ${item.id} file ${quote(item.name ?? "")} (${item.artifactId}) ${place}, ` +
|
|
990
1024
|
`${item.pages?.length ?? 0} page${item.pages?.length === 1 ? "" : "s"}`
|
|
@@ -1065,7 +1099,7 @@ export const commands = {
|
|
|
1065
1099
|
}),
|
|
1066
1100
|
},
|
|
1067
1101
|
"board add": {
|
|
1068
|
-
usage: 'orlan board add note|text|frame|shape ["<text>"] --at <x,y> [--size <S|M|L|XL|w,h>] [--kind <kind>] [--color <color>] [--frame <frame>|none] | arrow --from <item> --to <item> [--text "<label>"] [--id <id>] [--topic <topic>] [--agent <id>] [--json]',
|
|
1102
|
+
usage: 'orlan board add note|text|frame|shape ["<text>"] --at <x,y> [--size <S|M|L|XL|w,h>] [--text-color <color>] [--kind <kind>] [--color <color>] [--frame <frame>|none] | arrow --from <item>[@x,y] --to <item>[@x,y] [--text "<label>"] [--id <id>] [--topic <topic>] [--agent <id>] [--json]',
|
|
1069
1103
|
summary: "Add a note, a text, a shape, a frame or an arrow to the board.",
|
|
1070
1104
|
details: [
|
|
1071
1105
|
"A note, a text, a shape or a frame goes at --at, with its text (a frame's text is its title, a",
|
|
@@ -1083,12 +1117,16 @@ export const commands = {
|
|
|
1083
1117
|
`--size <size> The text size of a note, a text or a shape: ${TEXT_SIZES_HELP}. Default: M. Or the`,
|
|
1084
1118
|
" width and the height as w,h. Default: the default size above, the size a person",
|
|
1085
1119
|
" gets.",
|
|
1120
|
+
`--text-color <color> The color of the words of a note, a text or a shape: ${TEXT_COLORS_HELP}.`,
|
|
1121
|
+
" Default: ink.",
|
|
1086
1122
|
`--kind <kind> A shape's kind: ${SHAPE_KINDS_HELP}. Default: rectangle.`,
|
|
1087
1123
|
`--color <color> A shape's color: ${SHAPE_COLORS_HELP}. Default: white.`,
|
|
1088
1124
|
'--id <id> The id of the new item, for example "api": 1 to 64 letters, digits, - or _. It',
|
|
1089
1125
|
" must be new on the board. Default: a new uuid.",
|
|
1090
|
-
"--from <item> The item the arrow starts at.",
|
|
1091
|
-
"
|
|
1126
|
+
"--from <item> The item the arrow starts at, on its edge. <item>@x,y: at that exact spot of the",
|
|
1127
|
+
" item, x and y from 0 to 1 of its box, for example slide@0.25,0.6 for a spot on a",
|
|
1128
|
+
" slide or an image. On a shape the end sits on its outline, on the line to the spot.",
|
|
1129
|
+
"--to <item> The item the arrow points to, as --from: <item> or <item>@x,y.",
|
|
1092
1130
|
"--frame <frame> The frame the item goes in, by its id. none: in no frame. Default: the frame that",
|
|
1093
1131
|
" holds more than half of it.",
|
|
1094
1132
|
'--text <text> The text, as the "<text>" word gives it. For an arrow: its label, for example "yes".',
|
|
@@ -1102,6 +1140,7 @@ export const commands = {
|
|
|
1102
1140
|
text: { type: "string" },
|
|
1103
1141
|
at: { type: "string" },
|
|
1104
1142
|
size: { type: "string" },
|
|
1143
|
+
"text-color": { type: "string" },
|
|
1105
1144
|
kind: { type: "string" },
|
|
1106
1145
|
color: { type: "string" },
|
|
1107
1146
|
from: { type: "string" },
|
|
@@ -1122,7 +1161,13 @@ export const commands = {
|
|
|
1122
1161
|
if (typeof values.from !== "string" || typeof values.to !== "string") {
|
|
1123
1162
|
throw new OrlanError("An arrow needs --from <item> and --to <item>.", undefined, "orlan board (the item ids)");
|
|
1124
1163
|
}
|
|
1125
|
-
edit = {
|
|
1164
|
+
edit = {
|
|
1165
|
+
op: "create",
|
|
1166
|
+
type,
|
|
1167
|
+
...arrowEndOption(values, "from"),
|
|
1168
|
+
...arrowEndOption(values, "to"),
|
|
1169
|
+
...(text !== undefined ? { text } : {}),
|
|
1170
|
+
};
|
|
1126
1171
|
}
|
|
1127
1172
|
else if (type === "note" || type === "text" || type === "frame" || type === "shape") {
|
|
1128
1173
|
const at = numbersOption(values, "at", 2);
|
|
@@ -1140,7 +1185,10 @@ export const commands = {
|
|
|
1140
1185
|
else {
|
|
1141
1186
|
throw new OrlanError("The item is a note, a text, a shape, a frame or an arrow.", undefined, usage);
|
|
1142
1187
|
}
|
|
1143
|
-
// The server refuses a kind or a color on an item that is not a shape, and
|
|
1188
|
+
// The server refuses a kind or a color on an item that is not a shape, and a text color on an item
|
|
1189
|
+
// with no text color, and says why.
|
|
1190
|
+
if (typeof values["text-color"] === "string")
|
|
1191
|
+
edit.textColor = values["text-color"];
|
|
1144
1192
|
if (typeof values.kind === "string")
|
|
1145
1193
|
edit.kind = values.kind;
|
|
1146
1194
|
if (typeof values.color === "string")
|
|
@@ -1160,8 +1208,8 @@ export const commands = {
|
|
|
1160
1208
|
},
|
|
1161
1209
|
},
|
|
1162
1210
|
"board change": {
|
|
1163
|
-
usage: 'orlan board change <item> [--text "<text>"] [--at <x,y>] [--size <S|M|L|XL|w,h>] [--kind <kind>] [--color <color>] [--from <item
|
|
1164
|
-
summary: "Change an item of the board: its text, place, size, frame, a shape's kind and color, or the ends of an arrow.",
|
|
1211
|
+
usage: 'orlan board change <item> [--text "<text>"] [--at <x,y>] [--size <S|M|L|XL|w,h>] [--text-color <color>] [--kind <kind>] [--color <color>] [--from <item>[@x,y]|none] [--to <item>[@x,y]|none] [--frame <frame>|none] [--topic <topic>] [--agent <id>] [--json]',
|
|
1212
|
+
summary: "Change an item of the board: its text, place, size, text color, frame, a shape's kind and color, or the ends of an arrow.",
|
|
1165
1213
|
details: [
|
|
1166
1214
|
"The changes are one step, which a person can undo. A file on the board only moves (--at) and goes",
|
|
1167
1215
|
"in a frame or out of it (--frame). A note, a text and a shape grow to fit their new words or size.",
|
|
@@ -1176,10 +1224,13 @@ export const commands = {
|
|
|
1176
1224
|
" with =: --at=-50,120.",
|
|
1177
1225
|
`--size <size> The new text size of a note, a text or a shape: ${TEXT_SIZES_HELP}. Or the new width`,
|
|
1178
1226
|
" and height as w,h.",
|
|
1227
|
+
`--text-color <color> The new color of the words of a note, a text or a shape: ${TEXT_COLORS_HELP}.`,
|
|
1179
1228
|
`--kind <kind> A shape's new kind: ${SHAPE_KINDS_HELP}.`,
|
|
1180
1229
|
`--color <color> A shape's new color: ${SHAPE_COLORS_HELP}.`,
|
|
1181
|
-
"--from <item> The item the arrow starts at
|
|
1182
|
-
"
|
|
1230
|
+
"--from <item> The item the arrow starts at, on its edge. <item>@x,y: at that exact spot of the",
|
|
1231
|
+
" item, x and y from 0 to 1 of its box (on a shape: on its outline, on the line to the",
|
|
1232
|
+
" spot). none: the end stays where it is, on no item.",
|
|
1233
|
+
"--to <item> The item the arrow points to, as --from.",
|
|
1183
1234
|
"--frame <frame> Put the item in this frame, by its id. none: take it out of its frame. The item",
|
|
1184
1235
|
" stays where it is. With --at and no --frame, the item goes in the frame it lands in.",
|
|
1185
1236
|
"--topic <topic> The topic id or its name. Default: the topic the agent used last, else its only topic.",
|
|
@@ -1192,6 +1243,7 @@ export const commands = {
|
|
|
1192
1243
|
text: { type: "string" },
|
|
1193
1244
|
at: { type: "string" },
|
|
1194
1245
|
size: { type: "string" },
|
|
1246
|
+
"text-color": { type: "string" },
|
|
1195
1247
|
kind: { type: "string" },
|
|
1196
1248
|
color: { type: "string" },
|
|
1197
1249
|
from: { type: "string" },
|
|
@@ -1202,19 +1254,14 @@ export const commands = {
|
|
|
1202
1254
|
positionals: [1, 1],
|
|
1203
1255
|
run: (values, [id], io) => {
|
|
1204
1256
|
const at = numbersOption(values, "at", 2);
|
|
1205
|
-
const end = (option) => {
|
|
1206
|
-
const value = values[option];
|
|
1207
|
-
if (typeof value !== "string")
|
|
1208
|
-
return {};
|
|
1209
|
-
return { [option]: value === "none" ? null : value };
|
|
1210
|
-
};
|
|
1211
1257
|
const update = {
|
|
1212
1258
|
...(typeof values.text === "string" ? { text: values.text } : {}),
|
|
1213
1259
|
...sizeOption(values),
|
|
1260
|
+
...(typeof values["text-color"] === "string" ? { textColor: values["text-color"] } : {}),
|
|
1214
1261
|
...(typeof values.kind === "string" ? { kind: values.kind } : {}),
|
|
1215
1262
|
...(typeof values.color === "string" ? { color: values.color } : {}),
|
|
1216
|
-
...
|
|
1217
|
-
...
|
|
1263
|
+
...arrowEndOption(values, "from"),
|
|
1264
|
+
...arrowEndOption(values, "to"),
|
|
1218
1265
|
...frameOption(values),
|
|
1219
1266
|
};
|
|
1220
1267
|
const edits = [
|
|
@@ -1222,7 +1269,7 @@ export const commands = {
|
|
|
1222
1269
|
...(Object.keys(update).length > 0 ? [{ op: "update", id, ...update }] : []),
|
|
1223
1270
|
];
|
|
1224
1271
|
if (edits.length === 0) {
|
|
1225
|
-
throw new OrlanError("Give --text, --at, --size, --kind, --color, --from, --to or --frame.", undefined, `orlan board change ${id} --text "<text>"`);
|
|
1272
|
+
throw new OrlanError("Give --text, --at, --size, --text-color, --kind, --color, --from, --to or --frame.", undefined, `orlan board change ${id} --text "<text>"`);
|
|
1226
1273
|
}
|
|
1227
1274
|
return withAgent(values, async (api) => {
|
|
1228
1275
|
const { items, warnings } = await editBoard(api, values, edits);
|
|
@@ -1266,12 +1313,13 @@ export const commands = {
|
|
|
1266
1313
|
' {"op":"create","type":"arrow","from":"web","to":"api","text":"calls"}]',
|
|
1267
1314
|
"",
|
|
1268
1315
|
"create type note, text, shape or frame with x, y and text (and w, h, frame); a note, a text and a",
|
|
1269
|
-
` shape also size (${TEXT_SIZES_HELP}; default M)
|
|
1270
|
-
" from and to, and text (its label). id: 1 to 64 letters,
|
|
1316
|
+
` shape also size (${TEXT_SIZES_HELP}; default M) and textColor (default ink); a shape also`,
|
|
1317
|
+
" kind and color; type arrow with from and to, and text (its label). id: 1 to 64 letters,",
|
|
1318
|
+
" digits, - or _, new on the board.",
|
|
1271
1319
|
" Default: a new uuid.",
|
|
1272
1320
|
"move id, x, y. A frame moves with its items.",
|
|
1273
|
-
"update id, and text (an arrow: its label), w, h, size (a note, a text, a shape), kind,
|
|
1274
|
-
" shape), from and to (null: on no item), or frame (null: in no frame).",
|
|
1321
|
+
"update id, and text (an arrow: its label), w, h, size, textColor (a note, a text, a shape), kind,",
|
|
1322
|
+
" color (a shape), from and to (null: on no item), or frame (null: in no frame).",
|
|
1275
1323
|
"delete id. A file on the board only moves, and takes a frame.",
|
|
1276
1324
|
"",
|
|
1277
1325
|
BOARD_SIZES_HELP,
|
|
@@ -1279,6 +1327,8 @@ export const commands = {
|
|
|
1279
1327
|
...FRAMES_HELP,
|
|
1280
1328
|
`Shape kinds: ${SHAPE_KINDS_HELP}. Default: rectangle.`,
|
|
1281
1329
|
`Shape colors: ${SHAPE_COLORS_HELP}. Default: white.`,
|
|
1330
|
+
`Text colors: ${TEXT_COLORS_HELP}. Default: ink. The same names as the shape colors, in a strong`,
|
|
1331
|
+
"shade that reads on a note, on a shape and on the board. White reads only on a dark background.",
|
|
1282
1332
|
"An arrow goes from the middle of one item to the middle of the other, and cannot end on an arrow.",
|
|
1283
1333
|
"Its text is its label, on the middle of the arrow.",
|
|
1284
1334
|
"An unknown key, or a key the item cannot use (x on an arrow), is refused: nothing changes, and",
|
|
@@ -1323,10 +1373,13 @@ export const commands = {
|
|
|
1323
1373
|
"Each node and subgraph keeps its Mermaid id as its item id, so `orlan board change <id>` changes it",
|
|
1324
1374
|
"later. An id that is on the board already gets a prefix, m2-, m3-, and so on; the other ids stay as",
|
|
1325
1375
|
"they are. The answer gives each id.",
|
|
1326
|
-
"With --frame, the diagram goes in that frame,
|
|
1327
|
-
"
|
|
1328
|
-
"
|
|
1329
|
-
"in the frame stay, and do not move.",
|
|
1376
|
+
"With --frame, the diagram goes in that frame, and the frame fits it: it grows or shrinks to hold",
|
|
1377
|
+
"the diagram and the other items in it, with 24 px of room round them. It replaces the items your",
|
|
1378
|
+
"agent drew in that frame before, and the arrows to them, so a second import of a changed file",
|
|
1379
|
+
"changes the diagram in place and keeps its ids. A person's items in the frame stay, and do not move.",
|
|
1380
|
+
"Subgraphs that are stacked or side by side line up: one left edge and one width for a stack, one",
|
|
1381
|
+
"top and one height for a row, with even gaps. Subgraphs that no link joins go in the order of the",
|
|
1382
|
+
"file: so variants to compare (2-tier, 3-tier, 4-tier) are one file, one subgraph each.",
|
|
1330
1383
|
"",
|
|
1331
1384
|
"What works: the first line is flowchart or graph with TD, TB, BT, LR or RL.",
|
|
1332
1385
|
" A[text] rectangle, A(text) and A([text]) rounded, A((text)) ellipse, A{text} diamond,",
|
|
@@ -1334,10 +1387,14 @@ export const commands = {
|
|
|
1334
1387
|
' and A(((text))) take the nearest of these. "text" in quotes can hold brackets; <br> is a new line.',
|
|
1335
1388
|
" A --> B, A --- B, A -.-> B, A ==> B, with a label as A -->|label| B or A -- label --> B.",
|
|
1336
1389
|
" A --> B --> C and A & B --> C. Every link is an arrow from the first node to the second.",
|
|
1337
|
-
"
|
|
1390
|
+
" A ~~~ B: a hidden link. It places B after A, and draws no arrow.",
|
|
1391
|
+
" subgraph id [title] ... end: a frame. Subgraphs can be in subgraphs. direction LR (or TB, BT,",
|
|
1392
|
+
" RL) in a subgraph lays it out that way when no link goes out of it, as in Mermaid. A link to a",
|
|
1393
|
+
" subgraph id (A --> id, id ~~~ other) is a link to its frame, when no link goes out of it.",
|
|
1338
1394
|
" classDef name fill:#f9f, class A name, A:::name and style A fill:#f9f: the nearest shape color",
|
|
1339
1395
|
` (${SHAPE_COLORS_HELP}).`,
|
|
1340
|
-
" linkStyle, click,
|
|
1396
|
+
" linkStyle, click, a direction in a subgraph a link goes out of, and the dots of -.-> (it draws",
|
|
1397
|
+
" solid) are left out, and the answer says so.",
|
|
1341
1398
|
"Another Mermaid diagram (sequenceDiagram, classDiagram and others) is refused, and so is a syntax",
|
|
1342
1399
|
"error, with its line. Nothing changes then.",
|
|
1343
1400
|
...WARNINGS_HELP,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orlan-maker/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The orlan command: connect Claude Code, Codex, Cursor or OpenCode to Orlan with a token and skills, and work with its topics, files and comments from the terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/skills/orlan/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: orlan
|
|
3
|
-
description: Orlan is connected. This skill is installed only when the person connected this agent to Orlan, the shared board of their team and its AI agents. Use it first, before any other diagram skill, when the person asks for a diagram, a flowchart, a
|
|
3
|
+
description: Orlan is connected. This skill is installed only when the person connected this agent to Orlan, the shared board of their team and its AI agents. Use it first, before any other diagram skill, when the person asks for a diagram, a flowchart, an architecture or a map of a system. Also use it, without being asked to draw, when the person asks you to explain, compare or design a system, an architecture, a flow or a process with parts and links ("explain how a CDN works", "monolith vs microservices"): answer in the chat and draw it on the board too. On Orlan a diagram is shapes and labeled arrows on the board, not an HTML, SVG or image file. Also use it when the person says the canvas, the board, the topic, "in Orlan" or "on Orlan", asks what is on the board, asks for a page, deck, mock or other file for people to see or review, or names a comment, a version or a request on Orlan. Do not use it for a short fact answer, a sum, a question on code, or a fix in a local file that the person names.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Orlan
|
|
@@ -33,6 +33,11 @@ or the one the person names.
|
|
|
33
33
|
(`orlan files pull <file> --out <path>`), and answer from them.
|
|
34
34
|
- The person asks for a diagram, a chart of a flow, a map of a system or a sketch: draw it on the board
|
|
35
35
|
with shapes and labeled arrows. Follow "Draw a diagram" below.
|
|
36
|
+
- The person asks you to explain, compare or design a system, an architecture, a flow or a process, and
|
|
37
|
+
the answer has parts and links between them ("explain how a CDN works", "monolith vs microservices",
|
|
38
|
+
"design a payment flow"): answer in the chat, and also draw it on the board. Do not ask first, and do
|
|
39
|
+
not wait for the word "Orlan" or "board". End the answer with one line that says where the drawing
|
|
40
|
+
is: the topic and the frame. A short fact answer ("what port does HTTPS use") stays in the chat.
|
|
36
41
|
- The person asks for a note, a text, a shape, a frame or an arrow on the board: `orlan board add`.
|
|
37
42
|
Change one with `orlan board change <item>`, delete one with `orlan board delete <item>`.
|
|
38
43
|
- The person asks you to make something else that people look at: a page, a mock, a deck, a report, a
|
|
@@ -54,40 +59,46 @@ diagram, do not use notes as boxes, and do not use another diagram skill or tool
|
|
|
54
59
|
make a frame at a free place (right of or below the items `orlan board` shows), and draw it in the frame:
|
|
55
60
|
`orlan board add frame "<title>" --id <frame> --at <x,y>`, then
|
|
56
61
|
`orlan board mermaid <file.mmd> --frame <frame>`. Use `subgraph` for the groups, a diamond `{...}`
|
|
57
|
-
for a decision, a cylinder `[(...)]` for data.
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
for a decision, a cylinder `[(...)]` for data. The frame fits the diagram.
|
|
63
|
+
3. One picture is ONE Mermaid file and one import, also when it has several parts (a comparison,
|
|
64
|
+
variants, before and after, layers): one `subgraph` for each part, with no links between the parts.
|
|
65
|
+
`flowchart LR` stacks the parts as rows, `flowchart TB` puts them side by side; Orlan lines them up
|
|
66
|
+
in the order of the file. A key or a compare box is shapes in a subgraph of its own (`a ~~~ b` puts
|
|
67
|
+
two boxes side by side with no arrow), not a text item. All words keep one size.
|
|
68
|
+
A login, a payment or another exchange of messages between parties: one box for each step, in
|
|
69
|
+
order, that says who does what ("App sends the code to the auth server"), with a subgraph for each
|
|
70
|
+
phase. Do not draw each party once with arrows back and forth: the lines cross and the order is lost.
|
|
71
|
+
4. Do not move, resize or add items by hand after the import. To change the picture, change the file
|
|
72
|
+
and run the same command again: it replaces your old version and fits the frame again.
|
|
73
|
+
5. Another diagram (a grid, a layout, a timeline): `orlan board edit '<JSON>'` with the shapes, the
|
|
60
74
|
arrows and their labels in one step, each with an id of your own.
|
|
61
|
-
|
|
75
|
+
6. Read the warnings in the answer. Fix each one in the file (change `LR` to `TB`, make the boxes
|
|
62
76
|
fewer) and draw again, until the answer has no warning.
|
|
63
|
-
|
|
77
|
+
7. Look at it: `orlan board view --out <file.png> --frame <frame>`, open the picture, and fix words that
|
|
64
78
|
are cut, boxes that touch and arrows that are hard to follow.
|
|
65
|
-
|
|
66
|
-
or change each shape.
|
|
79
|
+
8. Tell the person what is on the board (the topic, the frame) in one line after your answer.
|
|
67
80
|
|
|
68
|
-
Example for "
|
|
81
|
+
Example for "monolith vs microservices", in `compare.mmd`:
|
|
69
82
|
|
|
70
83
|
```
|
|
71
84
|
flowchart LR
|
|
72
|
-
subgraph
|
|
73
|
-
|
|
85
|
+
subgraph mono [Monolith]
|
|
86
|
+
u1[Users] -->|HTTPS| app1[One app] -->|SQL| db1[(One database)]
|
|
74
87
|
end
|
|
75
|
-
subgraph
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
subgraph micro [Microservices]
|
|
89
|
+
u2[Users] -->|HTTPS| gw[API gateway] -->|REST| orders[Order service] -->|SQL| db2[(Orders DB)]
|
|
90
|
+
end
|
|
91
|
+
subgraph key [Compare]
|
|
92
|
+
k1[Monolith: simple to run] ~~~ k2[Microservices: scale each part]
|
|
78
93
|
end
|
|
79
|
-
spec -->|design schema| db
|
|
80
|
-
ui -->|run| test{Tests pass?}
|
|
81
|
-
test -->|no| api
|
|
82
|
-
test -->|yes| deploy[Deploy and monitor]
|
|
83
94
|
```
|
|
84
95
|
|
|
85
|
-
Then `orlan board add frame "
|
|
86
|
-
`orlan board mermaid
|
|
96
|
+
Then `orlan board add frame "Monolith vs microservices" --id compare --at <x,y>`,
|
|
97
|
+
`orlan board mermaid compare.mmd --frame compare` and `orlan board view --out compare.png --frame compare`.
|
|
87
98
|
|
|
88
99
|
## When not to use Orlan
|
|
89
100
|
|
|
90
|
-
Do not call Orlan for work that has no link to the board: a sum, a
|
|
101
|
+
Do not call Orlan for work that has no link to the board: a sum, a short fact answer, a question on
|
|
91
102
|
code, a fix in a local file the person names, a test run. Do the work and answer. Do not post a file
|
|
92
103
|
the person did not ask for.
|
|
93
104
|
|
|
@@ -118,13 +129,20 @@ show it, and every other command fails with `agent_stopped`. When you see it:
|
|
|
118
129
|
- `orlan board add note|text|frame "<text>" --at <x,y>`,
|
|
119
130
|
`orlan board add shape "<text>" --kind <kind> --color <color> --at <x,y>`,
|
|
120
131
|
`orlan board add arrow --from <item> --to <item> --text "<label>"`, `orlan board change <item>`,
|
|
121
|
-
`orlan board delete <item>` - change the board. A person can undo each change. `orlan board edit '<JSON>'` makes several changes in one step; give each new item an
|
|
132
|
+
`orlan board delete <item>` - change the board. A person can undo each change, or accept it: that takes your name label off its items, and the items still say you made them. Only a person accepts. `orlan board edit '<JSON>'` makes several changes in one step; give each new item an
|
|
122
133
|
`"id"` of your own (`"api"`) and let the arrows of the same step use it. A note, a text and a shape
|
|
123
|
-
take a text size, `--size S|M|L|XL` (`"size"` in the JSON; default M)
|
|
134
|
+
take a text size, `--size S|M|L|XL` (`"size"` in the JSON; default M), and a text color for their
|
|
135
|
+
words, `--text-color <color>` (`"textColor"`): ink (default) or a shape color name - white, gray,
|
|
136
|
+
blue, teal, green, yellow, orange, red. A color that a person cannot read on its fill in their theme
|
|
137
|
+
(white on a light note) draws in the ink of that theme. Their box grows to fit their
|
|
124
138
|
words, so the answer gives the final box: place the next item from that box, not from the size you asked.
|
|
125
139
|
A frame holds the items in it and moves with them: draw the frame, then put items in it
|
|
126
140
|
(`--frame <frame>`, `"frame"` in the JSON), and move the frame to move the group. `--frame none`
|
|
127
141
|
takes an item out.
|
|
142
|
+
An arrow ends on the edge of its item. To point at an exact spot of a slide, an image or a shape,
|
|
143
|
+
give `--to <item>@x,y` (or `--from`), x and y from 0 to 1 of the item's box (`"toAt": {"x", "y"}` in
|
|
144
|
+
the JSON). The spot moves with the item. On a shape the end sits on the outline, where the line to
|
|
145
|
+
the spot enters the shape, so arrows to different spots of one shape stay apart.
|
|
128
146
|
- `orlan board mermaid <file.mmd> [--frame <frame>]` - draws a Mermaid flowchart as shapes, labeled
|
|
129
147
|
arrows and frames.
|
|
130
148
|
- `orlan board view --out <file.png> [--frame <frame>]` - a PNG picture of the board as people see it.
|
|
@@ -142,6 +160,8 @@ Run `orlan <command> --help` for the options of a command.
|
|
|
142
160
|
- "create a diagram showing how to make a full stack app" - draw it in a frame with
|
|
143
161
|
`orlan board mermaid`, as in "Draw a diagram", check the warnings and the picture, and say what is on
|
|
144
162
|
the board. No HTML file, no notes.
|
|
163
|
+
- "explain how a CDN works" - explain it in the chat, draw the parts and their links in a frame as in
|
|
164
|
+
"Draw a diagram", and end with one line: "I drew it on the board of <topic>, in the frame <title>."
|
|
145
165
|
- "put the notes on the board" - post the notes as a file, or add them as a note:
|
|
146
166
|
`orlan board add note "<text>" --at <x,y>`, at a free place that `orlan board` shows.
|
|
147
167
|
- "what is 17*23?" - answer 391. No Orlan call.
|