@telepath-computer/television 0.1.70 → 0.1.72

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/dist/cli.cjs CHANGED
@@ -33968,6 +33968,7 @@ var ArtifactClient = class {
33968
33968
  title: input.title
33969
33969
  };
33970
33970
  if (input.externalFilePath !== void 0) body.externalFilePath = input.externalFilePath;
33971
+ if (input.externalURL !== void 0) body.externalURL = input.externalURL;
33971
33972
  if (input.screenID !== void 0) body.screenID = input.screenID;
33972
33973
  return this.#http.requestJSON("POST", "/artifacts", body);
33973
33974
  }
@@ -47942,12 +47943,46 @@ var ARTIFACT_TYPE_TO_EXTENSION = {
47942
47943
  function isArtifactType(value) {
47943
47944
  return ARTIFACT_TYPES.includes(value);
47944
47945
  }
47946
+ function isExternalArtifactURL(value) {
47947
+ let parsed;
47948
+ try {
47949
+ parsed = new URL(value);
47950
+ } catch {
47951
+ return false;
47952
+ }
47953
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
47954
+ }
47945
47955
  var ArtifactSchema = external_exports.object({
47946
47956
  id: external_exports.string().min(1),
47947
47957
  type: external_exports.enum(ARTIFACT_TYPES),
47948
47958
  title: external_exports.string(),
47949
47959
  status: external_exports.enum(ARTIFACT_STATUSES),
47950
- externalFilePath: external_exports.string().optional()
47960
+ externalFilePath: external_exports.string().optional(),
47961
+ externalURL: external_exports.string().optional()
47962
+ }).superRefine((value, ctx) => {
47963
+ if (value.externalFilePath !== void 0 && value.externalURL !== void 0) {
47964
+ ctx.addIssue({
47965
+ code: external_exports.ZodIssueCode.custom,
47966
+ message: "externalFilePath and externalURL are mutually exclusive",
47967
+ path: ["externalURL"]
47968
+ });
47969
+ }
47970
+ if (value.externalURL !== void 0) {
47971
+ if (!isExternalArtifactURL(value.externalURL)) {
47972
+ ctx.addIssue({
47973
+ code: external_exports.ZodIssueCode.custom,
47974
+ message: "externalURL must be an http(s) URL",
47975
+ path: ["externalURL"]
47976
+ });
47977
+ }
47978
+ if (value.status !== "committed") {
47979
+ ctx.addIssue({
47980
+ code: external_exports.ZodIssueCode.custom,
47981
+ message: "URL-backed artifacts must have status committed",
47982
+ path: ["status"]
47983
+ });
47984
+ }
47985
+ }
47951
47986
  });
47952
47987
  function createArtifact(input) {
47953
47988
  return {
@@ -47955,7 +47990,8 @@ function createArtifact(input) {
47955
47990
  type: input.type,
47956
47991
  title: input.title,
47957
47992
  status: input.status ?? "committed",
47958
- externalFilePath: input.externalFilePath
47993
+ externalFilePath: input.externalFilePath,
47994
+ externalURL: input.externalURL
47959
47995
  };
47960
47996
  }
47961
47997
  var ViewManifestSchema = external_exports.object({
@@ -48036,14 +48072,22 @@ var createScreenSchema = external_exports.object({
48036
48072
  name: external_exports.string(),
48037
48073
  id: external_exports.string().optional()
48038
48074
  });
48039
- var createArtifactSchema = external_exports.object({
48075
+ var createArtifactBaseFields = {
48040
48076
  type: external_exports.enum(ARTIFACT_TYPES),
48041
48077
  title: external_exports.string(),
48042
- externalFilePath: external_exports.string().optional()
48043
- }).strict();
48044
- var createArtifactBodySchema = createArtifactSchema.extend({
48078
+ externalFilePath: external_exports.string().optional(),
48079
+ externalURL: external_exports.string().optional()
48080
+ };
48081
+ var externalSourceMutexRefine = (value) => !(value.externalFilePath !== void 0 && value.externalURL !== void 0);
48082
+ var externalSourceMutexMessage = {
48083
+ message: "externalFilePath and externalURL are mutually exclusive",
48084
+ path: ["externalURL"]
48085
+ };
48086
+ var createArtifactSchema = external_exports.object(createArtifactBaseFields).strict().refine(externalSourceMutexRefine, externalSourceMutexMessage);
48087
+ var createArtifactBodySchema = external_exports.object({
48088
+ ...createArtifactBaseFields,
48045
48089
  screenID: external_exports.string().optional()
48046
- }).strict();
48090
+ }).strict().refine(externalSourceMutexRefine, externalSourceMutexMessage);
48047
48091
  var patchArtifactSchema = external_exports.object({
48048
48092
  title: external_exports.string().optional()
48049
48093
  }).refine((value) => value.title !== void 0, { message: "title is required" });
@@ -48055,9 +48099,11 @@ var focusSchema = external_exports.object({
48055
48099
  var HTTP_OK2 = 200;
48056
48100
  var HTTP_CREATED = 201;
48057
48101
  var HTTP_NO_CONTENT = 204;
48102
+ var HTTP_FOUND = 302;
48058
48103
  var HTTP_BAD_REQUEST = 400;
48059
48104
  var HTTP_FORBIDDEN = 403;
48060
48105
  var HTTP_NOT_FOUND = 404;
48106
+ var HTTP_METHOD_NOT_ALLOWED = 405;
48061
48107
  var HTTP_CONFLICT = 409;
48062
48108
  var CORS_HEADERS = {
48063
48109
  "Access-Control-Allow-Origin": "*",
@@ -48191,11 +48237,13 @@ function registerRoutes(app, store, options) {
48191
48237
  type: parsed.data.type,
48192
48238
  title: parsed.data.title,
48193
48239
  screenID: req.params.id,
48194
- externalFilePath: parsed.data.externalFilePath
48240
+ externalFilePath: parsed.data.externalFilePath,
48241
+ externalURL: parsed.data.externalURL
48195
48242
  });
48243
+ const externallyBacked = artifact.externalFilePath !== void 0 || artifact.externalURL !== void 0;
48196
48244
  res.status(HTTP_CREATED).json({
48197
48245
  artifact,
48198
- ...artifact.externalFilePath ? {} : { pendingPath: getArtifactPendingBundlePath(store.storagePath, artifact.id) }
48246
+ ...externallyBacked ? {} : { pendingPath: getArtifactPendingBundlePath(store.storagePath, artifact.id) }
48199
48247
  });
48200
48248
  } catch (error48) {
48201
48249
  handleStoreError(res, error48, "Failed to create artifact");
@@ -48247,10 +48295,11 @@ function registerRoutes(app, store, options) {
48247
48295
  const artifact = store.createArtifact({
48248
48296
  type: parsed.data.type,
48249
48297
  title: parsed.data.title,
48250
- externalFilePath: parsed.data.externalFilePath
48298
+ externalFilePath: parsed.data.externalFilePath,
48299
+ externalURL: parsed.data.externalURL
48251
48300
  });
48252
48301
  const body = { artifact };
48253
- if (!artifact.externalFilePath) {
48302
+ if (artifact.externalFilePath === void 0 && artifact.externalURL === void 0) {
48254
48303
  body.pendingPath = getArtifactPendingBundlePath(store.storagePath, artifact.id);
48255
48304
  }
48256
48305
  if (parsed.data.screenID !== void 0) {
@@ -48344,6 +48393,10 @@ function registerRoutes(app, store, options) {
48344
48393
  sendError(res, HTTP_NOT_FOUND, `Artifact not found: ${req.params.id}`);
48345
48394
  return;
48346
48395
  }
48396
+ if (artifact.externalURL) {
48397
+ res.redirect(HTTP_FOUND, artifact.externalURL);
48398
+ return;
48399
+ }
48347
48400
  const content = store.readArtifactContent(req.params.id);
48348
48401
  res.setHeader("Content-Type", contentTypeForArtifactType(artifact.type));
48349
48402
  res.send(content);
@@ -48385,6 +48438,11 @@ function registerRoutes(app, store, options) {
48385
48438
  });
48386
48439
  });
48387
48440
  app.put("/artifacts/:id/content", ...auth, import_express.default.text({ type: "*/*", limit: "10mb" }), (req, res) => {
48441
+ const artifact = store.getArtifact(req.params.id);
48442
+ if (artifact?.externalURL) {
48443
+ sendError(res, HTTP_METHOD_NOT_ALLOWED, "URL-backed artifacts have no server-writable content");
48444
+ return;
48445
+ }
48388
48446
  const body = typeof req.body === "string" ? req.body : "";
48389
48447
  try {
48390
48448
  store.writeArtifactContent(req.params.id, body);
@@ -49101,6 +49159,9 @@ var ServerStore = class extends EventTarget {
49101
49159
  if (!artifact) {
49102
49160
  return void 0;
49103
49161
  }
49162
+ if (artifact.externalURL) {
49163
+ return void 0;
49164
+ }
49104
49165
  if (artifact.externalFilePath) {
49105
49166
  return artifact.externalFilePath;
49106
49167
  }
@@ -49112,7 +49173,7 @@ var ServerStore = class extends EventTarget {
49112
49173
  }
49113
49174
  getArtifactPublicDir(id) {
49114
49175
  const artifact = this._artifacts.get(id);
49115
- if (!artifact || artifact.externalFilePath) {
49176
+ if (!artifact || artifact.externalFilePath || artifact.externalURL) {
49116
49177
  return void 0;
49117
49178
  }
49118
49179
  return getArtifactPublicDirPath(this.storagePath, id, "committed");
@@ -49143,11 +49204,16 @@ var ServerStore = class extends EventTarget {
49143
49204
  * responsibility (Phase 6).
49144
49205
  */
49145
49206
  writeArtifactContent(id, content) {
49207
+ const artifact = this._artifacts.get(id);
49208
+ if (artifact?.externalURL) {
49209
+ throw new InvalidRequestError(
49210
+ `Artifact ${id} is URL-backed; its content is not server-writable`
49211
+ );
49212
+ }
49146
49213
  const contentPath = this.getArtifactContentPath(id);
49147
49214
  if (!contentPath) {
49148
49215
  throw new NotFoundError(`Artifact not found: ${id}`, { entityType: "artifact", entityID: id });
49149
49216
  }
49150
- const artifact = this._artifacts.get(id);
49151
49217
  if (artifact && !artifact.externalFilePath && artifact.status === "pending" && !this.hasCommittedBundle(id)) {
49152
49218
  throw new InvalidRequestError(
49153
49219
  `Artifact ${id} has no committed content yet. Use commit or abandon after writing the pending bundle`
@@ -49172,19 +49238,26 @@ var ServerStore = class extends EventTarget {
49172
49238
  * mutation, no `artifact-attached` event.
49173
49239
  */
49174
49240
  createArtifact(input) {
49241
+ if (input.externalFilePath !== void 0 && input.externalURL !== void 0) {
49242
+ throw new InvalidRequestError(
49243
+ "externalFilePath and externalURL are mutually exclusive"
49244
+ );
49245
+ }
49175
49246
  const screen = input.screenID !== void 0 ? this.requireScreen(input.screenID) : null;
49247
+ const externallyBacked = input.externalFilePath !== void 0 || input.externalURL !== void 0;
49176
49248
  const artifact = createArtifact({
49177
49249
  type: input.type,
49178
49250
  title: input.title,
49179
- status: input.externalFilePath ? "committed" : "pending",
49180
- externalFilePath: input.externalFilePath ? this.normalizeExternalArtifactPath(input.externalFilePath, input.type) : void 0
49251
+ status: externallyBacked ? "committed" : "pending",
49252
+ externalFilePath: input.externalFilePath ? this.normalizeExternalArtifactPath(input.externalFilePath, input.type) : void 0,
49253
+ externalURL: input.externalURL ? this.normalizeExternalArtifactURL(input.externalURL) : void 0
49181
49254
  });
49182
- if (!artifact.externalFilePath) {
49255
+ if (!externallyBacked) {
49183
49256
  this.scaffoldPendingBundle(artifact.id, artifact.type);
49184
49257
  }
49185
49258
  this.writeArtifact(artifact);
49186
49259
  this._artifacts.set(artifact.id, artifact);
49187
- if (artifact.status === "committed") {
49260
+ if (artifact.status === "committed" && artifact.externalURL === void 0) {
49188
49261
  this.startWatchingArtifactContent(artifact.id);
49189
49262
  }
49190
49263
  this.dispatchEvent(new ArtifactCreatedEvent("artifact-created", { artifact }));
@@ -49219,7 +49292,7 @@ var ServerStore = class extends EventTarget {
49219
49292
  if (!artifact) {
49220
49293
  throw new NotFoundError(`Artifact not found: ${artifactID}`, { entityType: "artifact", entityID: artifactID });
49221
49294
  }
49222
- if (artifact.externalFilePath) {
49295
+ if (artifact.externalFilePath || artifact.externalURL) {
49223
49296
  throw new InvalidRequestError(`Only internal artifacts can enter pending edit: ${artifactID}`);
49224
49297
  }
49225
49298
  if (artifact.status === "pending") {
@@ -49268,7 +49341,7 @@ var ServerStore = class extends EventTarget {
49268
49341
  if (!artifact) {
49269
49342
  throw new NotFoundError(`Artifact not found: ${artifactID}`, { entityType: "artifact", entityID: artifactID });
49270
49343
  }
49271
- if (artifact.externalFilePath) {
49344
+ if (artifact.externalFilePath || artifact.externalURL) {
49272
49345
  throw new InvalidRequestError(`Only internal artifacts can abandon pending work: ${artifactID}`);
49273
49346
  }
49274
49347
  if (artifact.status !== "pending") {
@@ -49547,6 +49620,9 @@ var ServerStore = class extends EventTarget {
49547
49620
  }
49548
49621
  startContentWatchersForLoadedArtifacts() {
49549
49622
  for (const [artifactID, artifact] of this._artifacts.entries()) {
49623
+ if (artifact.externalURL) {
49624
+ continue;
49625
+ }
49550
49626
  if (artifact.status === "committed" || !artifact.externalFilePath && this.hasCommittedBundle(artifactID)) {
49551
49627
  this.startWatchingArtifactContent(artifactID);
49552
49628
  }
@@ -49647,11 +49723,11 @@ var ServerStore = class extends EventTarget {
49647
49723
  throw new NotFoundError(`Artifact not found: ${artifactID}`, { entityType: "artifact", entityID: artifactID });
49648
49724
  }
49649
49725
  const affectedScreenIDs = this.collectReferencingScreenIDs(artifactID);
49650
- if (!artifact.externalFilePath) {
49726
+ if (!artifact.externalFilePath && !artifact.externalURL) {
49651
49727
  (0, import_node_fs3.rmSync)(getArtifactPendingBundlePath(this.storagePath, artifactID), { recursive: true, force: true });
49652
49728
  }
49653
49729
  this.stopWatchingArtifactContent(artifactID);
49654
- if (!artifact.externalFilePath && artifact.status === "pending" && !this.hasCommittedBundle(artifactID)) {
49730
+ if (!artifact.externalFilePath && !artifact.externalURL && artifact.status === "pending" && !this.hasCommittedBundle(artifactID)) {
49655
49731
  this._artifacts.delete(artifactID);
49656
49732
  (0, import_node_fs3.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
49657
49733
  this.stripArtifactFromScreens(artifactID, affectedScreenIDs);
@@ -49693,6 +49769,14 @@ var ServerStore = class extends EventTarget {
49693
49769
  const metadataPath = getArtifactTrashMetadataPath(this.storagePath, artifact.id);
49694
49770
  (0, import_node_fs3.mkdirSync)(import_node_path6.default.dirname(metadataPath), { recursive: true });
49695
49771
  (0, import_node_fs3.writeFileSync)(metadataPath, JSON.stringify(artifact, null, JSON_INDENT_SPACES));
49772
+ if (artifact.externalURL) {
49773
+ return {
49774
+ outcome: "trashed",
49775
+ artifactID: artifact.id,
49776
+ metadataPath,
49777
+ externalURL: artifact.externalURL
49778
+ };
49779
+ }
49696
49780
  if (artifact.externalFilePath) {
49697
49781
  return {
49698
49782
  outcome: "trashed",
@@ -49944,6 +50028,14 @@ Review docs/storage.md for the current schema, and either update the file to mat
49944
50028
  get screensDir() {
49945
50029
  return import_node_path6.default.join(this.storagePath, "screens");
49946
50030
  }
50031
+ normalizeExternalArtifactURL(rawURL) {
50032
+ if (!isExternalArtifactURL(rawURL)) {
50033
+ throw new InvalidRequestError(
50034
+ `externalURL must be an absolute http(s) URL: ${rawURL}`
50035
+ );
50036
+ }
50037
+ return rawURL;
50038
+ }
49947
50039
  normalizeExternalArtifactPath(sourcePath, type) {
49948
50040
  if (!import_node_path6.default.isAbsolute(sourcePath)) {
49949
50041
  throw new InvalidRequestError("externalFilePath must be absolute");
@@ -50021,8 +50113,8 @@ function getDevPackageDir() {
50021
50113
  return import_node_path7.default.resolve(import_node_path7.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..");
50022
50114
  }
50023
50115
  function readCLIVersion() {
50024
- if ("0.1.70".length > 0) {
50025
- return "0.1.70";
50116
+ if ("0.1.72".length > 0) {
50117
+ return "0.1.72";
50026
50118
  }
50027
50119
  const devPackageJsonPath = import_node_path7.default.join(getDevPackageDir(), "package.json");
50028
50120
  if (!(0, import_node_fs4.existsSync)(devPackageJsonPath)) {
@@ -50031,8 +50123,8 @@ function readCLIVersion() {
50031
50123
  return JSON.parse((0, import_node_fs4.readFileSync)(devPackageJsonPath, "utf8")).version;
50032
50124
  }
50033
50125
  function readWorkflowHelpText() {
50034
- if ('# Television\n\nTelevision is a persistent artifact screen for agents. Use it when the user\nshould be able to inspect, revisit, and refine a file-backed result instead of\nonly reading a chat reply.\n\nIf you lose context, run:\n\n```bash\ntv help\n```\n\nThat command prints this full skill as one blob. There is no topic-scoped help\nin the current implementation.\n\n## Mental model\n\n- A **screen** is a named viewer surface with a layout.\n- An **artifact** is a file-backed result that can exist independently of any\n screen. It can be unplaced, attached to one screen, or attached to multiple\n screens.\n- **Screen membership** is separate from artifact identity: attaching/detaching\n controls which screens show an artifact; deleting removes the artifact\n globally. The CLI create commands require `--screen` so in-progress artifacts\n are visible immediately.\n- An **internal artifact** is a Television-managed bundle. You create a pending\n bundle, edit files in that bundle, then commit it.\n- An **external artifact** is a pointer to an existing absolute file on disk.\n Television displays that file but does not own or delete it.\n- **Pending** means a create or edit is staged but not yet committed.\n- **Trash** means metadata and committed internal bundles moved out of the live\n tree. There is no restore workflow in the current scope.\n\nThe core workflow is:\n\n1. Decide whether the result should be internal or external.\n2. Decide whether the user should be taken to the new screen or artifact now, or whether the work should happen in the background.\n3. Create or stage the artifact with the CLI.\n4. For internal artifacts, edit files in the pending bundle.\n5. Commit when the validation rules are satisfied.\n\n## Focus model\n\nTelevision separates state changes from focus.\n\nThere are two kinds of focus:\n\n- **Screen focus** is persistent. It decides which screen the TV is currently showing.\n- **Artifact focus** is transient. It may switch screens first, then scroll the artifact into view and briefly highlight it.\n\nImportant consequence:\n\n- there is a persisted focused screen\n- there is **not** a persisted focused artifact\n\nState-change commands can optionally trigger focus, but they do not imply it.\n\nAgent-facing create and attach commands require an explicit focus decision:\n\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`\n- `tv create-internal-artifact`, `tv create-external-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n\nUse this decision rule:\n\n- use `--focus-screen` when the user likely wants to go to the new screen immediately\n- use `--focus-artifact` when the user likely wants to inspect the new artifact immediately\n- use `--no-focus` when the work should happen in the background while keeping the current screen and artifact context unchanged\n\nHeuristic examples:\n\n- use a focus flag for requests like "show me", "open it", "put it on screen", "take me there", or "let me review it"\n- treat user language like **active**, **current**, **showing**, **visible**, **switch to**, **change to**, **go to**, or **show me that** as focus intent for the relevant screen or artifact\n- requests like "switch to the other screen", "show me that artifact", or "change to that screen" should usually translate to `tv focus-screen` or `tv focus-artifact`\n- use `--no-focus` for requests like "set this up", "make it in the background", "prepare it", or "wire this in"\n- also use `--no-focus` when the user says things like "in the background", "while I do something else", "while I work on X", or otherwise signals that your work should proceed on a parallel thread decoupled from their main task\n\nDirect focus commands:\n\n- `tv focus-screen --id <screen-id>` sets persistent screen focus\n- `tv focus-artifact --id <artifact-id> [--screen <screen-id>]` sends a transient artifact-focus nudge\n- `tv focus-status` reports the current persistent screen focus and connected client count\n\nImportant communication rule:\n\n- when you use `--no-focus`, explicitly say what you did in chat so the user knows the work happened even though Television did not visibly change\n\nIf you forget these rules or the CLI rejects a command for missing focus intent, run `tv help` and reread this section before retrying.\n\n## User communication during multi-step workflows\n\nWhen you are doing a multi-step artifact workflow, keep the user informed as you\nprogress.\n\nRequired communication style:\n\n- verbalize key actions and decisions as they happen\n- keep the language concise\n- prefer short updates over long explanations\n- frame updates in the user\'s world and goals, not in the internal mechanics of the skill or CLI workflow\n- avoid technical workflow jargon unless the user explicitly asks for it\n- do not write reports, long paragraphs, or chatty summaries while the work is in progress\n- do not use lists unless the user explicitly asks for one\n- optimize for speed and token efficiency\n\nGood examples:\n\n- "Starting the artifact now."\n- "Reviewing the draft and source material."\n- "Updating the HTML and efficiently navigating the artifact creation flow."\n- "The artifact did not pass validation yet; fixing the draft notes and retrying."\n- "Finalizing the artifact now."\n- "Done."\n\nBad examples:\n\n- multi-paragraph progress reports\n- long retrospective narration during execution\n- verbose bullet lists for routine workflow steps\n\n## Internal versus external\n\nUse an **internal artifact** when:\n\n- the artifact is purpose-built for Television\n- Television should own the bundle structure\n- future agents should be able to maintain the result by reading bundle files\n- you need a staged create or staged edit workflow\n\nUse an **external artifact** when:\n\n- a real file already exists on disk\n- the user wants Television to display that existing file\n- you do not need a Television-managed bundle\n\nDecision rule:\n\n- If the result should be maintained as a Television-owned long-lived artifact,\n choose internal.\n- If the result is already a real file outside Television and should stay that\n way, choose external.\n\nSupported artifact types:\n\n- `text/markdown`\n- `text/html`\n\n## Internal bundle files\n\nEvery internal artifact bundle contains:\n\n- `artifact.md`\n- `data.json`\n- `memory.md`\n- `public/index.md` or `public/index.html`\n\nFresh pending bundles are intentionally minimal:\n\n- `artifact.md` is blank\n- `memory.md` is blank\n- `public/index.md` or `public/index.html` is blank\n- `data.json` is exactly `{}`\n\nThe scaffold is not commit-valid by itself. Learn the required structure from\nthis skill, not from placeholder content in the scaffold.\n\n### `artifact.md`\n\n`artifact.md` is the contract for the artifact. It explains what the artifact\nis for, what conceptual material it is based on, how it should render, and how\nlater agents should maintain it.\n\nBefore commit, `artifact.md` must be non-empty and contain all of these exact\nheadings:\n\n```md\n## User intent\n## Purpose\n## Data shape\n## Data sources\n## Rendering\n## Update workflow\n## Non-goals\n```\n\nWhat each section should capture:\n\n- `## User intent`: faithful restatement or quotation of what the user actually said they wanted; this is critical and should preserve the user\'s language as closely as practical, including requests, feedback, complaints, constraints, and guidance\n- `## Purpose`: what the artifact is trying to achieve\n- `## Data shape`: the conceptual shape you reasoned about while authoring; for markdown artifacts this will often just be `{}`\n- `## Data sources`: where the underlying facts, notes, or source material came from and how they were obtained\n- `## Rendering`: how `public/index.md` or `public/index.html` should present it\n- `## Update workflow`: how future agents should refresh or modify it\n- `## Non-goals`: what is intentionally excluded, especially application-like runtime behavior\n\n### `data.json`\n\n`data.json` is a **thinking artifact**, not a runtime payload.\n\nIts purpose is to help the model separate:\n\n- reasoning / planning / authoring structure\n- from final presentation in `public/index.md` or `public/index.html`\n\nUse it to capture the pure conceptual shape of what you are about to render.\nThis is an authoring aid for agents, not an application data layer.\n\nHard rules:\n\n- **Do not treat `data.json` as live runtime state.**\n- **Do not write HTML/JS that loads, depends on, or synchronizes against `data.json`.**\n- **Do not build application-like data-driven artifacts.**\n- **We do not support runtime data-backed artifacts at this time.**\n- Artifacts are static markdown or static HTML documents.\n- HTML artifacts may include JavaScript and extra assets under `public/`, but\n that JavaScript must stay presentation-oriented and self-contained, not\n driven by `data.json` as an application state container.\n\nFor `text/markdown` artifacts, leave `data.json` as exactly:\n\n```json\n{}\n```\n\nThere is usually little value in separating content from presentation for\nmarkdown artifacts, so prefer `{}` unless there is a very strong authoring\nreason not to.\n\nFor `text/html` artifacts, use `data.json` only when it helps you think clearly\nabout the material before rendering. It may describe the conceptual structure\nof the artifact, but it must not become a runtime contract.\n\nValidation rule:\n\n- `data.json` must exist and contain valid JSON\n\nThe current validator does not require the JSON value to be an object, but an\nobject is the normal choice.\n\n### `memory.md`\n\n`memory.md` is the working scratchpad for later agents. Record decisions,\nlimitations, data-retrieval notes, problems encountered, what changed, and what\nshould be watched during future edits.\n\nRequired validation anchors:\n\n- `memory.md` must contain `## Activity Log`\n- `memory.md` must contain at least one UTC timestamp in exact\n `YYYY-MM-DDTHH:MM:SSZ` format\n- at least one timestamp must be from the last 30 minutes when you commit\n\nThe minimum required heading is:\n\n```md\n## Activity Log\n```\n\nWhat to record beyond that is up to the artifact and the work performed.\n\n### `public/index.md` and `public/index.html`\n\nThis is the rendered entry file that Television serves.\n\n- Markdown artifacts use `public/index.md`\n- HTML artifacts use `public/index.html`\n- the entry file must match the artifact `type`\n- the entry file must be non-empty before commit\n\nFor HTML artifacts:\n\n- `public/index.html` is a full HTML document, not a body fragment\n- additional public assets may live under `public/`\n- keep paths relative to `public/`\n\n## Quality bar\n\nBuild artifacts that are durable, truthful, and maintainable by later agents.\n\nRequired quality standards:\n\n- be faithful to source data\n- do not invent or hallucinate missing facts\n- do not silently truncate a dataset and pretend it is complete\n- prefer truth over completeness when those goals conflict\n- make limitations, sampling, missing data, and freshness visible\n- keep rendering aligned with the reasoning captured in `artifact.md`, `data.json`, and `memory.md`\n- keep `data.json` as an authoring/thinking artifact rather than a runtime dependency\n- keep the artifact maintainable by a future agent reading only the bundle files\n\nAnti-patterns:\n\n- cursory or low-effort data collection\n- fake data added to make the artifact look complete\n- brittle one-off hacks that a later agent cannot reproduce\n- hidden dependencies that are not documented in `artifact.md` or `memory.md`\n- layout churn during simple data refreshes when the data model did not change\n\n## HTML house style\n\nHTML artifacts should feel intentional and readable inside Television tiles.\n\nTelevision provides a full base stylesheet for HTML artifacts. Only add custom\nCSS when you need something not covered by the built-in styles. Prefer the base\nstyles and theme tokens so artifacts stay visually coherent with the rest of\nTelevision.\n\nHouse-style guidance:\n\n- use semantic HTML first\n- keep the most important information near the top\n- design for small, medium, and large tile sizes\n- avoid horizontal overflow unless there is no reasonable alternative\n- make empty states and error states explicit\n- prefer the built-in HTML styling before inventing custom component chrome\n\n### Elements\n\nStandard elements already have sensible defaults, so you usually do not need to\nstyle from scratch:\n\n- headings (`h1`\u2013`h6`) \u2014 sized and weighted\n- `p`, `ul`, `ol` \u2014 readable defaults\n- `code` and `pre` \u2014 monospace, muted background\n- `blockquote` \u2014 left border, muted text\n- `table`, `th`, `td` \u2014 bordered, striped headers\n- `button` \u2014 styled with border and hover state; use `size="sm"` or `size="md"` when appropriate\n- `hr` \u2014 subtle border\n- `a` \u2014 inherits color by default\n\n### `.prose` class\n\nUse a `.prose` wrapper for document-style HTML where readable vertical rhythm is\nappropriate. Do not rely on `.prose` for dashboards, tables, control surfaces,\nor dense custom layouts.\n\n```html\n<div class="prose">\n <h1>Title</h1>\n <p>Some content with proper spacing between elements.</p>\n <ul>\n <li>Item one</li>\n <li>Item two</li>\n </ul>\n</div>\n```\n\n### CSS variables\n\nUse the existing Television tokens when they are available in the runtime.\nThese are the preferred way to stay aligned with the app theme.\n\nColors:\n- `--color-bg` \u2014 page background\n- `--color-bg-muted` \u2014 subtle background\n- `--color-surface` \u2014 card or panel background\n- `--color-text` \u2014 primary text\n- `--color-text-muted` \u2014 secondary or label text\n- `--color-border` \u2014 border color\n\nSpacing:\n- `--space-4`\n- `--space-8`\n- `--space-12`\n- `--space-16`\n- `--space-24`\n- `--space-32`\n\nFonts:\n- `--font-sans`\n- `--font-mono`\n\nText sizes:\n- `--text-sm`\n- `--text-base`\n- `--text-lg`\n- `--text-xl`\n\nRadius:\n- `--radius-4`\n- `--radius-8`\n\n## Workflows\n\n### Create new internal artifact\n\n1. Decide that the result should be an internal artifact.\n2. Start the pending bundle:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --no-focus\n```\n\n`--screen` is required for internal artifact creation so the artifact has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\n3. Read the returned pending path and edit files there.\n4. Write `artifact.md`.\n5. In `artifact.md`, capture the user\'s language faithfully in `## User intent` before doing the rest of the authoring work. Use direct quotes when helpful, or a close paraphrase when that is clearer, but keep it representative of what the user actually said they wanted.\n6. Think through the artifact in a pure way and write `data.json` only as an authoring aid.\n7. For markdown artifacts, leave `data.json` as `{}` unless there is a compelling authoring reason not to.\n8. Render `public/index.md` or `public/index.html`.\n9. Append a current timestamped activity entry in `memory.md`.\n10. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Update internal artifact with fresh data\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md` before changing anything.\n3. Refresh the underlying facts or source material.\n4. Update `data.json` only if it helps clarify the authoring plan.\n5. For markdown artifacts, prefer to keep `data.json` as `{}`.\n6. Make the minimum rendering changes needed to keep the artifact correct.\n7. Record what changed in `memory.md`.\n8. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\nAvoid unnecessary layout or styling churn during data-only refreshes.\n\n### Modify internal artifact from user feedback\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md`.\n3. Update `artifact.md` if the user intent or non-goals changed.\n4. When the user has added feedback, complaints, corrections, or new guidance, update `## User intent` so it remains a faithful record of what the user actually wants now. Preserve the user\'s language as closely as practical, using direct quotes or close paraphrases.\n5. Update `data.json` only if it improves the authoring model of the artifact.\n6. For markdown artifacts, prefer to keep `data.json` as `{}`.\n7. Adjust `public/index.md` or `public/index.html` as narrowly as possible.\n8. Record the request, decision, and resulting change in `memory.md`.\n9. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Abandon pending work\n\nIf the staged work should be discarded instead of committed:\n\n```bash\ntv abandon-pending-artifact --id "<artifact-id>"\n```\n\n### Create external artifact\n\nUse this when the file already exists on disk and Television should display it\nwithout owning a bundle:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --path /absolute/path/to/file.md --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --path /absolute/path/to/file.html --no-focus\n```\n\n`--screen` is required for CLI creation so the file has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--path` must be absolute\n- the file must already exist and be readable\n- the extension must match `type`\n- external artifacts do not use pending create, pending edit, commit, or abandon\n\n## CLI reference\n\nArtifact commands:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" (--focus-artifact|--no-focus)\ntv create-external-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" --path /absolute/path (--focus-artifact|--no-focus)\ntv edit-internal-artifact --id "<artifact-id>"\ntv commit-pending-artifact --id "<artifact-id>"\ntv abandon-pending-artifact --id "<artifact-id>"\ntv update-artifact --id "<artifact-id>" --title "New title"\ntv list-artifacts [--screen "<screen-id>"] [--unplaced]\ntv get-artifact --id "<artifact-id>"\ntv delete-artifact --id "<artifact-id>"\n```\n\nScreen commands:\n\n```bash\ntv create-screen --name "Screen name" (--focus-screen|--no-focus)\ntv list-screens\ntv get-screen --id "<screen-id>"\ntv remove-screen --id "<screen-id>"\n```\n\nScreen membership commands:\n\n```bash\ntv attach-artifact --id "<artifact-id>" --screen "<screen-id>" (--focus-artifact|--no-focus)\ntv detach-artifact --id "<artifact-id>" --screen "<screen-id>"\n```\n\nFocus commands:\n\n```bash\ntv focus-status\ntv focus-screen --id "<screen-id>"\ntv focus-artifact --id "<artifact-id>" [--screen "<screen-id>"]\n```\n\nServer commands:\n\n```bash\ntv status\ntv storage-path\ntv serve\ntv stop\n```\n\nCLI behavior notes:\n\n- `--screen` is required on CLI create commands so new artifacts get immediate screen membership; use `tv attach-artifact` and `tv detach-artifact` for later screen membership changes\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`; `tv create-internal-artifact`, `tv create-external-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n- workflow and mutation commands print plain text\n- read commands print JSON\n- `tv get-screen` includes artifact `kind` and `status`\n- `tv attach-artifact` appends a default-sized card to the right end of the strip; idempotent if the artifact is already on that screen\n- `tv detach-artifact` removes the card from a screen\'s layout; the artifact metadata is never touched, even on the last reference\n- `tv delete-artifact` is the way to globally remove an artifact (detaches from every screen, then trashes the bundle / forgets the external pointer / discards pending-create)\n- `tv list-artifacts` accepts `--screen <id>` to filter by screen membership and `--unplaced` to surface artifacts attached to no screen\n- `tv update-artifact` changes title metadata only\n- `tv focus-screen` sets which screen the GUI is focused on; the change is persisted and broadcast to connected clients\n- `tv focus-artifact` is a transient nudge: clients switch screens if needed, scroll the artifact\'s card into view, and play a brief highlight animation; pass `--screen <id>` to pin which screen, otherwise the server picks one (preferring the active screen when the artifact is there)\n- `tv focus-status` prints the active screen ID and the count of connected GUI clients\n- when the CLI reports an error, follow the directive to run `tv help`\n\n## Deferred or out of scope\n\nThese are not part of the current implementation:\n\n- `tv help <topic>`\n- restore-from-trash\n- pending-listing commands\n- attestation or nonce commands\n- stale pending cleanup or stale trash cleanup\n- markdown editor UI recovery\n- client-side pending presentation work\n- multi-section help output'.length > 0) {
50035
- return '# Television\n\nTelevision is a persistent artifact screen for agents. Use it when the user\nshould be able to inspect, revisit, and refine a file-backed result instead of\nonly reading a chat reply.\n\nIf you lose context, run:\n\n```bash\ntv help\n```\n\nThat command prints this full skill as one blob. There is no topic-scoped help\nin the current implementation.\n\n## Mental model\n\n- A **screen** is a named viewer surface with a layout.\n- An **artifact** is a file-backed result that can exist independently of any\n screen. It can be unplaced, attached to one screen, or attached to multiple\n screens.\n- **Screen membership** is separate from artifact identity: attaching/detaching\n controls which screens show an artifact; deleting removes the artifact\n globally. The CLI create commands require `--screen` so in-progress artifacts\n are visible immediately.\n- An **internal artifact** is a Television-managed bundle. You create a pending\n bundle, edit files in that bundle, then commit it.\n- An **external artifact** is a pointer to an existing absolute file on disk.\n Television displays that file but does not own or delete it.\n- **Pending** means a create or edit is staged but not yet committed.\n- **Trash** means metadata and committed internal bundles moved out of the live\n tree. There is no restore workflow in the current scope.\n\nThe core workflow is:\n\n1. Decide whether the result should be internal or external.\n2. Decide whether the user should be taken to the new screen or artifact now, or whether the work should happen in the background.\n3. Create or stage the artifact with the CLI.\n4. For internal artifacts, edit files in the pending bundle.\n5. Commit when the validation rules are satisfied.\n\n## Focus model\n\nTelevision separates state changes from focus.\n\nThere are two kinds of focus:\n\n- **Screen focus** is persistent. It decides which screen the TV is currently showing.\n- **Artifact focus** is transient. It may switch screens first, then scroll the artifact into view and briefly highlight it.\n\nImportant consequence:\n\n- there is a persisted focused screen\n- there is **not** a persisted focused artifact\n\nState-change commands can optionally trigger focus, but they do not imply it.\n\nAgent-facing create and attach commands require an explicit focus decision:\n\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`\n- `tv create-internal-artifact`, `tv create-external-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n\nUse this decision rule:\n\n- use `--focus-screen` when the user likely wants to go to the new screen immediately\n- use `--focus-artifact` when the user likely wants to inspect the new artifact immediately\n- use `--no-focus` when the work should happen in the background while keeping the current screen and artifact context unchanged\n\nHeuristic examples:\n\n- use a focus flag for requests like "show me", "open it", "put it on screen", "take me there", or "let me review it"\n- treat user language like **active**, **current**, **showing**, **visible**, **switch to**, **change to**, **go to**, or **show me that** as focus intent for the relevant screen or artifact\n- requests like "switch to the other screen", "show me that artifact", or "change to that screen" should usually translate to `tv focus-screen` or `tv focus-artifact`\n- use `--no-focus` for requests like "set this up", "make it in the background", "prepare it", or "wire this in"\n- also use `--no-focus` when the user says things like "in the background", "while I do something else", "while I work on X", or otherwise signals that your work should proceed on a parallel thread decoupled from their main task\n\nDirect focus commands:\n\n- `tv focus-screen --id <screen-id>` sets persistent screen focus\n- `tv focus-artifact --id <artifact-id> [--screen <screen-id>]` sends a transient artifact-focus nudge\n- `tv focus-status` reports the current persistent screen focus and connected client count\n\nImportant communication rule:\n\n- when you use `--no-focus`, explicitly say what you did in chat so the user knows the work happened even though Television did not visibly change\n\nIf you forget these rules or the CLI rejects a command for missing focus intent, run `tv help` and reread this section before retrying.\n\n## User communication during multi-step workflows\n\nWhen you are doing a multi-step artifact workflow, keep the user informed as you\nprogress.\n\nRequired communication style:\n\n- verbalize key actions and decisions as they happen\n- keep the language concise\n- prefer short updates over long explanations\n- frame updates in the user\'s world and goals, not in the internal mechanics of the skill or CLI workflow\n- avoid technical workflow jargon unless the user explicitly asks for it\n- do not write reports, long paragraphs, or chatty summaries while the work is in progress\n- do not use lists unless the user explicitly asks for one\n- optimize for speed and token efficiency\n\nGood examples:\n\n- "Starting the artifact now."\n- "Reviewing the draft and source material."\n- "Updating the HTML and efficiently navigating the artifact creation flow."\n- "The artifact did not pass validation yet; fixing the draft notes and retrying."\n- "Finalizing the artifact now."\n- "Done."\n\nBad examples:\n\n- multi-paragraph progress reports\n- long retrospective narration during execution\n- verbose bullet lists for routine workflow steps\n\n## Internal versus external\n\nUse an **internal artifact** when:\n\n- the artifact is purpose-built for Television\n- Television should own the bundle structure\n- future agents should be able to maintain the result by reading bundle files\n- you need a staged create or staged edit workflow\n\nUse an **external artifact** when:\n\n- a real file already exists on disk\n- the user wants Television to display that existing file\n- you do not need a Television-managed bundle\n\nDecision rule:\n\n- If the result should be maintained as a Television-owned long-lived artifact,\n choose internal.\n- If the result is already a real file outside Television and should stay that\n way, choose external.\n\nSupported artifact types:\n\n- `text/markdown`\n- `text/html`\n\n## Internal bundle files\n\nEvery internal artifact bundle contains:\n\n- `artifact.md`\n- `data.json`\n- `memory.md`\n- `public/index.md` or `public/index.html`\n\nFresh pending bundles are intentionally minimal:\n\n- `artifact.md` is blank\n- `memory.md` is blank\n- `public/index.md` or `public/index.html` is blank\n- `data.json` is exactly `{}`\n\nThe scaffold is not commit-valid by itself. Learn the required structure from\nthis skill, not from placeholder content in the scaffold.\n\n### `artifact.md`\n\n`artifact.md` is the contract for the artifact. It explains what the artifact\nis for, what conceptual material it is based on, how it should render, and how\nlater agents should maintain it.\n\nBefore commit, `artifact.md` must be non-empty and contain all of these exact\nheadings:\n\n```md\n## User intent\n## Purpose\n## Data shape\n## Data sources\n## Rendering\n## Update workflow\n## Non-goals\n```\n\nWhat each section should capture:\n\n- `## User intent`: faithful restatement or quotation of what the user actually said they wanted; this is critical and should preserve the user\'s language as closely as practical, including requests, feedback, complaints, constraints, and guidance\n- `## Purpose`: what the artifact is trying to achieve\n- `## Data shape`: the conceptual shape you reasoned about while authoring; for markdown artifacts this will often just be `{}`\n- `## Data sources`: where the underlying facts, notes, or source material came from and how they were obtained\n- `## Rendering`: how `public/index.md` or `public/index.html` should present it\n- `## Update workflow`: how future agents should refresh or modify it\n- `## Non-goals`: what is intentionally excluded, especially application-like runtime behavior\n\n### `data.json`\n\n`data.json` is a **thinking artifact**, not a runtime payload.\n\nIts purpose is to help the model separate:\n\n- reasoning / planning / authoring structure\n- from final presentation in `public/index.md` or `public/index.html`\n\nUse it to capture the pure conceptual shape of what you are about to render.\nThis is an authoring aid for agents, not an application data layer.\n\nHard rules:\n\n- **Do not treat `data.json` as live runtime state.**\n- **Do not write HTML/JS that loads, depends on, or synchronizes against `data.json`.**\n- **Do not build application-like data-driven artifacts.**\n- **We do not support runtime data-backed artifacts at this time.**\n- Artifacts are static markdown or static HTML documents.\n- HTML artifacts may include JavaScript and extra assets under `public/`, but\n that JavaScript must stay presentation-oriented and self-contained, not\n driven by `data.json` as an application state container.\n\nFor `text/markdown` artifacts, leave `data.json` as exactly:\n\n```json\n{}\n```\n\nThere is usually little value in separating content from presentation for\nmarkdown artifacts, so prefer `{}` unless there is a very strong authoring\nreason not to.\n\nFor `text/html` artifacts, use `data.json` only when it helps you think clearly\nabout the material before rendering. It may describe the conceptual structure\nof the artifact, but it must not become a runtime contract.\n\nValidation rule:\n\n- `data.json` must exist and contain valid JSON\n\nThe current validator does not require the JSON value to be an object, but an\nobject is the normal choice.\n\n### `memory.md`\n\n`memory.md` is the working scratchpad for later agents. Record decisions,\nlimitations, data-retrieval notes, problems encountered, what changed, and what\nshould be watched during future edits.\n\nRequired validation anchors:\n\n- `memory.md` must contain `## Activity Log`\n- `memory.md` must contain at least one UTC timestamp in exact\n `YYYY-MM-DDTHH:MM:SSZ` format\n- at least one timestamp must be from the last 30 minutes when you commit\n\nThe minimum required heading is:\n\n```md\n## Activity Log\n```\n\nWhat to record beyond that is up to the artifact and the work performed.\n\n### `public/index.md` and `public/index.html`\n\nThis is the rendered entry file that Television serves.\n\n- Markdown artifacts use `public/index.md`\n- HTML artifacts use `public/index.html`\n- the entry file must match the artifact `type`\n- the entry file must be non-empty before commit\n\nFor HTML artifacts:\n\n- `public/index.html` is a full HTML document, not a body fragment\n- additional public assets may live under `public/`\n- keep paths relative to `public/`\n\n## Quality bar\n\nBuild artifacts that are durable, truthful, and maintainable by later agents.\n\nRequired quality standards:\n\n- be faithful to source data\n- do not invent or hallucinate missing facts\n- do not silently truncate a dataset and pretend it is complete\n- prefer truth over completeness when those goals conflict\n- make limitations, sampling, missing data, and freshness visible\n- keep rendering aligned with the reasoning captured in `artifact.md`, `data.json`, and `memory.md`\n- keep `data.json` as an authoring/thinking artifact rather than a runtime dependency\n- keep the artifact maintainable by a future agent reading only the bundle files\n\nAnti-patterns:\n\n- cursory or low-effort data collection\n- fake data added to make the artifact look complete\n- brittle one-off hacks that a later agent cannot reproduce\n- hidden dependencies that are not documented in `artifact.md` or `memory.md`\n- layout churn during simple data refreshes when the data model did not change\n\n## HTML house style\n\nHTML artifacts should feel intentional and readable inside Television tiles.\n\nTelevision provides a full base stylesheet for HTML artifacts. Only add custom\nCSS when you need something not covered by the built-in styles. Prefer the base\nstyles and theme tokens so artifacts stay visually coherent with the rest of\nTelevision.\n\nHouse-style guidance:\n\n- use semantic HTML first\n- keep the most important information near the top\n- design for small, medium, and large tile sizes\n- avoid horizontal overflow unless there is no reasonable alternative\n- make empty states and error states explicit\n- prefer the built-in HTML styling before inventing custom component chrome\n\n### Elements\n\nStandard elements already have sensible defaults, so you usually do not need to\nstyle from scratch:\n\n- headings (`h1`\u2013`h6`) \u2014 sized and weighted\n- `p`, `ul`, `ol` \u2014 readable defaults\n- `code` and `pre` \u2014 monospace, muted background\n- `blockquote` \u2014 left border, muted text\n- `table`, `th`, `td` \u2014 bordered, striped headers\n- `button` \u2014 styled with border and hover state; use `size="sm"` or `size="md"` when appropriate\n- `hr` \u2014 subtle border\n- `a` \u2014 inherits color by default\n\n### `.prose` class\n\nUse a `.prose` wrapper for document-style HTML where readable vertical rhythm is\nappropriate. Do not rely on `.prose` for dashboards, tables, control surfaces,\nor dense custom layouts.\n\n```html\n<div class="prose">\n <h1>Title</h1>\n <p>Some content with proper spacing between elements.</p>\n <ul>\n <li>Item one</li>\n <li>Item two</li>\n </ul>\n</div>\n```\n\n### CSS variables\n\nUse the existing Television tokens when they are available in the runtime.\nThese are the preferred way to stay aligned with the app theme.\n\nColors:\n- `--color-bg` \u2014 page background\n- `--color-bg-muted` \u2014 subtle background\n- `--color-surface` \u2014 card or panel background\n- `--color-text` \u2014 primary text\n- `--color-text-muted` \u2014 secondary or label text\n- `--color-border` \u2014 border color\n\nSpacing:\n- `--space-4`\n- `--space-8`\n- `--space-12`\n- `--space-16`\n- `--space-24`\n- `--space-32`\n\nFonts:\n- `--font-sans`\n- `--font-mono`\n\nText sizes:\n- `--text-sm`\n- `--text-base`\n- `--text-lg`\n- `--text-xl`\n\nRadius:\n- `--radius-4`\n- `--radius-8`\n\n## Workflows\n\n### Create new internal artifact\n\n1. Decide that the result should be an internal artifact.\n2. Start the pending bundle:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --no-focus\n```\n\n`--screen` is required for internal artifact creation so the artifact has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\n3. Read the returned pending path and edit files there.\n4. Write `artifact.md`.\n5. In `artifact.md`, capture the user\'s language faithfully in `## User intent` before doing the rest of the authoring work. Use direct quotes when helpful, or a close paraphrase when that is clearer, but keep it representative of what the user actually said they wanted.\n6. Think through the artifact in a pure way and write `data.json` only as an authoring aid.\n7. For markdown artifacts, leave `data.json` as `{}` unless there is a compelling authoring reason not to.\n8. Render `public/index.md` or `public/index.html`.\n9. Append a current timestamped activity entry in `memory.md`.\n10. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Update internal artifact with fresh data\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md` before changing anything.\n3. Refresh the underlying facts or source material.\n4. Update `data.json` only if it helps clarify the authoring plan.\n5. For markdown artifacts, prefer to keep `data.json` as `{}`.\n6. Make the minimum rendering changes needed to keep the artifact correct.\n7. Record what changed in `memory.md`.\n8. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\nAvoid unnecessary layout or styling churn during data-only refreshes.\n\n### Modify internal artifact from user feedback\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md`.\n3. Update `artifact.md` if the user intent or non-goals changed.\n4. When the user has added feedback, complaints, corrections, or new guidance, update `## User intent` so it remains a faithful record of what the user actually wants now. Preserve the user\'s language as closely as practical, using direct quotes or close paraphrases.\n5. Update `data.json` only if it improves the authoring model of the artifact.\n6. For markdown artifacts, prefer to keep `data.json` as `{}`.\n7. Adjust `public/index.md` or `public/index.html` as narrowly as possible.\n8. Record the request, decision, and resulting change in `memory.md`.\n9. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Abandon pending work\n\nIf the staged work should be discarded instead of committed:\n\n```bash\ntv abandon-pending-artifact --id "<artifact-id>"\n```\n\n### Create external artifact\n\nUse this when the file already exists on disk and Television should display it\nwithout owning a bundle:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --path /absolute/path/to/file.md --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --path /absolute/path/to/file.html --no-focus\n```\n\n`--screen` is required for CLI creation so the file has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--path` must be absolute\n- the file must already exist and be readable\n- the extension must match `type`\n- external artifacts do not use pending create, pending edit, commit, or abandon\n\n## CLI reference\n\nArtifact commands:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" (--focus-artifact|--no-focus)\ntv create-external-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" --path /absolute/path (--focus-artifact|--no-focus)\ntv edit-internal-artifact --id "<artifact-id>"\ntv commit-pending-artifact --id "<artifact-id>"\ntv abandon-pending-artifact --id "<artifact-id>"\ntv update-artifact --id "<artifact-id>" --title "New title"\ntv list-artifacts [--screen "<screen-id>"] [--unplaced]\ntv get-artifact --id "<artifact-id>"\ntv delete-artifact --id "<artifact-id>"\n```\n\nScreen commands:\n\n```bash\ntv create-screen --name "Screen name" (--focus-screen|--no-focus)\ntv list-screens\ntv get-screen --id "<screen-id>"\ntv remove-screen --id "<screen-id>"\n```\n\nScreen membership commands:\n\n```bash\ntv attach-artifact --id "<artifact-id>" --screen "<screen-id>" (--focus-artifact|--no-focus)\ntv detach-artifact --id "<artifact-id>" --screen "<screen-id>"\n```\n\nFocus commands:\n\n```bash\ntv focus-status\ntv focus-screen --id "<screen-id>"\ntv focus-artifact --id "<artifact-id>" [--screen "<screen-id>"]\n```\n\nServer commands:\n\n```bash\ntv status\ntv storage-path\ntv serve\ntv stop\n```\n\nCLI behavior notes:\n\n- `--screen` is required on CLI create commands so new artifacts get immediate screen membership; use `tv attach-artifact` and `tv detach-artifact` for later screen membership changes\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`; `tv create-internal-artifact`, `tv create-external-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n- workflow and mutation commands print plain text\n- read commands print JSON\n- `tv get-screen` includes artifact `kind` and `status`\n- `tv attach-artifact` appends a default-sized card to the right end of the strip; idempotent if the artifact is already on that screen\n- `tv detach-artifact` removes the card from a screen\'s layout; the artifact metadata is never touched, even on the last reference\n- `tv delete-artifact` is the way to globally remove an artifact (detaches from every screen, then trashes the bundle / forgets the external pointer / discards pending-create)\n- `tv list-artifacts` accepts `--screen <id>` to filter by screen membership and `--unplaced` to surface artifacts attached to no screen\n- `tv update-artifact` changes title metadata only\n- `tv focus-screen` sets which screen the GUI is focused on; the change is persisted and broadcast to connected clients\n- `tv focus-artifact` is a transient nudge: clients switch screens if needed, scroll the artifact\'s card into view, and play a brief highlight animation; pass `--screen <id>` to pin which screen, otherwise the server picks one (preferring the active screen when the artifact is there)\n- `tv focus-status` prints the active screen ID and the count of connected GUI clients\n- when the CLI reports an error, follow the directive to run `tv help`\n\n## Deferred or out of scope\n\nThese are not part of the current implementation:\n\n- `tv help <topic>`\n- restore-from-trash\n- pending-listing commands\n- attestation or nonce commands\n- stale pending cleanup or stale trash cleanup\n- markdown editor UI recovery\n- client-side pending presentation work\n- multi-section help output';
50126
+ if ('# Television\n\nTelevision is a persistent artifact screen for agents. Use it when the user\nshould be able to inspect, revisit, and refine a file-backed result instead of\nonly reading a chat reply.\n\nIf you lose context, run:\n\n```bash\ntv help\n```\n\nThat command prints this full skill as one blob. There is no topic-scoped help\nin the current implementation.\n\n## Mental model\n\n- A **screen** is a named viewer surface with a layout.\n- An **artifact** is a file-backed result that can exist independently of any\n screen. It can be unplaced, attached to one screen, or attached to multiple\n screens.\n- **Screen membership** is separate from artifact identity: attaching/detaching\n controls which screens show an artifact; deleting removes the artifact\n globally. The CLI create commands require `--screen` so in-progress artifacts\n are visible immediately.\n- An **internal artifact** is a Television-managed bundle. You create a pending\n bundle, edit files in that bundle, then commit it.\n- An **external artifact** is a pointer to an existing absolute file on disk.\n Television displays that file but does not own or delete it.\n- **Pending** means a create or edit is staged but not yet committed.\n- **Trash** means metadata and committed internal bundles moved out of the live\n tree. There is no restore workflow in the current scope.\n\nThe core workflow is:\n\n1. Decide whether the result should be internal or external.\n2. Decide whether the user should be taken to the new screen or artifact now, or whether the work should happen in the background.\n3. Create or stage the artifact with the CLI.\n4. For internal artifacts, edit files in the pending bundle.\n5. Commit when the validation rules are satisfied.\n\n## Focus model\n\nTelevision separates state changes from focus.\n\nThere are two kinds of focus:\n\n- **Screen focus** is persistent. It decides which screen the TV is currently showing.\n- **Artifact focus** is transient. It may switch screens first, then scroll the artifact into view and briefly highlight it.\n\nImportant consequence:\n\n- there is a persisted focused screen\n- there is **not** a persisted focused artifact\n\nState-change commands can optionally trigger focus, but they do not imply it.\n\nAgent-facing create and attach commands require an explicit focus decision:\n\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`\n- `tv create-internal-artifact`, `tv create-external-artifact`, `tv create-url-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n\nUse this decision rule:\n\n- use `--focus-screen` when the user likely wants to go to the new screen immediately\n- use `--focus-artifact` when the user likely wants to inspect the new artifact immediately\n- use `--no-focus` when the work should happen in the background while keeping the current screen and artifact context unchanged\n\nHeuristic examples:\n\n- use a focus flag for requests like "show me", "open it", "put it on screen", "take me there", or "let me review it"\n- treat user language like **active**, **current**, **showing**, **visible**, **switch to**, **change to**, **go to**, or **show me that** as focus intent for the relevant screen or artifact\n- requests like "switch to the other screen", "show me that artifact", or "change to that screen" should usually translate to `tv focus-screen` or `tv focus-artifact`\n- use `--no-focus` for requests like "set this up", "make it in the background", "prepare it", or "wire this in"\n- also use `--no-focus` when the user says things like "in the background", "while I do something else", "while I work on X", or otherwise signals that your work should proceed on a parallel thread decoupled from their main task\n\nDirect focus commands:\n\n- `tv focus-screen --id <screen-id>` sets persistent screen focus\n- `tv focus-artifact --id <artifact-id> [--screen <screen-id>]` sends a transient artifact-focus nudge\n- `tv focus-status` reports the current persistent screen focus and connected client count\n\nImportant communication rule:\n\n- when you use `--no-focus`, explicitly say what you did in chat so the user knows the work happened even though Television did not visibly change\n\nIf you forget these rules or the CLI rejects a command for missing focus intent, run `tv help` and reread this section before retrying.\n\n## User communication during multi-step workflows\n\nWhen you are doing a multi-step artifact workflow, keep the user informed as you\nprogress.\n\nRequired communication style:\n\n- verbalize key actions and decisions as they happen\n- keep the language concise\n- prefer short updates over long explanations\n- frame updates in the user\'s world and goals, not in the internal mechanics of the skill or CLI workflow\n- avoid technical workflow jargon unless the user explicitly asks for it\n- do not write reports, long paragraphs, or chatty summaries while the work is in progress\n- do not use lists unless the user explicitly asks for one\n- optimize for speed and token efficiency\n\nGood examples:\n\n- "Starting the artifact now."\n- "Reviewing the draft and source material."\n- "Updating the HTML and efficiently navigating the artifact creation flow."\n- "The artifact did not pass validation yet; fixing the draft notes and retrying."\n- "Finalizing the artifact now."\n- "Done."\n\nBad examples:\n\n- multi-paragraph progress reports\n- long retrospective narration during execution\n- verbose bullet lists for routine workflow steps\n\n## Internal versus external\n\nUse an **internal artifact** when:\n\n- the artifact is purpose-built for Television\n- Television should own the bundle structure\n- future agents should be able to maintain the result by reading bundle files\n- you need a staged create or staged edit workflow\n\nUse an **external artifact** when:\n\n- a real file already exists on disk\n- the user wants Television to display that existing file\n- you do not need a Television-managed bundle\n\nDecision rule:\n\n- If the result should be maintained as a Television-owned long-lived artifact,\n choose internal.\n- If the result is already a real file outside Television and should stay that\n way, choose external.\n\nSupported artifact types:\n\n- `text/markdown`\n- `text/html`\n\n## Internal bundle files\n\nEvery internal artifact bundle contains:\n\n- `artifact.md`\n- `data.json`\n- `memory.md`\n- `public/index.md` or `public/index.html`\n\nFresh pending bundles are intentionally minimal:\n\n- `artifact.md` is blank\n- `memory.md` is blank\n- `public/index.md` or `public/index.html` is blank\n- `data.json` is exactly `{}`\n\nThe scaffold is not commit-valid by itself. Learn the required structure from\nthis skill, not from placeholder content in the scaffold.\n\n### `artifact.md`\n\n`artifact.md` is the contract for the artifact. It explains what the artifact\nis for, what conceptual material it is based on, how it should render, and how\nlater agents should maintain it.\n\nBefore commit, `artifact.md` must be non-empty and contain all of these exact\nheadings:\n\n```md\n## User intent\n## Purpose\n## Data shape\n## Data sources\n## Rendering\n## Update workflow\n## Non-goals\n```\n\nWhat each section should capture:\n\n- `## User intent`: faithful restatement or quotation of what the user actually said they wanted; this is critical and should preserve the user\'s language as closely as practical, including requests, feedback, complaints, constraints, and guidance\n- `## Purpose`: what the artifact is trying to achieve\n- `## Data shape`: the conceptual shape you reasoned about while authoring; for markdown artifacts this will often just be `{}`\n- `## Data sources`: where the underlying facts, notes, or source material came from and how they were obtained\n- `## Rendering`: how `public/index.md` or `public/index.html` should present it\n- `## Update workflow`: how future agents should refresh or modify it\n- `## Non-goals`: what is intentionally excluded, especially application-like runtime behavior\n\n### `data.json`\n\n`data.json` is a **thinking artifact**, not a runtime payload.\n\nIts purpose is to help the model separate:\n\n- reasoning / planning / authoring structure\n- from final presentation in `public/index.md` or `public/index.html`\n\nUse it to capture the pure conceptual shape of what you are about to render.\nThis is an authoring aid for agents, not an application data layer.\n\nHard rules:\n\n- **Do not treat `data.json` as live runtime state.**\n- **Do not write HTML/JS that loads, depends on, or synchronizes against `data.json`.**\n- **Do not build application-like data-driven artifacts.**\n- **We do not support runtime data-backed artifacts at this time.**\n- Artifacts are static markdown or static HTML documents.\n- HTML artifacts may include JavaScript and extra assets under `public/`, but\n that JavaScript must stay presentation-oriented and self-contained, not\n driven by `data.json` as an application state container.\n\nFor `text/markdown` artifacts, leave `data.json` as exactly:\n\n```json\n{}\n```\n\nThere is usually little value in separating content from presentation for\nmarkdown artifacts, so prefer `{}` unless there is a very strong authoring\nreason not to.\n\nFor `text/html` artifacts, use `data.json` only when it helps you think clearly\nabout the material before rendering. It may describe the conceptual structure\nof the artifact, but it must not become a runtime contract.\n\nValidation rule:\n\n- `data.json` must exist and contain valid JSON\n\nThe current validator does not require the JSON value to be an object, but an\nobject is the normal choice.\n\n### `memory.md`\n\n`memory.md` is the working scratchpad for later agents. Record decisions,\nlimitations, data-retrieval notes, problems encountered, what changed, and what\nshould be watched during future edits.\n\nRequired validation anchors:\n\n- `memory.md` must contain `## Activity Log`\n- `memory.md` must contain at least one UTC timestamp in exact\n `YYYY-MM-DDTHH:MM:SSZ` format\n- at least one timestamp must be from the last 30 minutes when you commit\n\nThe minimum required heading is:\n\n```md\n## Activity Log\n```\n\nWhat to record beyond that is up to the artifact and the work performed.\n\n### `public/index.md` and `public/index.html`\n\nThis is the rendered entry file that Television serves.\n\n- Markdown artifacts use `public/index.md`\n- HTML artifacts use `public/index.html`\n- the entry file must match the artifact `type`\n- the entry file must be non-empty before commit\n\nFor HTML artifacts:\n\n- `public/index.html` is a full HTML document, not a body fragment\n- additional public assets may live under `public/`\n- keep paths relative to `public/`\n\n## Quality bar\n\nBuild artifacts that are durable, truthful, and maintainable by later agents.\n\nRequired quality standards:\n\n- be faithful to source data\n- do not invent or hallucinate missing facts\n- do not silently truncate a dataset and pretend it is complete\n- prefer truth over completeness when those goals conflict\n- make limitations, sampling, missing data, and freshness visible\n- keep rendering aligned with the reasoning captured in `artifact.md`, `data.json`, and `memory.md`\n- keep `data.json` as an authoring/thinking artifact rather than a runtime dependency\n- keep the artifact maintainable by a future agent reading only the bundle files\n\nAnti-patterns:\n\n- cursory or low-effort data collection\n- fake data added to make the artifact look complete\n- brittle one-off hacks that a later agent cannot reproduce\n- hidden dependencies that are not documented in `artifact.md` or `memory.md`\n- layout churn during simple data refreshes when the data model did not change\n\n## HTML house style\n\nHTML artifacts should feel intentional and readable inside Television tiles.\n\nTelevision provides a full base stylesheet for HTML artifacts. Only add custom\nCSS when you need something not covered by the built-in styles. Prefer the base\nstyles and theme tokens so artifacts stay visually coherent with the rest of\nTelevision.\n\nHouse-style guidance:\n\n- use semantic HTML first\n- keep the most important information near the top\n- design for small, medium, and large tile sizes\n- avoid horizontal overflow unless there is no reasonable alternative\n- make empty states and error states explicit\n- prefer the built-in HTML styling before inventing custom component chrome\n\n### Elements\n\nStandard elements already have sensible defaults, so you usually do not need to\nstyle from scratch:\n\n- headings (`h1`\u2013`h6`) \u2014 sized and weighted\n- `p`, `ul`, `ol` \u2014 readable defaults\n- `code` and `pre` \u2014 monospace, muted background\n- `blockquote` \u2014 left border, muted text\n- `table`, `th`, `td` \u2014 bordered, striped headers\n- `button` \u2014 styled with border and hover state; use `size="sm"` or `size="md"` when appropriate\n- `hr` \u2014 subtle border\n- `a` \u2014 inherits color by default\n\n### `.prose` class\n\nUse a `.prose` wrapper for document-style HTML where readable vertical rhythm is\nappropriate. Do not rely on `.prose` for dashboards, tables, control surfaces,\nor dense custom layouts.\n\n```html\n<div class="prose">\n <h1>Title</h1>\n <p>Some content with proper spacing between elements.</p>\n <ul>\n <li>Item one</li>\n <li>Item two</li>\n </ul>\n</div>\n```\n\n### CSS variables\n\nUse the existing Television tokens when they are available in the runtime.\nThese are the preferred way to stay aligned with the app theme.\n\nColors:\n- `--color-bg` \u2014 page background\n- `--color-bg-muted` \u2014 subtle background\n- `--color-surface` \u2014 card or panel background\n- `--color-text` \u2014 primary text\n- `--color-text-muted` \u2014 secondary or label text\n- `--color-border` \u2014 border color\n\nSpacing:\n- `--space-4`\n- `--space-8`\n- `--space-12`\n- `--space-16`\n- `--space-24`\n- `--space-32`\n\nFonts:\n- `--font-sans`\n- `--font-mono`\n\nText sizes:\n- `--text-sm`\n- `--text-base`\n- `--text-lg`\n- `--text-xl`\n\nRadius:\n- `--radius-4`\n- `--radius-8`\n\n## Workflows\n\n### Create new internal artifact\n\n1. Decide that the result should be an internal artifact.\n2. Start the pending bundle:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --no-focus\n```\n\n`--screen` is required for internal artifact creation so the artifact has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\n3. Read the returned pending path and edit files there.\n4. Write `artifact.md`.\n5. In `artifact.md`, capture the user\'s language faithfully in `## User intent` before doing the rest of the authoring work. Use direct quotes when helpful, or a close paraphrase when that is clearer, but keep it representative of what the user actually said they wanted.\n6. Think through the artifact in a pure way and write `data.json` only as an authoring aid.\n7. For markdown artifacts, leave `data.json` as `{}` unless there is a compelling authoring reason not to.\n8. Render `public/index.md` or `public/index.html`.\n9. Append a current timestamped activity entry in `memory.md`.\n10. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Update internal artifact with fresh data\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md` before changing anything.\n3. Refresh the underlying facts or source material.\n4. Update `data.json` only if it helps clarify the authoring plan.\n5. For markdown artifacts, prefer to keep `data.json` as `{}`.\n6. Make the minimum rendering changes needed to keep the artifact correct.\n7. Record what changed in `memory.md`.\n8. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\nAvoid unnecessary layout or styling churn during data-only refreshes.\n\n### Modify internal artifact from user feedback\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md`.\n3. Update `artifact.md` if the user intent or non-goals changed.\n4. When the user has added feedback, complaints, corrections, or new guidance, update `## User intent` so it remains a faithful record of what the user actually wants now. Preserve the user\'s language as closely as practical, using direct quotes or close paraphrases.\n5. Update `data.json` only if it improves the authoring model of the artifact.\n6. For markdown artifacts, prefer to keep `data.json` as `{}`.\n7. Adjust `public/index.md` or `public/index.html` as narrowly as possible.\n8. Record the request, decision, and resulting change in `memory.md`.\n9. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Abandon pending work\n\nIf the staged work should be discarded instead of committed:\n\n```bash\ntv abandon-pending-artifact --id "<artifact-id>"\n```\n\n### Create external artifact\n\nUse this when the file already exists on disk and Television should display it\nwithout owning a bundle:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --path /absolute/path/to/file.md --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --path /absolute/path/to/file.html --no-focus\n```\n\n`--screen` is required for CLI creation so the file has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--path` must be absolute\n- the file must already exist and be readable\n- the extension must match `type`\n- external artifacts do not use pending create, pending edit, commit, or abandon\n\n### Create URL artifact\n\nUse this when the artifact should display a live web page (an internal docs\npage, a dashboard, a third-party site) rather than content Television owns or a\nfile on disk:\n\n```bash\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com --no-focus\n```\n\n`--screen` is required for CLI creation so the page has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--url` must be `http://` or `https://`\n- the type is always `text/html` \u2014 do not pass `--type`\n- URL artifacts are committed immediately on creation; they have no pending create, pending edit, commit, or abandon lifecycle\n- in the desktop app the page renders in a sandboxed `<webview>`; in the browser it renders in an `<iframe>`\n\n## CLI reference\n\nArtifact commands:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" (--focus-artifact|--no-focus)\ntv create-external-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" --path /absolute/path (--focus-artifact|--no-focus)\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com (--focus-artifact|--no-focus)\ntv edit-internal-artifact --id "<artifact-id>"\ntv commit-pending-artifact --id "<artifact-id>"\ntv abandon-pending-artifact --id "<artifact-id>"\ntv update-artifact --id "<artifact-id>" --title "New title"\ntv list-artifacts [--screen "<screen-id>"] [--unplaced]\ntv get-artifact --id "<artifact-id>"\ntv delete-artifact --id "<artifact-id>"\n```\n\nScreen commands:\n\n```bash\ntv create-screen --name "Screen name" (--focus-screen|--no-focus)\ntv list-screens\ntv get-screen --id "<screen-id>"\ntv remove-screen --id "<screen-id>"\n```\n\nScreen membership commands:\n\n```bash\ntv attach-artifact --id "<artifact-id>" --screen "<screen-id>" (--focus-artifact|--no-focus)\ntv detach-artifact --id "<artifact-id>" --screen "<screen-id>"\n```\n\nFocus commands:\n\n```bash\ntv focus-status\ntv focus-screen --id "<screen-id>"\ntv focus-artifact --id "<artifact-id>" [--screen "<screen-id>"]\n```\n\nServer commands:\n\n```bash\ntv status\ntv storage-path\ntv serve\ntv stop\n```\n\nCLI behavior notes:\n\n- `--screen` is required on CLI create commands so new artifacts get immediate screen membership; use `tv attach-artifact` and `tv detach-artifact` for later screen membership changes\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`; `tv create-internal-artifact`, `tv create-external-artifact`, `tv create-url-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n- workflow and mutation commands print plain text\n- read commands print JSON\n- `tv get-screen` includes artifact `kind` and `status`\n- `tv attach-artifact` appends a default-sized card to the right end of the strip; idempotent if the artifact is already on that screen\n- `tv detach-artifact` removes the card from a screen\'s layout; the artifact metadata is never touched, even on the last reference\n- `tv delete-artifact` is the way to globally remove an artifact (detaches from every screen, then trashes the bundle / forgets the external pointer / discards pending-create)\n- `tv list-artifacts` accepts `--screen <id>` to filter by screen membership and `--unplaced` to surface artifacts attached to no screen\n- `tv update-artifact` changes title metadata only\n- `tv focus-screen` sets which screen the GUI is focused on; the change is persisted and broadcast to connected clients\n- `tv focus-artifact` is a transient nudge: clients switch screens if needed, scroll the artifact\'s card into view, and play a brief highlight animation; pass `--screen <id>` to pin which screen, otherwise the server picks one (preferring the active screen when the artifact is there)\n- `tv focus-status` prints the active screen ID and the count of connected GUI clients\n- when the CLI reports an error, follow the directive to run `tv help`\n\n## Deferred or out of scope\n\nThese are not part of the current implementation:\n\n- `tv help <topic>`\n- restore-from-trash\n- pending-listing commands\n- attestation or nonce commands\n- stale pending cleanup or stale trash cleanup\n- markdown editor UI recovery\n- client-side pending presentation work\n- multi-section help output'.length > 0) {
50127
+ return '# Television\n\nTelevision is a persistent artifact screen for agents. Use it when the user\nshould be able to inspect, revisit, and refine a file-backed result instead of\nonly reading a chat reply.\n\nIf you lose context, run:\n\n```bash\ntv help\n```\n\nThat command prints this full skill as one blob. There is no topic-scoped help\nin the current implementation.\n\n## Mental model\n\n- A **screen** is a named viewer surface with a layout.\n- An **artifact** is a file-backed result that can exist independently of any\n screen. It can be unplaced, attached to one screen, or attached to multiple\n screens.\n- **Screen membership** is separate from artifact identity: attaching/detaching\n controls which screens show an artifact; deleting removes the artifact\n globally. The CLI create commands require `--screen` so in-progress artifacts\n are visible immediately.\n- An **internal artifact** is a Television-managed bundle. You create a pending\n bundle, edit files in that bundle, then commit it.\n- An **external artifact** is a pointer to an existing absolute file on disk.\n Television displays that file but does not own or delete it.\n- **Pending** means a create or edit is staged but not yet committed.\n- **Trash** means metadata and committed internal bundles moved out of the live\n tree. There is no restore workflow in the current scope.\n\nThe core workflow is:\n\n1. Decide whether the result should be internal or external.\n2. Decide whether the user should be taken to the new screen or artifact now, or whether the work should happen in the background.\n3. Create or stage the artifact with the CLI.\n4. For internal artifacts, edit files in the pending bundle.\n5. Commit when the validation rules are satisfied.\n\n## Focus model\n\nTelevision separates state changes from focus.\n\nThere are two kinds of focus:\n\n- **Screen focus** is persistent. It decides which screen the TV is currently showing.\n- **Artifact focus** is transient. It may switch screens first, then scroll the artifact into view and briefly highlight it.\n\nImportant consequence:\n\n- there is a persisted focused screen\n- there is **not** a persisted focused artifact\n\nState-change commands can optionally trigger focus, but they do not imply it.\n\nAgent-facing create and attach commands require an explicit focus decision:\n\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`\n- `tv create-internal-artifact`, `tv create-external-artifact`, `tv create-url-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n\nUse this decision rule:\n\n- use `--focus-screen` when the user likely wants to go to the new screen immediately\n- use `--focus-artifact` when the user likely wants to inspect the new artifact immediately\n- use `--no-focus` when the work should happen in the background while keeping the current screen and artifact context unchanged\n\nHeuristic examples:\n\n- use a focus flag for requests like "show me", "open it", "put it on screen", "take me there", or "let me review it"\n- treat user language like **active**, **current**, **showing**, **visible**, **switch to**, **change to**, **go to**, or **show me that** as focus intent for the relevant screen or artifact\n- requests like "switch to the other screen", "show me that artifact", or "change to that screen" should usually translate to `tv focus-screen` or `tv focus-artifact`\n- use `--no-focus` for requests like "set this up", "make it in the background", "prepare it", or "wire this in"\n- also use `--no-focus` when the user says things like "in the background", "while I do something else", "while I work on X", or otherwise signals that your work should proceed on a parallel thread decoupled from their main task\n\nDirect focus commands:\n\n- `tv focus-screen --id <screen-id>` sets persistent screen focus\n- `tv focus-artifact --id <artifact-id> [--screen <screen-id>]` sends a transient artifact-focus nudge\n- `tv focus-status` reports the current persistent screen focus and connected client count\n\nImportant communication rule:\n\n- when you use `--no-focus`, explicitly say what you did in chat so the user knows the work happened even though Television did not visibly change\n\nIf you forget these rules or the CLI rejects a command for missing focus intent, run `tv help` and reread this section before retrying.\n\n## User communication during multi-step workflows\n\nWhen you are doing a multi-step artifact workflow, keep the user informed as you\nprogress.\n\nRequired communication style:\n\n- verbalize key actions and decisions as they happen\n- keep the language concise\n- prefer short updates over long explanations\n- frame updates in the user\'s world and goals, not in the internal mechanics of the skill or CLI workflow\n- avoid technical workflow jargon unless the user explicitly asks for it\n- do not write reports, long paragraphs, or chatty summaries while the work is in progress\n- do not use lists unless the user explicitly asks for one\n- optimize for speed and token efficiency\n\nGood examples:\n\n- "Starting the artifact now."\n- "Reviewing the draft and source material."\n- "Updating the HTML and efficiently navigating the artifact creation flow."\n- "The artifact did not pass validation yet; fixing the draft notes and retrying."\n- "Finalizing the artifact now."\n- "Done."\n\nBad examples:\n\n- multi-paragraph progress reports\n- long retrospective narration during execution\n- verbose bullet lists for routine workflow steps\n\n## Internal versus external\n\nUse an **internal artifact** when:\n\n- the artifact is purpose-built for Television\n- Television should own the bundle structure\n- future agents should be able to maintain the result by reading bundle files\n- you need a staged create or staged edit workflow\n\nUse an **external artifact** when:\n\n- a real file already exists on disk\n- the user wants Television to display that existing file\n- you do not need a Television-managed bundle\n\nDecision rule:\n\n- If the result should be maintained as a Television-owned long-lived artifact,\n choose internal.\n- If the result is already a real file outside Television and should stay that\n way, choose external.\n\nSupported artifact types:\n\n- `text/markdown`\n- `text/html`\n\n## Internal bundle files\n\nEvery internal artifact bundle contains:\n\n- `artifact.md`\n- `data.json`\n- `memory.md`\n- `public/index.md` or `public/index.html`\n\nFresh pending bundles are intentionally minimal:\n\n- `artifact.md` is blank\n- `memory.md` is blank\n- `public/index.md` or `public/index.html` is blank\n- `data.json` is exactly `{}`\n\nThe scaffold is not commit-valid by itself. Learn the required structure from\nthis skill, not from placeholder content in the scaffold.\n\n### `artifact.md`\n\n`artifact.md` is the contract for the artifact. It explains what the artifact\nis for, what conceptual material it is based on, how it should render, and how\nlater agents should maintain it.\n\nBefore commit, `artifact.md` must be non-empty and contain all of these exact\nheadings:\n\n```md\n## User intent\n## Purpose\n## Data shape\n## Data sources\n## Rendering\n## Update workflow\n## Non-goals\n```\n\nWhat each section should capture:\n\n- `## User intent`: faithful restatement or quotation of what the user actually said they wanted; this is critical and should preserve the user\'s language as closely as practical, including requests, feedback, complaints, constraints, and guidance\n- `## Purpose`: what the artifact is trying to achieve\n- `## Data shape`: the conceptual shape you reasoned about while authoring; for markdown artifacts this will often just be `{}`\n- `## Data sources`: where the underlying facts, notes, or source material came from and how they were obtained\n- `## Rendering`: how `public/index.md` or `public/index.html` should present it\n- `## Update workflow`: how future agents should refresh or modify it\n- `## Non-goals`: what is intentionally excluded, especially application-like runtime behavior\n\n### `data.json`\n\n`data.json` is a **thinking artifact**, not a runtime payload.\n\nIts purpose is to help the model separate:\n\n- reasoning / planning / authoring structure\n- from final presentation in `public/index.md` or `public/index.html`\n\nUse it to capture the pure conceptual shape of what you are about to render.\nThis is an authoring aid for agents, not an application data layer.\n\nHard rules:\n\n- **Do not treat `data.json` as live runtime state.**\n- **Do not write HTML/JS that loads, depends on, or synchronizes against `data.json`.**\n- **Do not build application-like data-driven artifacts.**\n- **We do not support runtime data-backed artifacts at this time.**\n- Artifacts are static markdown or static HTML documents.\n- HTML artifacts may include JavaScript and extra assets under `public/`, but\n that JavaScript must stay presentation-oriented and self-contained, not\n driven by `data.json` as an application state container.\n\nFor `text/markdown` artifacts, leave `data.json` as exactly:\n\n```json\n{}\n```\n\nThere is usually little value in separating content from presentation for\nmarkdown artifacts, so prefer `{}` unless there is a very strong authoring\nreason not to.\n\nFor `text/html` artifacts, use `data.json` only when it helps you think clearly\nabout the material before rendering. It may describe the conceptual structure\nof the artifact, but it must not become a runtime contract.\n\nValidation rule:\n\n- `data.json` must exist and contain valid JSON\n\nThe current validator does not require the JSON value to be an object, but an\nobject is the normal choice.\n\n### `memory.md`\n\n`memory.md` is the working scratchpad for later agents. Record decisions,\nlimitations, data-retrieval notes, problems encountered, what changed, and what\nshould be watched during future edits.\n\nRequired validation anchors:\n\n- `memory.md` must contain `## Activity Log`\n- `memory.md` must contain at least one UTC timestamp in exact\n `YYYY-MM-DDTHH:MM:SSZ` format\n- at least one timestamp must be from the last 30 minutes when you commit\n\nThe minimum required heading is:\n\n```md\n## Activity Log\n```\n\nWhat to record beyond that is up to the artifact and the work performed.\n\n### `public/index.md` and `public/index.html`\n\nThis is the rendered entry file that Television serves.\n\n- Markdown artifacts use `public/index.md`\n- HTML artifacts use `public/index.html`\n- the entry file must match the artifact `type`\n- the entry file must be non-empty before commit\n\nFor HTML artifacts:\n\n- `public/index.html` is a full HTML document, not a body fragment\n- additional public assets may live under `public/`\n- keep paths relative to `public/`\n\n## Quality bar\n\nBuild artifacts that are durable, truthful, and maintainable by later agents.\n\nRequired quality standards:\n\n- be faithful to source data\n- do not invent or hallucinate missing facts\n- do not silently truncate a dataset and pretend it is complete\n- prefer truth over completeness when those goals conflict\n- make limitations, sampling, missing data, and freshness visible\n- keep rendering aligned with the reasoning captured in `artifact.md`, `data.json`, and `memory.md`\n- keep `data.json` as an authoring/thinking artifact rather than a runtime dependency\n- keep the artifact maintainable by a future agent reading only the bundle files\n\nAnti-patterns:\n\n- cursory or low-effort data collection\n- fake data added to make the artifact look complete\n- brittle one-off hacks that a later agent cannot reproduce\n- hidden dependencies that are not documented in `artifact.md` or `memory.md`\n- layout churn during simple data refreshes when the data model did not change\n\n## HTML house style\n\nHTML artifacts should feel intentional and readable inside Television tiles.\n\nTelevision provides a full base stylesheet for HTML artifacts. Only add custom\nCSS when you need something not covered by the built-in styles. Prefer the base\nstyles and theme tokens so artifacts stay visually coherent with the rest of\nTelevision.\n\nHouse-style guidance:\n\n- use semantic HTML first\n- keep the most important information near the top\n- design for small, medium, and large tile sizes\n- avoid horizontal overflow unless there is no reasonable alternative\n- make empty states and error states explicit\n- prefer the built-in HTML styling before inventing custom component chrome\n\n### Elements\n\nStandard elements already have sensible defaults, so you usually do not need to\nstyle from scratch:\n\n- headings (`h1`\u2013`h6`) \u2014 sized and weighted\n- `p`, `ul`, `ol` \u2014 readable defaults\n- `code` and `pre` \u2014 monospace, muted background\n- `blockquote` \u2014 left border, muted text\n- `table`, `th`, `td` \u2014 bordered, striped headers\n- `button` \u2014 styled with border and hover state; use `size="sm"` or `size="md"` when appropriate\n- `hr` \u2014 subtle border\n- `a` \u2014 inherits color by default\n\n### `.prose` class\n\nUse a `.prose` wrapper for document-style HTML where readable vertical rhythm is\nappropriate. Do not rely on `.prose` for dashboards, tables, control surfaces,\nor dense custom layouts.\n\n```html\n<div class="prose">\n <h1>Title</h1>\n <p>Some content with proper spacing between elements.</p>\n <ul>\n <li>Item one</li>\n <li>Item two</li>\n </ul>\n</div>\n```\n\n### CSS variables\n\nUse the existing Television tokens when they are available in the runtime.\nThese are the preferred way to stay aligned with the app theme.\n\nColors:\n- `--color-bg` \u2014 page background\n- `--color-bg-muted` \u2014 subtle background\n- `--color-surface` \u2014 card or panel background\n- `--color-text` \u2014 primary text\n- `--color-text-muted` \u2014 secondary or label text\n- `--color-border` \u2014 border color\n\nSpacing:\n- `--space-4`\n- `--space-8`\n- `--space-12`\n- `--space-16`\n- `--space-24`\n- `--space-32`\n\nFonts:\n- `--font-sans`\n- `--font-mono`\n\nText sizes:\n- `--text-sm`\n- `--text-base`\n- `--text-lg`\n- `--text-xl`\n\nRadius:\n- `--radius-4`\n- `--radius-8`\n\n## Workflows\n\n### Create new internal artifact\n\n1. Decide that the result should be an internal artifact.\n2. Start the pending bundle:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --no-focus\n```\n\n`--screen` is required for internal artifact creation so the artifact has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\n3. Read the returned pending path and edit files there.\n4. Write `artifact.md`.\n5. In `artifact.md`, capture the user\'s language faithfully in `## User intent` before doing the rest of the authoring work. Use direct quotes when helpful, or a close paraphrase when that is clearer, but keep it representative of what the user actually said they wanted.\n6. Think through the artifact in a pure way and write `data.json` only as an authoring aid.\n7. For markdown artifacts, leave `data.json` as `{}` unless there is a compelling authoring reason not to.\n8. Render `public/index.md` or `public/index.html`.\n9. Append a current timestamped activity entry in `memory.md`.\n10. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Update internal artifact with fresh data\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md` before changing anything.\n3. Refresh the underlying facts or source material.\n4. Update `data.json` only if it helps clarify the authoring plan.\n5. For markdown artifacts, prefer to keep `data.json` as `{}`.\n6. Make the minimum rendering changes needed to keep the artifact correct.\n7. Record what changed in `memory.md`.\n8. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\nAvoid unnecessary layout or styling churn during data-only refreshes.\n\n### Modify internal artifact from user feedback\n\n1. Stage the edit:\n\n```bash\ntv edit-internal-artifact --id "<artifact-id>"\n```\n\n2. Read `artifact.md`, `data.json`, and `memory.md`.\n3. Update `artifact.md` if the user intent or non-goals changed.\n4. When the user has added feedback, complaints, corrections, or new guidance, update `## User intent` so it remains a faithful record of what the user actually wants now. Preserve the user\'s language as closely as practical, using direct quotes or close paraphrases.\n5. Update `data.json` only if it improves the authoring model of the artifact.\n6. For markdown artifacts, prefer to keep `data.json` as `{}`.\n7. Adjust `public/index.md` or `public/index.html` as narrowly as possible.\n8. Record the request, decision, and resulting change in `memory.md`.\n9. Commit:\n\n```bash\ntv commit-pending-artifact --id "<artifact-id>"\n```\n\n### Abandon pending work\n\nIf the staged work should be discarded instead of committed:\n\n```bash\ntv abandon-pending-artifact --id "<artifact-id>"\n```\n\n### Create external artifact\n\nUse this when the file already exists on disk and Television should display it\nwithout owning a bundle:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/markdown --title "Artifact title" --path /absolute/path/to/file.md --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-external-artifact --screen "<screen-id>" --type text/html --title "Artifact title" --path /absolute/path/to/file.html --no-focus\n```\n\n`--screen` is required for CLI creation so the file has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--path` must be absolute\n- the file must already exist and be readable\n- the extension must match `type`\n- external artifacts do not use pending create, pending edit, commit, or abandon\n\n### Create URL artifact\n\nUse this when the artifact should display a live web page (an internal docs\npage, a dashboard, a third-party site) rather than content Television owns or a\nfile on disk:\n\n```bash\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com --focus-artifact\n```\n\nOr:\n\n```bash\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com --no-focus\n```\n\n`--screen` is required for CLI creation so the page has immediate screen membership. `--focus-artifact` or `--no-focus` is also required so you explicitly decide whether the user should be taken to it now.\n\nRules:\n\n- `--url` must be `http://` or `https://`\n- the type is always `text/html` \u2014 do not pass `--type`\n- URL artifacts are committed immediately on creation; they have no pending create, pending edit, commit, or abandon lifecycle\n- in the desktop app the page renders in a sandboxed `<webview>`; in the browser it renders in an `<iframe>`\n\n## CLI reference\n\nArtifact commands:\n\n```bash\ntv create-internal-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" (--focus-artifact|--no-focus)\ntv create-external-artifact --screen "<screen-id>" --type <text/markdown|text/html> --title "Artifact title" --path /absolute/path (--focus-artifact|--no-focus)\ntv create-url-artifact --screen "<screen-id>" --title "Artifact title" --url https://example.com (--focus-artifact|--no-focus)\ntv edit-internal-artifact --id "<artifact-id>"\ntv commit-pending-artifact --id "<artifact-id>"\ntv abandon-pending-artifact --id "<artifact-id>"\ntv update-artifact --id "<artifact-id>" --title "New title"\ntv list-artifacts [--screen "<screen-id>"] [--unplaced]\ntv get-artifact --id "<artifact-id>"\ntv delete-artifact --id "<artifact-id>"\n```\n\nScreen commands:\n\n```bash\ntv create-screen --name "Screen name" (--focus-screen|--no-focus)\ntv list-screens\ntv get-screen --id "<screen-id>"\ntv remove-screen --id "<screen-id>"\n```\n\nScreen membership commands:\n\n```bash\ntv attach-artifact --id "<artifact-id>" --screen "<screen-id>" (--focus-artifact|--no-focus)\ntv detach-artifact --id "<artifact-id>" --screen "<screen-id>"\n```\n\nFocus commands:\n\n```bash\ntv focus-status\ntv focus-screen --id "<screen-id>"\ntv focus-artifact --id "<artifact-id>" [--screen "<screen-id>"]\n```\n\nServer commands:\n\n```bash\ntv status\ntv storage-path\ntv serve\ntv stop\n```\n\nCLI behavior notes:\n\n- `--screen` is required on CLI create commands so new artifacts get immediate screen membership; use `tv attach-artifact` and `tv detach-artifact` for later screen membership changes\n- `tv create-screen` requires exactly one of `--focus-screen` or `--no-focus`; `tv create-internal-artifact`, `tv create-external-artifact`, `tv create-url-artifact`, and `tv attach-artifact` require exactly one of `--focus-artifact` or `--no-focus`\n- workflow and mutation commands print plain text\n- read commands print JSON\n- `tv get-screen` includes artifact `kind` and `status`\n- `tv attach-artifact` appends a default-sized card to the right end of the strip; idempotent if the artifact is already on that screen\n- `tv detach-artifact` removes the card from a screen\'s layout; the artifact metadata is never touched, even on the last reference\n- `tv delete-artifact` is the way to globally remove an artifact (detaches from every screen, then trashes the bundle / forgets the external pointer / discards pending-create)\n- `tv list-artifacts` accepts `--screen <id>` to filter by screen membership and `--unplaced` to surface artifacts attached to no screen\n- `tv update-artifact` changes title metadata only\n- `tv focus-screen` sets which screen the GUI is focused on; the change is persisted and broadcast to connected clients\n- `tv focus-artifact` is a transient nudge: clients switch screens if needed, scroll the artifact\'s card into view, and play a brief highlight animation; pass `--screen <id>` to pin which screen, otherwise the server picks one (preferring the active screen when the artifact is there)\n- `tv focus-status` prints the active screen ID and the count of connected GUI clients\n- when the CLI reports an error, follow the directive to run `tv help`\n\n## Deferred or out of scope\n\nThese are not part of the current implementation:\n\n- `tv help <topic>`\n- restore-from-trash\n- pending-listing commands\n- attestation or nonce commands\n- stale pending cleanup or stale trash cleanup\n- markdown editor UI recovery\n- client-side pending presentation work\n- multi-section help output';
50036
50128
  }
50037
50129
  const devSkillPath = import_node_path7.default.join(getDevPackageDir(), "skill/SKILL.md");
50038
50130
  if (!(0, import_node_fs4.existsSync)(devSkillPath)) {
@@ -50145,6 +50237,13 @@ function formatArtifactRemovalResult(result) {
50145
50237
  ` bundle: ${result.bundlePath}`
50146
50238
  ];
50147
50239
  }
50240
+ if ("externalURL" in result) {
50241
+ return [
50242
+ ` ${result.artifactID}: trashed`,
50243
+ ` metadata: ${result.metadataPath}`,
50244
+ ` external URL ${result.externalURL} not touched.`
50245
+ ];
50246
+ }
50148
50247
  return [
50149
50248
  ` ${result.artifactID}: trashed`,
50150
50249
  ` metadata: ${result.metadataPath}`,
@@ -50165,6 +50264,13 @@ function formatDeleteArtifactResult(result) {
50165
50264
  ` bundle: ${result.bundlePath}`
50166
50265
  ];
50167
50266
  }
50267
+ if ("externalURL" in result) {
50268
+ return [
50269
+ `URL artifact ${result.artifactID} moved to trash:`,
50270
+ ` metadata: ${result.metadataPath}`,
50271
+ ` external URL ${result.externalURL} not touched.`
50272
+ ];
50273
+ }
50168
50274
  return [
50169
50275
  `External artifact ${result.artifactID} moved to trash:`,
50170
50276
  ` metadata: ${result.metadataPath}`,
@@ -50387,6 +50493,21 @@ If you wish to display temporary content to the user, use an internal artifact i
50387
50493
  writeLine(env.stdout, `External artifact ${artifact.id} created.`);
50388
50494
  writeLine(env.stdout, `Television will display content from ${externalPath} and watch it for changes.`);
50389
50495
  });
50496
+ program2.command("create-url-artifact").description("Create a URL-backed artifact that embeds an external page").requiredOption("--screen <id>", "Target screen ID").requiredOption("--title <title>", "Artifact title").requiredOption("--url <url>", "http(s) URL of the page to embed").option("--focus-artifact", "Focus the new artifact after creation").option("--no-focus", "Create the artifact in the background without changing focus").option("--server <url>", "Server URL", DEFAULT_SERVER_URL).action(async (opts) => {
50497
+ const shouldFocus = resolveFocusDirective(argv, "create-url-artifact", "--focus-artifact");
50498
+ const client = createAuthenticatedClient(opts.server);
50499
+ const { artifact } = await client.artifacts.create({
50500
+ externalURL: opts.url,
50501
+ title: opts.title,
50502
+ type: "text/html",
50503
+ screenID: opts.screen
50504
+ });
50505
+ if (shouldFocus) {
50506
+ await client.viewer.focus({ artifactID: artifact.id, screenID: opts.screen });
50507
+ }
50508
+ writeLine(env.stdout, `URL artifact ${artifact.id} created.`);
50509
+ writeLine(env.stdout, `Television will display ${opts.url} in an embedded frame.`);
50510
+ });
50390
50511
  program2.command("storage-path").description("Print the Television storage path").action(() => {
50391
50512
  writeJSON(env.stdout, { storagePath: getTelevisionStoragePath() });
50392
50513
  });