@taloswork/ose-shared 0.1.15 → 0.1.16

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.
@@ -37319,8 +37319,31 @@ ${TEMPLATE_CENTER_JS}
37319
37319
  continue;
37320
37320
  }
37321
37321
  const fill = color(cs.backgroundColor);
37322
- const bw = parseFloat(cs.borderTopWidth);
37323
- const borderColor = bw > 0 && cs.borderTopStyle !== "none" ? color(cs.borderTopColor) : undefined;
37322
+ // Borders, per side. Four equal sides \u2192 the rect's own outline. Otherwise
37323
+ // each painted side is its own bar filled with the border color, on the
37324
+ // border-box edge (a lone left rule on a takeaway, a top rule on a card):
37325
+ // reading the top side alone dropped a left rule and outlined all four
37326
+ // sides of a top-only one. The bars are square-ended \u2014 a rounded corner's
37327
+ // curve is not followed.
37328
+ const sides = ["Top", "Right", "Bottom", "Left"].map((s) => {
37329
+ const w = parseFloat(cs["border" + s + "Width"]);
37330
+ const st = cs["border" + s + "Style"];
37331
+ const c = w > 0 && st !== "none" && st !== "hidden" ? color(cs["border" + s + "Color"]) : undefined;
37332
+ return c ? { w, st, c } : null;
37333
+ });
37334
+ const s0 = sides[0];
37335
+ const uniform = s0 && sides.every((s) => s && s.w === s0.w && s.st === s0.st && s.c === s0.c);
37336
+ const borderColor = uniform ? s0.c : undefined;
37337
+ const bw = uniform ? s0.w : 0;
37338
+ const borderBars = uniform ? [] : sides.flatMap((s, i) => {
37339
+ if (!s) return [];
37340
+ const f = rel(r);
37341
+ const bar = i === 0 ? { x: f.x, y: f.y, w: f.w, h: s.w }
37342
+ : i === 1 ? { x: f.x + f.w - s.w, y: f.y, w: s.w, h: f.h }
37343
+ : i === 2 ? { x: f.x, y: f.y + f.h - s.w, w: f.w, h: s.w }
37344
+ : { x: f.x, y: f.y, w: s.w, h: f.h };
37345
+ return [{ kind: "rect", frame: bar, fill: s.c, radiusPx: 0, ...(ea ? { anim: ea } : {}) }];
37346
+ });
37324
37347
  // box-shadow \u2192 native pptx outer shadow: the FIRST NON-INSET layer (computed
37325
37348
  // serialization is "color offX offY blur spread[ inset]", layers comma-joined).
37326
37349
  // An inset layer beside it (cool/apple cards' 1px top highlight) is dropped \u2014
@@ -37358,13 +37381,14 @@ ${TEMPLATE_CENTER_JS}
37358
37381
  elements.push({
37359
37382
  kind: "rect", frame: rel(r), fill, borderColor,
37360
37383
  ...(borderColor ? { borderWidthPx: bw } : {}),
37361
- ...(borderColor && (cs.borderTopStyle === "dashed" || cs.borderTopStyle === "dotted") ? { borderDash: true } : {}),
37384
+ ...(borderColor && (s0.st === "dashed" || s0.st === "dotted") ? { borderDash: true } : {}),
37362
37385
  ...(shadow ? { shadow } : {}),
37363
37386
  ...(gradient ? { gradient } : {}),
37364
37387
  radiusPx: parseFloat(cs.borderTopLeftRadius) || 0,
37365
37388
  ...(ea ? { anim: ea } : {}),
37366
37389
  });
37367
37390
  }
37391
+ elements.push(...borderBars);
37368
37392
  walk(child);
37369
37393
  }
37370
37394
  };
@@ -60,4 +60,4 @@ Flags and outputs only; the workflow is deck-design.md.
60
60
  ## Validate and convert
61
61
 
62
62
  - `bin/doc-validate.cjs <file.docx|pptx|xlsx|pdf> [--strict] [--design-lint]` — structural validation gate: OOXML zip/parts/rels/Content_Types/XML well-formedness, plus pptx `notes-keynote` (→ `pptx-notes <in> {} <out>`) and docx `docx-default-style` (→ `docx-defaults`); a PDF must parse, open without a password (`pdf-encrypted` error: validate before `pdf-ops encrypt`) and hold at least one page (reports `pages` and a `pdf-parse-only` warning — how it looks is render-and-look's job). Exit 1 = broken, fix before delivering. `--strict` (OOXML only; on a PDF it just adds a `strict-not-applicable` warning) adds full ECMA-376 schema (XSD) validation of every recognised part — REQUIRED after custom-code (doc-run) or raw-XML output; a strict failure means the file is non-standard even if Office opens it. `--design-lint` (pptx only) adds deck-qa's lint as advisory warnings (never changes the exit code; codes under deck-qa in Decks) — real design bugs, fix them.
63
- - `bin/doc-convert.cjs <in.docx|pptx|xlsx> <out.pdf> [--cache-dir <dir>] [--require-office]` — Office → PDF. **PPTX requires Office**: local `soffice`, then `TALOS_OFFICE_URL` sidecar. Missing Office exits 69; failed Office exits 75. Never rebuild a deck as browser HTML. Spreadsheets also require Office. DOCX can fall back to reconstructed HTML via the app's Chromium or headless Chromium (content retained, layout lost); `--require-office` blocks this fallback. Report the `[pdf (engine)]` result and any caveat. `--cache-dir` (or `TALOS_OFFICE_CACHE_DIR`) reuses the local Office result for identical source bytes; after any PPTX edit, convert again. Fonts come from the Office environment, which must have the required faces for faithful typography.
63
+ - `bin/doc-convert.cjs <in.docx|pptx|xlsx> <out.pdf> [--cache-dir <dir>] [--require-office]` — Office → PDF. **PPTX requires Office**: local `soffice`, then Keynote (Mac) / PowerPoint (Win) for .pptx, then `TALOS_OFFICE_URL` sidecar. Missing Office exits 69; failed Office exits 75. Never rebuild a deck as browser HTML. Spreadsheets also require Office. DOCX can fall back to reconstructed HTML via the app's Chromium or headless Chromium (content retained, layout lost); `--require-office` blocks this fallback. Report the `[pdf (engine)]` result and any caveat. `--cache-dir` (or `TALOS_OFFICE_CACHE_DIR`) reuses the local Office result for identical source bytes; after any PPTX edit, convert again. Fonts come from the Office environment, which must have the required faces for faithful typography.
@@ -0,0 +1,7 @@
1
+ © 2026 Nexora AI Limited. All rights reserved.
2
+
3
+ LICENSE: This Skill (including all prompts, reference documents, and other
4
+ components) is proprietary to Nexora AI Limited and provided solely for use
5
+ within the Talos application under your agreement with Nexora AI Limited.
6
+ Redistribution, extraction, or use outside Talos is not permitted without
7
+ prior written consent.
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: expert-create
3
+ description: "Use when the user wants to create, edit or delete their own expert (an OSE: a named assistant persona with its own role prompt, shown in the expert switcher). Triggers: create an expert / make a new expert / build an assistant for X / edit my expert / change the expert's role / delete my expert, 新建专家 / 创建一个专家 / 做一个XX专家 / 修改专家 / 改一下专家的角色 / 删除专家. Not for installing skills (install_skill) or for switching experts."
4
+ license: Proprietary. LICENSE.txt has complete terms
5
+ ---
6
+
7
+ # Create, edit and delete local experts
8
+
9
+ An expert is a persona the user switches to: a name, a one-line description,
10
+ an optional English label, up to four starter prompts, and a role prompt
11
+ (`role.md`) that becomes the opening of every conversation with it. Experts the
12
+ user makes here are **local**: only on this machine, editable and deletable.
13
+ Published experts cannot be changed here.
14
+
15
+ Everything goes through one command, where `<skill>` is this skill's
16
+ directory. The Talos app validates, writes and refreshes the expert list; never
17
+ create or edit expert files yourself, and never state where they live.
18
+
19
+ ```
20
+ node <skill>/bin/expert-create.cjs list
21
+ node <skill>/bin/expert-create.cjs show <id>
22
+ node <skill>/bin/expert-create.cjs create <draft.json>
23
+ node <skill>/bin/expert-create.cjs update <id> <patch.json>
24
+ node <skill>/bin/expert-create.cjs delete <id>
25
+ ```
26
+
27
+ `create`, `update` and `delete` each wait for the user to confirm in a Talos
28
+ dialog that shows what will change; agreeing in the chat does not replace it.
29
+ The dialog stays open for up to 5 minutes (300 seconds), so run these three
30
+ without a `timeout`, or with one of at least 330 seconds. A shorter timeout
31
+ kills the command while the user is still reading the dialog: the dialog
32
+ closes, nothing is written, and running it again opens a second one.
33
+
34
+ Exit codes:
35
+
36
+ - 0 done (JSON on stdout).
37
+ - 1 the app refused or failed (read stderr; if it names a field, fix it and
38
+ retry).
39
+ - 3 the user said no in the dialog; nothing was written.
40
+ - 4 the dialog went unanswered and closed after 5 minutes; nothing was written.
41
+ - 5 `update` would change nothing: the patch matches the expert as it is. No
42
+ dialog was shown and nothing was written; tell the user it already reads
43
+ that way.
44
+ - 64 usage; 66/65 the JSON file could not be read or parsed.
45
+ - 69 the Talos app is not reachable or not ready — not running inside Talos,
46
+ this is a scheduled task, a sub-agent or a channel session (which cannot
47
+ change experts), or Talos was starting or switched profile. Nothing was
48
+ written. Do not retry; tell the user, and do not work around it.
49
+
50
+ ## Draft JSON
51
+
52
+ ```json
53
+ {
54
+ "name": "Sales Coach",
55
+ "desc": "Coaches sales calls",
56
+ "role": "…the full role.md…",
57
+ "en": { "name": "Sales Coach", "desc": "Coaches sales calls" },
58
+ "demos": [{ "label": "Review a call", "output": "PDF", "icon": "search", "seed": "…" }]
59
+ }
60
+ ```
61
+
62
+ Limits the app enforces: `name`, `desc`, `en.name`, `en.desc` and each demo
63
+ `label` are one line (no line breaks); `name` ≤ 24 characters; `desc` ≤ 20
64
+ characters when it contains Chinese/Japanese/Korean text or full-width
65
+ punctuation, otherwise ≤ 38; `en.name` ≤ 40, `en.desc` ≤ 38; `role` up to 32 KiB; at most 4 `demos`, each with a
66
+ `label` and a `seed` (≤ 420 characters, a prompt that stands alone), `icon` one
67
+ of search, file, image, clock, code, chart, table, bug. A patch for `update`
68
+ holds only the fields that change.
69
+
70
+ ## Creating
71
+
72
+ 1. Ask, in one message, what the expert is for: who uses it, the work it does,
73
+ the output it should produce, and anything it must or must not do. Do not
74
+ ask one question at a time.
75
+ 2. Draft every field. Write `role` following `references/role-guide.md`.
76
+ Write the name, description and demos in the language the user speaks; add
77
+ `en` when that language is not English.
78
+ 3. Show the user the name, description, demos and the full role, and revise
79
+ until they confirm. Do not create before they confirm.
80
+ 4. Write the draft to a JSON file in the working directory and run `create`;
81
+ Talos asks the user to confirm. Exit 3 means they said no — ask what to
82
+ change. On success, report the expert's name. Offer to switch to it — switching is the user's
83
+ action in the expert switcher.
84
+ 5. The new expert starts with the shared skills only. If the work needs more,
85
+ say which skills would help; installing them is a separate step.
86
+
87
+ ## Editing
88
+
89
+ Run `show <id>` (get the id from `list`), change only what the user asked for,
90
+ show the before and after of each changed field, and after confirmation run
91
+ `update` with a patch of just those fields. Talos shows the user the change in
92
+ its own dialog and keeps the version it replaces; exit 3 means they said no.
93
+ An edit to the expert in use takes effect from the next message.
94
+
95
+ ## Deleting
96
+
97
+ Say what goes: the expert itself. Its conversations stay on this machine but will no longer appear in Talos. Deleting the expert currently in use
98
+ switches the user back to the default expert once this reply ends. Deleting
99
+ the expert this conversation is with ends the conversation: finish your reply,
100
+ and tell the user to continue with another expert, because Talos refuses any
101
+ further message here.
102
+ Then run `delete <id>`; Talos asks the user to confirm in its own dialog. Exit 3
103
+ means they said no — leave it there.
104
+
105
+ For any of the three, exit 4 means the dialog went unanswered and closed after
106
+ 5 minutes; nothing was written — ask the user before trying again.
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // expert-create — thin client for the Talos app's local expert service.
3
+ // All validation and every file write happen in the app; this only forwards.
4
+ //
5
+ // expert-create list
6
+ // expert-create show <id>
7
+ // expert-create create <draft.json> (Talos asks the user to confirm)
8
+ // expert-create update <id> <patch.json> (Talos asks the user to confirm)
9
+ // expert-create delete <id> (Talos asks the user to confirm)
10
+ "use strict";
11
+ const { readFileSync } = require("node:fs");
12
+ const http = require("node:http");
13
+
14
+ function die(message, code) { process.stderr.write(`expert-create: ${message}\n`); process.exit(code); }
15
+ const USAGE = "usage: expert-create list | show <id> | create <draft.json> | update <id> <patch.json> | delete <id>";
16
+
17
+ function readJson(path) {
18
+ let raw;
19
+ try { raw = readFileSync(path, "utf8"); } catch (e) { die(`cannot read ${path}: ${e.message}`, 66); }
20
+ try { return JSON.parse(raw); } catch (e) { die(`${path} is not valid JSON: ${e.message}`, 65); }
21
+ }
22
+
23
+ function request(argv) {
24
+ const [action, a, b] = argv;
25
+ switch (action) {
26
+ case "list": return { action };
27
+ case "show": case "delete": if (!a) die(USAGE, 64); return { action, id: a };
28
+ case "create": if (!a) die(USAGE, 64); return { action, draft: readJson(a) };
29
+ case "update": if (!a || !b) die(USAGE, 64); return { action, id: a, patch: readJson(b) };
30
+ default: die(USAGE, 64);
31
+ }
32
+ }
33
+
34
+ // What a refused or unanswered confirmation left undone, per action.
35
+ const UNDONE = {
36
+ create: ["creation", "created"],
37
+ update: ["edit", "changed"],
38
+ delete: ["deletion", "deleted"],
39
+ };
40
+
41
+ function errorCode(text) {
42
+ try { return JSON.parse(text).error; } catch { return undefined; }
43
+ }
44
+ function messageOf(text) {
45
+ try { const m = JSON.parse(text).message; return typeof m === "string" ? m : undefined; } catch { return undefined; }
46
+ }
47
+
48
+ // node:http, not global fetch: fetch (undici) drops a response whose headers
49
+ // take over 300 s, and a create, update or delete waits on the user's
50
+ // confirmation for up to 5 minutes. The app ends that wait itself (HTTP 408).
51
+ function post(url, token, body) {
52
+ return new Promise((resolve, reject) => {
53
+ const payload = Buffer.from(JSON.stringify(body));
54
+ const req = http.request(`${url}/expert`, {
55
+ method: "POST",
56
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json", "content-length": payload.length },
57
+ }, (res) => {
58
+ const chunks = [];
59
+ res.on("data", (c) => chunks.push(c));
60
+ res.on("end", () => resolve({ status: res.statusCode ?? 0, text: Buffer.concat(chunks).toString("utf8") }));
61
+ res.on("error", reject);
62
+ });
63
+ req.on("error", reject);
64
+ req.end(payload);
65
+ });
66
+ }
67
+
68
+ async function main() {
69
+ const body = request(process.argv.slice(2));
70
+ const url = process.env.TALOS_EXPERT_URL;
71
+ const token = process.env.TALOS_EXPERT_TOKEN;
72
+ // Talos hands these only to a conversation with the user; a scheduled task,
73
+ // a sub-agent or a channel session never has them.
74
+ if (!url || !token) die("this needs the Talos app and a conversation with the user (TALOS_EXPERT_URL is not set). Scheduled tasks and sub-agents cannot change experts; do not write expert files yourself.", 69);
75
+ let res;
76
+ try {
77
+ res = await post(url, token, body);
78
+ } catch (e) { die(`cannot reach the Talos app: ${e.message}`, 69); }
79
+ const { status, text } = res;
80
+ const undone = UNDONE[body.action];
81
+ const error = errorCode(text);
82
+ if (undone && status === 409 && error === "cancelled_by_user") die(`the user cancelled the ${undone[0]}; nothing was ${undone[1]}`, 3);
83
+ if (undone && status === 408) die(`confirmation timed out; nothing was ${undone[1]}`, 4);
84
+ // The update would leave the expert as it is: the app wrote nothing and
85
+ // asked the user nothing.
86
+ if (status === 422 && error === "no_change") die(`${messageOf(text) ?? "the patch changes nothing"}; nothing was changed`, 5);
87
+ // Talos itself is not ready (no runtime yet, or the profile changed while
88
+ // the dialog was open). Not the request's fault, so not exit 1 ("fix the
89
+ // field and retry"): a retry would ask the user again about a runtime that
90
+ // is not the one they were shown.
91
+ if (status === 503 && error === "not_ready") die(`the Talos app is not ready (it is starting or switched profile); nothing was ${undone ? undone[1] : "done"}. Do not retry; tell the user.`, 69);
92
+ if (status < 200 || status >= 300) die(`HTTP ${status}${text ? `: ${text}` : ""}`, 1);
93
+ process.stdout.write(`${text}\n`);
94
+ }
95
+
96
+ main();
@@ -0,0 +1,24 @@
1
+ # Writing an expert's role
2
+
3
+ The role is the first thing the model reads in every conversation with this
4
+ expert, before Talos's own rules and tools. Keep it about the domain and the
5
+ voice; Talos already explains its tools, files and safety rules — do not repeat
6
+ or override them.
7
+
8
+ Structure, in this order, as plain Markdown:
9
+
10
+ 1. **Who you are** — one or two sentences: the role, who it serves.
11
+ 2. **What you do** — the concrete jobs, as a short list.
12
+ 3. **How you work** — the method: what to check first, what to ask the user
13
+ before starting, the order of work, the quality bar.
14
+ 4. **What you deliver** — formats and shapes of the output (a PDF report, an
15
+ Excel model, a list of options with a recommendation).
16
+ 5. **Boundaries** — what this expert does not do, and when to say so.
17
+
18
+ Rules:
19
+ - Write instructions to the model ("Ask for the target market before…"), not a
20
+ description of a product.
21
+ - Be specific to the domain; a generic "be helpful and accurate" adds nothing.
22
+ - No tool names, file paths or claims about Talos features — they change and
23
+ the role would mislead.
24
+ - Under ~600 words is usually enough; length is not quality.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: image-layout
3
- description: "Use when EXISTING images (files already on disk) are combined or altered as pixels: 拼图 / 拼接 / 宫格 / 九宫格 / 长图 / 对比图 / 加水印 / 加标签 / 裁剪 / 圆角 / 缩放, stitch / combine / collage / contact sheet / side-by-side / before-after / watermark / label / crop / resize. The mechanical test: if the images do not exist as files yet, this is NOT the skill. Do NOT trigger for generating NEW images, INCLUDING when the request names a layout word ('生成一张九宫格的图' is one generated picture); documents (document skill); Xiaohongshu cards (talos-xhs-ops); or upscaling / background removal, which need a model."
3
+ description: "Use when EXISTING images (files already on disk) are combined or altered as pixels: 拼图 / 拼接 / 宫格 / 九宫格 / 长图 / 对比图 / 加水印 / 加标签 / 裁剪 / 圆角 / 缩放, stitch / combine / collage / contact sheet / side-by-side / before-after / watermark / label / crop / resize. The mechanical test: if the images do not exist as files yet, this is NOT the skill. Do NOT trigger for generating NEW images, INCLUDING when the request names a layout word ('生成一张九宫格的图' is one generated picture); documents (document skill); social-post cards (social-cards); or upscaling / background removal, which need a model."
4
4
  license: Proprietary. LICENSE.txt has complete terms
5
5
  ---
6
6
 
@@ -146,7 +146,7 @@ tool.
146
146
  creates imagery, it only arranges what exists.
147
147
  - **A PDF / Word / PowerPoint deliverable** → the document skill (`doc-gen`,
148
148
  `html2pptx`). Do not build a "poster" here and call it a document.
149
- - **Xiaohongshu campaign cards** → `talos-xhs-ops` (`render-cards`), which owns
150
- the 1080×1440 card format and its QA.
149
+ - **Cards for a social post** → `social-cards` (`render`), which owns card
150
+ templates, every platform's card size and their QA.
151
151
  - **Pixel-level algorithms** — background removal, super-resolution,
152
152
  content-aware fill. CSS cannot do these; they need a model.