create-rigline-plugin 1.0.0-alpha.10 → 1.0.0-alpha.11
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/index.js +1 -1
- package/package.json +2 -2
- package/template/README.md +9 -3
- package/template/plugins/__NAME__/README.md +8 -7
- package/template/plugins/__NAME__/package.json +4 -1
- package/template/plugins/__NAME__/rigline.json +7 -3
- package/template/plugins/__NAME__/src/index.test.ts +1 -1
- package/template/plugins/__NAME__/src/index.tsx +64 -0
- package/template/plugins/__NAME__/tsconfig.json +1 -1
- package/template/tsconfig.plugin.json +2 -1
- package/template/plugins/__NAME__/src/index.ts +0 -82
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ export function nextSteps(result) {
|
|
|
82
82
|
"",
|
|
83
83
|
"Then reload the webview: Developer: Reload Webviews.",
|
|
84
84
|
"",
|
|
85
|
-
`${join(here, "README.md")} has the rest, including the
|
|
85
|
+
`${join(here, "README.md")} has the rest, including the rules worth reading first.`,
|
|
86
86
|
"",
|
|
87
87
|
"Read the plugin policy once — what Rigline promises Anthropic, and the part your plugin has",
|
|
88
88
|
"to hold up. It applies whether or not you ever publish this:",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-rigline-plugin",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.11",
|
|
4
4
|
"description": "Scaffold a workspace for Claude Code VS Code extension plugins",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rigline",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"create-rigline-plugin": "./dist/index.js"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@rigline/plugin-api": "1.0.0-alpha.
|
|
35
|
+
"@rigline/plugin-api": "1.0.0-alpha.11"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.build.json",
|
package/template/README.md
CHANGED
|
@@ -37,6 +37,10 @@ which is the whole reason to declare rather than to reach.
|
|
|
37
37
|
Put a dependency you can do without under `uses.optional`: it is checked the same way and costs the
|
|
38
38
|
plugin that one decoration rather than the whole plugin.
|
|
39
39
|
|
|
40
|
+
`elements` is the other half: what the plugin contributes, each with the places it may go and where
|
|
41
|
+
it goes by default — or `null` for off. A place this extension version cannot provide costs that
|
|
42
|
+
element and nothing else, and `check` says so.
|
|
43
|
+
|
|
40
44
|
## The plugin policy
|
|
41
45
|
|
|
42
46
|
**This applies whether or not you ever publish.** A plugin you wrote for yourself and will never
|
|
@@ -48,8 +52,8 @@ takes a minute.
|
|
|
48
52
|
Rigline modifies Anthropic's extension and publishes a compliance position saying what it does and
|
|
49
53
|
does not do. Your plugin runs inside that modification, so the position has to hold for it too.
|
|
50
54
|
|
|
51
|
-
Most of it is not left to you — the webview has no network egress, no filesystem, and no way
|
|
52
|
-
the extension host, so the usual ways to do harm are absent rather than forbidden. What the policy
|
|
55
|
+
Most of it is not left to you — the webview has no network egress, no filesystem, and no way to run
|
|
56
|
+
code in the extension host, so the usual ways to do harm are absent rather than forbidden. What the policy
|
|
53
57
|
asks is the part the architecture cannot cover: do not deceive the person using it, do not reach for
|
|
54
58
|
credentials, do not carry conversation content off the machine by a path the closed network does not
|
|
55
59
|
cover, and keep any host patch to switching on a capability the extension already has.
|
|
@@ -115,6 +119,8 @@ what you bumped.
|
|
|
115
119
|
you placed.
|
|
116
120
|
- **Ask what a container does about its children before decorating it.** The composer footer
|
|
117
121
|
measures its own element children and re-measures on any foreign change inside it; footer
|
|
118
|
-
|
|
122
|
+
elements go before `footerSpacer`.
|
|
123
|
+
- **Keep what you place steady.** An element in the footer whose text keeps changing makes the
|
|
124
|
+
footer re-measure each time, and one in `rigRow` whose height keeps changing re-renders the panel.
|
|
119
125
|
- **Do not poll for an element.** `ctx.watch(name, …)` hands it over when it appears and again when
|
|
120
126
|
the app replaces it.
|
|
@@ -4,15 +4,16 @@ __DESCRIPTION__
|
|
|
4
4
|
|
|
5
5
|
## Depends on
|
|
6
6
|
|
|
7
|
-
- `uses.anchors: ["footerSpacer"]` — the flexible gap dividing the composer footer's left cluster
|
|
8
|
-
from its right, and the anchor every footer decoration uses. The footer measures the widths of its
|
|
9
|
-
own element children to pick a fit stage; the spacer renders in every stage, so a decoration
|
|
10
|
-
beside it contributes a constant width and the measurement settles.
|
|
11
|
-
- `uses.mount` — `ctx.watch` and `ctx.mountBefore`, which place the badge and keep it placed across
|
|
12
|
-
a re-render.
|
|
13
|
-
- `uses.style` — one stylesheet, scoped to the class this plugin puts on its own element.
|
|
14
7
|
- `uses.tools` — `ctx.onToolUse`, every completed tool call the assistant makes.
|
|
15
8
|
|
|
9
|
+
## Contributes
|
|
10
|
+
|
|
11
|
+
- `elements.badge` — the tool-call count, before `footerSpacer` by default: the flexible gap
|
|
12
|
+
dividing the composer footer's left cluster from its right, and the place footer elements go. The
|
|
13
|
+
footer measures the widths of its own element children to pick a fit stage; the spacer renders in
|
|
14
|
+
every stage, so an element beside it contributes a constant width and the measurement settles. It
|
|
15
|
+
may also go in `rigRow`, the row under the composer's controls.
|
|
16
|
+
|
|
16
17
|
## Notes
|
|
17
18
|
|
|
18
19
|
`badgeText` is exported and tested because it is the half a plain test run can hold. Everything
|
|
@@ -6,9 +6,13 @@
|
|
|
6
6
|
"entry": "dist/index.js",
|
|
7
7
|
"surfaces": ["editor", "sidebar"],
|
|
8
8
|
"uses": {
|
|
9
|
-
"anchors": ["footerSpacer"],
|
|
10
|
-
"mount": true,
|
|
11
|
-
"style": true,
|
|
12
9
|
"tools": true
|
|
10
|
+
},
|
|
11
|
+
"elements": {
|
|
12
|
+
"badge": {
|
|
13
|
+
"title": "Tool calls",
|
|
14
|
+
"placements": [{ "anchor": "footerSpacer", "at": "before" }, "rigRow"],
|
|
15
|
+
"default": { "anchor": "footerSpacer", "at": "before" }
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and only the app can answer it: `pnpm build`, `pnpm rigline add`, reload the webview, look.
|
|
8
8
|
*/
|
|
9
9
|
import { describe, expect, it } from "vitest";
|
|
10
|
-
import { badgeText } from "./index.
|
|
10
|
+
import { badgeText } from "./index.tsx";
|
|
11
11
|
|
|
12
12
|
describe("badgeText", () => {
|
|
13
13
|
it("says something before anything has happened", () => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* __DESCRIPTION__
|
|
3
|
+
*
|
|
4
|
+
* A worked example of the loop every plugin is: declare what you contribute and depend on in
|
|
5
|
+
* `rigline.json`, keep what you learn in a store, and render it from a component. Replace the body;
|
|
6
|
+
* keep the shape.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
definePlugin,
|
|
10
|
+
type PluginContext,
|
|
11
|
+
type Store,
|
|
12
|
+
store,
|
|
13
|
+
type Teardown,
|
|
14
|
+
} from "@rigline/plugin-api";
|
|
15
|
+
import { Pill, useStore } from "@rigline/plugin-api/ui";
|
|
16
|
+
import type { ReactNode } from "react";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* What the badge reads, for a given number of tool calls.
|
|
20
|
+
*
|
|
21
|
+
* Pure, and exported, because this is the half a plain `vitest` run can hold: anything that renders
|
|
22
|
+
* wants the app itself, and anything that does not should not need it. See `src/index.test.ts`.
|
|
23
|
+
*/
|
|
24
|
+
export function badgeText(calls: number): string {
|
|
25
|
+
if (calls === 0) return "no tools yet";
|
|
26
|
+
return `${calls} tool ${calls === 1 ? "call" : "calls"}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Badge(props: { readonly calls: Store<number> }): ReactNode {
|
|
30
|
+
return <Pill>{badgeText(useStore(props.calls))}</Pill>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default definePlugin({
|
|
34
|
+
setup(ctx: PluginContext): Teardown {
|
|
35
|
+
// A store, made here rather than in the component, because state caught in `setup` is caught
|
|
36
|
+
// from the moment the plugin loads, and the badge and anything else can share it.
|
|
37
|
+
const calls = store(0);
|
|
38
|
+
|
|
39
|
+
// Every completed tool call the assistant makes. `uses.tools` is what makes this fire; without
|
|
40
|
+
// the declaration it throws and disables the plugin, which is the point of declaring.
|
|
41
|
+
const stopTools = ctx.onToolUse(() => calls.set(calls.get() + 1));
|
|
42
|
+
|
|
43
|
+
// `badge` is declared under `elements` in rigline.json, which says where it goes by default:
|
|
44
|
+
// before `footerSpacer`, at the end of the composer footer's left cluster. Rigline places it,
|
|
45
|
+
// keeps it placed, and moves it if the user asks for it somewhere else.
|
|
46
|
+
const stopBadge = ctx.element("badge", () => <Badge calls={calls} />);
|
|
47
|
+
|
|
48
|
+
// One line in Rigline's diagnostics panel, under this plugin's name. Whether the badge is on
|
|
49
|
+
// screen is Rigline's to say, and it does; ask what only your own state can answer, and say
|
|
50
|
+
// `n/a` with a reason when there is nothing to report yet. The host runs this about once a
|
|
51
|
+
// second, so read state you already keep rather than computing anything here.
|
|
52
|
+
const stopCheck = ctx.check("tool calls observed", () =>
|
|
53
|
+
calls.get() === 0
|
|
54
|
+
? { verdict: "n/a", detail: "none yet" }
|
|
55
|
+
: { verdict: "pass", detail: badgeText(calls.get()) },
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
stopCheck();
|
|
60
|
+
stopBadge();
|
|
61
|
+
stopTools();
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* __DESCRIPTION__
|
|
3
|
-
*
|
|
4
|
-
* A worked example of the loop every plugin is: declare what you depend on in `rigline.json`, wait
|
|
5
|
-
* to be handed the element you decorate, and put something beside it. Replace the body; keep the
|
|
6
|
-
* shape.
|
|
7
|
-
*/
|
|
8
|
-
import { definePlugin, type PluginContext, type Teardown } from "@rigline/plugin-api";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* What the badge reads, for a given number of tool calls.
|
|
12
|
-
*
|
|
13
|
-
* Pure, and exported, because this is the half a plain `vitest` run can hold: anything that touches
|
|
14
|
-
* the DOM wants the app itself, and anything that does not should not need it. See
|
|
15
|
-
* `src/index.test.ts`.
|
|
16
|
-
*/
|
|
17
|
-
export function badgeText(calls: number): string {
|
|
18
|
-
if (calls === 0) return "no tools yet";
|
|
19
|
-
return `${calls} tool ${calls === 1 ? "call" : "calls"}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default definePlugin({
|
|
23
|
-
setup(ctx: PluginContext): Teardown {
|
|
24
|
-
let calls = 0;
|
|
25
|
-
const badge = document.createElement("span");
|
|
26
|
-
badge.className = "example-badge";
|
|
27
|
-
badge.textContent = badgeText(calls);
|
|
28
|
-
|
|
29
|
-
// Scoped to a class this plugin put on its own element. Never scope a rule to an anchor's bare
|
|
30
|
-
// class: one class is applied wherever that look is wanted, so a rule written against it lands
|
|
31
|
-
// on every control wearing it. See the anchors guide.
|
|
32
|
-
const stopStyle = ctx.style(`
|
|
33
|
-
.example-badge {
|
|
34
|
-
font-size: 11px;
|
|
35
|
-
opacity: 0.7;
|
|
36
|
-
padding: 0 6px;
|
|
37
|
-
white-space: nowrap;
|
|
38
|
-
}
|
|
39
|
-
`);
|
|
40
|
-
|
|
41
|
-
// Every completed tool call the assistant makes. `uses.tools` is what makes this fire; without
|
|
42
|
-
// the declaration it throws and disables the plugin, which is the point of declaring.
|
|
43
|
-
const stopTools = ctx.onToolUse(() => {
|
|
44
|
-
calls += 1;
|
|
45
|
-
badge.textContent = badgeText(calls);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// `watch` hands over the element for an anchor whenever one is in the document, and again if
|
|
49
|
-
// the app replaces it. No plugin polls for an element.
|
|
50
|
-
//
|
|
51
|
-
// `footerSpacer` and `mountBefore` together, rather than any other footer anchor: the composer
|
|
52
|
-
// footer measures the widths of its own element children to pick a fit stage, and resets that
|
|
53
|
-
// measurement on any foreign change inside it. A decoration whose membership of the footer
|
|
54
|
-
// changes with the stage fights the ladder that moved it. The spacer renders in every stage, so
|
|
55
|
-
// a decoration beside it contributes a constant width and the ladder settles.
|
|
56
|
-
const stopWatch = ctx.watch("footerSpacer", (spacer) => ctx.mountBefore(spacer, () => badge));
|
|
57
|
-
|
|
58
|
-
// One line in Rigline's diagnostics panel, under this plugin's name. It declares nothing: the
|
|
59
|
-
// host calls it, hands it nothing, and gets a verdict back.
|
|
60
|
-
//
|
|
61
|
-
// Worth the four lines from the first day, because the failure a plugin has is normally silent.
|
|
62
|
-
// An extension update can leave this one loaded, declared, styled and drawing nothing, and
|
|
63
|
-
// every other line in that panel will say it is fine. Ask the question only your own state can
|
|
64
|
-
// answer — here, whether the badge is actually in the document — and say `n/a` with a reason
|
|
65
|
-
// when there is nothing to report yet.
|
|
66
|
-
//
|
|
67
|
-
// The host runs this about once a second for the life of the window, so read state you already
|
|
68
|
-
// keep. Do not walk the DOM or recompute an answer here; do that work where it already happens.
|
|
69
|
-
const stopCheck = ctx.check("badge is mounted", () =>
|
|
70
|
-
badge.isConnected
|
|
71
|
-
? { verdict: "pass", detail: badgeText(calls) }
|
|
72
|
-
: { verdict: "fail", detail: "the badge is not in the document" },
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
return () => {
|
|
76
|
-
stopCheck();
|
|
77
|
-
stopWatch();
|
|
78
|
-
stopTools();
|
|
79
|
-
stopStyle();
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
});
|