@aliou/pi-dev-kit 0.6.5 → 0.7.1
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/README.md +2 -2
- package/package.json +11 -10
- package/src/commands/index.ts +5 -1
- package/src/commands/update.ts +125 -83
- package/src/skills/pi-extension/SKILL.md +108 -137
- package/src/skills/pi-extension/references/additional-apis.md +252 -208
- package/src/skills/pi-extension/references/commands.md +113 -33
- package/src/skills/pi-extension/references/components.md +267 -102
- package/src/skills/pi-extension/references/hooks.md +229 -156
- package/src/skills/pi-extension/references/messages.md +94 -106
- package/src/skills/pi-extension/references/modes.md +80 -90
- package/src/skills/pi-extension/references/providers.md +255 -96
- package/src/skills/pi-extension/references/publish.md +76 -62
- package/src/skills/pi-extension/references/state.md +80 -33
- package/src/skills/pi-extension/references/structure.md +126 -269
- package/src/skills/pi-extension/references/testing.md +1 -1
- package/src/skills/pi-extension/references/tools.md +198 -823
- package/src/tools/changelog-tool.ts +15 -3
- package/src/tools/docs-tool.ts +3 -3
- package/src/tools/index.ts +5 -1
- package/src/tools/package-manager-tool.ts +8 -4
- package/src/tools/utils.ts +33 -23
- package/src/tools/version-tool.ts +8 -4
- package/src/index.ts +0 -8
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Providers
|
|
2
2
|
|
|
3
|
-
Providers add LLM backends
|
|
3
|
+
Providers add or override LLM backends through `pi.registerProvider(name, config)`. Use them for proxies, custom endpoints, OAuth/SSO, and custom streaming APIs.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read Pi `docs/custom-provider.md` and `docs/models.md` before implementing a provider.
|
|
6
6
|
|
|
7
|
-
## Registration
|
|
7
|
+
## Quick Registration
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import type { ExtensionAPI, ProviderConfig } from "@
|
|
10
|
+
import type { ExtensionAPI, ProviderConfig } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const myProvider: ProviderConfig = {
|
|
13
|
+
name: "My Provider",
|
|
13
14
|
baseUrl: "https://api.example.com/v1",
|
|
14
15
|
apiKey: "MY_API_KEY",
|
|
15
16
|
api: "openai-completions",
|
|
@@ -26,134 +27,292 @@ const myProviderConfig: ProviderConfig = {
|
|
|
26
27
|
],
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
export default function (pi: ExtensionAPI) {
|
|
30
|
-
pi.registerProvider("my-provider",
|
|
30
|
+
export default function providersExtension(pi: ExtensionAPI) {
|
|
31
|
+
pi.registerProvider("my-provider", myProvider);
|
|
31
32
|
}
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Use legacy `@mariozechner/*` imports only when the target `@earendil-works/*` package is not available yet.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
## Async Model Discovery
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
39
|
+
If models come from a remote endpoint, fetch them in an async extension factory, not `session_start`. Pi waits for the factory before startup continues, so models are available during startup and `pi --list-models`.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
```typescript
|
|
42
|
+
export default async function providersExtension(pi: ExtensionAPI) {
|
|
43
|
+
const response = await fetch("http://localhost:1234/v1/models");
|
|
44
|
+
const payload = (await response.json()) as {
|
|
45
|
+
data: Array<{ id: string; name?: string; context_window?: number; max_tokens?: number }>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
pi.registerProvider("local-openai", {
|
|
49
|
+
name: "Local OpenAI",
|
|
50
|
+
baseUrl: "http://localhost:1234/v1",
|
|
51
|
+
apiKey: "LOCAL_OPENAI_API_KEY",
|
|
52
|
+
api: "openai-completions",
|
|
53
|
+
models: payload.data.map((model) => ({
|
|
54
|
+
id: model.id,
|
|
55
|
+
name: model.name ?? model.id,
|
|
56
|
+
reasoning: false,
|
|
57
|
+
input: ["text"],
|
|
58
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
59
|
+
contextWindow: model.context_window ?? 128000,
|
|
60
|
+
maxTokens: model.max_tokens ?? 4096,
|
|
61
|
+
})),
|
|
44
62
|
});
|
|
45
63
|
}
|
|
46
64
|
```
|
|
47
65
|
|
|
48
|
-
|
|
66
|
+
## Override Existing Providers
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
When only `baseUrl` and/or `headers` are provided, Pi keeps built-in models and auth.
|
|
51
69
|
|
|
52
70
|
```typescript
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
id: "my-model",
|
|
63
|
-
name: "My Model",
|
|
64
|
-
reasoning: false,
|
|
65
|
-
input: ["text"],
|
|
66
|
-
cost: { input: 0.5, output: 1.5, cacheRead: 0, cacheWrite: 0 },
|
|
67
|
-
contextWindow: 128000,
|
|
68
|
-
maxTokens: 8192,
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
});
|
|
72
|
-
}
|
|
71
|
+
pi.registerProvider("anthropic", {
|
|
72
|
+
baseUrl: "https://proxy.example.com",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
pi.registerProvider("openai", {
|
|
76
|
+
headers: {
|
|
77
|
+
"X-Custom-Header": "MY_HEADER_ENV_OR_LITERAL",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
73
80
|
```
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
## Unregister Providers
|
|
76
83
|
|
|
77
84
|
```typescript
|
|
78
85
|
pi.unregisterProvider("my-provider");
|
|
79
86
|
```
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
Unregistering removes dynamic models, API key fallback, OAuth registration, and stream handlers. Built-in behavior overridden by that provider is restored.
|
|
82
89
|
|
|
83
90
|
## ProviderConfig Fields
|
|
84
91
|
|
|
85
|
-
| Field |
|
|
86
|
-
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `apiKey` |
|
|
90
|
-
| `api` |
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
92
|
+
| Field | Notes |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `name` | Display name for `/login` and UI. |
|
|
95
|
+
| `baseUrl` | Endpoint URL. Required when defining models. |
|
|
96
|
+
| `apiKey` | API key, env var name, or auth value. Required unless `oauth` handles auth. |
|
|
97
|
+
| `api` | API type. Required at provider or model level when defining models. |
|
|
98
|
+
| `headers` | Static custom headers. Values can be env var names. |
|
|
99
|
+
| `authHeader` | When `true`, Pi sends `Authorization: Bearer <key>`. |
|
|
100
|
+
| `models` | Replaces registered models for this provider when provided. |
|
|
101
|
+
| `oauth` | Adds `/login` support. |
|
|
102
|
+
| `streamSimple` | Custom streaming implementation for non-standard APIs. |
|
|
103
|
+
|
|
104
|
+
Supported API types include:
|
|
105
|
+
|
|
106
|
+
- `anthropic-messages`
|
|
107
|
+
- `openai-completions`
|
|
108
|
+
- `openai-responses`
|
|
109
|
+
- `azure-openai-responses`
|
|
110
|
+
- `openai-codex-responses`
|
|
111
|
+
- `mistral-conversations`
|
|
112
|
+
- `google-generative-ai`
|
|
113
|
+
- `google-vertex`
|
|
114
|
+
- `bedrock-converse-stream`
|
|
115
|
+
|
|
116
|
+
Prefer a built-in API type. Use `streamSimple` only when the upstream API cannot be adapted with config and compatibility flags.
|
|
117
|
+
|
|
118
|
+
## Model Fields
|
|
119
|
+
|
|
120
|
+
| Field | Required | Notes |
|
|
121
|
+
|---|---:|---|
|
|
122
|
+
| `id` | Yes | Model ID sent to the API. |
|
|
123
|
+
| `name` | Yes for provider extensions | Human-readable display name. |
|
|
124
|
+
| `api` | No | Model-level API override. |
|
|
125
|
+
| `baseUrl` | No | Model-level endpoint override. |
|
|
126
|
+
| `reasoning` | Yes | Whether extended thinking is supported. |
|
|
127
|
+
| `thinkingLevelMap` | No | Maps Pi thinking levels to provider-specific values; `null` hides unsupported levels. |
|
|
128
|
+
| `input` | Yes | `Array<"text" | "image">`. |
|
|
129
|
+
| `cost` | Yes | Per-million token cost: `{ input, output, cacheRead, cacheWrite }`. |
|
|
130
|
+
| `contextWindow` | Yes | Context window in tokens. |
|
|
131
|
+
| `maxTokens` | Yes | Maximum output tokens. |
|
|
132
|
+
| `headers` | No | Model-specific headers. |
|
|
133
|
+
| `compat` | No | Provider compatibility flags. |
|
|
134
|
+
|
|
135
|
+
### Thinking level map
|
|
136
|
+
|
|
137
|
+
Use model-level `thinkingLevelMap`; do not use older `compat.reasoningEffortMap`.
|
|
94
138
|
|
|
95
|
-
|
|
139
|
+
```typescript
|
|
140
|
+
{
|
|
141
|
+
id: "custom-reasoner",
|
|
142
|
+
name: "Custom Reasoner",
|
|
143
|
+
reasoning: true,
|
|
144
|
+
thinkingLevelMap: {
|
|
145
|
+
minimal: null,
|
|
146
|
+
low: null,
|
|
147
|
+
medium: null,
|
|
148
|
+
high: "default",
|
|
149
|
+
xhigh: "max",
|
|
150
|
+
},
|
|
151
|
+
input: ["text"],
|
|
152
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
153
|
+
contextWindow: 128000,
|
|
154
|
+
maxTokens: 8192,
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Compatibility Flags
|
|
96
159
|
|
|
97
|
-
|
|
160
|
+
For OpenAI-compatible servers, use `compat` instead of custom streaming when possible.
|
|
98
161
|
|
|
99
|
-
|
|
162
|
+
```typescript
|
|
163
|
+
compat: {
|
|
164
|
+
supportsDeveloperRole: false,
|
|
165
|
+
supportsReasoningEffort: false,
|
|
166
|
+
supportsUsageInStreaming: false,
|
|
167
|
+
maxTokensField: "max_tokens",
|
|
168
|
+
requiresToolResultName: true,
|
|
169
|
+
thinkingFormat: "qwen-chat-template",
|
|
170
|
+
cacheControlFormat: "anthropic",
|
|
171
|
+
}
|
|
172
|
+
```
|
|
100
173
|
|
|
101
|
-
|
|
102
|
-
|---|---|---|
|
|
103
|
-
| `id` | `string` | Model identifier within the provider config. |
|
|
104
|
-
| `name` | `string` | Display name shown in model selection UI. |
|
|
105
|
-
| `reasoning` | `boolean` | Whether the model is a reasoning model. |
|
|
106
|
-
| `input` | `Array<"text" \| "image" \| "audio" \| "pdf">` | Input modalities supported by the model. |
|
|
107
|
-
| `cost` | `object` | `{ input, output, cacheRead, cacheWrite }` cost values. |
|
|
108
|
-
| `contextWindow` | `number` | Maximum context window. |
|
|
109
|
-
| `maxTokens` | `number` | Maximum output tokens. |
|
|
174
|
+
Common flags:
|
|
110
175
|
|
|
111
|
-
|
|
176
|
+
- `supportsDeveloperRole`
|
|
177
|
+
- `supportsReasoningEffort`
|
|
178
|
+
- `supportsUsageInStreaming`
|
|
179
|
+
- `maxTokensField`
|
|
180
|
+
- `requiresToolResultName`
|
|
181
|
+
- `requiresAssistantAfterToolResult`
|
|
182
|
+
- `requiresThinkingAsText`
|
|
183
|
+
- `requiresReasoningContentOnAssistantMessages`
|
|
184
|
+
- `thinkingFormat`
|
|
185
|
+
- `cacheControlFormat`
|
|
186
|
+
- `supportsStrictMode`
|
|
187
|
+
- `supportsLongCacheRetention`
|
|
188
|
+
- `openRouterRouting`
|
|
189
|
+
- `vercelGatewayRouting`
|
|
112
190
|
|
|
113
|
-
|
|
191
|
+
For Anthropic-compatible proxies, check `supportsEagerToolInputStreaming` and `supportsLongCacheRetention` in Pi docs.
|
|
114
192
|
|
|
115
|
-
|
|
116
|
-
- Register the provider with `pi.registerProvider(name, config)`.
|
|
117
|
-
- Point `apiKey` at the environment variable name that holds the credential.
|
|
118
|
-
- If the provider should exist even when tools are disabled, still register it.
|
|
193
|
+
## OAuth Providers
|
|
119
194
|
|
|
120
|
-
|
|
121
|
-
- Gate those registrations separately in your extension entry point.
|
|
195
|
+
OAuth integrates with `/login`.
|
|
122
196
|
|
|
123
197
|
```typescript
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
198
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
|
|
199
|
+
|
|
200
|
+
pi.registerProvider("corporate-ai", {
|
|
201
|
+
name: "Corporate AI",
|
|
202
|
+
baseUrl: "https://ai.corp.com/v1",
|
|
203
|
+
api: "openai-responses",
|
|
204
|
+
models,
|
|
205
|
+
oauth: {
|
|
206
|
+
name: "Corporate AI (SSO)",
|
|
207
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
208
|
+
callbacks.onAuth({ url: "https://sso.corp.com/authorize" });
|
|
209
|
+
const code = await callbacks.onPrompt({ message: "Enter code:" });
|
|
210
|
+
return exchangeCodeForTokens(code);
|
|
211
|
+
},
|
|
212
|
+
async refreshToken(credentials) {
|
|
213
|
+
return refreshTokens(credentials.refresh);
|
|
214
|
+
},
|
|
215
|
+
getApiKey(credentials) {
|
|
216
|
+
return credentials.access;
|
|
217
|
+
},
|
|
218
|
+
modifyModels(models, credentials) {
|
|
219
|
+
return models;
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Use `modifyModels` when subscription, tenant, or region information in credentials changes model endpoints or availability.
|
|
141
226
|
|
|
142
|
-
|
|
227
|
+
## API Key Gating
|
|
228
|
+
|
|
229
|
+
Provider registration and tool registration are separate.
|
|
230
|
+
|
|
231
|
+
- Register providers even when the API key is absent; Pi resolves auth and can show login/setup UI.
|
|
232
|
+
- Gate tools and commands that directly call the same API.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
export default function extension(pi: ExtensionAPI) {
|
|
236
|
+
pi.registerProvider("my-provider", providerConfig);
|
|
143
237
|
|
|
144
|
-
|
|
145
|
-
|
|
238
|
+
if (!process.env.MY_API_KEY) {
|
|
239
|
+
pi.on("session_start", (_event, ctx) => {
|
|
240
|
+
ctx.ui.notify("MY_API_KEY not set. my-provider tools disabled.", "warning");
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
pi.registerTool(createSearchTool(process.env.MY_API_KEY));
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Missing keys should disable affected tools gracefully, not crash extension loading.
|
|
250
|
+
|
|
251
|
+
## Custom Streaming
|
|
252
|
+
|
|
253
|
+
Use `streamSimple` only for non-standard APIs. Follow Pi provider implementations and tests.
|
|
254
|
+
|
|
255
|
+
Basic pattern:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import {
|
|
259
|
+
calculateCost,
|
|
260
|
+
createAssistantMessageEventStream,
|
|
261
|
+
type AssistantMessage,
|
|
262
|
+
type Context,
|
|
263
|
+
type Model,
|
|
264
|
+
type SimpleStreamOptions,
|
|
265
|
+
} from "@earendil-works/pi-ai";
|
|
266
|
+
|
|
267
|
+
function streamMyProvider(model: Model<any>, context: Context, options?: SimpleStreamOptions) {
|
|
268
|
+
const stream = createAssistantMessageEventStream();
|
|
269
|
+
|
|
270
|
+
(async () => {
|
|
271
|
+
const output: AssistantMessage = {
|
|
272
|
+
role: "assistant",
|
|
273
|
+
content: [],
|
|
274
|
+
api: model.api,
|
|
275
|
+
provider: model.provider,
|
|
276
|
+
model: model.id,
|
|
277
|
+
usage: {
|
|
278
|
+
input: 0,
|
|
279
|
+
output: 0,
|
|
280
|
+
cacheRead: 0,
|
|
281
|
+
cacheWrite: 0,
|
|
282
|
+
totalTokens: 0,
|
|
283
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
284
|
+
},
|
|
285
|
+
stopReason: "stop",
|
|
286
|
+
timestamp: Date.now(),
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
stream.push({ type: "start", partial: output });
|
|
291
|
+
// Push text/thinking/toolcall events as data arrives.
|
|
292
|
+
calculateCost(model, output.usage);
|
|
293
|
+
stream.push({ type: "done", reason: "stop", message: output });
|
|
294
|
+
stream.end();
|
|
295
|
+
} catch (error) {
|
|
296
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
297
|
+
output.errorMessage = error instanceof Error ? error.message : String(error);
|
|
298
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
299
|
+
stream.end();
|
|
300
|
+
}
|
|
301
|
+
})();
|
|
302
|
+
|
|
303
|
+
return stream;
|
|
146
304
|
}
|
|
147
305
|
```
|
|
148
306
|
|
|
149
|
-
|
|
307
|
+
Test custom providers against streaming, abort, usage, unicode, tool call, and context-overflow cases.
|
|
150
308
|
|
|
151
|
-
##
|
|
309
|
+
## Checklist
|
|
152
310
|
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
-
|
|
311
|
+
- [ ] Provider uses `pi.registerProvider(name, config)`.
|
|
312
|
+
- [ ] Dynamic model discovery happens in an async factory.
|
|
313
|
+
- [ ] Existing provider overrides do not redefine models unless needed.
|
|
314
|
+
- [ ] New models include current fields and `thinkingLevelMap` when relevant.
|
|
315
|
+
- [ ] Compatibility flags are used before custom streaming.
|
|
316
|
+
- [ ] OAuth providers implement login, refresh, getApiKey, and optional modifyModels.
|
|
317
|
+
- [ ] Tools needing the same credential are gated separately.
|
|
318
|
+
- [ ] Missing credentials produce a notification, not a crash.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# Publishing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pi packages are published to npm or installed from git/local paths with `pi install`.
|
|
4
4
|
|
|
5
5
|
## Package Setup
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Key fields for publishing:
|
|
7
|
+
A publishable package needs Pi metadata and discoverability fields.
|
|
10
8
|
|
|
11
9
|
```json
|
|
12
10
|
{
|
|
@@ -15,20 +13,42 @@ Key fields for publishing:
|
|
|
15
13
|
"type": "module",
|
|
16
14
|
"license": "MIT",
|
|
17
15
|
"private": false,
|
|
16
|
+
"keywords": ["pi-package", "pi-extension", "pi"],
|
|
18
17
|
"publishConfig": { "access": "public" },
|
|
19
18
|
"files": ["src", "README.md"],
|
|
20
19
|
"pi": {
|
|
21
|
-
"extensions": ["./src/index.ts"]
|
|
20
|
+
"extensions": ["./src/tools/index.ts", "./src/commands/index.ts"]
|
|
22
21
|
},
|
|
23
22
|
"peerDependencies": {
|
|
24
|
-
"@
|
|
23
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
24
|
+
"typebox": "*"
|
|
25
|
+
},
|
|
26
|
+
"peerDependenciesMeta": {
|
|
27
|
+
"@earendil-works/pi-coding-agent": { "optional": true },
|
|
28
|
+
"typebox": { "optional": true }
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
```
|
|
28
32
|
|
|
33
|
+
Use the legacy `@mariozechner/*` namespace only while the target Pi packages are not published under `@earendil-works/*`.
|
|
34
|
+
|
|
35
|
+
Pi core packages imported at runtime belong in optional `peerDependencies` with `"*"`. Keep exact target versions in `devDependencies` for local type checking. Third-party runtime packages belong in `dependencies`.
|
|
36
|
+
|
|
37
|
+
## Installation Specs
|
|
38
|
+
|
|
39
|
+
Users can install with:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pi install npm:@scope/pi-my-extension
|
|
43
|
+
pi install git:github.com/org/pi-my-extension@v1.0.0
|
|
44
|
+
pi install ./relative/path/to/package
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
By default, global installs write to `~/.pi/agent/settings.json`; `-l` writes project settings.
|
|
48
|
+
|
|
29
49
|
## Versioning with Changesets
|
|
30
50
|
|
|
31
|
-
Use
|
|
51
|
+
Use Changesets for versioning and changelogs.
|
|
32
52
|
|
|
33
53
|
### `.changeset/config.json`
|
|
34
54
|
|
|
@@ -48,92 +68,86 @@ Use [changesets](https://github.com/changesets/changesets) for versioning and ch
|
|
|
48
68
|
|
|
49
69
|
### Creating a changeset
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
In headless agent sessions, write the file directly instead of running the interactive CLI.
|
|
52
72
|
|
|
53
73
|
```md
|
|
54
74
|
---
|
|
55
75
|
"@scope/pi-my-extension": patch
|
|
56
76
|
---
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
Describe the user-visible change.
|
|
59
79
|
```
|
|
60
80
|
|
|
61
|
-
|
|
81
|
+
Use:
|
|
82
|
+
|
|
83
|
+
- `patch` for fixes and internal compatibility updates.
|
|
84
|
+
- `minor` for new user-facing features.
|
|
85
|
+
- `major` for breaking changes.
|
|
62
86
|
|
|
63
|
-
Commit the changeset
|
|
87
|
+
Commit the changeset with the change it describes.
|
|
64
88
|
|
|
65
|
-
###
|
|
89
|
+
### Manual release
|
|
66
90
|
|
|
67
91
|
```bash
|
|
68
|
-
pnpm changeset version
|
|
69
|
-
pnpm changeset publish
|
|
92
|
+
pnpm changeset version
|
|
93
|
+
pnpm changeset publish
|
|
70
94
|
```
|
|
71
95
|
|
|
72
96
|
## GitHub Actions Automation
|
|
73
97
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- **Pending changesets present**: opens (or updates) a version PR titled `Updating @scope/pi-my-extension to version X.Y.Z`.
|
|
77
|
-
- **Version PR merged**: publishes the package to npm and creates a GitHub release with a matching git tag.
|
|
78
|
-
|
|
79
|
-
Copy `.github/workflows/publish.yml` from `pi-extension-template` into the new repo. It uses `changesets/action@v1` under the hood.
|
|
98
|
+
Use the template publish workflow from `pi-extension-template`.
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
It handles:
|
|
82
101
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
102
|
+
- Pending changesets: opens or updates a version PR.
|
|
103
|
+
- Version PR merged: publishes to npm and creates a GitHub release.
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
Required secrets:
|
|
87
106
|
|
|
88
|
-
|
|
107
|
+
- `GITHUB_TOKEN`: provided by GitHub Actions.
|
|
108
|
+
- `NPM_TOKEN`: npm automation token with publish access.
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
Keep `NPM_CONFIG_PROVENANCE=true` and `id-token: write` for provenance.
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
2. Add the `NPM_TOKEN` secret to the repo (see above).
|
|
94
|
-
3. The first time the version PR is merged, the workflow publishes the package. npm will create the package entry automatically — no manual `npm publish` needed.
|
|
112
|
+
## First Publish
|
|
95
113
|
|
|
96
|
-
|
|
114
|
+
Before first publish:
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
pi install @scope/pi-my-extension
|
|
104
|
-
```
|
|
116
|
+
1. Set `"private": false`.
|
|
117
|
+
2. Set `"publishConfig": { "access": "public" }` for scoped public packages.
|
|
118
|
+
3. Add `NPM_TOKEN` to repo secrets.
|
|
119
|
+
4. Merge the first Changesets version PR.
|
|
105
120
|
|
|
106
|
-
|
|
121
|
+
npm creates the package entry on first publish. If the npm scope is new to your account, you may need to create the scope or publish once manually with `--access public`.
|
|
107
122
|
|
|
108
|
-
## Dependency
|
|
123
|
+
## Monorepo Dependency Rules
|
|
109
124
|
|
|
110
|
-
|
|
125
|
+
Public packages cannot depend on private workspace packages. Users installing from npm will not have those private packages.
|
|
111
126
|
|
|
112
|
-
|
|
127
|
+
When adding a dependency:
|
|
113
128
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
- `pnpm run check:public-deps` validates all dependencies
|
|
129
|
+
1. If it is a public workspace package, use `workspace:^` in the monorepo and make sure it is published.
|
|
130
|
+
2. If it is private, do not depend on it from a public package.
|
|
131
|
+
3. For external packages, use a normal npm range.
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
1. Check if the dependency is public (`"private": false` or `"publishConfig": { "access": "public" }`).
|
|
121
|
-
2. If the dependency is private, either make it public, make your package private, or remove the dependency.
|
|
133
|
+
Run the repo's public-dependency check when available, for example `pnpm run check:public-deps`.
|
|
122
134
|
|
|
123
135
|
## Pre-publish Checklist
|
|
124
136
|
|
|
125
|
-
- [ ] `
|
|
126
|
-
- [ ] `
|
|
127
|
-
- [ ] `
|
|
128
|
-
- [ ] `
|
|
129
|
-
- [ ]
|
|
130
|
-
- [ ] `
|
|
131
|
-
- [ ]
|
|
132
|
-
- [ ]
|
|
133
|
-
- [ ]
|
|
134
|
-
- [ ]
|
|
135
|
-
- [ ]
|
|
136
|
-
- [ ]
|
|
137
|
-
- [ ] README documents
|
|
138
|
-
- [ ]
|
|
137
|
+
- [ ] `private` is `false`.
|
|
138
|
+
- [ ] `publishConfig.access` is `public` for scoped public packages.
|
|
139
|
+
- [ ] `keywords` includes `pi-package`.
|
|
140
|
+
- [ ] `files` lists only shipped files users need.
|
|
141
|
+
- [ ] `pi.extensions`, `pi.skills`, `pi.prompts`, and `pi.themes` paths are correct.
|
|
142
|
+
- [ ] Demo `pi.video` or `pi.image` metadata is present when available.
|
|
143
|
+
- [ ] Imported Pi core packages are optional peers with `"*"`.
|
|
144
|
+
- [ ] Imported Pi core packages are exact dev dependencies for type checking.
|
|
145
|
+
- [ ] Third-party runtime packages are in `dependencies`.
|
|
146
|
+
- [ ] No private workspace dependencies in public packages.
|
|
147
|
+
- [ ] `prepare` is `[ -d .git ] && husky || true`, not bare `husky`.
|
|
148
|
+
- [ ] `check:lockfile` exists.
|
|
149
|
+
- [ ] README documents setup, tools, commands, providers, env vars, and limitations.
|
|
150
|
+
- [ ] Missing API keys are handled with notifications or disabled features, not crashes.
|
|
139
151
|
- [ ] `pnpm typecheck` and `pnpm lint` pass.
|
|
152
|
+
- [ ] `.github/workflows/publish.yml` is present if using CI publish.
|
|
153
|
+
- [ ] `NPM_TOKEN` is configured before relying on CI publish.
|