@gotgenes/pi-anthropic-auth 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 +21 -0
- package/README.md +125 -0
- package/dist/anthropic-oauth.d.ts +8 -0
- package/dist/anthropic-oauth.js +24 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +16 -0
- package/dist/request-shaping.d.ts +1 -0
- package/dist/request-shaping.js +149 -0
- package/dist/system-prompt-shaping.d.ts +1 -0
- package/dist/system-prompt-shaping.js +21 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christopher D. Lasher
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# pi-anthropic-auth
|
|
2
|
+
|
|
3
|
+
Minimal Pi extension package for Anthropic Claude Pro/Max OAuth compatibility.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package minimally overrides Pi's built-in `anthropic` provider to improve Claude Pro/Max OAuth compatibility while preserving Pi's normal Anthropic behavior wherever possible.
|
|
8
|
+
|
|
9
|
+
The design goal is to preserve:
|
|
10
|
+
|
|
11
|
+
1. built-in provider name: `anthropic`
|
|
12
|
+
2. built-in model list
|
|
13
|
+
3. normal Anthropic API-key behavior
|
|
14
|
+
4. native `/login anthropic` UX
|
|
15
|
+
|
|
16
|
+
## Current Behavior
|
|
17
|
+
|
|
18
|
+
The extension currently does the following:
|
|
19
|
+
|
|
20
|
+
1. re-registers the built-in `anthropic` provider with only an `oauth` override
|
|
21
|
+
2. reuses Pi's native Anthropic login and refresh helpers from `@mariozechner/pi-ai/oauth`
|
|
22
|
+
3. preserves the old refresh token when refresh responses omit `refresh_token`
|
|
23
|
+
4. shapes OAuth Anthropic request payloads through `before_provider_request`
|
|
24
|
+
5. prepends the Anthropic billing/content-consistency header block to `system[]`
|
|
25
|
+
6. avoids adding `cache_control` to the injected billing block
|
|
26
|
+
7. normalizes assistant `[tool_use..., text]` ordering for Anthropic OAuth payloads
|
|
27
|
+
8. replaces Pi's default prompt body with a minimal neutral prompt for Anthropic OAuth when the default Pi harness prompt is detected, while preserving project context
|
|
28
|
+
|
|
29
|
+
The extension does not currently replace Pi's built-in Anthropic streaming transport.
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
1. `pnpm`
|
|
34
|
+
2. a local `pi` installation
|
|
35
|
+
3. Anthropic OAuth credentials configured through Pi
|
|
36
|
+
|
|
37
|
+
## Install Dependencies
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pnpm install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
Typecheck:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm run build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run tests:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm test
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Run both:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm run check
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Load In Pi
|
|
64
|
+
|
|
65
|
+
You can load the extension directly from the local source file:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pi -e /absolute/path/to/pi-anthropic-auth/src/index.ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pi -e /Users/chris/development/pi-anthropic-auth/src/index.ts
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Fast Repro Loop
|
|
78
|
+
|
|
79
|
+
The most useful non-interactive repro loop is:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pi \
|
|
83
|
+
--model anthropic/claude-sonnet-4-6 \
|
|
84
|
+
--no-session \
|
|
85
|
+
--tools read,grep,find,ls \
|
|
86
|
+
-e /Users/chris/development/pi-anthropic-auth/src/index.ts \
|
|
87
|
+
-p "How many lines are in @AGENTS.md ?"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
That path was used to validate:
|
|
91
|
+
|
|
92
|
+
1. simple prompts
|
|
93
|
+
2. tool use
|
|
94
|
+
3. multi-turn continuation
|
|
95
|
+
4. structured output
|
|
96
|
+
5. expired-token refresh
|
|
97
|
+
|
|
98
|
+
## Usage Notes
|
|
99
|
+
|
|
100
|
+
1. `/login anthropic` should continue using Pi's native Anthropic UX.
|
|
101
|
+
2. API-key Anthropic behavior should remain the baseline behavior.
|
|
102
|
+
3. The extension's compatibility logic is intended to affect only Anthropic OAuth requests.
|
|
103
|
+
|
|
104
|
+
## Project Skills
|
|
105
|
+
|
|
106
|
+
Project-local skills live in `.agents/skills/`:
|
|
107
|
+
|
|
108
|
+
1. `anthropic`: Anthropic OAuth debugging workflow and lessons learned
|
|
109
|
+
2. `pi-cli-repro`: repeatable `pi -p ... -e ...` repro workflow
|
|
110
|
+
3. `frontmatter`: Pi skill frontmatter template and rules
|
|
111
|
+
|
|
112
|
+
## Key Files
|
|
113
|
+
|
|
114
|
+
1. `src/index.ts`
|
|
115
|
+
2. `src/anthropic-oauth.ts`
|
|
116
|
+
3. `src/request-shaping.ts`
|
|
117
|
+
4. `src/system-prompt-shaping.ts`
|
|
118
|
+
5. `docs/plans/minimal-anthropic-override.md`
|
|
119
|
+
6. `docs/plans/gap-analysis-and-next-steps.md`
|
|
120
|
+
7. `AGENTS.md`
|
|
121
|
+
8. `.agents/skills/`
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@mariozechner/pi-ai";
|
|
2
|
+
export declare function mergeRefreshedCredentials(credentials: OAuthCredentials, refreshed: Partial<OAuthCredentials>): OAuthCredentials;
|
|
3
|
+
export declare const anthropicOAuthOverride: {
|
|
4
|
+
readonly name: "Anthropic (Claude Pro/Max)";
|
|
5
|
+
readonly login: (callbacks: OAuthLoginCallbacks) => Promise<OAuthCredentials>;
|
|
6
|
+
readonly refreshToken: (credentials: OAuthCredentials) => Promise<OAuthCredentials>;
|
|
7
|
+
readonly getApiKey: (credentials: OAuthCredentials) => string;
|
|
8
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { loginAnthropic, refreshAnthropicToken, } from "@mariozechner/pi-ai/oauth";
|
|
2
|
+
export function mergeRefreshedCredentials(credentials, refreshed) {
|
|
3
|
+
return {
|
|
4
|
+
...credentials,
|
|
5
|
+
...refreshed,
|
|
6
|
+
refresh: typeof refreshed.refresh === "string" &&
|
|
7
|
+
refreshed.refresh.trim().length > 0
|
|
8
|
+
? refreshed.refresh
|
|
9
|
+
: credentials.refresh,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export const anthropicOAuthOverride = {
|
|
13
|
+
name: "Anthropic (Claude Pro/Max)",
|
|
14
|
+
login(callbacks) {
|
|
15
|
+
return loginAnthropic(callbacks);
|
|
16
|
+
},
|
|
17
|
+
async refreshToken(credentials) {
|
|
18
|
+
const refreshed = await refreshAnthropicToken(credentials.refresh);
|
|
19
|
+
return mergeRefreshedCredentials(credentials, refreshed);
|
|
20
|
+
},
|
|
21
|
+
getApiKey(credentials) {
|
|
22
|
+
return credentials.access;
|
|
23
|
+
},
|
|
24
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { anthropicOAuthOverride } from "./anthropic-oauth.js";
|
|
2
|
+
import { shapeAnthropicOAuthPayload } from "./request-shaping.js";
|
|
3
|
+
import { shapeAnthropicOAuthSystemPrompt } from "./system-prompt-shaping.js";
|
|
4
|
+
export default function (pi) {
|
|
5
|
+
pi.registerProvider("anthropic", {
|
|
6
|
+
oauth: anthropicOAuthOverride,
|
|
7
|
+
});
|
|
8
|
+
pi.on("before_agent_start", (event) => {
|
|
9
|
+
return {
|
|
10
|
+
systemPrompt: shapeAnthropicOAuthSystemPrompt(event.systemPrompt),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
pi.on("before_provider_request", (event) => {
|
|
14
|
+
return shapeAnthropicOAuthPayload(event.payload);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function shapeAnthropicOAuthPayload(payload: unknown): unknown;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const ANTHROPIC_OAUTH_BETAS = ["claude-code-20250219", "oauth-2025-04-20"];
|
|
3
|
+
const BILLING_HEADER_SALT = "59cf53e54c78";
|
|
4
|
+
const BILLING_HEADER_POSITIONS = [4, 7, 20];
|
|
5
|
+
const CLAUDE_CODE_VERSION = "2.1.87";
|
|
6
|
+
const CLAUDE_CODE_ENTRYPOINT = "sdk-cli";
|
|
7
|
+
const CLAUDE_CODE_IDENTITY_PREFIX = "You are Claude Code, Anthropic's official CLI";
|
|
8
|
+
const PI_MINIMAL_ANTHROPIC_PROMPT_PREFIX = "You are an expert coding assistant.";
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function isAnthropicMessagesPayload(payload) {
|
|
13
|
+
return (isRecord(payload) &&
|
|
14
|
+
typeof payload.model === "string" &&
|
|
15
|
+
Array.isArray(payload.messages) &&
|
|
16
|
+
typeof payload.stream === "boolean");
|
|
17
|
+
}
|
|
18
|
+
function isOAuthAnthropicPayload(payload) {
|
|
19
|
+
if (!Array.isArray(payload.system)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return payload.system.some(hasOAuthAnthropicSystemMarker);
|
|
23
|
+
}
|
|
24
|
+
function hasOAuthAnthropicSystemMarker(block) {
|
|
25
|
+
if (!isRecord(block) ||
|
|
26
|
+
block.type !== "text" ||
|
|
27
|
+
typeof block.text !== "string") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return (block.text.includes(CLAUDE_CODE_IDENTITY_PREFIX) ||
|
|
31
|
+
block.text.includes("x-anthropic-billing-header:") ||
|
|
32
|
+
block.text.startsWith(PI_MINIMAL_ANTHROPIC_PROMPT_PREFIX));
|
|
33
|
+
}
|
|
34
|
+
function _hasClaudeCodeIdentity(block) {
|
|
35
|
+
return (isRecord(block) &&
|
|
36
|
+
block.type === "text" &&
|
|
37
|
+
typeof block.text === "string" &&
|
|
38
|
+
block.text.includes(CLAUDE_CODE_IDENTITY_PREFIX));
|
|
39
|
+
}
|
|
40
|
+
function getFirstUserText(messages) {
|
|
41
|
+
const firstUserMessage = messages.find((message) => message.role === "user");
|
|
42
|
+
if (!firstUserMessage)
|
|
43
|
+
return "";
|
|
44
|
+
if (typeof firstUserMessage.content === "string") {
|
|
45
|
+
return firstUserMessage.content;
|
|
46
|
+
}
|
|
47
|
+
if (!Array.isArray(firstUserMessage.content)) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
const firstTextBlock = firstUserMessage.content.find((block) => block?.type === "text" && typeof block.text === "string");
|
|
51
|
+
return typeof firstTextBlock?.text === "string" ? firstTextBlock.text : "";
|
|
52
|
+
}
|
|
53
|
+
function buildBillingHeaderValue(messages) {
|
|
54
|
+
const messageText = getFirstUserText(messages);
|
|
55
|
+
if (!messageText) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const cch = createHash("sha256")
|
|
59
|
+
.update(messageText)
|
|
60
|
+
.digest("hex")
|
|
61
|
+
.slice(0, 5);
|
|
62
|
+
const sampledCharacters = BILLING_HEADER_POSITIONS.map((index) => messageText[index] || "0").join("");
|
|
63
|
+
const suffix = createHash("sha256")
|
|
64
|
+
.update(`${BILLING_HEADER_SALT}${sampledCharacters}${CLAUDE_CODE_VERSION}`)
|
|
65
|
+
.digest("hex")
|
|
66
|
+
.slice(0, 3);
|
|
67
|
+
return [
|
|
68
|
+
"x-anthropic-billing-header:",
|
|
69
|
+
`cc_version=${CLAUDE_CODE_VERSION}.${suffix};`,
|
|
70
|
+
`cc_entrypoint=${CLAUDE_CODE_ENTRYPOINT};`,
|
|
71
|
+
`cch=${cch};`,
|
|
72
|
+
].join(" ");
|
|
73
|
+
}
|
|
74
|
+
function normalizeSystemBlock(block) {
|
|
75
|
+
if (typeof block === "string") {
|
|
76
|
+
return { type: "text", text: block };
|
|
77
|
+
}
|
|
78
|
+
if (isRecord(block) && typeof block.text === "string") {
|
|
79
|
+
return {
|
|
80
|
+
...block,
|
|
81
|
+
type: block.type === "text" ? "text" : "text",
|
|
82
|
+
text: block.text,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { type: "text", text: String(block ?? "") };
|
|
86
|
+
}
|
|
87
|
+
function prependBillingHeader(system, messages) {
|
|
88
|
+
const billingHeader = buildBillingHeaderValue(messages);
|
|
89
|
+
if (!billingHeader) {
|
|
90
|
+
return system;
|
|
91
|
+
}
|
|
92
|
+
const systemBlocks = Array.isArray(system)
|
|
93
|
+
? system.map(normalizeSystemBlock)
|
|
94
|
+
: system == null
|
|
95
|
+
? []
|
|
96
|
+
: [normalizeSystemBlock(system)];
|
|
97
|
+
if (systemBlocks.some((block) => block.text.includes("x-anthropic-billing-header:"))) {
|
|
98
|
+
return systemBlocks;
|
|
99
|
+
}
|
|
100
|
+
const billingBlock = { type: "text", text: billingHeader };
|
|
101
|
+
return [billingBlock, ...systemBlocks];
|
|
102
|
+
}
|
|
103
|
+
function mergeAnthropicBetas(betaHeader) {
|
|
104
|
+
const existing = (betaHeader ?? "")
|
|
105
|
+
.split(",")
|
|
106
|
+
.map((value) => value.trim())
|
|
107
|
+
.filter(Boolean);
|
|
108
|
+
return [...new Set([...ANTHROPIC_OAUTH_BETAS, ...existing])].join(",");
|
|
109
|
+
}
|
|
110
|
+
function splitAssistantToolUseTrailingContent(messages) {
|
|
111
|
+
return messages.flatMap((message) => {
|
|
112
|
+
if (message.role !== "assistant" || !Array.isArray(message.content)) {
|
|
113
|
+
return [message];
|
|
114
|
+
}
|
|
115
|
+
const firstToolUseIndex = message.content.findIndex((block) => block?.type === "tool_use");
|
|
116
|
+
if (firstToolUseIndex === -1) {
|
|
117
|
+
return [message];
|
|
118
|
+
}
|
|
119
|
+
const trailingBlocks = message.content.slice(firstToolUseIndex);
|
|
120
|
+
if (!trailingBlocks.some((block) => block?.type !== "tool_use")) {
|
|
121
|
+
return [message];
|
|
122
|
+
}
|
|
123
|
+
const nonToolUseBlocks = message.content.filter((block) => block?.type !== "tool_use");
|
|
124
|
+
const toolUseBlocks = message.content.filter((block) => block?.type === "tool_use");
|
|
125
|
+
return [
|
|
126
|
+
{ ...message, content: nonToolUseBlocks },
|
|
127
|
+
{ ...message, content: toolUseBlocks },
|
|
128
|
+
];
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export function shapeAnthropicOAuthPayload(payload) {
|
|
132
|
+
if (!isAnthropicMessagesPayload(payload)) {
|
|
133
|
+
return payload;
|
|
134
|
+
}
|
|
135
|
+
const messages = payload.messages;
|
|
136
|
+
if (!isOAuthAnthropicPayload(payload)) {
|
|
137
|
+
return payload;
|
|
138
|
+
}
|
|
139
|
+
const normalizedMessages = splitAssistantToolUseTrailingContent(messages);
|
|
140
|
+
const shapedPayload = {
|
|
141
|
+
...payload,
|
|
142
|
+
messages: normalizedMessages,
|
|
143
|
+
system: prependBillingHeader(payload.system, normalizedMessages),
|
|
144
|
+
};
|
|
145
|
+
if (typeof payload["anthropic-beta"] === "string") {
|
|
146
|
+
shapedPayload["anthropic-beta"] = mergeAnthropicBetas(payload["anthropic-beta"]);
|
|
147
|
+
}
|
|
148
|
+
return shapedPayload;
|
|
149
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function shapeAnthropicOAuthSystemPrompt(systemPrompt: string): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const PI_DEFAULT_PROMPT_PREFIX = "You are an expert coding assistant operating inside pi, a coding agent harness.";
|
|
2
|
+
const MINIMAL_ANTHROPIC_OAUTH_PROMPT = [
|
|
3
|
+
"You are an expert coding assistant.",
|
|
4
|
+
"Be concise and helpful.",
|
|
5
|
+
"Use the available tools to answer the user's request.",
|
|
6
|
+
"Show file paths clearly when working with files.",
|
|
7
|
+
].join("\n");
|
|
8
|
+
function findProjectContextStart(systemPrompt) {
|
|
9
|
+
const marker = "\n\n# Project Context\n\n";
|
|
10
|
+
return systemPrompt.indexOf(marker);
|
|
11
|
+
}
|
|
12
|
+
export function shapeAnthropicOAuthSystemPrompt(systemPrompt) {
|
|
13
|
+
if (!systemPrompt.includes(PI_DEFAULT_PROMPT_PREFIX)) {
|
|
14
|
+
return systemPrompt;
|
|
15
|
+
}
|
|
16
|
+
const projectContextStart = findProjectContextStart(systemPrompt);
|
|
17
|
+
if (projectContextStart === -1) {
|
|
18
|
+
return MINIMAL_ANTHROPIC_OAUTH_PROMPT;
|
|
19
|
+
}
|
|
20
|
+
return `${MINIMAL_ANTHROPIC_OAUTH_PROMPT}${systemPrompt.slice(projectContextStart)}`;
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gotgenes/pi-anthropic-auth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pi extension package for Anthropic OAuth compatibility",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Chris Lasher"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"pi",
|
|
21
|
+
"pi-package",
|
|
22
|
+
"anthropic",
|
|
23
|
+
"oauth"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/gotgenes/pi-anthropic-auth.git"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.build.json",
|
|
35
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"lint": "biome check .",
|
|
37
|
+
"lint:fix": "biome check --write .",
|
|
38
|
+
"lint:md": "markdownlint-cli2 '*.md'",
|
|
39
|
+
"lint:all": "pnpm run lint && pnpm run lint:md",
|
|
40
|
+
"format": "biome format --write .",
|
|
41
|
+
"test": "tsx --test test/**/*.test.ts"
|
|
42
|
+
},
|
|
43
|
+
"pi": {
|
|
44
|
+
"extensions": [
|
|
45
|
+
"./dist/index.js"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@anthropic-ai/sdk": "^0.52.0",
|
|
50
|
+
"@mariozechner/pi-ai": "^0.68.0",
|
|
51
|
+
"@mariozechner/pi-coding-agent": "^0.68.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@biomejs/biome": "^2.4.12",
|
|
55
|
+
"markdownlint-cli2": "^0.22.0",
|
|
56
|
+
"tsx": "^4.20.6",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
}
|
|
59
|
+
}
|