artifacty 0.1.2 → 0.2.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/AGENTS.md CHANGED
@@ -8,11 +8,11 @@ Artifacty shares LLM artifacts over HTTP, CLI, and MCP.
8
8
  - `src/mcp-server.js`: MCP stdio server exposing Artifacty tools.
9
9
  - `src/cli.js`: command-line interface for serving, publishing, importing, listing, and reading artifacts.
10
10
  - `src/lib/storage.js`: SQLite metadata store and immutable version-file handling.
11
- - `src/lib/converters.js`: agent artifact conversion rules for Claude, Codex, Gemini, and generic payloads.
11
+ - `src/lib/converters.js`: agent artifact conversion rules for Claude, Codex, Gemini, GitHub Copilot, Cursor, and generic payloads.
12
12
  - `src/lib/installer.js`: MCP config installers for Claude, Codex, and Gemini.
13
13
  - `src/lib/render.js`: server-rendered dashboard, viewer, and editor HTML.
14
14
  - `test/*.test.js`: Node test runner suites.
15
- - `docs/integrations.md`: setup notes for Claude Code, Codex, and Gemini CLI.
15
+ - `docs/integrations.md`: setup notes for Claude Code, Codex, Gemini CLI, GitHub Copilot in VS Code, and Cursor.
16
16
 
17
17
  ## Build, Test, and Development Commands
18
18
 
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  [![Node.js >=22.5](https://img.shields.io/badge/node-%3E%3D22.5-339933.svg)](package.json)
7
7
 
8
- Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. Claude, Codex, Gemini, and other MCP-capable tools can publish an artifact once, then other agents can list, read, update, and continue from it without copying content through chat.
8
+ Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. Claude, Codex, Gemini, GitHub Copilot, Cursor, and other MCP-capable tools can publish an artifact once, then other agents can list, read, update, and continue from it without copying content through chat.
9
9
 
10
10
  ![Artifacty overview showing multiple AI agents sharing artifacts through a local exchange](docs/assets/artifacty.png)
11
11
 
@@ -70,6 +70,8 @@ Install MCP configuration for local agents:
70
70
  artifacty install claude
71
71
  artifacty install codex --dry-run
72
72
  artifacty install gemini
73
+ artifacty install copilot
74
+ artifacty install cursor
73
75
  artifacty install all
74
76
  artifacty check
75
77
  ```
@@ -77,7 +79,7 @@ artifacty check
77
79
  Use `artifacty install codex --timeout 30000` or
78
80
  `artifacty install gemini --timeout 30000` to tune supported MCP client timeouts.
79
81
 
80
- See [docs/integrations.md](docs/integrations.md) for Claude Code, Codex, and Gemini CLI setup.
82
+ See [docs/integrations.md](docs/integrations.md) for Claude Code, Codex, Gemini CLI, GitHub Copilot in VS Code, and Cursor setup.
81
83
 
82
84
  ## Quick Start
83
85
 
@@ -113,11 +115,14 @@ Import an artifact produced by another agent and convert it to Artifacty format:
113
115
  artifacty import --agent claude --file ./deploy-failures.html --tag review
114
116
  artifacty import --agent gemini --content '{"title":"Plan","returnDisplay":"# Plan\n- Ship it"}'
115
117
  artifacty import --agent codex --content '{"agent":"codex","title":"Implementation Handoff","goal":"Continue Phase 3","changedFiles":[{"path":"src/lib/render.js","status":"modified"}],"nextSteps":["Add CodeMirror read-only viewer"]}'
118
+ artifacty import --agent copilot --content '{"agent":"github-copilot","title":"PR Review","findings":[{"severity":"medium","file":"src/app.js","line":42,"title":"Handle missing state"}]}'
119
+ artifacty import --agent cursor --content '{"sourceAgent":"cursor","title":"Cursor Handoff","summary":"Editor pass complete.","nextSteps":["Run visual QA."]}'
116
120
  ```
117
121
 
118
- Codex structured payloads can become `handoff`, `bundle`, `diff-walkthrough`,
119
- `code-review`, or `test-report` artifacts when the payload explicitly identifies
120
- Codex through `agent` or `sourceAgent`. Plain Codex Markdown stays a normal
122
+ Codex, GitHub Copilot, and Cursor structured payloads can become `handoff`,
123
+ `bundle`, `diff-walkthrough`, `code-review`, or `test-report` artifacts when
124
+ the payload explicitly identifies the agent through `agent` or `sourceAgent`.
125
+ Plain Markdown from these agents stays a normal
121
126
  `document` unless you pass an explicit `artifactType`.
122
127
 
123
128
  ## Agent Handoff Example
@@ -236,7 +241,7 @@ Browser routes:
236
241
  - `/`: list artifacts with search, tag, and source filters.
237
242
  - `/new`: create an Artifacty-native artifact with the CodeMirror editor.
238
243
  - `/import`: paste an external agent artifact and convert it with automatic editor mode detection.
239
- - `/artifacts/:id/edit`: save a new version with Markdown, HTML, JSON, text, code, SVG, Mermaid, or React syntax support.
244
+ - `/artifacts/:id/edit`: save a new version with Markdown, HTML, JSON, text, code, SVG, Mermaid, React, SARIF, CSV, image, or video syntax support.
240
245
  - `/artifacts/:id/diff`: compare versions.
241
246
  - `/api/audit`: list audit events.
242
247
 
@@ -249,8 +254,9 @@ Schema and storage:
249
254
  - Metadata lives in SQLite with `schemaVersion: 1`, `artifactType`, and `archivedAt`.
250
255
  - Archive hides artifacts from default lists without deleting versions.
251
256
  - Bundle artifacts store multiple files or base64 assets as portable JSON.
252
- - Supported formats are `html`, `markdown`, `text`, `json`, `code`, `svg`, `mermaid`, and `react`.
253
- - Diagram, component, and source snippet artifacts use `diagram`, `component`, and `snippet` artifact types.
257
+ - Supported formats are `html`, `markdown`, `text`, `json`, `code`, `svg`, `mermaid`, `react`, `sarif`, `csv`, `image`, and `video`.
258
+ - Diagram, component, source snippet, analysis report, table, and media assets use `diagram`, `component`, `snippet`, `analysis-report`, `table`, and `asset` artifact types.
259
+ - Copilot/Cursor examples cover PR reviews, screenshots, demo recordings, and visual evidence bundles.
254
260
  - See [docs/artifact-schema-v1.md](docs/artifact-schema-v1.md).
255
261
  - See [docs/sarif-csv-artifact-plan.md](docs/sarif-csv-artifact-plan.md) for the SARIF/CSV output artifact roadmap.
256
262
 
@@ -268,6 +274,9 @@ Schema and storage:
268
274
  - SVG artifacts render in a scriptless sandboxed iframe and are sanitized for `<script>`, `on*` attributes, and `javascript:` links in the viewer. The raw source remains unchanged.
269
275
  - Mermaid artifacts render with the vendored local Mermaid package in a sandboxed iframe without `allow-same-origin`.
270
276
  - React artifacts are source-only by default. Set `ARTIFACTY_ENABLE_REACT_RENDERER=true` to execute them in a sandboxed frame with a frame-scoped CSP that permits JSX transformation.
277
+ - SARIF artifacts render a bounded findings summary and keep the full formatted JSON behind a raw-source details panel.
278
+ - CSV artifacts render as an escaped, bounded table; `/raw` preserves the original text.
279
+ - Image and video artifacts store base64 media inline, render safe previews, and decode bytes through `/raw`.
271
280
  - Artifact content should still be treated as untrusted; use the raw view when handing content back to an agent.
272
281
 
273
282
  See [docs/release-checklist.md](docs/release-checklist.md) before publishing or running a shared instance.
@@ -35,6 +35,8 @@ Allowed `artifactType` values:
35
35
  - `diagram`
36
36
  - `component`
37
37
  - `snippet`
38
+ - `analysis-report`
39
+ - `table`
38
40
  - `unknown`
39
41
 
40
42
  Unknown legacy or external types should be mapped to `unknown`, not rejected during conversion. Native create/update rejects unsupported explicit types.
@@ -57,7 +59,11 @@ Each version is immutable and points at one content file.
57
59
  ```
58
60
 
59
61
  Allowed `format` values are `html`, `markdown`, `text`, `json`, `code`, `svg`,
60
- `mermaid`, and `react`.
62
+ `mermaid`, `react`, `sarif`, `csv`, `image`, and `video`.
63
+
64
+ Common `artifactType` values include `document`, `handoff`, `code-review`,
65
+ `test-report`, `dashboard`, `bundle`, `diagram`, `component`, `snippet`,
66
+ `analysis-report`, and `table`.
61
67
 
62
68
  ## Renderer Policy
63
69
 
@@ -74,6 +80,11 @@ viewer concern and must treat all source as untrusted:
74
80
  - `react`: source-only by default. `ARTIFACTY_ENABLE_REACT_RENDERER=true`
75
81
  enables a separate sandboxed frame with frame-scoped CSP for JSX transform and
76
82
  execution.
83
+ - `sarif`: bounded findings summary plus a formatted raw JSON details panel.
84
+ - `csv`: RFC 4180-style escaped table rendering with bounded rows and columns.
85
+ - `image`: base64 media source rendered with `<img>`; `/raw` decodes bytes.
86
+ - `video`: base64 media source rendered with `<video controls>`; `/raw` decodes
87
+ bytes.
77
88
 
78
89
  ## Metadata
79
90
 
@@ -85,9 +96,12 @@ Metadata is free-form JSON, but converter-generated metadata uses these keys:
85
96
  - `bundlePolicy`: how bundled files were preserved.
86
97
  - `language`: source language for code or component artifacts when supplied by an
87
98
  upstream agent.
88
- - `codexContinuation`: structured Codex handoff metadata such as changed files,
89
- commands, tests, blockers, decisions, next steps, findings, diff text, and
90
- residual risk.
99
+ - `continuation`: structured handoff/review/verification metadata for agent
100
+ outputs, including changed files, commands, tests, blockers, decisions, next
101
+ steps, findings, diff text, and residual risk.
102
+ - `<agent>Continuation`: compatibility mirror for structured continuation
103
+ metadata, such as `codexContinuation`, `copilotContinuation`, or
104
+ `cursorContinuation`.
91
105
 
92
106
  ## Archive Semantics
93
107
 
@@ -118,4 +132,12 @@ Gemini multimodal payloads use the same bundle type with `parts` and `assets`.
118
132
 
119
133
  ## Asset Policy
120
134
 
121
- Base64 assets are preserved inline inside bundle JSON with `encoding: "base64"`, `mimeType`, `sizeBytes`, and `sha256`. Consumers must treat decoded assets as untrusted. Large binary asset externalization is intentionally deferred; schema v1 keeps all converted assets inspectable and portable.
135
+ Base64 assets are preserved inline inside bundle JSON with `encoding: "base64"`, `mimeType`, `sizeBytes`, and `sha256`. Consumers must treat decoded assets as untrusted.
136
+
137
+ First-class `image` and `video` artifacts store base64 content in the immutable
138
+ version file. Importers should set `metadata.encoding: "base64"` and
139
+ `metadata.mimeType` to one of the supported media types. Browser `/raw` decodes
140
+ the stored base64 into bytes with the media content type, while API and MCP reads
141
+ return the stored base64 string. Supported media types are PNG, JPEG, GIF, WebP,
142
+ MP4, and WebM. Large binary externalization is intentionally deferred; schema v1
143
+ keeps converted assets inspectable and portable.
@@ -42,7 +42,7 @@ Create artifacts directly in the browser at `http://127.0.0.1:8787/new`.
42
42
 
43
43
  For LAN or VPN sharing, keep the default local binding unless you intentionally need another machine to reach the server. See [network-sharing.md](network-sharing.md) before using `--host 0.0.0.0`.
44
44
 
45
- The browser create/import/edit screens use CodeMirror 6 for Markdown, HTML, JSON, source-like text, SVG, Mermaid, and React artifact editing. Editor and renderer assets are served from local npm dependencies through `/assets/*.js` and allowlisted `/vendor/npm/*` module routes. For sandboxed iframe imports, `Origin: null` requests receive `Access-Control-Allow-Origin: null`, which lets opaque-origin iframes import local ESM without `allow-same-origin`.
45
+ The browser create/import/edit screens use CodeMirror 6 for Markdown, HTML, JSON, source-like text, SVG, Mermaid, React, SARIF, CSV, image, and video artifact editing. Editor and renderer assets are served from local npm dependencies through `/assets/*.js` and allowlisted `/vendor/npm/*` module routes. For sandboxed iframe imports, `Origin: null` requests receive `Access-Control-Allow-Origin: null`, which lets opaque-origin iframes import local ESM without `allow-same-origin`.
46
46
 
47
47
  The browser UI defaults to English. Add `?lang=ko` to browser routes to use Korean UI labels; API and MCP payloads are not localized.
48
48
 
@@ -70,6 +70,8 @@ Artifacty can write MCP configuration for supported local agents:
70
70
  node src/cli.js install claude
71
71
  node src/cli.js install codex --dry-run
72
72
  node src/cli.js install gemini
73
+ node src/cli.js install copilot
74
+ node src/cli.js install cursor
73
75
  node src/cli.js install all
74
76
  node src/cli.js check
75
77
  ```
@@ -77,6 +79,8 @@ node src/cli.js check
77
79
  - Claude: writes project `.mcp.json`. Claude Code's startup timeout is controlled by the parent `MCP_TIMEOUT` environment variable and defaults to 30 seconds, so Artifacty does not add a per-server `.mcp.json` `timeout` field.
78
80
  - Codex: writes or replaces the `[mcp_servers.artifacty]` block in `~/.codex/config.toml` unless `--config` is provided. The generated block uses a 30 second startup timeout so slower Windows or cold-start environments can load the MCP server reliably.
79
81
  - Gemini: writes project `.gemini/settings.json` with a 30 second timeout.
82
+ - GitHub Copilot in VS Code: writes workspace `.vscode/mcp.json` using the VS Code `servers` shape. Pass `--config` to target a user-profile `mcp.json` instead.
83
+ - Cursor: writes project `.cursor/mcp.json` using the Cursor `mcpServers` shape. Pass `--config ~/.cursor/mcp.json` for global Cursor setup.
80
84
  - `--dry-run` returns the generated config without writing it.
81
85
  - `--timeout <ms>` adjusts Codex `startup_timeout_sec` and Gemini `timeout`. It does not change Claude Code startup behavior; set `MCP_TIMEOUT` before launching Claude Code if you need a larger value there.
82
86
  - `check` starts the local MCP server and verifies required tools through `initialize` and `tools/list`.
@@ -114,7 +118,7 @@ Project-scoped `.mcp.json` shape:
114
118
  }
115
119
  ```
116
120
 
117
- Claude plugins can bundle MCP servers, so this MCP server can later be wrapped as a plugin. The MVP keeps the server standalone so Claude, Codex, Gemini, and other MCP clients use the same integration surface.
121
+ Claude plugins can bundle MCP servers, so this MCP server can later be wrapped as a plugin. The MVP keeps the server standalone so Claude, Codex, Gemini, GitHub Copilot, Cursor, and other MCP clients use the same integration surface.
118
122
 
119
123
  Claude Code uses a 30 second MCP startup timeout by default. If a slower
120
124
  environment needs more time, launch Claude Code with a larger `MCP_TIMEOUT`
@@ -181,7 +185,7 @@ Then run `/mcp` inside Gemini CLI to confirm that the Artifacty tools are connec
181
185
 
182
186
  - `artifacty_create`: create a new Artifacty-native artifact.
183
187
  - `artifacty_publish`: backwards-compatible alias for `artifacty_create`.
184
- - `artifacty_import`: convert a Claude, Codex, Gemini, Artifacty, or generic artifact payload into Artifacty format and save it.
188
+ - `artifacty_import`: convert a Claude, Codex, Gemini, GitHub Copilot, Cursor, Artifacty, or generic artifact payload into Artifacty format and save it.
185
189
  - `artifacty_list`: discover artifacts by query, tag, or source agent.
186
190
  - `artifacty_get`: read artifact metadata and content.
187
191
  - `artifacty_update`: append a new version.
@@ -200,6 +204,10 @@ Use `artifacty_import` or the CLI `import` command when the artifact was produce
200
204
  node src/cli.js import --agent claude --file ./artifact.html --tag review
201
205
  node src/cli.js import --agent codex --file ./handoff.md --tag handoff
202
206
  node src/cli.js import --agent codex --content '{"agent":"codex","title":"Verification","verification":{"status":"passed","commands":[{"command":"npm test","status":"passed"}]}}'
207
+ node src/cli.js import --agent copilot --content '{"agent":"github-copilot","title":"PR Review","findings":[{"severity":"medium","file":"src/app.js","line":42,"title":"Handle missing state"}]}'
208
+ node src/cli.js import --agent cursor --content '{"sourceAgent":"cursor","title":"Cursor Handoff","summary":"Editor pass complete.","nextSteps":["Run visual QA."]}'
209
+ node src/cli.js import --agent cursor --file ./browser-screenshot.png --tag visual
210
+ node src/cli.js import --agent cursor --file ./demo.webm --tag demo
203
211
  node src/cli.js import --agent gemini --content '{"title":"Options","returnDisplay":"# Options\n- A\n- B"}'
204
212
  ```
205
213
 
@@ -207,11 +215,17 @@ Supported converter inputs:
207
215
 
208
216
  - Claude: local `.html`, `.htm`, `.md`, `.svg`, `.mmd`, `.jsx`, `.tsx`, source files, or JSON payloads with `title`/`content`/Claude artifact `type`.
209
217
  - Codex: markdown/text/json handoff files; Artifacty-compatible JSON payloads; structured handoff, bundle, diff, review, and verification JSON payloads with `agent` or `sourceAgent` set to `codex`.
218
+ - GitHub Copilot: markdown/text/json outputs; Artifacty-compatible JSON payloads; structured handoff, review, diff, and verification JSON payloads with `agent` or `sourceAgent` set to `github-copilot` or `copilot`.
219
+ - Cursor: markdown/text/json outputs; Artifacty-compatible JSON payloads; structured handoff, review, diff, verification, screenshot, demo/video, and visual evidence bundle JSON payloads with `agent` or `sourceAgent` set to `cursor`.
210
220
  - Gemini: `returnDisplay`, `llmContent`, text blocks, or local markdown/text/json files.
211
- - Generic: file extension, content type, HTML doctype, JSON shape, and markdown headings are used to infer format and title.
221
+ - Generic: file extension, content type, HTML doctype, JSON shape, media data URLs, and markdown headings are used to infer format and title.
212
222
 
213
223
  The converter adds `imported` and source-agent tags, preserves the raw content as an immutable Artifacty version, and records source details under `metadata.artifactyImport`.
214
224
 
225
+ Fixture examples for Copilot/Cursor PR review, screenshot, demo recording, and
226
+ visual evidence bundle live under `test/fixtures/` and are covered by converter
227
+ tests.
228
+
215
229
  ## HTTP API
216
230
 
217
231
  - `GET /`: dashboard.
@@ -245,6 +259,9 @@ Renderer notes:
245
259
  - `svg` artifacts render in a scriptless sandboxed iframe after viewer-side sanitization; `/raw` still returns the original SVG.
246
260
  - `mermaid` artifacts load the vendored local Mermaid bundle from `/vendor/npm/mermaid/...` in a sandboxed iframe without `allow-same-origin`. The JavaScript asset route answers the iframe's `Origin: null` module request with `Access-Control-Allow-Origin: null`.
247
261
  - `react` artifacts are source-only unless `ARTIFACTY_ENABLE_REACT_RENDERER=true` is set. When enabled, JSX transformation and React execution happen only in `/artifacts/:id/react-frame`, with `unsafe-eval` scoped to that frame CSP.
262
+ - `sarif` artifacts render a bounded findings summary and keep the complete formatted JSON available in the browser viewer and `/raw`.
263
+ - `csv` artifacts render as an escaped table with bounded rows and columns; `/raw` preserves the original CSV text.
264
+ - `image` and `video` artifacts store base64 content inline, render with native browser media elements, and decode bytes through `/raw`.
248
265
 
249
266
  ## Background Service
250
267
 
@@ -1,65 +1,44 @@
1
- # SARIF and CSV Artifact Support Plan
2
-
3
- This plan scopes SARIF and CSV support to **output artifacts** that Codex or
4
- other agents generate for downstream review. It does not cover Codex input
5
- context, appshots, thread state, or client-specific UI state.
6
-
7
- ## Goals
8
-
9
- - Preserve SARIF and CSV files as immutable Artifacty versions.
10
- - Classify common Codex Security exports without requiring manual
11
- `artifactType` overrides.
12
- - Render CSV outputs in a readable table view while keeping `/raw` unchanged.
13
- - Keep SARIF as JSON-first, with future room for a findings-focused viewer.
14
-
15
- ## Current Behavior
16
-
17
- - `.sarif` content is stored as `json` when the payload is valid JSON, but it
18
- currently falls back to `artifactType: "unknown"`.
19
- - `.csv` and `text/csv` content can be stored as `text`, but there is no CSV
20
- detection, table renderer, or artifact-type inference.
21
- - Security findings exported as JSON, CSV, or SARIF can be shared today, but the
22
- browsing experience is not yet tailored to review workflows.
23
-
24
- ## Phase 1: Detection and Taxonomy
25
-
26
- - Detect `.sarif` and `application/sarif+json` as `json`.
27
- - Detect SARIF shape (`version`, `runs[]`, `tool.driver`) and classify as
28
- `code-review`.
29
- - Detect `.csv` and `text/csv` as either a new `csv` format or `text` with
30
- `metadata.delimitedText`.
31
- - Infer `code-review` for CSV/SARIF files named like `findings`, `security`,
32
- `review`, or containing columns such as `severity`, `file`, and `message`.
33
-
34
- Exit criteria: CLI, HTTP, and MCP imports classify SARIF and common findings CSV
35
- without explicit overrides.
36
-
37
- ## Phase 2: CSV Viewer
38
-
39
- - Add a lightweight RFC 4180-style CSV parser for browser rendering.
40
- - Render CSV as a scrollable table with sticky headers and escaped cell content.
41
- - Cap rendered rows and columns for very large files, with a visible truncation
42
- notice and a link to `/raw`.
43
- - Preserve plain text fallback when parsing fails.
44
-
45
- Exit criteria: CSV artifacts are readable in the browser and raw fidelity is
46
- unchanged.
47
-
48
- ## Phase 3: SARIF Summary Viewer
49
-
50
- - Keep SARIF source as JSON.
51
- - Add an optional summary above the JSON view: rule id, severity/level, message,
52
- file URI, region, and result count by level.
53
- - Avoid implementing the complete SARIF spec in v1; parse only stable top-level
54
- fields and fail closed to JSON rendering.
55
-
56
- Exit criteria: SARIF exports are useful for quick triage while preserving the
57
- full JSON source.
58
-
59
- ## Tests
60
-
61
- - Converter fixtures: Codex Security SARIF, findings CSV, generic CSV, malformed
62
- CSV, and large CSV.
63
- - Storage round trips: content type, extension, size, hash, and `/raw` fidelity.
64
- - Server rendering: CSV table escaping, truncation notice, SARIF summary fallback.
65
- - MCP schema and import tests for explicit and inferred artifact types.
1
+ # SARIF and CSV Artifact Support
2
+
3
+ Artifacty supports SARIF and CSV as **output artifacts** that Codex and other
4
+ agents can hand off for downstream review. This scope excludes Codex input
5
+ context, appshots, thread state, and client-specific UI state.
6
+
7
+ ## Implemented Behavior
8
+
9
+ - `sarif` and `csv` are first-class `format` values across HTTP, CLI, MCP,
10
+ storage, and browser forms.
11
+ - `.sarif`, `.sarif.json`, `application/sarif+json`, `.csv`, and `text/csv`
12
+ inputs are detected during import.
13
+ - SARIF top-level objects with `version` and `runs[]` are imported as
14
+ `analysis-report` artifacts.
15
+ - CSV inputs default to `table`; CSV files that look like security or review
16
+ findings infer `analysis-report`.
17
+ - `/raw` always returns the original stored source.
18
+
19
+ ## Browser Rendering
20
+
21
+ - SARIF renders a bounded findings summary with run, result, error, warning,
22
+ and note counts.
23
+ - SARIF result rows show level, rule id, message, first location, and tool name.
24
+ - The full formatted SARIF JSON remains available in a details panel.
25
+ - CSV renders as an escaped table with bounded rows and columns.
26
+ - Malformed CSV or non-SARIF JSON fails closed to escaped source or formatted
27
+ JSON fallback.
28
+
29
+ ## Verification Coverage
30
+
31
+ - Storage round trips cover format enums, content types, extensions, and type
32
+ inference.
33
+ - Converter tests cover SARIF extension/MIME/object detection, findings CSV,
34
+ and generic CSV.
35
+ - Server tests cover SARIF summary rendering, CSV escaping, `/raw` fidelity,
36
+ and browser form options.
37
+ - MCP tests assert the new format and artifact type enums are exposed.
38
+
39
+ ## Future Extensions
40
+
41
+ - Add real-world fixtures from CodeQL, Semgrep, Trivy, and other scanners.
42
+ - Add sorting/filtering for SARIF levels and CSV columns.
43
+ - Add optional download helpers for filtered CSV/SARIF views without changing
44
+ immutable source storage.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifacty",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Local artifact exchange for heterogeneous LLM agents via HTTP and MCP.",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/cli.js CHANGED
@@ -145,7 +145,7 @@ async function main() {
145
145
  if (command === "install") {
146
146
  const agent = options._[0];
147
147
  if (!agent) {
148
- throw new Error("install requires an agent: claude, codex, gemini, or all");
148
+ throw new Error("install requires an agent: claude, codex, gemini, copilot, cursor, or all");
149
149
  }
150
150
  const result = await installAgent(agent, {
151
151
  projectDir: options.projectDir || process.cwd(),
@@ -311,7 +311,11 @@ function parseArgs(args) {
311
311
 
312
312
  async function readContent(options) {
313
313
  if (options.file) {
314
- return readFile(options.file, "utf8");
314
+ const filePath = path.resolve(options.file);
315
+ if (shouldReadFileAsBase64(options, filePath)) {
316
+ return (await readFile(filePath)).toString("base64");
317
+ }
318
+ return readFile(filePath, "utf8");
315
319
  }
316
320
  if (options.content !== undefined) {
317
321
  return options.content;
@@ -319,6 +323,16 @@ async function readContent(options) {
319
323
  throw new Error("Provide --file or --content");
320
324
  }
321
325
 
326
+ function shouldReadFileAsBase64(options, filePath) {
327
+ const format = String(options.format || "").toLowerCase();
328
+ const contentType = String(options.contentType || "").toLowerCase();
329
+ return format === "image" ||
330
+ format === "video" ||
331
+ contentType.startsWith("image/") ||
332
+ contentType.startsWith("video/") ||
333
+ /\.(png|jpe?g|gif|webp|mp4|webm)$/i.test(filePath);
334
+ }
335
+
322
336
  async function withUrls(store, artifact) {
323
337
  const publicBaseUrl = await resolvePublicBaseUrl(store);
324
338
  return {
@@ -349,8 +363,8 @@ Usage:
349
363
  artifacty status [--home ~/.artifacty]
350
364
  artifacty stop [--home ~/.artifacty] [--timeout 5000] [--force]
351
365
  artifacty publish --title <title> (--file <path> | --content <text>) [--format html|markdown|text|json|code|svg|mermaid|react] [--source agent] [--tag tag]
352
- artifacty import --agent claude|codex|gemini|auto (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react] [--tag tag]
353
- artifacty install claude|codex|gemini|all [--dry-run] [--config <path>] [--server-path <path>] [--url http://127.0.0.1:8787] [--timeout 30000]
366
+ artifacty import --agent claude|codex|gemini|copilot|cursor|auto (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react] [--tag tag]
367
+ artifacty install claude|codex|gemini|copilot|cursor|all [--dry-run] [--config <path>] [--server-path <path>] [--url http://127.0.0.1:8787] [--timeout 30000]
354
368
  artifacty check [--server-path <path>] [--timeout 5000]
355
369
  artifacty update <id> (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react]
356
370
  artifacty archive <id>
@@ -15,7 +15,7 @@ const messages = {
15
15
  ...(globalThis.ARTIFACTY_I18N || {})
16
16
  };
17
17
 
18
- const SUPPORTED_FORMATS = ["markdown", "html", "json", "text", "code", "svg", "mermaid", "react"];
18
+ const SUPPORTED_FORMATS = ["markdown", "html", "json", "text", "code", "svg", "mermaid", "react", "sarif", "csv", "image", "video"];
19
19
 
20
20
  const editorTheme = EditorView.theme({
21
21
  "&": {
@@ -128,7 +128,7 @@ function enhanceTextarea(textarea) {
128
128
  updatePreview();
129
129
 
130
130
  function updateToolbar(format) {
131
- formatJsonButton.hidden = format !== "json";
131
+ formatJsonButton.hidden = format !== "json" && format !== "sarif";
132
132
  status.textContent = formatLabel(format);
133
133
  }
134
134
 
@@ -158,7 +158,7 @@ function enhanceTextarea(textarea) {
158
158
  return;
159
159
  }
160
160
 
161
- if (format === "json") {
161
+ if (format === "json" || format === "sarif") {
162
162
  const pre = document.createElement("pre");
163
163
  try {
164
164
  pre.textContent = JSON.stringify(JSON.parse(content), null, 2);
@@ -191,7 +191,7 @@ function languageExtension(format) {
191
191
  if (format === "html" || format === "svg") {
192
192
  return html();
193
193
  }
194
- if (format === "json") {
194
+ if (format === "json" || format === "sarif") {
195
195
  return json();
196
196
  }
197
197
  if (format === "markdown") {
@@ -212,6 +212,18 @@ function detectFormat({ explicit, fileName, content }) {
212
212
  if (lowerName.endsWith(".md") || lowerName.endsWith(".markdown")) {
213
213
  return "markdown";
214
214
  }
215
+ if (lowerName.endsWith(".sarif") || lowerName.endsWith(".sarif.json")) {
216
+ return "sarif";
217
+ }
218
+ if (lowerName.endsWith(".csv")) {
219
+ return "csv";
220
+ }
221
+ if (/\.(png|jpe?g|gif|webp)$/.test(lowerName)) {
222
+ return "image";
223
+ }
224
+ if (/\.(mp4|webm)$/.test(lowerName)) {
225
+ return "video";
226
+ }
215
227
  if (lowerName.endsWith(".json")) {
216
228
  return "json";
217
229
  }
@@ -241,6 +253,18 @@ function detectFormat({ explicit, fileName, content }) {
241
253
  if (trimmed.startsWith("<!doctype") || trimmed.startsWith("<html") || trimmed.startsWith("<")) {
242
254
  return "html";
243
255
  }
256
+ if (looksLikeSarif(trimmed)) {
257
+ return "sarif";
258
+ }
259
+ if (looksLikeCsv(trimmed)) {
260
+ return "csv";
261
+ }
262
+ if (/^data:image\/(?:png|jpeg|gif|webp);base64,/i.test(trimmed)) {
263
+ return "image";
264
+ }
265
+ if (/^data:video\/(?:mp4|webm);base64,/i.test(trimmed)) {
266
+ return "video";
267
+ }
244
268
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
245
269
  return "json";
246
270
  }
@@ -255,6 +279,54 @@ function formatLabel(format) {
255
279
  return messages.mode.replaceAll("{format}", label);
256
280
  }
257
281
 
282
+ function looksLikeSarif(value) {
283
+ const trimmed = String(value || "").trim();
284
+ if (!trimmed.startsWith("{")) {
285
+ return false;
286
+ }
287
+ try {
288
+ const parsed = JSON.parse(trimmed);
289
+ return parsed &&
290
+ typeof parsed === "object" &&
291
+ Array.isArray(parsed.runs) &&
292
+ (typeof parsed.version === "string" || String(parsed.$schema || "").toLowerCase().includes("sarif"));
293
+ } catch {
294
+ return false;
295
+ }
296
+ }
297
+
298
+ function looksLikeCsv(value) {
299
+ const lines = String(value || "")
300
+ .trim()
301
+ .split(/\r?\n/)
302
+ .map((line) => line.trimEnd())
303
+ .filter(Boolean)
304
+ .slice(0, 5);
305
+ if (lines.length < 2 || lines[0].trimStart().startsWith("|")) {
306
+ return false;
307
+ }
308
+ const counts = lines.map(csvFieldCount);
309
+ return counts[0] > 1 && counts.every((count) => count === counts[0]);
310
+ }
311
+
312
+ function csvFieldCount(line) {
313
+ let count = 1;
314
+ let inQuotes = false;
315
+ for (let index = 0; index < line.length; index += 1) {
316
+ const char = line[index];
317
+ if (char === "\"") {
318
+ if (inQuotes && line[index + 1] === "\"") {
319
+ index += 1;
320
+ } else {
321
+ inQuotes = !inQuotes;
322
+ }
323
+ } else if (char === "," && !inQuotes) {
324
+ count += 1;
325
+ }
326
+ }
327
+ return count;
328
+ }
329
+
258
330
  function invalidJsonMessage(error) {
259
331
  return messages.invalidJson.replaceAll("{message}", error.message);
260
332
  }