@intentic/extension-manifest 1.223.0 → 1.225.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +16 -16
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundle.js.map +1 -1
  4. package/dist/manifest.js +5 -5
  5. package/dist/manifest.js.map +1 -1
  6. package/dist/mark.js +3 -3
  7. package/dist/mark.js.map +1 -1
  8. package/dist/permissions.js +1 -1
  9. package/dist/permissions.js.map +1 -1
  10. package/dist/points/automation-templates.d.ts +1 -1
  11. package/dist/points/automation-templates.d.ts.map +1 -1
  12. package/dist/points/automation-templates.js +6 -6
  13. package/dist/points/automation-templates.js.map +1 -1
  14. package/dist/points/bin.d.ts +1 -1
  15. package/dist/points/bin.d.ts.map +1 -1
  16. package/dist/points/bin.js +1 -1
  17. package/dist/points/bin.js.map +1 -1
  18. package/dist/points/capabilities.d.ts +1 -1
  19. package/dist/points/capabilities.d.ts.map +1 -1
  20. package/dist/points/capabilities.js +13 -13
  21. package/dist/points/capabilities.js.map +1 -1
  22. package/dist/points/commands.js +1 -1
  23. package/dist/points/commands.js.map +1 -1
  24. package/dist/points/environment.d.ts +1 -1
  25. package/dist/points/environment.d.ts.map +1 -1
  26. package/dist/points/environment.js +2 -2
  27. package/dist/points/environment.js.map +1 -1
  28. package/dist/points/files.js +2 -2
  29. package/dist/points/files.js.map +1 -1
  30. package/dist/points/index.d.ts +7 -7
  31. package/dist/points/listener.d.ts +1 -1
  32. package/dist/points/listener.d.ts.map +1 -1
  33. package/dist/points/listener.js +3 -3
  34. package/dist/points/listener.js.map +1 -1
  35. package/dist/points/processes.d.ts +1 -1
  36. package/dist/points/processes.d.ts.map +1 -1
  37. package/dist/points/processes.js +1 -1
  38. package/dist/points/processes.js.map +1 -1
  39. package/dist/points/settings.js +1 -1
  40. package/dist/points/settings.js.map +1 -1
  41. package/dist/points/viewers.d.ts +1 -1
  42. package/dist/points/viewers.d.ts.map +1 -1
  43. package/dist/points/viewers.js +3 -3
  44. package/dist/points/viewers.js.map +1 -1
  45. package/intentic-extension.schema.json +73 -73
  46. package/package.json +3 -3
  47. package/src/bundle.ts +5 -5
  48. package/src/contribution-point.ts +5 -5
  49. package/src/json-schema.ts +6 -6
  50. package/src/manifest.ts +18 -18
  51. package/src/mark.ts +11 -11
  52. package/src/permissions.ts +2 -2
  53. package/src/points/agent.ts +1 -1
  54. package/src/points/automation-templates.ts +10 -10
  55. package/src/points/bin.ts +2 -2
  56. package/src/points/capabilities.ts +38 -38
  57. package/src/points/commands.ts +4 -4
  58. package/src/points/documents.ts +2 -2
  59. package/src/points/environment.ts +2 -2
  60. package/src/points/files.ts +9 -9
  61. package/src/points/index.ts +4 -4
  62. package/src/points/listener.ts +7 -7
  63. package/src/points/processes.ts +1 -1
  64. package/src/points/settings.ts +1 -1
  65. package/src/points/viewers.ts +8 -8
  66. package/src/powers-diff.ts +5 -5
package/src/bundle.ts CHANGED
@@ -1,8 +1,8 @@
1
- /* WHAT A PUBLISHED BUNDLE MAY IMPORT — the loader's side of the manifest contract, stated where both sides can
1
+ /* WHAT A PUBLISHED BUNDLE MAY IMPORT, the loader's side of the manifest contract, stated where both sides can
2
2
  * read it. The host fetches an extension's entry bytes and imports them from a blob: URL, which has two hard
3
3
  * consequences: a relative import resolves against a blob: URL that was never created (a 404 for a file that
4
4
  * exists on disk), and a bare specifier resolves only if the shell's import map publishes it. Both failures are
5
- * invisible to the author — their own workspace loads the directory live — and fatal for every installer.
5
+ * invisible to the author, their own workspace loads the directory live, and fatal for every installer.
6
6
  *
7
7
  * It lives HERE, beside the manifest schema, because two independent judges have to agree on it: the daemon's
8
8
  * readiness check (before an author publishes) and the registry scanner (re-deriving the same answer cold, at
@@ -14,7 +14,7 @@
14
14
  export const HOST_PUBLISHED_SPECIFIERS = ["vue", "@intentic/extension-api", "@intentic/extension-ui", "@tanstack/vue-query"] as const;
15
15
 
16
16
  // Every specifier a single-file ESM bundle names: static imports, re-exports, bare side-effect imports, and
17
- // dynamic import(). A bundle is one file by contract, so a regex over its text is the right instrument — there
17
+ // dynamic import(). A bundle is one file by contract, so a regex over its text is the right instrument, there
18
18
  // is no module graph to walk.
19
19
  export const bundleSpecifiers = (source: string): string[] => [
20
20
  ...new Set([
@@ -30,12 +30,12 @@ export const bundleProblem = (source: string): string | undefined => {
30
30
  const specifiers = bundleSpecifiers(source);
31
31
  const relative = specifiers.filter((specifier) => specifier.startsWith(".") || specifier.startsWith("/"));
32
32
  if (relative.length > 0) {
33
- return `imports a second file (${relative.join(", ")}) — a bundle is imported from a blob URL, so nothing relative to it can resolve`;
33
+ return `imports a second file (${relative.join(", ")}): a bundle is imported from a blob URL, so nothing relative to it can resolve`;
34
34
  }
35
35
  const published = new Set<string>(HOST_PUBLISHED_SPECIFIERS);
36
36
  const unpublished = specifiers.filter((specifier) => !published.has(specifier));
37
37
  if (unpublished.length > 0) {
38
- return `imports ${unpublished.join(", ")}, which the host does not publish — bundle it in, or use one of: ${HOST_PUBLISHED_SPECIFIERS.join(", ")}`;
38
+ return `imports ${unpublished.join(", ")}, which the host does not publish, bundle it in, or use one of: ${HOST_PUBLISHED_SPECIFIERS.join(", ")}`;
39
39
  }
40
40
  return undefined;
41
41
  };
@@ -1,17 +1,17 @@
1
1
  import type { z } from "zod";
2
2
 
3
- /* A CONTRIBUTION POINT, DEFINED ONCE — its key, its shape, and the sentence that explains it to the person
3
+ /* A CONTRIBUTION POINT, DEFINED ONCE, its key, its shape, and the sentence that explains it to the person
4
4
  * writing the manifest, in one object.
5
5
  *
6
6
  * The three used to live apart, and only two of them lived anywhere at all. The key and the shape sat in one
7
7
  * file that every feature had to edit to add anything, in two places (the schema, then the key on `contributes`);
8
- * the explanation sat in a `//` comment above it, where the extension author — the only reader it was written
9
- * for — could never see it. So the manifest was a thing you wrote by copying another extension's and guessing,
8
+ * the explanation sat in a `//` comment above it, where the extension author, the only reader it was written
9
+ * for, could never see it. So the manifest was a thing you wrote by copying another extension's and guessing,
10
10
  * with a misspelt contribution point dropped in silence rather than named.
11
11
  *
12
12
  * Binding the description to the schema is what changes that: it rides `z.describe`, so it reaches the generated
13
13
  * authoring schema (json-schema.ts) and comes back as hover text in the author's editor. The prose that is for
14
- * MAINTAINERS — why a point exists, what it replaced, what it deliberately does not do — stays a comment in the
14
+ * MAINTAINERS, why a point exists, what it replaced, what it deliberately does not do, stays a comment in the
15
15
  * point's own file, because it is not what someone filling in a field needs to read. */
16
16
  export interface ContributionPoint<Name extends string = string, Schema extends z.ZodType = z.ZodType> {
17
17
  // The key under `contributes` in intentic-extension.json.
@@ -20,7 +20,7 @@ export interface ContributionPoint<Name extends string = string, Schema extends
20
20
  * them, and what the host does with it. It lands verbatim in editor hover text, so a paragraph of rationale
21
21
  * here is a paragraph in a tooltip. */
22
22
  readonly description: string;
23
- // The value shape under that key — an array for a point that takes many entries, the entry itself for a
23
+ // The value shape under that key, an array for a point that takes many entries, the entry itself for a
24
24
  // point that takes one. `contributes` makes every one of them optional; a point is never required.
25
25
  readonly schema: Schema;
26
26
  }
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { ExtensionManifestSchema } from "./manifest.js";
3
3
 
4
- /* THE AUTHORING SCHEMA — what an editor reads to help someone write `intentic-extension.json`.
4
+ /* THE AUTHORING SCHEMA, what an editor reads to help someone write `intentic-extension.json`.
5
5
  *
6
6
  * A manifest was previously written by copying another extension's and guessing. Nothing told the author what a
7
7
  * field meant, because every explanation lived in a `//` comment in this package; and nothing told them when
8
8
  * they got one wrong, because zod STRIPS unknown keys at every level rather than refusing them. A misspelt
9
- * `viewers` was not an error — it was a viewer that never appeared, discovered at install, with the manifest
9
+ * `viewers` was not an error, it was a viewer that never appeared, discovered at install, with the manifest
10
10
  * parsing perfectly.
11
11
  *
12
12
  * So: the same points, emitted as JSON Schema, with the descriptions that ride each one (contribution-point.ts)
@@ -15,7 +15,7 @@ import { ExtensionManifestSchema } from "./manifest.js";
15
15
  *
16
16
  * STRICT HERE, LENIENT AT RUNTIME, and the asymmetry is the point. Authoring is where an unknown key is a typo
17
17
  * and should be shouted about. Runtime is where an unknown key is a manifest written for a NEWER host, which an
18
- * older daemon must go on installing with the point it doesn't understand ignored — refusing it outright would
18
+ * older daemon must go on installing with the point it doesn't understand ignored, refusing it outright would
19
19
  * make every addition to this list a breaking change. */
20
20
 
21
21
  // Where the published copy answers, so `$schema` in a manifest resolves for an author who has installed nothing.
@@ -23,7 +23,7 @@ export const MANIFEST_SCHEMA_URL = "https://intentic.dev/intentic-extension.sche
23
23
 
24
24
  /* `additionalProperties: false` on every object node, so a key nothing declares is flagged where it is typed.
25
25
  *
26
- * Skips a node that already carries `additionalProperties` — that is a `z.record`, whose whole shape is "any key,
26
+ * Skips a node that already carries `additionalProperties`, that is a `z.record`, whose whole shape is "any key,
27
27
  * this value" (a cli capability's `env`), and pinning it closed would reject every entry it exists to accept. */
28
28
  const closeToUnknownKeys = (node: unknown): void => {
29
29
  if (Array.isArray(node)) {
@@ -44,7 +44,7 @@ const closeToUnknownKeys = (node: unknown): void => {
44
44
  }
45
45
  };
46
46
 
47
- /* The manifest schema as JSON Schema. `io: "input"` because this describes what an author WRITES — the shape
47
+ /* The manifest schema as JSON Schema. `io: "input"` because this describes what an author WRITES, the shape
48
48
  * going in, before any refinement or default has been applied to it. */
49
49
  export const manifestJsonSchema = (): Record<string, unknown> => {
50
50
  const schema = z.toJSONSchema(ExtensionManifestSchema, { unrepresentable: "any", io: "input" }) as Record<string, unknown>;
@@ -58,5 +58,5 @@ export const manifestJsonSchema = (): Record<string, unknown> => {
58
58
  };
59
59
 
60
60
  // The committed file's exact bytes, so the generator and the check that guards it cannot disagree about
61
- // formatting — four spaces and a trailing newline, the repo's shape for a committed generated document.
61
+ // formatting, four spaces and a trailing newline, the repo's shape for a committed generated document.
62
62
  export const serializeManifestJsonSchema = (schema: Record<string, unknown>): string => `${JSON.stringify(schema, undefined, 4)}\n`;
package/src/manifest.ts CHANGED
@@ -3,25 +3,25 @@ import { MARK_FIELDS } from "./mark.js";
3
3
  import { contributesSchema } from "./points/index.js";
4
4
 
5
5
  /* The extension manifest: `intentic-extension.json` at the extension repo root (deliberately NOT inside
6
- * .claude-plugin/ — that directory is Claude Code's namespace with its own semantics). The manifest is the
6
+ * .claude-plugin/, that directory is Claude Code's namespace with its own semantics). The manifest is the
7
7
  * approval surface: the install dialog shows exactly these declared contributions before the owner confirms,
8
8
  * and the host refuses runtime registrations (views, commands) whose ids the approved manifest never declared.
9
9
  *
10
- * This file is the ENVELOPE only — who the extension is, which host it needs, what code it ships, how far it
10
+ * This file is the ENVELOPE only, who the extension is, which host it needs, what code it ships, how far it
11
11
  * may reach. What it may CONTRIBUTE is one file per contribution point under points/, assembled here; see
12
12
  * contribution-point.ts for why the description travels with the schema instead of sitting in a comment. */
13
13
 
14
14
  export const ExtensionManifestSchema = z.object({
15
- /* The authoring schema this manifest is written against — editors read it and give the author completion,
15
+ /* The authoring schema this manifest is written against, editors read it and give the author completion,
16
16
  * hover text and a red squiggle on a misspelt key. Declared so it survives the parse rather than being
17
17
  * silently stripped, which is what would otherwise happen to the one field an author is most likely to add
18
18
  * by hand. Nothing at runtime reads it. */
19
19
  $schema: z.string().optional().describe("The authoring schema, for editor completion and validation. Nothing at runtime reads it."),
20
20
  publisher: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
21
21
  name: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
22
- // The extension's own semver — display/identity only; the installed code identity is the pinned commit sha.
23
- version: z.string().min(1).describe("Your own semver — display and identity only. The installed code's identity is the pinned commit sha."),
24
- /* The section this extension sits under in the Sandbox hub's Extensions tab — a grouping by what it is FOR,
22
+ // The extension's own semver, display/identity only; the installed code identity is the pinned commit sha.
23
+ version: z.string().min(1).describe("Your own semver, display and identity only. The installed code's identity is the pinned commit sha."),
24
+ /* The section this extension sits under in the Sandbox hub's Extensions tab, a grouping by what it is FOR,
25
25
  * declared because it cannot be derived. Nine of the first-party extensions contribute a rail tile, so a
26
26
  * grouping read off `contributes` puts more than half the list in one section and says nothing about any of
27
27
  * them. Deliberately a loose string, exactly like a connector's `catalog.category`: the vocabulary belongs
@@ -32,15 +32,15 @@ export const ExtensionManifestSchema = z.object({
32
32
  .min(1)
33
33
  .optional()
34
34
  .describe(
35
- "Which section of the Extensions tab this sits under — a grouping by what it is FOR, which cannot be derived from what it contributes. A section this app has never heard of lands in “Other” rather than failing to install.",
35
+ "Which section of the Extensions tab this sits under: a grouping by what it is FOR, which cannot be derived from what it contributes. A section this app has never heard of lands in 'Other' rather than failing to install.",
36
36
  ),
37
- /* What the extension is drawn as wherever it is LISTED rather than used — the Extensions tab, a registry
37
+ /* What the extension is drawn as wherever it is LISTED rather than used, the Extensions tab, a registry
38
38
  * being browsed, the gallery. Deliberately here and not on a view: `Activation.icon` is the glyph of one
39
39
  * rail tile, it only exists once the extension's code has activated in this browser, and nine of the
40
40
  * first-party extensions register no view at all. An extension that is switched off, daemon-only, or not
41
41
  * yet installed still has to look like something. See MARK_FIELDS. */
42
42
  ...MARK_FIELDS,
43
- // Semver range over the host's extension API version (extensionApiVersion) — checked before activation.
43
+ // Semver range over the host's extension API version (extensionApiVersion), checked before activation.
44
44
  engines: z
45
45
  .object({ intentic: z.string().min(1) })
46
46
  .describe("A semver range over the host's extension API version, checked before your code is activated."),
@@ -54,7 +54,7 @@ export const ExtensionManifestSchema = z.object({
54
54
  .describe(
55
55
  "Repo-relative path of your prebuilt single-file ESM bundle, built with `vue` and `@intentic/extension-api` as externals. Absent ⇒ an extension with no UI.",
56
56
  ),
57
- /* Repo-relative path of the prebuilt single-file node ESM SERVER bundle — the extension's BACKEND half,
57
+ /* Repo-relative path of the prebuilt single-file node ESM SERVER bundle, the extension's BACKEND half,
58
58
  * exporting `activateServer(api, context)`. Loaded by the daemon's backend host (a separate supervised
59
59
  * process, so a toggle or a live edit is a host restart rather than a daemon death) and served under the
60
60
  * extension's own route namespace `/x/<id>/…`, which the daemon proxies. Self-contained by construction:
@@ -68,12 +68,12 @@ export const ExtensionManifestSchema = z.object({
68
68
  .describe(
69
69
  "Repo-relative path of your prebuilt single-file node ESM server bundle, exporting `activateServer`. Served under your own route namespace, which the daemon proxies. Nothing is provided at runtime but node builtins, so bundle everything else in. Absent ⇒ no backend.",
70
70
  ),
71
- // Declared reach, both halves in one grammar — "<METHOD> <path-glob>" where `*` matches one path segment
72
- // (e.g. "GET /panels", "POST /panels/*/start") — so the install dialog, the gate and the usage ledger read
71
+ // Declared reach, both halves in one grammar, "<METHOD> <path-glob>" where `*` matches one path segment
72
+ // (e.g. "GET /panels", "POST /panels/*/start"), so the install dialog, the gate and the usage ledger read
73
73
  // one vocabulary.
74
- // sandbox — the daemon routes the UI half may call through api.sandbox (the host refuses undeclared
74
+ // sandbox, the daemon routes the UI half may call through api.sandbox (the host refuses undeclared
75
75
  // ones). An extension's OWN namespace `/x/<its id>/…` needs no entry: its backend is its own.
76
- // daemon — the daemon routes the SERVER half may call through api.daemon, enforced by the daemon's
76
+ // daemon , the daemon routes the SERVER half may call through api.daemon, enforced by the daemon's
77
77
  // extension-token grant. Separate from `sandbox` because the halves run as different
78
78
  // principals: the UI acts with the owner's session, the backend with a minted per-extension
79
79
  // token, and a grant to one must never quietly widen the other.
@@ -83,22 +83,22 @@ export const ExtensionManifestSchema = z.object({
83
83
  sandbox: z
84
84
  .array(z.string())
85
85
  .optional()
86
- .describe("Daemon routes your UI half may call. Your own backend namespace needs no entry — its backend is your own code."),
86
+ .describe("Daemon routes your UI half may call. Your own backend namespace needs no entry: its backend is your own code."),
87
87
  daemon: z
88
88
  .array(z.string())
89
89
  .optional()
90
90
  .describe(
91
- "Daemon routes your SERVER half may call. Separate from `sandbox` because the two halves run as different principals — the UI as the owner's session, the backend as a minted per-extension token — so a grant to one must never quietly widen the other.",
91
+ "Daemon routes your SERVER half may call. Separate from `sandbox` because the two halves run as different principals: the UI as the owner's session, the backend as a minted per-extension token, so a grant to one must never quietly widen the other.",
92
92
  ),
93
93
  })
94
94
  .optional()
95
95
  .describe(
96
- 'How far this extension may reach into the daemon, as "<METHOD> <path-glob>" entries where `*` matches one path segment — e.g. "GET /panels", "POST /panels/*/start". The install dialog shows these, the host refuses anything undeclared, and the usage ledger records which were actually earned.',
96
+ 'How far this extension may reach into the daemon, as "<METHOD> <path-glob>" entries where `*` matches one path segment: e.g. "GET /panels", "POST /panels/*/start". The install dialog shows these, the host refuses anything undeclared, and the usage ledger records which were actually earned.',
97
97
  ),
98
98
  contributes: contributesSchema.optional(),
99
99
  });
100
100
  export type ExtensionManifest = z.infer<typeof ExtensionManifestSchema>;
101
101
 
102
- // The extension's identity everywhere (capability entries, /ext routes, settings namespaces) — derived, never
102
+ // The extension's identity everywhere (capability entries, /ext routes, settings namespaces), derived, never
103
103
  // declared, so it can't contradict the publisher/name the install dialog showed.
104
104
  export const extensionIdOf = (manifest: Pick<ExtensionManifest, "publisher" | "name">): string => `${manifest.publisher}.${manifest.name}`;
package/src/mark.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
 
3
- /* HOW SOMETHING LOOKS BEFORE ANY OF ITS CODE RUNS — the mark a capability card and an extension are drawn
3
+ /* HOW SOMETHING LOOKS BEFORE ANY OF ITS CODE RUNS, the mark a capability card and an extension are drawn
4
4
  * with, in ONE shape because one component draws both (<BrandMark>) and a second copy of these two fields is a
5
5
  * second answer to what happens when a slug 404s.
6
6
  *
@@ -8,7 +8,7 @@ import { z } from "zod";
8
8
  * author controls completely, and the only one that can make a grid of unfamiliar names look like a shelf of
9
9
  * distinct products rather than a column of identical glyphs. `logo` is a simple-icons slug fetched from a
10
10
  * CDN: exactly right for a card standing in for somebody else's product (GitHub, Postgres, Slack), useless
11
- * for the many things that have no brand in that set, and unreachable in an offline sandbox — so it can never
11
+ * for the many things that have no brand in that set, and unreachable in an offline sandbox, so it can never
12
12
  * be the only tier. `icon` is a name from the host's own bundled vocabulary, which ships in the image, follows
13
13
  * the theme and costs no request; it is what carries a first-party extension that has not been drawn yet. What
14
14
  * declares none of them is drawn as its initials, so no row is ever blank and no author is obliged to have a
@@ -16,15 +16,15 @@ import { z } from "zod";
16
16
  *
17
17
  * ART BEATS A BRAND SLUG because they answer different questions. A slug says "this is Slack"; art says "this
18
18
  * is mine". An extension that stands in for somebody else's product should declare the slug and no art, and
19
- * one that is its own thing should declare art — but where both arrive, the author's own drawing is the more
19
+ * one that is its own thing should declare art, but where both arrive, the author's own drawing is the more
20
20
  * specific claim and wins.
21
21
  *
22
22
  * Artwork that will not parse, a slug that fails to load and an icon name this build has never heard of all
23
- * fall to the tier BELOW rather than to a hole — the rule the rail already applies to Activation.icon, here for
23
+ * fall to the tier BELOW rather than to a hole, the rule the rail already applies to Activation.icon, here for
24
24
  * the surfaces that must draw an extension whose code is not running: one that is switched off, one that is
25
25
  * daemon-only, one being read about in a registry before it is installed at all. */
26
26
 
27
- /* Big enough for a drawn mark, far too small for a traced photograph — which is the line being drawn. This
27
+ /* Big enough for a drawn mark, far too small for a traced photograph, which is the line being drawn. This
28
28
  * string rides every registry row and every manifest read, so it is a budget as much as a limit: at 4 KB the
29
29
  * whole official registry's artwork costs less than one screenshot, and an author who needs more than that is
30
30
  * shipping a raster they should be shipping as a brand slug or not at all. */
@@ -33,24 +33,24 @@ const ART_MAX_BYTES = 4096;
33
33
  export const MARK_FIELDS = {
34
34
  /* THE SVG DOCUMENT ITSELF, not a URL and not base64.
35
35
  *
36
- * A URL would put a stranger's server in the render path of a page listing extensions — a fetch that
36
+ * A URL would put a stranger's server in the render path of a page listing extensions, a fetch that
37
37
  * tracks who is browsing what, breaks in an offline sandbox, and 404s long after the row was approved:
38
38
  * the three failures the `logo` tier already documents, with none of its excuse. Inline costs one string
39
39
  * and always draws.
40
40
  *
41
41
  * Kept as READABLE TEXT rather than a data URI because of who reads it. The registry's curated file is
42
42
  * reviewed by a human before anything is published, and `<rect fill="#5B4FE9"/><circle .../>` can be read
43
- * in a diff, while base64 is a wall nobody checks — an opaque blob in the one file whose entire purpose is
43
+ * in a diff, while base64 is a wall nobody checks, an opaque blob in the one file whose entire purpose is
44
44
  * to be checked. The renderer does its own encoding at the point of use.
45
45
  *
46
46
  * It is drawn INERT (an <img>, never inline in the document), so a hostile registry row cannot script the
47
- * page it is listed on — see <BrandMark>, which owns that guarantee and the sniff test that enforces it. */
47
+ * page it is listed on, see <BrandMark>, which owns that guarantee and the sniff test that enforces it. */
48
48
  art: z
49
49
  .string()
50
50
  .max(ART_MAX_BYTES)
51
51
  .optional()
52
52
  .describe(
53
- "This extension's own mark, as a complete SVG document inline — the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.",
53
+ "This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.",
54
54
  ),
55
55
  // A simple-icons slug (https://cdn.simpleicons.org/<slug>). A "/<hex>" suffix forces a colour for marks
56
56
  // that vanish against the surface they land on (github's near-black).
@@ -58,13 +58,13 @@ export const MARK_FIELDS = {
58
58
  .string()
59
59
  .optional()
60
60
  .describe(
61
- 'A simple-icons slug, fetched from a CDN — right for standing in for somebody else\'s product. Add a "/<hex>" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.',
61
+ 'A simple-icons slug, fetched from a CDN: right for standing in for somebody else\'s product. Add a "/<hex>" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.',
62
62
  ),
63
63
  // A name from the host's icon set (@intentic/ui IconName), drawn when no simple-icons slug fits.
64
64
  icon: z
65
65
  .string()
66
66
  .optional()
67
67
  .describe(
68
- "A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request — what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.",
68
+ "A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.",
69
69
  ),
70
70
  };
@@ -1,6 +1,6 @@
1
1
  /* The sandbox-route permission model. An extension declares in its manifest exactly which daemon routes it may
2
2
  * reach through `api.sandbox.request/json`, as "<METHOD> <path-glob>" strings where `*` matches exactly one path
3
- * segment. The host matches every call against these and refuses an undeclared route — so an extension's backend
3
+ * segment. The host matches every call against these and refuses an undeclared route, so an extension's backend
4
4
  * reach is explicit, diffable, and reviewable rather than an ambient client to the whole daemon. */
5
5
 
6
6
  const escapeRegExp = (literal: string): string => literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -15,7 +15,7 @@ interface CompiledRoute {
15
15
  const compile = (entry: string): CompiledRoute => {
16
16
  const spaceIndex = entry.indexOf(" ");
17
17
  if (spaceIndex < 0) {
18
- throw new Error(`invalid sandbox permission "${entry}" — expected "<METHOD> <path-glob>", e.g. "GET /panels"`);
18
+ throw new Error(`invalid sandbox permission "${entry}": expected "<METHOD> <path-glob>", e.g. "GET /panels"`);
19
19
  }
20
20
  const method = entry.slice(0, spaceIndex).trim().toUpperCase();
21
21
  const glob = entry.slice(spaceIndex + 1).trim();
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
4
  // "This checkout is ALSO a Claude Code plugin": the daemon hands the directory to the Agent SDK's plugin
5
- // loader, which reads skills/agents/hooks/commands/.mcp.json each turn — the daemon never parses plugin
5
+ // loader, which reads skills/agents/hooks/commands/.mcp.json each turn, the daemon never parses plugin
6
6
  // internals.
7
7
  export const AgentContributionSchema = z.object({
8
8
  path: z.string().optional().describe("Relative to the extension checkout. Absent ⇒ the checkout root."),
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
- /* A STARTING POINT in the automation composer — a trigger, a prompt written for that trigger's payload, and
4
+ /* A STARTING POINT in the automation composer, a trigger, a prompt written for that trigger's payload, and
5
5
  * whatever guard or hold makes it safe to leave on. Pure prefill: creating one makes an ordinary automation and
6
6
  * the daemon knows nothing about templates afterwards.
7
7
  *
8
8
  * IT LIVES WITH THE AREA THAT KNOWS THE SERVICE, which is the point of it being a contribution at all. The
9
- * automation surface used to carry every one of these — Komodo's, Sentry's, Stripe's, CI's, the chore book's —
9
+ * automation surface used to carry every one of these. Komodo's, Sentry's, Stripe's, CI's, the chore book's,
10
10
  * so a pack that gained something worth reacting to could not say so without an edit to a surface it has
11
11
  * nothing to do with. A template declared here appears when the pack is installed and its capability connected,
12
12
  * and disappears with it.
@@ -14,22 +14,22 @@ import type { ContributionPoint } from "../contribution-point.js";
14
14
  * The daemon validates each one against the real trigger schema when it builds the catalogue and drops what
15
15
  * does not parse, so a template can never offer a trigger that `upsert` would refuse. */
16
16
  export const AutomationTemplateContributionSchema = z.object({
17
- // Prefills the automation name, and is what "does one of these exist already" is asked by — so it must be
17
+ // Prefills the automation name, and is what "does one of these exist already" is asked by, so it must be
18
18
  // spelled as an automation id, not as prose.
19
19
  id: z
20
20
  .string()
21
21
  .regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/)
22
- .describe('Prefills the automation name, and is what "does one of these exist already" is asked by — so spell it as an id, not as prose.'),
22
+ .describe('Prefills the automation name, and is what "does one of these exist already" is asked by, so spell it as an id, not as prose.'),
23
23
  title: z.string().min(1),
24
24
  logo: z.string().min(1).optional().describe("A simple-icons slug for the card."),
25
25
  icon: z.string().min(1).optional().describe("A name from the host's icon set, drawn when no simple-icons slug fits."),
26
- // Capability providers that make this template WORK — any one connected is enough (fixing CI rides github
26
+ // Capability providers that make this template WORK, any one connected is enough (fixing CI rides github
27
27
  // or gitlab). Omitted ⇒ nothing to connect, always offered.
28
28
  requires: z
29
29
  .array(z.string().min(1))
30
30
  .optional()
31
31
  .describe(
32
- "Capability providers that make this template work — any one connected is enough (fixing CI rides github or gitlab). Omitted ⇒ nothing to connect, so it is always offered.",
32
+ "Capability providers that make this template work: any one connected is enough (fixing CI rides github or gitlab). Omitted ⇒ nothing to connect, so it is always offered.",
33
33
  ),
34
34
  // Shaped loosely here and parsed strictly at the merge: the manifest package cannot see the trigger union
35
35
  // (the dependency runs the other way), so the daemon is where a declaration meets the real schema.
@@ -48,7 +48,7 @@ export const AutomationTemplateContributionSchema = z.object({
48
48
  .string()
49
49
  .min(1)
50
50
  .optional()
51
- .describe("A condition that must hold before the turn runs — what makes a template safe to leave switched on."),
51
+ .describe("A condition that must hold before the turn runs: what makes a template safe to leave switched on."),
52
52
  holdForSeconds: z.number().int().positive().optional().describe("Wait this long and coalesce repeats, rather than firing on every event."),
53
53
  prompt: z.string().min(1).describe("The turn this starts. You own the trigger's payload vocabulary, so you own the prompt that reads it."),
54
54
  note: z.string().min(1).optional(),
@@ -63,7 +63,7 @@ export const AutomationTemplateContributionSchema = z.object({
63
63
  .enum(["create", "configure"])
64
64
  .optional()
65
65
  .describe(
66
- "Absent ⇒ it waits in the gallery, where you go once you know what you want. `create` puts a card on the page that makes it, switched off, in one click. `configure` puts one there that opens the dialog prefilled, for a template that cannot work unconfigured. Both are for what a user would never think to go looking for — mark everything as offered and you have rebuilt the gallery with extra steps.",
66
+ "Absent ⇒ it waits in the gallery, where you go once you know what you want. `create` puts a card on the page that makes it, switched off, in one click. `configure` puts one there that opens the dialog prefilled, for a template that cannot work unconfigured. Both are for what a user would never think to go looking for: mark everything as offered and you have rebuilt the gallery with extra steps.",
67
67
  ),
68
68
  // Whether what this makes watches THIS codebase (the chores shelf) rather than the outside world. Declared
69
69
  // rather than read off the trigger: a nightly dependency sweep and a nightly Stripe poll are both schedules.
@@ -71,7 +71,7 @@ export const AutomationTemplateContributionSchema = z.object({
71
71
  .boolean()
72
72
  .optional()
73
73
  .describe(
74
- "Whether what this makes watches THIS codebase rather than the outside world. Declared rather than read off the trigger — a nightly dependency sweep and a nightly Stripe poll are both schedules.",
74
+ "Whether what this makes watches THIS codebase rather than the outside world. Declared rather than read off the trigger: a nightly dependency sweep and a nightly Stripe poll are both schedules.",
75
75
  ),
76
76
  });
77
77
  export type AutomationTemplateContribution = z.infer<typeof AutomationTemplateContributionSchema>;
@@ -79,6 +79,6 @@ export type AutomationTemplateContribution = z.infer<typeof AutomationTemplateCo
79
79
  export const automationTemplatesPoint = {
80
80
  name: "automationTemplates",
81
81
  description:
82
- "Starting points this pack offers in the automation composer — a trigger, a prompt written for that trigger's payload, and whatever guard makes it safe to leave on. Declared by whoever knows the service rather than by the composer, so they appear when your pack is installed and disappear with it. Pure prefill: creating one makes an ordinary automation.",
82
+ "Starting points this pack offers in the automation composer, a trigger, a prompt written for that trigger's payload, and whatever guard makes it safe to leave on. Declared by whoever knows the service rather than by the composer, so they appear when your pack is installed and disappear with it. Pure prefill: creating one makes an ordinary automation.",
83
83
  schema: z.array(AutomationTemplateContributionSchema),
84
84
  } as const satisfies ContributionPoint;
package/src/points/bin.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { z } from "zod";
2
2
  import type { ContributionPoint } from "../contribution-point.js";
3
3
 
4
- // A checkout-relative directory of executables the daemon prepends to the AGENT's PATH each turn — how an
4
+ // A checkout-relative directory of executables the daemon prepends to the AGENT's PATH each turn, how an
5
5
  // extension ships a command-line tool for the agent (the CLI-tools path). The files ARE the approved code (they
6
6
  // ride the sha-pinned checkout); the daemon only adds the dir to PATH.
7
7
  export const binPoint = {
8
8
  name: "bin",
9
9
  description:
10
- "A checkout-relative directory of executables the daemon puts on the agent's PATH every turn — how you ship the agent a command-line tool. The files are the approved code themselves: they ride the pinned checkout, and the daemon only adds the directory to PATH.",
10
+ "A checkout-relative directory of executables the daemon puts on the agent's PATH every turn, how you ship the agent a command-line tool. The files are the approved code themselves: they ride the pinned checkout, and the daemon only adds the directory to PATH.",
11
11
  schema: z
12
12
  .string()
13
13
  .min(1)