@aliou/pi-linkup 0.1.0 → 0.3.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.
@@ -4,6 +4,12 @@ on:
4
4
  push:
5
5
  branches:
6
6
  - main
7
+ workflow_dispatch:
8
+ inputs:
9
+ skip-checks:
10
+ description: "Skip lint and typecheck"
11
+ type: boolean
12
+ default: false
7
13
 
8
14
  concurrency:
9
15
  group: ${{ github.workflow }}-${{ github.ref }}
@@ -11,6 +17,7 @@ concurrency:
11
17
 
12
18
  jobs:
13
19
  check:
20
+ if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.skip-checks) }}
14
21
  runs-on: ubuntu-latest
15
22
  steps:
16
23
  - uses: actions/checkout@v4
@@ -34,6 +41,7 @@ jobs:
34
41
  publish:
35
42
  name: Publish
36
43
  needs: check
44
+ if: ${{ always() && (needs.check.result == 'success' || needs.check.result == 'skipped') }}
37
45
  runs-on: ubuntu-latest
38
46
  permissions:
39
47
  contents: write
package/AGENTS.md ADDED
@@ -0,0 +1,48 @@
1
+ # pi-linkup
2
+
3
+ Public Pi extension providing web search, answer, and fetch tools via the Linkup API. People could be using this, so consider backwards compatibility when making changes.
4
+
5
+ Pi is pre-1.0.0, so breaking changes can happen between Pi versions. This extension must stay up to date with Pi or things will break.
6
+
7
+ ## Stack
8
+
9
+ - TypeScript (strict mode)
10
+ - pnpm 10.26.1
11
+ - Biome for linting/formatting
12
+ - Changesets for versioning
13
+
14
+ ## Scripts
15
+
16
+ ```bash
17
+ pnpm typecheck # Type check
18
+ pnpm lint # Lint (runs on pre-commit)
19
+ pnpm format # Format
20
+ pnpm changeset # Create changeset for versioning
21
+ ```
22
+
23
+ ## Structure
24
+
25
+ ```
26
+ src/
27
+ index.ts # Extension entry, registers tools and commands
28
+ client.ts # Linkup API client
29
+ types.ts # Shared types
30
+ tools/ # Tool implementations
31
+ commands/ # Command implementations
32
+ skills/
33
+ linkup/SKILL.md # Skill docs for agents using this extension
34
+ ```
35
+
36
+ ## Conventions
37
+
38
+ - New tools: follow patterns in `src/tools/`
39
+ - API keys come from environment (`LINKUP_API_KEY`)
40
+ - Update `skills/linkup/SKILL.md` when tool behavior changes
41
+
42
+ ## Versioning
43
+
44
+ Uses changesets. Run `pnpm changeset` before committing user-facing changes.
45
+
46
+ - `patch`: bug fixes
47
+ - `minor`: new features/tools
48
+ - `major`: breaking changes
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @aliou/pi-linkup
2
+
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e57f6fe: Enhanced balance command to show remaining requests per operation type (standard search, deep search, fetch with/without JS) using a themed custom message renderer instead of a transient notification.
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 2307674: Update pi packages to 0.51.0. Adapt tool execute signatures to new parameter order.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-linkup",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/aliou/pi-linkup"
@@ -19,8 +19,8 @@
19
19
  "devDependencies": {
20
20
  "@biomejs/biome": "^2.3.13",
21
21
  "@changesets/cli": "^2.27.11",
22
- "@mariozechner/pi-coding-agent": "^0.50.1",
23
- "@mariozechner/pi-tui": "^0.50.1",
22
+ "@mariozechner/pi-coding-agent": "0.51.0",
23
+ "@mariozechner/pi-tui": "0.51.0",
24
24
  "@sinclair/typebox": "^0.34.48",
25
25
  "@types/node": "^25.0.10",
26
26
  "husky": "^9.1.7",
@@ -9,10 +9,12 @@ export function registerBalanceCommand(pi: ExtensionAPI) {
9
9
 
10
10
  try {
11
11
  const response = await client.getBalance();
12
- ctx.ui.notify(
13
- `Linkup Balance: ${response.balance.toFixed(2)} credits`,
14
- "info",
15
- );
12
+ pi.sendMessage({
13
+ customType: "linkup-balance",
14
+ content: `Linkup Balance: ${response.balance.toFixed(2)} credits`,
15
+ display: true,
16
+ details: { balance: response.balance },
17
+ });
16
18
  } catch (error) {
17
19
  const message =
18
20
  error instanceof Error ? error.message : "Unknown error";
@@ -0,0 +1,45 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { Box, Text } from "@mariozechner/pi-tui";
3
+ import { LINKUP_PRICING } from "../types";
4
+
5
+ export function registerBalanceRenderer(pi: ExtensionAPI) {
6
+ pi.registerMessageRenderer("linkup-balance", (message, _options, theme) => {
7
+ const details = message.details as { balance: number };
8
+ const balance = details.balance;
9
+
10
+ // Build header line
11
+ const header = [
12
+ theme.fg("accent", "Linkup"),
13
+ theme.fg("muted", " · "),
14
+ theme.fg("muted", `${balance} credits`),
15
+ ].join("");
16
+
17
+ // Calculate remaining requests for each operation type
18
+ const remaining = {
19
+ standardSearch: Math.floor(balance / LINKUP_PRICING.standardSearch),
20
+ deepSearch: Math.floor(balance / LINKUP_PRICING.deepSearch),
21
+ fetchNoJs: Math.floor(balance / LINKUP_PRICING.fetchNoJs),
22
+ fetchWithJs: Math.floor(balance / LINKUP_PRICING.fetchWithJs),
23
+ };
24
+
25
+ // Format number with locale string and ~ prefix
26
+ const fmt = (num: number): string => `~${num.toLocaleString()}`;
27
+
28
+ // Table rows with right-aligned numbers (padded to 7 chars)
29
+ const row = (num: number, label: string) =>
30
+ `${theme.fg("accent", fmt(num).padStart(7))} ${theme.fg("dim", label)}`;
31
+
32
+ const lines = [
33
+ header,
34
+ "",
35
+ row(remaining.standardSearch, "standard searches"),
36
+ row(remaining.deepSearch, "deep searches"),
37
+ row(remaining.fetchNoJs, "fetches (no JS)"),
38
+ row(remaining.fetchWithJs, "fetches (with JS)"),
39
+ ].join("\n");
40
+
41
+ const box = new Box(1, 1, (t) => theme.bg("customMessageBg", t));
42
+ box.addChild(new Text(lines, 0, 0));
43
+ return box;
44
+ });
45
+ }
@@ -0,0 +1,6 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { registerBalanceRenderer } from "./balance-renderer";
3
+
4
+ export function registerRenderers(pi: ExtensionAPI) {
5
+ registerBalanceRenderer(pi);
6
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import { registerBalanceCommand } from "./commands/balance";
3
+ import { registerRenderers } from "./components";
3
4
  import { registerWebAnswerTool } from "./tools/web-answer";
4
5
  import { registerWebFetchTool } from "./tools/web-fetch";
5
6
  import { registerWebSearchTool } from "./tools/web-search";
@@ -30,4 +31,7 @@ export default function (pi: ExtensionAPI) {
30
31
 
31
32
  // Register commands
32
33
  registerBalanceCommand(pi);
34
+
35
+ // Register renderers
36
+ registerRenderers(pi);
33
37
  }
@@ -31,7 +31,7 @@ export function registerWebAnswerTool(pi: ExtensionAPI) {
31
31
  ),
32
32
  }),
33
33
 
34
- async execute(_toolCallId, params, onUpdate, _ctx, _signal) {
34
+ async execute(_toolCallId, params, _signal, onUpdate, _ctx) {
35
35
  const client = getClient();
36
36
 
37
37
  try {
@@ -28,7 +28,7 @@ export function registerWebFetchTool(pi: ExtensionAPI) {
28
28
  ),
29
29
  }),
30
30
 
31
- async execute(_toolCallId, params, onUpdate, _ctx, _signal) {
31
+ async execute(_toolCallId, params, _signal, onUpdate, _ctx) {
32
32
  const client = getClient();
33
33
 
34
34
  try {
@@ -30,7 +30,7 @@ export function registerWebSearchTool(pi: ExtensionAPI) {
30
30
  ),
31
31
  }),
32
32
 
33
- async execute(_toolCallId, params, onUpdate, _ctx, _signal) {
33
+ async execute(_toolCallId, params, _signal, onUpdate, _ctx) {
34
34
  const client = getClient();
35
35
 
36
36
  try {
package/src/types.ts CHANGED
@@ -32,3 +32,14 @@ export interface LinkupErrorResponse {
32
32
  message?: string;
33
33
  };
34
34
  }
35
+
36
+ /**
37
+ * Credit cost per request by operation type.
38
+ * Source: https://docs.linkup.so/pages/documentation/development/pricing
39
+ */
40
+ export const LINKUP_PRICING = {
41
+ standardSearch: 0.005,
42
+ deepSearch: 0.05,
43
+ fetchNoJs: 0.001,
44
+ fetchWithJs: 0.005,
45
+ } as const;