@graview/skills 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,96 @@
1
+ Elastic License 2.0
2
+
3
+ ## Acceptance
4
+
5
+ By using the software, you agree to all of the terms and conditions below.
6
+
7
+ ## Copyright License
8
+
9
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
10
+ non-sublicensable, non-transferable license to use, copy, distribute, make
11
+ available, and prepare derivative works of the software, in each case subject
12
+ to the limitations and conditions below.
13
+
14
+ ## Limitations
15
+
16
+ You may not provide the software to third parties as a hosted or managed
17
+ service, where the service provides users with access to any substantial set
18
+ of the features or functionality of the software.
19
+
20
+ You may not move, change, disable, or circumvent the license key
21
+ functionality in the software, and you may not remove or obscure any
22
+ functionality in the software that is protected by the license key.
23
+
24
+ You may not alter, remove, or obscure any licensing, copyright, or other
25
+ notices of the licensor in the software. Any use of the licensor's trademarks
26
+ is subject to applicable law.
27
+
28
+ ## Patents
29
+
30
+ The licensor grants you a license, under any patent claims the licensor can
31
+ license, or becomes able to license, to make, have made, use, sell, offer for
32
+ sale, import and have imported the software, in each case subject to the
33
+ limitations and conditions in this license. This license does not cover any
34
+ patent claims that you cause to be infringed by modifications or additions to
35
+ the software. If you or your company make any written claim that the software
36
+ infringes or contributes to infringement of any patent, your patent license
37
+ for the software granted under these terms ends immediately. If your company
38
+ makes such a claim, your patent license ends immediately for work on behalf
39
+ of your company.
40
+
41
+ ## Notices
42
+
43
+ You must ensure that anyone who gets a copy of any part of the software from
44
+ you also gets a copy of these terms.
45
+
46
+ If you modify the software, you must include in any modified copies of the
47
+ software prominent notices stating that you have modified the software.
48
+
49
+ ## No Other Rights
50
+
51
+ These terms do not imply any licenses other than those expressly granted in
52
+ these terms.
53
+
54
+ ## Termination
55
+
56
+ If you use the software in violation of these terms, such use is not
57
+ licensed, and your licenses will automatically terminate. If the licensor
58
+ provides you with a notice of your violation, and you cease all violation of
59
+ this license no later than 30 days after you receive that notice, your
60
+ licenses will be reinstated retroactively. However, if you violate these
61
+ terms after such reinstatement, any additional violation of these terms will
62
+ cause your licenses to terminate automatically and permanently.
63
+
64
+ ## No Liability
65
+
66
+ *As far as the law allows, the software comes as is, without any warranty or
67
+ condition, and the licensor will not be liable to you for any damages arising
68
+ out of these terms or the use or nature of the software, under any kind of
69
+ legal claim.*
70
+
71
+ ## Definitions
72
+
73
+ The **licensor** is the entity offering these terms, and the **software** is
74
+ the software the licensor makes available under these terms, including any
75
+ portion of it.
76
+
77
+ **you** refers to the individual or entity agreeing to these terms.
78
+
79
+ **your company** is any legal entity, sole proprietorship, or other kind of
80
+ organization that you work for, plus all organizations that have control
81
+ over, are under the control of, or are under common control with that
82
+ organization. **control** means ownership of substantially all the assets of
83
+ an entity, or the power to direct its management and policies by vote,
84
+ contract, or otherwise. Control can be direct or indirect.
85
+
86
+ **your licenses** are all the licenses granted to you for the software under
87
+ these terms.
88
+
89
+ **use** means anything you do with the software requiring one of your
90
+ licenses.
91
+
92
+ **trademark** means trademarks, service marks, and similar rights.
93
+
94
+ ---
95
+
96
+ Copyright 2025-2026 En Dash Consulting
package/README.md CHANGED
@@ -1,3 +1,47 @@
1
- # Temporary Holding Version
1
+ # @graview/skills
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Skills that teach an assistant how to build with Graview — and, more usefully,
4
+ how to find out whether it worked.
5
+
6
+ ```sh
7
+ npx graview skills install . # into .claude/skills and .agents/skills
8
+ npx graview skills list
9
+ ```
10
+
11
+ The command line is the `graview` package; this one ships the skills and
12
+ the move, and `graview skills` dispatches here.
13
+
14
+ ## Why this framework is worth having skills for
15
+
16
+ Most skills end in a claim: "you have added a node kind." That is worth very
17
+ little, because the assistant writing it is the same one that just decided it
18
+ was done.
19
+
20
+ Graview can answer the question itself. `graview check` reads the declaration
21
+ and reports an edge to a kind nobody wrote, an invariant scoped to a kind that
22
+ does not exist, a repair naming a mutation nobody registered, a lens role
23
+ bound to a missing field, a mutation with no title, a palette pair that fails
24
+ WCAG AA, a role that may do nothing. So every skill here **ends in a check and
25
+ reports the real verdict** — including when the verdict is bad.
26
+
27
+ A skill that cannot verify its own outcome says so. That is the rule, and the
28
+ skills say which parts of their work the checker cannot see.
29
+
30
+ ## The skills
31
+
32
+ | Skill | The move it teaches |
33
+ |---|---|
34
+ | `graview-node-kind` | Add a node kind: fields, edges, plural, roles, and what renders for free |
35
+ | `graview-invariant` | Write a rule that names the mutations that would repair it |
36
+ | `graview-lens` | Build a lens that binds roles rather than field names, so another domain can reuse it |
37
+ | `graview-agent-seat` | Wire an agent seat that shares the interface's actions rather than shadowing them |
38
+ | `graview-permissions` | Declare who may do what, once, and let the strip and the seat narrow themselves |
39
+ | `graview-brand` | Put someone's name on an installation without forking a package |
40
+ | `graview-new-app` | Start a product on Graview in its own repository, with the CI that keeps it honest |
41
+ | `graview-pages` | The routed face: derived pages, one page in the app's words, a product design over every surface, and the embed |
42
+ | `graview-port-app` | Port an existing application onto Graview, deciding what is a node and what is a field |
43
+ | `graview-studio` | Open the declaration as a graph, change it with acts, check before applying, migrate, and write it back as the scaffold's files |
44
+
45
+ The worked examples live in `apps/` in the framework repository. The skills
46
+ point at them rather than restating them — a skill that copies an example goes
47
+ stale the moment the example changes.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `graview skills` — installs the skills where Claude Code and Codex look
3
+ * for them.
4
+ *
5
+ * Copies rather than symlinks: a symlink into `node_modules` breaks the moment
6
+ * somebody prunes or reinstalls, and a skill that silently stops existing is
7
+ * worse than one that is a little stale. `install` is idempotent and replaces
8
+ * what it wrote before.
9
+ *
10
+ * Reached through the `graview` command line, which dispatches here; this
11
+ * package ships the skills and the move, not a bin of its own.
12
+ */
13
+ export declare const SKILLS_USAGE: string;
14
+ export declare function skills(argv: readonly string[]): number;
15
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,YAAY,QAGxB,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAiCtD"}
package/dist/cli.js ADDED
@@ -0,0 +1,54 @@
1
+ import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { join, relative, resolve } from "node:path";
3
+ import { readSkills, SKILLS_DIR, SKILL_DESTINATIONS } from "./index.js";
4
+ /**
5
+ * `graview skills` — installs the skills where Claude Code and Codex look
6
+ * for them.
7
+ *
8
+ * Copies rather than symlinks: a symlink into `node_modules` breaks the moment
9
+ * somebody prunes or reinstalls, and a skill that silently stops existing is
10
+ * worse than one that is a little stale. `install` is idempotent and replaces
11
+ * what it wrote before.
12
+ *
13
+ * Reached through the `graview` command line, which dispatches here; this
14
+ * package ships the skills and the move, not a bin of its own.
15
+ */
16
+ export const SKILLS_USAGE = ` graview skills install [dir] copy the skills into ${SKILL_DESTINATIONS.join(" and ")}
17
+ graview skills list what is in this package
18
+ graview skills path where the skill files live
19
+ `;
20
+ export function skills(argv) {
21
+ const [command = "help", target = process.cwd()] = argv;
22
+ const root = resolve(target);
23
+ if (command === "list") {
24
+ for (const skill of readSkills()) {
25
+ process.stdout.write(`${skill.name.padEnd(22)} ${skill.description}\n`);
26
+ }
27
+ return 0;
28
+ }
29
+ if (command === "path") {
30
+ process.stdout.write(`${SKILLS_DIR}\n`);
31
+ return 0;
32
+ }
33
+ if (command === "install") {
34
+ const all = readSkills();
35
+ for (const destination of SKILL_DESTINATIONS) {
36
+ const dir = join(root, destination);
37
+ mkdirSync(dir, { recursive: true });
38
+ for (const skill of all) {
39
+ const into = join(dir, skill.name);
40
+ // Replace rather than merge: a stale file left behind from an older
41
+ // version of a skill is a instruction nobody meant to give.
42
+ if (existsSync(into))
43
+ rmSync(into, { recursive: true, force: true });
44
+ cpSync(join(SKILLS_DIR, skill.name), into, { recursive: true });
45
+ }
46
+ process.stdout.write(`${all.length} skills → ${relative(root, dir) || destination}\n`);
47
+ }
48
+ process.stdout.write("\nEvery one of them ends in `graview check` and reports what it actually said.\n");
49
+ return 0;
50
+ }
51
+ process.stdout.write(`graview skills — the authoring moves, each ending in a real check\n\n${SKILLS_USAGE}`);
52
+ return command === "help" ? 0 : 1;
53
+ }
54
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,yDAAyD,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;;;CAGpH,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,IAAuB;IAC5C,MAAM,CAAC,OAAO,GAAG,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7B,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,IAAI,CAAC,CAAC;QACxC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,KAAK,MAAM,WAAW,IAAI,kBAAkB,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACpC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;gBACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACnC,oEAAoE;gBACpE,4DAA4D;gBAC5D,IAAI,UAAU,CAAC,IAAI,CAAC;oBAAE,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,aAAa,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,WAAW,IAAI,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kFAAkF,CAAC,CAAC;QACzG,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,YAAY,EAAE,CAAC,CAAC;IAC7G,OAAO,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Where the skill files live, relative to this module.
3
+ *
4
+ * `dist/` sits one level below the package root and `skills/` sits at it, so
5
+ * this resolves the same whether it is running from source or from an
6
+ * installed tarball — both of which ship `skills/`.
7
+ */
8
+ export declare const SKILLS_DIR: string;
9
+ export interface SkillFile {
10
+ readonly name: string;
11
+ readonly description: string;
12
+ readonly path: string;
13
+ readonly body: string;
14
+ }
15
+ /** Every skill this package ships, read from disk. */
16
+ export declare function readSkills(dir?: string): readonly SkillFile[];
17
+ /**
18
+ * Where each assistant looks for skills.
19
+ *
20
+ * Both, always. A skill installed for one and not the other is a skill that
21
+ * works until somebody opens the project in the other editor, and finding out
22
+ * that way is worse than not having it.
23
+ */
24
+ export declare const SKILL_DESTINATIONS: readonly [".claude/skills", ".agents/skills"];
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAItB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAgBD,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAmB,GAAG,SAAS,SAAS,EAAE,CAezE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,+CAAgD,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /**
5
+ * Where the skill files live, relative to this module.
6
+ *
7
+ * `dist/` sits one level below the package root and `skills/` sits at it, so
8
+ * this resolves the same whether it is running from source or from an
9
+ * installed tarball — both of which ship `skills/`.
10
+ */
11
+ export const SKILLS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "skills");
12
+ /** Reads the frontmatter a skill is required to carry. */
13
+ function frontmatter(text) {
14
+ if (!text.startsWith("---\n"))
15
+ return {};
16
+ const end = text.indexOf("\n---", 4);
17
+ if (end === -1)
18
+ return {};
19
+ const fields = {};
20
+ for (const line of text.slice(4, end).split("\n")) {
21
+ const colon = line.indexOf(":");
22
+ if (colon === -1)
23
+ continue;
24
+ fields[line.slice(0, colon).trim()] = line.slice(colon + 1).trim();
25
+ }
26
+ return fields;
27
+ }
28
+ /** Every skill this package ships, read from disk. */
29
+ export function readSkills(dir = SKILLS_DIR) {
30
+ return readdirSync(dir, { withFileTypes: true })
31
+ .filter((entry) => entry.isDirectory())
32
+ .map((entry) => {
33
+ const path = join(dir, entry.name, "SKILL.md");
34
+ const body = readFileSync(path, "utf8");
35
+ const fields = frontmatter(body);
36
+ return {
37
+ name: fields["name"] ?? entry.name,
38
+ description: fields["description"] ?? "",
39
+ path,
40
+ body,
41
+ };
42
+ })
43
+ .sort((a, b) => a.name.localeCompare(b.name));
44
+ }
45
+ /**
46
+ * Where each assistant looks for skills.
47
+ *
48
+ * Both, always. A skill installed for one and not the other is a skill that
49
+ * works until somebody opens the project in the other editor, and finding out
50
+ * that way is worse than not having it.
51
+ */
52
+ export const SKILL_DESTINATIONS = [".claude/skills", ".agents/skills"];
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAC/B,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EACvC,IAAI,EACJ,QAAQ,CACT,CAAC;AASF,0DAA0D;AAC1D,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACrC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,SAAS;QAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,UAAU,CAAC,MAAc,UAAU;IACjD,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;SACtC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI;YAClC,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE;YACxC,IAAI;YACJ,IAAI;SACL,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,gBAAgB,EAAE,gBAAgB,CAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@graview/skills",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Skills that teach an assistant the authoring moves, each ending in a real graview check verdict.",
6
+ "keywords": [
7
+ "graview",
8
+ "context-graph",
9
+ "typed-graph",
10
+ "interface",
11
+ "declarative",
12
+ "claude-code",
13
+ "agent-skills",
14
+ "ai-assistant"
15
+ ],
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./cli": {
24
+ "types": "./dist/cli.d.ts",
25
+ "default": "./dist/cli.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "skills",
31
+ "README.md"
32
+ ],
33
+ "sideEffects": false,
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/en-dash-consulting/graview.git",
37
+ "directory": "packages/skills"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "license": "Elastic-2.0",
43
+ "homepage": "https://graview.dev/docs/packages/skills.html",
44
+ "bugs": {
45
+ "url": "https://github.com/en-dash-consulting/graview/issues"
46
+ },
47
+ "engines": {
48
+ "node": ">=22"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -b"
52
+ }
6
53
  }
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: graview-agent-seat
3
+ description: Wire an agent seat into a Graview app that shares the interface's own actions rather than shadowing them, and verify the diffs are identical rather than assuming they are.
4
+ ---
5
+
6
+ # Wire an agent seat
7
+
8
+ There is no agent API to build. The tools generate from the same mutation
9
+ declarations a person's buttons come from, which is why an agent's edit
10
+ produces literally the same diff, lands in the same op log with an author, and
11
+ is undone by the same control. If you find yourself writing a second code path
12
+ for the agent, stop: that path is the one that will drift.
13
+
14
+ ## Do this
15
+
16
+ 1. **Create the runtime with a principal.**
17
+
18
+ ```ts
19
+ const runtime = createToolRuntime(store, {
20
+ author: { kind: "agent", id: "claude", session: "ui", roles: ["analyst"] },
21
+ });
22
+ ```
23
+
24
+ The author is a `Principal` — an author with roles — so what the log blames
25
+ and what the policy judged are one object. A seat's tools narrow to what
26
+ that principal may run, with no second list to keep in step. See
27
+ `graview-permissions`.
28
+
29
+ 2. **Pick a transport.** `createInAppAdapter(runtime)` for a seat inside the
30
+ interface; `createMcpAdapter(runtime)` for an external client. Both are thin
31
+ wrappers over the same runtime — that is the whole design.
32
+
33
+ 3. **Show the calls.** `runtime.onCall(listener)` announces every call as it
34
+ starts and as it settles. Feed it to `ActivityRail`. A diff says what
35
+ changed and never what was *considered*, so an agent turn without this is a
36
+ spinner and a toast.
37
+
38
+ 4. **Report what it READ.** `useAttention(runtime)` pipes a settled read-only
39
+ call's node ids into the picture, so a kind lights faintly where the agent
40
+ looked. That is the half a diff cannot show, and the half that says whether
41
+ to trust what it then did.
42
+
43
+ 5. **Find by name with `search_graph`, before `get_graph`.** It is the Find
44
+ box's matcher: hits with why they matched, `key:value` conditions
45
+ (`done:false`, `is:any` for the past, `kind:task`), and the records it
46
+ names counted as reads. Reading the whole graph to scan it is the move a
47
+ person with a search box never makes.
48
+
49
+ 6. **Prefer `get_affordances` over composing calls by hand.** The tool
50
+ descriptions say so, and it matters: derived affordances cannot name an
51
+ action that does not exist or is not legal on this selection.
52
+
53
+ 7. **Host the seat where the data is, for an agent outside the page.** An
54
+ editor's assistant or a scheduled worker does not need glue of its own:
55
+
56
+ ```sh
57
+ graview mcp ./dist/domain/app.js --data ./data --as cursor --roles keeper
58
+ graview mcp ./dist/domain/app.js --remote-url http://localhost:5196 --roles keeper
59
+ graview apply ./dist/domain/app.js --data ./data --roles keeper \
60
+ --call add-task --args '{"listId":"today","label":"Book the van","id":"t-van"}'
61
+ ```
62
+
63
+ `mcp` is MCP over stdio around this same runtime; `apply` is one act, a
64
+ plan of many as one batch, or an undo, from a shell. Both open the store
65
+ `graview serve` opens — a folder, SQLite, or a running server — and act
66
+ under the seat you name, so the policy refuses there what it refuses here.
67
+ Put `serve` and `mcp` in the app's scripts so the door is always there,
68
+ and tell the agent the seed is a first-install snapshot: it changes the
69
+ live graph through these, never by editing `example.json` and wiping.
70
+ `graview mcp <entry> --list --roles …` prints the seat's tools as
71
+ `tools/list` JSON — the catalog a host registers without writing one.
72
+
73
+ ## Worked examples
74
+
75
+ - `apps/todo/src/ui/app.tsx` — the seat, the runtime, and an agent that finds
76
+ what it will change with `search_graph` before it moves
77
+ - `packages/tools/src/agent/tools.ts` — how the tools generate, and what a
78
+ read-only call reports about what it looked at
79
+
80
+ ## Then find out whether it worked
81
+
82
+ ```sh
83
+ pnpm build && npx graview check ./dist/domain/app.js
84
+ ```
85
+
86
+ The checker verifies the mutations behind the tools: `mutation-untitled`,
87
+ `mutation-undescribed`, `mutation-title-ambiguous`. A tool description that
88
+ falls back to a title is a LABEL where an instruction belongs, and an agent
89
+ reads that string to decide whether to reach for it.
90
+
91
+ **And prove the paths are one path**, which is the claim this design exists to
92
+ support:
93
+
94
+ ```ts
95
+ it("produces the same diff whether a human or an agent acts", async () => {
96
+ const byHand = human.apply({ name: "reassign", args });
97
+ const byAgent = await seat.call("reassign", args);
98
+ // A refusal is a RESULT, so `ToolResult` is a union: establish there was a
99
+ // diff before claiming anything about it, or this does not compile.
100
+ if (!byAgent.ok) throw new Error(byAgent.error);
101
+ expect(byAgent.diff).toEqual(byHand.diff);
102
+ });
103
+ ```
104
+
105
+ **And prove the seat is bounded**, if it holds a restricted principal: call a
106
+ mutation it was not given a tool for and check it is refused by the STORE
107
+ rather than merely absent from the schema.
108
+
109
+ ## What the check cannot see
110
+
111
+ - Whether the agent's turn is legible while it is happening. Open the app and
112
+ watch one; `pnpm watching` measures the marks but not whether they read.
113
+ - Whether the tool descriptions are good instructions. They are prose an agent
114
+ reasons from, and "get the graph" is worse than "read the whole graph: start
115
+ here when you need the shape of the domain rather than one thing in it".
116
+ - Whether the agent should be doing this at all.
117
+
118
+ ## The seat is one of four surfaces on one seam
119
+
120
+ Everything intelligent travels the same contract — validated proposed calls
121
+ to declared mutations — so adding AI is choosing a provider, never a second
122
+ path to the store:
123
+
124
+ - **Providers** whisper suggestions into the companion's acts
125
+ (`insightProvider` ships in the defaults; `intelligenceProvider(...)`
126
+ wraps any `Intelligence`).
127
+ - **The seat** (this skill) runs one-press turns.
128
+ - **The companion** — `<Companion />`, the scene's left rail — is the one
129
+ place the seat lives: it names its subject (the selection, else the pick
130
+ the pointer settled on, else where you are), lists that subject's acts,
131
+ its relations, the conversation and the key. Right-click opens the same
132
+ acts at the pointer, so the context menu and the assistant are one
133
+ construct. The seat has no figure in the picture; other people's agents
134
+ still have theirs. `useSubject()` gives the same answer to any surface.
135
+ - **The conversation** — a section of that rail — answers questions in words.
136
+ Keyless it answers from the graph (`graphResponder`: standings, named
137
+ things, when/who, mutations phrased in their own titles); a model plugs in
138
+ through one completion function (`llmResponder`, `xaiCompletion`,
139
+ `localCompletion`), chosen by the person in the panel's gear. **Your own
140
+ responder goes in through the shell**: `<Shell chat={{ respond }} />`,
141
+ which hands it to the companion.
142
+ "Why do I still have mosquitoes?" is a walk through THIS graph, and the
143
+ generic answer to it is a plausible paragraph about gardens.
144
+ - **External agents** arrive over the derived tool surface with a scoped
145
+ principal.
146
+ - **A desk** — the model INSIDE the product, taking photographs or words and
147
+ proposing a plan somebody reviews. That is `graview-desk`; this skill is
148
+ the seat beside the product rather than the surface in it.
149
+
150
+ Declare what runs where on the app: `intelligence: [{ name, kind:
151
+ "graph" | "llm" | "external" | "decision", may: [...mutations] }]` —
152
+ `graview check` refuses an allowlist naming a mutation nobody registered,
153
+ refuses a `"decision"` provider (one that answers typed questions — a
154
+ choice, a truth, a score — and never prose) any act whose required
155
+ arguments want text, and the STORE
156
+ enforces it: hand the store `intelligence` and an author
157
+ `{ kind: "agent", id: "<provider name>" }` is refused anything outside its
158
+ `may`, on `apply` as on a tool call. `openStore` and the embed pass it
159
+ through for you.