diff --git a/.agents/skills/AGENTS.md b/.agents/skills/AGENTS.md index 72c0a6e..37e1a62 100644 --- a/.agents/skills/AGENTS.md +++ b/.agents/skills/AGENTS.md @@ -15,4 +15,5 @@ - Keep these cross-repository workflows vendored and independently usable; never resolve a skill through a sibling checkout or Git submodule. - Refresh the KB skills from one reviewed immutable `hraness/kb` release and the orchestration skill from the reviewed portable template, then validate the complete copied directories. - Keep product-specific operating skills in the root `skills/` directory when present. Portable repository workflows belong here. +- Mark every portable repository workflow with `metadata.internal: true` so public `skills add hraness/atet` discovery exposes only the product-owned `atet` skill. - Preserve upstream attribution and license notices for adapted public resources. diff --git a/.agents/skills/percolate-kb/SKILL.md b/.agents/skills/percolate-kb/SKILL.md index 77fd3d9..691102e 100644 --- a/.agents/skills/percolate-kb/SKILL.md +++ b/.agents/skills/percolate-kb/SKILL.md @@ -1,6 +1,8 @@ --- name: percolate-kb description: Review a hraness/kb Markdown vault for recurring ideas and missing structural connections, then promote evidence-backed concepts and typed relationships with the KB CLI. Use after materially adding or revising notes, when organizing an accumulated vault, or when an agent needs to turn repeated tags and prose references into an explicit queryable knowledge graph. +metadata: + internal: true --- # Percolate concepts and relationships diff --git a/.agents/skills/phase-orchestrator/SKILL.md b/.agents/skills/phase-orchestrator/SKILL.md index 542628d..7a613a6 100644 --- a/.agents/skills/phase-orchestrator/SKILL.md +++ b/.agents/skills/phase-orchestrator/SKILL.md @@ -1,6 +1,8 @@ --- name: phase-orchestrator description: Orchestrate phase-based implementation plans in Codex with dependency analysis, parallel sub-agent lanes, join gates, validation, review/fix loops, and final integration. Use when the user provides a plan, checklist, PRD, or phased task and explicitly asks Codex to execute it with phases, delegation, parallel agents, or orchestration in any repository. +metadata: + internal: true --- # Phase Orchestrator diff --git a/.agents/skills/plan-kb/SKILL.md b/.agents/skills/plan-kb/SKILL.md index d2123d8..2c6abc1 100644 --- a/.agents/skills/plan-kb/SKILL.md +++ b/.agents/skills/plan-kb/SKILL.md @@ -1,6 +1,8 @@ --- name: plan-kb description: Create or evolve a durable Markdown plan inside a hraness/kb vault. Use when a user asks for an implementation plan, proposal, RFC, migration plan, execution audit, phased checklist, or an update to an existing plan's decisions, progress, review findings, verification evidence, or final result. +metadata: + internal: true --- # Write a durable plan diff --git a/.agents/skills/query-kb/SKILL.md b/.agents/skills/query-kb/SKILL.md index fefd2f3..f69804c 100644 --- a/.agents/skills/query-kb/SKILL.md +++ b/.agents/skills/query-kb/SKILL.md @@ -1,6 +1,8 @@ --- name: query-kb description: Load scoped repository context, then search and navigate a hraness/kb Markdown vault with hybrid text retrieval, exact metadata, bounded links and typed relationships, backlinks, whole-vault graph reports, and optional Git provenance. Use when an agent needs the applicable repository instructions, rationale, prior knowledge, plans, captures, decisions, concepts, relationships, or evidence before answering, planning, or changing code. +metadata: + internal: true --- # Query the knowledge base diff --git a/.agents/skills/refresh-kb/SKILL.md b/.agents/skills/refresh-kb/SKILL.md index d38ed85..ec5692b 100644 --- a/.agents/skills/refresh-kb/SKILL.md +++ b/.agents/skills/refresh-kb/SKILL.md @@ -1,6 +1,8 @@ --- name: refresh-kb description: Refresh and validate a hraness/kb Markdown knowledge graph after notes, concepts, typed relationships, attachments, repository scopes, or context mappings change. Use when an agent needs to maintain a managed or authored catalog, inspect graph and lifecycle findings, validate local artifacts and scope hubs, or complete a vault health check. +metadata: + internal: true --- # Refresh a knowledge base diff --git a/.agents/skills/save-pdf-kb/SKILL.md b/.agents/skills/save-pdf-kb/SKILL.md index 25fa9b7..9d6d7f3 100644 --- a/.agents/skills/save-pdf-kb/SKILL.md +++ b/.agents/skills/save-pdf-kb/SKILL.md @@ -7,6 +7,8 @@ description: >- archive, extract, or turn a PDF into KB Markdown, especially scanned documents and PDFs containing screenshots, Slack messages, charts, photos, or a mix of native text and images. +metadata: + internal: true --- # Save a PDF to the knowledge base diff --git a/.agents/skills/save-url-kb/SKILL.md b/.agents/skills/save-url-kb/SKILL.md index b80c243..35e95d8 100644 --- a/.agents/skills/save-url-kb/SKILL.md +++ b/.agents/skills/save-url-kb/SKILL.md @@ -8,6 +8,8 @@ description: >- YouTube page, or another page already visible in their browser. Supports URL capture, the current signed-in tab, temporary browser-profile snapshots, saved HTML, media, evidence, honest completeness, and knowledge-base linking. +metadata: + internal: true --- # Capture web content diff --git a/README.md b/README.md index b400be2..152dacd 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Atet: AI media generation and video editing for coding agents](https://atet.sh/og.png)](https://atet.sh) +[![skills.sh](https://skills.sh/b/hraness/atet)](https://skills.sh/hraness/atet) + **AI media generation and video editing for coding agents.** Atet lets Codex, Claude, and other coding agents generate images, video, and @@ -17,15 +19,30 @@ an Atet service. ## Install Atet -Atet requires [Bun 1.3.14 or newer](https://bun.sh). Run these commands once: +Install the single Atet Agent Skill with either runner: + +```sh +npx skills add hraness/atet +# or +bunx skills add hraness/atet +``` + +Start a new agent session after installing the skill. It teaches Codex, Claude +Code, Cursor, and other compatible agents how to turn a finished-media request +into checked Atet operations. + +Atet requires [Bun 1.3.14 or newer](https://bun.sh). Install the current CLI, +then inspect the local media host: ```sh bun add --global github:hraness/atet -atet skill install +atet doctor ``` -The first command installs the Atet CLI. The second installs a short guide that -teaches Codex when and how to use it. +The CLI carries the guide released with that exact CLI version. Prefer the +public `skills` command for the current repository guide. Use +`atet skill install` when you specifically need the CLI's version-matched +runner installer. Move into the project where you want Atet to work, then check the available recording, rendering, browser, and media tools: @@ -35,8 +52,9 @@ cd /path/to/your/project atet doctor ``` -Start a new agent session after installing the skill. The default installation -works with Codex across your user account. +The public `skills` command follows the scope selected in that installer. If +you instead use the CLI's version-matched guide, `atet skill install` defaults +to Codex across your user account. For Claude Code or another system that reads Agent Skills: diff --git a/apps/web/scripts/build.ts b/apps/web/scripts/build.ts index ad56b57..3a92e35 100644 --- a/apps/web/scripts/build.ts +++ b/apps/web/scripts/build.ts @@ -71,6 +71,37 @@ function renderAppearanceMenu(): string { ` } +type CopyCommandOptions = Readonly<{ + alternateCommand: string + command: string + id: string +}> + +function escapeHtml(value: string): string { + return value.replace(/[&<>"']/gu, character => ({ + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + })[character] ?? character) +} + +function renderCopyCommand(options: CopyCommandOptions): string { + const alternateCommand = escapeHtml(options.alternateCommand) + const command = escapeHtml(options.command) + const id = escapeHtml(options.id) + + return `
+ ${command} + +

Using Bun? ${alternateCommand}

+

+
` +} + type BuildEnvironment = Readonly> type BuildOptions = Readonly<{ @@ -179,6 +210,11 @@ export async function buildWebsite(options: BuildOptions = {}): Promise`, + "{{SKILL_INSTALL_COMMAND}}": renderCopyCommand({ + alternateCommand: "bunx skills add hraness/atet", + command: "npx skills add hraness/atet", + id: "skill-install-copy-status", + }), } as const await rm(outputDirectory, { force: true, recursive: true }) diff --git a/apps/web/site.test.ts b/apps/web/site.test.ts index 1bcdcba..de207c1 100644 --- a/apps/web/site.test.ts +++ b/apps/web/site.test.ts @@ -178,14 +178,15 @@ describe("static Atet site", () => { }) test("puts the complete agent install before the first section ends", async () => { - const html = await readSource("index.html") + const html = await readBuilt("index.html") const searchableHtml = html.replace(/\s+/gu, " ") const commands = [ + "npx skills add hraness/atet", "bun add --global github:hraness/atet", - "atet skill install", "atet doctor", ] const positions = commands.map(command => html.indexOf(command)) + const heroHtml = html.slice(0, html.indexOf("") + "".length) expect(positions.every(position => position >= 0)).toBe(true) expect(positions).toEqual([...positions].sort((left, right) => left - right)) @@ -195,15 +196,41 @@ describe("static Atet site", () => { expect(searchableHtml).toContain("edit screen recordings and imported footage") expect(searchableHtml).toContain("add captions, graphics, and motion") expect(searchableHtml).toContain("export finished videos") - expect(html).toContain("Requires Bun 1.3.14+ · Installs for Codex by default") + expect(html).toContain("Install the Atet Agent Skill") + expect(html).toContain("Install the local media tools · Requires Bun 1.3.14+") + expect(html).toContain("Using Bun? bunx skills add hraness/atet") expect(html).toContain("inside the project you want to work") expect(html).toContain("start a new agent session") + expect(heroHtml).not.toContain("atet skill install") + expect(html).toContain("When that command is not being used") expect(html).toContain("atet skill install --target claude") expect(html).toContain("atet skill install --target agents") expect(html).toContain("--scope project") expect(html).not.toContain("github:hraness/atet#") }) + test("renders a progressively enhanced reusable copy command in the hero", async () => { + const [html, build, client] = await Promise.all([ + readBuilt("index.html"), + readFile(join(appDirectory, "scripts/build.ts"), "utf8"), + readSource("copy-command.ts"), + ]) + + expect(build).toContain("function renderCopyCommand(options: CopyCommandOptions)") + expect(html.match(/data-copy-command(?:>|\s)/gu)).toHaveLength(1) + expect(html).toContain('npx skills add hraness/atet') + expect(html).toContain("bunx skills add hraness/atet") + expect(html).toContain('aria-label="Copy install command"') + expect(html).toContain("data-copy-command-button hidden type=\"button\">Copy") + expect(html).toContain('aria-live="polite"') + expect(html).toContain('aria-describedby="skill-install-copy-status"') + expect(client).toContain("button.hidden = false") + expect(client).toContain("navigator.clipboard.writeText(value)") + expect(client).toContain('ownerDocument.execCommand("copy")') + expect(client).toContain('button.addEventListener("click"') + expect(client).not.toMatch(/fetch\(|XMLHttpRequest|WebSocket|EventSource|sendBeacon/) + }) + test("uses one install, examples, workflow, and design information architecture", async () => { const html = await readSource("index.html") const modes = ["> Install<", "> Examples<", "> Workflow<", "> Design<"] @@ -368,6 +395,7 @@ describe("static Atet site", () => { const html = await readSource("index.html") const css = await readSource("styles.css") const theme = await readSource("theme.ts") + const copyCommand = await readSource("copy-command.ts") const analytics = await readSource("analytics.ts") const build = await readFile(join(appDirectory, "scripts/build.ts"), "utf8") const manifest = JSON.parse( @@ -391,6 +419,7 @@ describe("static Atet site", () => { expect(new TextEncoder().encode(html).byteLength).toBeLessThan(20_000) expect(new TextEncoder().encode(css).byteLength).toBeLessThan(28_000) expect(new TextEncoder().encode(theme).byteLength).toBeLessThan(3_000) + expect(new TextEncoder().encode(copyCommand).byteLength).toBeLessThan(4_000) expect(html).not.toMatch(/https:\/\/[^"']+\.(?:css|js)/) expect(html).toContain('') expect(html).toContain('') @@ -401,7 +430,9 @@ describe("static Atet site", () => { expect(html.match(/ { expect(build).toContain('format: "iife"') expect(build).toContain('import.meta.resolve("@hraness/design-kit/appearance-menu.css")') expect(build).toContain("renderAppearanceMenu()") + expect(build).toContain("renderCopyCommand({") expect(build).toContain('environment.VERCEL_ENV !== "production"') expect(build).not.toContain("docsTemplate") expect(build).not.toContain('outputDirectory, "docs"') diff --git a/apps/web/src/copy-command.ts b/apps/web/src/copy-command.ts new file mode 100644 index 0000000..46159be --- /dev/null +++ b/apps/web/src/copy-command.ts @@ -0,0 +1,68 @@ +const feedbackDurationMilliseconds = 2_500 + +function fallbackCopy(value: string, ownerDocument: Document): boolean { + const input = ownerDocument.createElement("textarea") + input.value = value + input.readOnly = true + input.style.position = "fixed" + input.style.inset = "0 auto auto -9999px" + input.style.opacity = "0" + ownerDocument.body.append(input) + input.select() + input.setSelectionRange(0, value.length) + + try { + return ownerDocument.execCommand("copy") + } finally { + input.remove() + } +} + +async function copyText(value: string, ownerDocument: Document): Promise { + try { + await navigator.clipboard.writeText(value) + return true + } catch { + try { + return fallbackCopy(value, ownerDocument) + } catch { + return false + } + } +} + +export function installCopyCommands(ownerDocument: Document = document): void { + for (const component of ownerDocument.querySelectorAll("[data-copy-command]")) { + const command = component.querySelector("[data-copy-command-value]") + const button = component.querySelector("[data-copy-command-button]") + const status = component.querySelector("[data-copy-command-status]") + if (command === null || button === null || status === null) { + continue + } + + button.hidden = false + let resetTimer: number | undefined + button.addEventListener("click", async () => { + if (resetTimer !== undefined) { + window.clearTimeout(resetTimer) + } + + const copied = await copyText(command.textContent ?? "", ownerDocument) + button.textContent = copied ? "Copied" : "Copy" + button.dataset.copyState = copied ? "copied" : "failed" + status.textContent = copied + ? "Install command copied." + : "Could not copy the command. Select it and copy it manually." + button.focus() + + if (copied) { + resetTimer = window.setTimeout(() => { + button.textContent = "Copy" + delete button.dataset.copyState + status.textContent = "" + resetTimer = undefined + }, feedbackDurationMilliseconds) + } + }) + } +} diff --git a/apps/web/src/index.html b/apps/web/src/index.html index c820629..8ab11d7 100644 --- a/apps/web/src/index.html +++ b/apps/web/src/index.html @@ -143,17 +143,20 @@

Make and edit visual media with your coding agent.

-
-

Requires Bun 1.3.14+ · Installs for Codex by default

-
    -
  1. 01bun add --global github:hraness/atet
  2. -
  3. 02atet skill install
  4. -
  5. 03atet doctor
  6. -
-

- Run atet doctor inside the project you want to work - in. Then start a new agent session and describe the finished result. -

+
+

Install the Atet Agent Skill

+ {{SKILL_INSTALL_COMMAND}} +
+

Install the local media tools · Requires Bun 1.3.14+

+
    +
  1. 01bun add --global github:hraness/atet
  2. +
  3. 02atet doctor
  4. +
+

+ Run atet doctor inside the project you want to work + in. Then start a new agent session and describe the finished result. +

+
@@ -166,14 +169,16 @@

Make and edit visual media with your coding agent.

-

Other setups

+

Version-matched guide

-

Install the instructions where your agent can read them.

+

Use the guide shipped with the CLI when you need it.

The CLI performs the work. The Agent Skill explains the creative tools and the decisions an agent should make before it uses them. - By default, Atet installs that guide for Codex across your user - account. + The public skills command above installs the current repository + guide. When that command is not being used, atet skill + install installs the guide shipped with your CLI for Codex + by default.

Claude Code
atet skill install --target claude
diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 153e27b..acca573 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -345,10 +345,83 @@ blockquote { color: var(--gold-bright); } +.copy-command { + position: relative; + display: grid; + min-width: 0; + margin-top: 1rem; + border: 1px solid var(--night-line); + border-radius: var(--radius-control); + background: var(--night-raised); + grid-template-columns: minmax(0, 1fr) auto; +} + +.copy-command__value { + min-width: 0; + padding: 0.95rem 1rem; + overflow-x: auto; + color: var(--night-ink); + font-size: clamp(0.72rem, 1.2vw, 0.86rem); + line-height: 1.5; + white-space: nowrap; +} + +.copy-command__button { + min-width: 4.4rem; + min-height: 2.75rem; + padding: 0.65rem 0.9rem; + border: 0; + border-left: 1px solid var(--night-line); + border-radius: 0 var(--radius-control) var(--radius-control) 0; + background: transparent; + color: var(--night-muted); + cursor: pointer; + font-size: 0.73rem; + font-weight: 750; +} + +.copy-command__button:hover, +.copy-command__button[data-copy-state="copied"] { + background: color-mix(in srgb, var(--gold-bright) 13%, transparent); + color: var(--night-ink); +} + +.copy-command__button[data-copy-state="failed"] { + color: var(--gold-bright); +} + +.copy-command__note { + padding: 0.7rem 1rem; + border-top: 1px solid var(--night-line); + color: var(--night-muted); + font-size: 0.76rem; + grid-column: 1 / -1; +} + +.copy-command__note code { + color: var(--night-ink); + font-family: var(--font-mono); +} + +.copy-command__status { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} + +.cli-install { + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid var(--night-line); +} + .install-commands { position: relative; padding: 0; - margin-top: 1.6rem; + margin-top: 1rem; list-style: none; } @@ -1199,6 +1272,21 @@ blockquote { padding: 1.2rem; } + .copy-command { + grid-template-columns: minmax(0, 1fr); + } + + .copy-command__button { + min-width: 0; + border-top: 1px solid var(--night-line); + border-left: 0; + border-radius: 0; + } + + .copy-command__note { + grid-column: auto; + } + .install-commands li { gap: 0.5rem; grid-template-columns: 1.5rem minmax(0, 1fr); @@ -1230,6 +1318,7 @@ blockquote { @media (forced-colors: active) { .button, + .copy-command, .install-panel, .plain-aside, .reference-block { @@ -1241,6 +1330,10 @@ blockquote { color: Canvas; } + .copy-command__button { + border-color: CanvasText; + } + .solar-mark { display: none; } @@ -1253,6 +1346,10 @@ blockquote { display: none; } + .copy-command__button { + display: none; + } + body, .system-section, .install-panel { diff --git a/apps/web/src/theme.ts b/apps/web/src/theme.ts index ed75761..8563e68 100644 --- a/apps/web/src/theme.ts +++ b/apps/web/src/theme.ts @@ -1,7 +1,15 @@ import { installAppearanceMenus } from "@hraness/design-kit/browser" +import { installCopyCommands } from "./copy-command" + installAppearanceMenus({ darkThemeColor: "#0b0b0e", lightThemeColor: "#f7f3ea", storageKey: "atet.appearance", }) + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", () => installCopyCommands(), { once: true }) +} else { + installCopyCommands() +} diff --git a/scripts/package-smoke.ts b/scripts/package-smoke.ts index 3e9e0a7..c984644 100644 --- a/scripts/package-smoke.ts +++ b/scripts/package-smoke.ts @@ -28,7 +28,7 @@ const importSpecifiers = [ `${packageName}/local/html-overlay`, ] as const; const verificationPackages = [ - "@types/bun@1.3.14", + "@types/bun@^1.3.14", "@types/json-schema@^7.0.15", "@types/node@^24.10.0", "@types/react@^19.2.14", diff --git a/scripts/validate-skill.ts b/scripts/validate-skill.ts index 58f8030..6dfd71a 100644 --- a/scripts/validate-skill.ts +++ b/scripts/validate-skill.ts @@ -1,9 +1,46 @@ -import { access, readFile } from "node:fs/promises" -import { join } from "node:path" +import { access, readFile, readdir } from "node:fs/promises" +import { dirname, isAbsolute, join, relative, resolve } from "node:path" const root = join(process.cwd(), "skills", "atet") const skillPath = join(root, "SKILL.md") const text = await readFile(skillPath, "utf8") + +const publicSkills = (await readdir(join(process.cwd(), "skills"), { withFileTypes: true })) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) +if (publicSkills.length !== 1 || publicSkills[0] !== "atet") { + throw new Error(`Package must expose only skills/atet; found ${publicSkills.join(", ")}`) +} + +async function collectMarkdownFiles(directory: string): Promise { + const paths: string[] = [] + for (const entry of await readdir(directory, { withFileTypes: true })) { + const path = join(directory, entry.name) + if (entry.isDirectory()) paths.push(...(await collectMarkdownFiles(path))) + else if (entry.isFile() && entry.name.endsWith(".md")) paths.push(path) + } + return paths +} + +async function validateLocalMarkdownLinks(): Promise { + for (const markdownPath of await collectMarkdownFiles(root)) { + const markdown = await readFile(markdownPath, "utf8") + for (const link of markdown.matchAll(/\]\(([^)#]+\.md)(?:#[^)]+)?\)/g)) { + const targetText = link[1] + if (targetText === undefined || /^[a-z][a-z0-9+.-]*:/iu.test(targetText)) continue + const target = resolve(dirname(markdownPath), targetText) + const skillRelative = relative(root, target) + if (skillRelative.startsWith("..") || isAbsolute(skillRelative)) { + throw new Error(`Skill link escapes its bundle: ${targetText}`) + } + try { + await access(target) + } catch { + throw new Error(`Skill link target is missing: ${targetText}`) + } + } + } +} const match = /^---\n([\s\S]*?)\n---\n/.exec(text) if (match === null) throw new Error("SKILL.md must start with YAML frontmatter") @@ -28,20 +65,45 @@ if ((frontmatter.description?.length ?? 0) < 40) { if ((frontmatter.description?.length ?? 0) > 1024) { throw new Error("Skill description must be at most 1024 characters") } +const linkedReferences = new Set( + [...text.matchAll(/\]\((references\/[^)#]+)(?:#[^)]+)?\)/g)] + .map((reference) => reference[1]) + .filter((path): path is string => path !== undefined), +) for (const relativePath of [ - "agents/openai.yaml", "references/customization.md", + "references/gateway-media.md", + "references/install.md", "references/reference-led-3d.md", + "references/video-projects.md", "references/visual-communication.md", ]) { + if (!linkedReferences.has(relativePath)) { + throw new Error(`SKILL.md must route to ${relativePath}`) + } try { await access(join(root, relativePath)) } catch { throw new Error(`Skill is missing ${relativePath}`) } } +try { + await access(join(root, "agents", "openai.yaml")) +} catch { + throw new Error("Skill is missing agents/openai.yaml") +} const openai = await readFile(join(root, "agents", "openai.yaml"), "utf8") for (const required of ["display_name:", "short_description:", "default_prompt:"]) { if (!openai.includes(required)) throw new Error(`agents/openai.yaml is missing ${required}`) } +if (!openai.includes("$atet")) throw new Error("agents/openai.yaml default prompt must invoke $atet") +const install = await readFile(join(root, "references", "install.md"), "utf8") +const manifest = JSON.parse(await readFile(join(process.cwd(), "package.json"), "utf8")) as { + readonly version?: unknown +} +if (typeof manifest.version !== "string") throw new Error("package version is missing") +if (!install.includes(`github:hraness/atet#v${manifest.version}`)) { + throw new Error("Skill install pin must match the package version") +} +await validateLocalMarkdownLinks() console.log("atet skill is valid") diff --git a/skills/atet/SKILL.md b/skills/atet/SKILL.md index 96602b6..dee6518 100644 --- a/skills/atet/SKILL.md +++ b/skills/atet/SKILL.md @@ -1,6 +1,6 @@ --- name: atet -description: Make and edit visual media with the installed Atet CLI. Use for screen and camera recording, non-destructive video editing, pause or filler-word cleanup, speaker framing, screen zooms, captions, overlays, social video variants, AI image or video generation, speech, transcription, editable diagrams, raster-to-SVG conversion, deterministic animated loops, Three.js scenes, semantic Atet operations, or typed Bun workflows. +description: Use Atet to create, generate, record, edit, and render visual media with a local CLI and coding agent. Trigger for AI image or video generation, screen, camera, microphone, or system-audio recording, non-destructive video editing, silence and filler-word removal, captions or subtitles, speaker framing, screen zooms, overlays, motion graphics, social video variants, voice or speech, transcription, editable diagrams and flowcharts, raster-to-SVG vectorization, Three.js or 3D scenes, transparent animated loops, semantic media operations, and typed Bun workflows. --- # Make and edit visual media with Atet @@ -9,6 +9,14 @@ Use the installed `atet` CLI as the execution surface. Begin with the result the user asked for, inspect the source and existing Atet project state, then choose the narrowest workflow that can produce and verify that result. +## Install or verify Atet + +Start with `atet --help`. If the command is unavailable, read +[installation and readiness](references/install.md) and install the pinned CLI +before continuing when the user's request includes installing or using Atet. +Do not guess a source-tree command or initialize a media project merely because +the skill was installed. + ## Choose the right workflow - For recording, imported footage, video cleanup, camera framing, screen diff --git a/skills/atet/agents/openai.yaml b/skills/atet/agents/openai.yaml index d5e3bb9..169680d 100644 --- a/skills/atet/agents/openai.yaml +++ b/skills/atet/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Atet" - short_description: "Create checked visuals, images, and reviewed 3D scenes" - default_prompt: "Create or update a visual that follows my prompt literally, keeps checked source and exact references, and uses reviewed Atet workflows with bounded rendering defaults." + short_description: "Generate and edit video, images, audio, and diagrams" + default_prompt: "Use $atet to create or edit this visual or media project from checked source, show a real preview, and preserve my literal requirements." diff --git a/skills/atet/references/install.md b/skills/atet/references/install.md new file mode 100644 index 0000000..6b4b4a0 --- /dev/null +++ b/skills/atet/references/install.md @@ -0,0 +1,45 @@ +# Install and diagnose Atet + +Use this reference only when `atet` is unavailable or `atet doctor` reports a +dependency required by the selected media workflow. + +## Install the CLI + +Atet requires Bun 1.3.14 or newer. Check the current machine before changing +it: + +```sh +command -v bun +command -v atet +``` + +If Bun is missing, stop and direct the user to the official +[Bun installation guide](https://bun.sh/docs/installation). Do not switch +package managers or pipe an unreviewed installer into a shell. + +When the user asked to install or use Atet, install the current immutable +release, then inspect its real capabilities: + +```sh +bun add --global github:hraness/atet#v2.0.0 +atet --help +atet doctor --json +atet workflows list --json +``` + +Do not clone the source repository merely to run Atet. `atet skill path` prints +the version-matched packaged skill, while `atet skill install` can install that +copy for a named agent runner when the public `skills` CLI is not being used. + +## Add only required optional tools + +Treat `atet doctor --json` as the readiness report. Install FFmpeg, a supported +browser, native capture support, VTracer, tldraw Offline, or another optional +dependency only when the requested workflow needs it and the user has +authorized that machine change. Atet obtains its checksum-pinned VTracer on +first vectorization use; do not replace that path with an unverified binary. + +Gateway generation uses the caller's `AI_GATEWAY_API_KEY` or +`VERCEL_OIDC_TOKEN`. Keep credentials in the process environment and never +persist, print, or place them on argv. A missing credential is not permission +to switch providers. diff --git a/src/host-resource-posix.ts b/src/host-resource-posix.ts index ccf4e3a..fc13bad 100644 --- a/src/host-resource-posix.ts +++ b/src/host-resource-posix.ts @@ -8,12 +8,13 @@ const lockUnlock = 0x08 const interruptedErrno = 4 type NativeFlock = (descriptor: number, operation: number) => number -type NativeErrnoLocation = () => Pointer | null +type NativePointer = Pointer | bigint +type NativeErrnoLocation = () => NativePointer | null interface NativeLocking { readonly errnoLocation: NativeErrnoLocation readonly flock: NativeFlock - readonly readInt32: (pointer: Pointer) => number + readonly readInt32: (pointer: NativePointer) => number } let nativeLocking: NativeLocking | undefined @@ -75,7 +76,7 @@ function initializeNativeLocking(): NativeLocking { nativeLocking = { errnoLocation: library.symbols.__error, flock: library.symbols.flock, - readInt32: read.i32, + readInt32: read.i32 as (pointer: NativePointer) => number, } return nativeLocking } @@ -94,7 +95,7 @@ function initializeNativeLocking(): NativeLocking { nativeLocking = { errnoLocation: library.symbols.__errno_location, flock: library.symbols.flock, - readInt32: read.i32, + readInt32: read.i32 as (pointer: NativePointer) => number, } return nativeLocking } catch (cause) {