@timqi/pier 0.1.14 → 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.
- package/README.md +44 -22
- package/dist/agent/config.js +2 -2
- package/dist/agent/events.js +32 -3
- package/dist/agent/listing.js +14 -10
- package/dist/agent/packages.js +6 -10
- package/dist/agent/pi.js +113 -70
- package/dist/agent/roles.js +104 -0
- package/dist/agent/types.js +78 -0
- package/dist/boards/boards.js +42 -32
- package/dist/channels/chains.js +0 -4
- package/dist/channels/commands.js +7 -12
- package/dist/channels/config.js +27 -1
- package/dist/channels/control.js +5 -16
- package/dist/channels/conversations.js +4 -9
- package/dist/channels/lark-api.js +20 -26
- package/dist/channels/lark-outbound.js +21 -29
- package/dist/channels/lark-panel.js +1 -1
- package/dist/channels/lark.js +81 -39
- package/dist/channels/lines.js +33 -1
- package/dist/channels/panel.js +17 -107
- package/dist/channels/routes.js +11 -21
- package/dist/channels/runtime.js +79 -25
- package/dist/channels/slack-outbound.js +12 -14
- package/dist/channels/slack-panel.js +1 -1
- package/dist/channels/slack-render.js +1 -1
- package/dist/channels/slack-thread.js +65 -0
- package/dist/channels/slack.js +85 -124
- package/dist/channels/types.js +1 -1
- package/dist/cli.js +7 -2
- package/dist/config-sync.js +42 -37
- package/dist/core/chain.js +221 -0
- package/dist/core/identity.js +36 -13
- package/dist/core/inbound-file.js +11 -4
- package/dist/core/reply.js +111 -81
- package/dist/core/router.js +111 -118
- package/dist/core/search.js +65 -0
- package/dist/core/types.js +38 -77
- package/dist/db.js +41 -2
- package/dist/drain.js +1 -1
- package/dist/main.js +65 -40
- package/dist/settings.js +26 -19
- package/dist/socket.js +1 -0
- package/dist/tasks/agent.js +11 -5
- package/dist/tasks/callbacks.js +55 -5
- package/dist/tasks/cli.js +22 -11
- package/dist/tasks/definitions.js +30 -17
- package/dist/tasks/execution.js +11 -5
- package/dist/tasks/groups.js +5 -2
- package/dist/tasks/messages.js +7 -7
- package/dist/tasks/open-items.js +88 -0
- package/dist/tasks/operations.js +76 -39
- package/dist/tasks/outbox.js +19 -6
- package/dist/tasks/routes.js +19 -249
- package/dist/tasks/service.js +166 -34
- package/dist/tasks/store.js +123 -57
- package/dist/tasks/types.js +3 -0
- package/dist/tools.js +12 -5
- package/dist/web/auth.js +2 -2
- package/dist/web/config-sync.js +4 -5
- package/dist/web/config.js +9 -2
- package/dist/web/explorer.js +2 -2
- package/dist/web/fs.js +16 -1
- package/dist/web/instance.js +8 -5
- package/dist/web/packages.js +1 -1
- package/dist/web/passkeys.js +3 -3
- package/dist/web/providers.js +3 -3
- package/dist/web/public/assets/code-CWT27KH1.js +2 -0
- package/dist/web/public/assets/code-CWT27KH1.js.br +0 -0
- package/dist/web/public/assets/code-CWT27KH1.js.gz +0 -0
- package/dist/web/public/assets/explorer-C4DhXFCm.js +5 -0
- package/dist/web/public/assets/explorer-C4DhXFCm.js.br +0 -0
- package/dist/web/public/assets/explorer-C4DhXFCm.js.gz +0 -0
- package/dist/web/public/assets/highlight-CDSk2hRJ.js +72 -0
- package/dist/web/public/assets/highlight-CDSk2hRJ.js.br +0 -0
- package/dist/web/public/assets/highlight-CDSk2hRJ.js.gz +0 -0
- package/dist/web/public/assets/{hljs-tWqyD59G.js → hljs-BRz6a8Dq.js} +2 -2
- package/dist/web/public/assets/hljs-BRz6a8Dq.js.br +0 -0
- package/dist/web/public/assets/hljs-BRz6a8Dq.js.gz +0 -0
- package/dist/web/public/assets/index-D0ZYdweB.js +14 -0
- package/dist/web/public/assets/index-D0ZYdweB.js.br +0 -0
- package/dist/web/public/assets/index-D0ZYdweB.js.gz +0 -0
- package/dist/web/public/assets/index-JN0Ic8K5.css +2 -0
- package/dist/web/public/assets/index-JN0Ic8K5.css.br +0 -0
- package/dist/web/public/assets/index-JN0Ic8K5.css.gz +0 -0
- package/dist/web/public/assets/settings-DSTeuIyc.js +9 -0
- package/dist/web/public/assets/settings-DSTeuIyc.js.br +0 -0
- package/dist/web/public/assets/settings-DSTeuIyc.js.gz +0 -0
- package/dist/web/public/index.html +39 -109
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/manifest.webmanifest +0 -2
- package/dist/web/push.js +3 -3
- package/dist/web/server.js +89 -116
- package/dist/web/session-state.js +4 -40
- package/docs/deploy.md +5 -3
- package/package.json +2 -1
- package/skills/pier-boards/SKILL.md +14 -4
- package/skills/pier-help/SKILL.md +62 -23
- package/skills/pier-search/SKILL.md +27 -0
- package/skills/pier-tasks/SKILL.md +68 -18
- package/dist/channels/handoff.js +0 -94
- package/dist/web/public/assets/activity-Ds6fCHnb.js +0 -5
- package/dist/web/public/assets/activity-Ds6fCHnb.js.br +0 -0
- package/dist/web/public/assets/activity-Ds6fCHnb.js.gz +0 -0
- package/dist/web/public/assets/boards-BKCj6EwK.js +0 -1
- package/dist/web/public/assets/boards-BKCj6EwK.js.br +0 -0
- package/dist/web/public/assets/boards-BKCj6EwK.js.gz +0 -0
- package/dist/web/public/assets/explorer-DnTm975c.js +0 -4
- package/dist/web/public/assets/explorer-DnTm975c.js.br +0 -0
- package/dist/web/public/assets/explorer-DnTm975c.js.gz +0 -0
- package/dist/web/public/assets/hljs-tWqyD59G.js.br +0 -0
- package/dist/web/public/assets/hljs-tWqyD59G.js.gz +0 -0
- package/dist/web/public/assets/index-BQo-haPN.js +0 -85
- package/dist/web/public/assets/index-BQo-haPN.js.br +0 -0
- package/dist/web/public/assets/index-BQo-haPN.js.gz +0 -0
- package/dist/web/public/assets/index-DiHj0w1i.css +0 -2
- package/dist/web/public/assets/index-DiHj0w1i.css.br +0 -0
- package/dist/web/public/assets/index-DiHj0w1i.css.gz +0 -0
- package/dist/web/public/assets/runs-C_AthWcW.js +0 -1
- package/dist/web/public/assets/runs-C_AthWcW.js.br +0 -0
- package/dist/web/public/assets/runs-C_AthWcW.js.gz +0 -0
- package/dist/web/public/assets/settings-DDaAtFlc.js +0 -5
- package/dist/web/public/assets/settings-DDaAtFlc.js.br +0 -0
- package/dist/web/public/assets/settings-DDaAtFlc.js.gz +0 -0
- package/dist/web/public/assets/task-runs-0pvdITiV.js +0 -3
- package/dist/web/public/assets/task-runs-0pvdITiV.js.br +0 -0
- package/dist/web/public/assets/task-runs-0pvdITiV.js.gz +0 -0
- package/dist/web/public/assets/tasks-Bz29caHJ.js +0 -4
- package/dist/web/public/assets/tasks-Bz29caHJ.js.br +0 -0
- package/dist/web/public/assets/tasks-Bz29caHJ.js.gz +0 -0
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@ something is worth showing.
|
|
|
9
9
|
One instance, one account, your own machine. The agent runs shell commands in
|
|
10
10
|
directories you name — meant for a machine you own, not a shared host.
|
|
11
11
|
|
|
12
|
-
**Status: pre-release.**
|
|
13
|
-
|
|
14
|
-
reachable.
|
|
12
|
+
**Status: pre-release.** Releases may still change behaviour and the
|
|
13
|
+
database schema between versions; schema upgrades are one-way. Read
|
|
14
|
+
`docs/deploy.md` before putting it anywhere reachable.
|
|
15
15
|
|
|
16
16
|
## Requirements
|
|
17
17
|
|
|
@@ -30,15 +30,35 @@ pier serve
|
|
|
30
30
|
|
|
31
31
|
It listens on `127.0.0.1:3141` (`PORT`, `HOST`) and keeps everything under
|
|
32
32
|
`~/.pier` (`PIER_HOME`): one SQLite database, the boards it serves, the
|
|
33
|
-
`master.key` that seals stored credentials,
|
|
34
|
-
|
|
35
|
-
otherwise).
|
|
33
|
+
`master.key` that seals stored credentials, the conversation's memory
|
|
34
|
+
(`~/.pier/home`), and the Pi runtime with its session transcripts
|
|
35
|
+
(`~/.pier/pi`, unless `PI_CODING_AGENT_DIR` says otherwise).
|
|
36
36
|
|
|
37
37
|
**The first start generates a password and prints it once.** Lost it?
|
|
38
38
|
`sqlite3 ~/.pier/db/pier.db 'DELETE FROM auth'` and restart. Open
|
|
39
|
-
`http://localhost:3141`, sign in; **Console → Settings**
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
`http://localhost:3141`, sign in; **Console → Settings** has Models, Agent,
|
|
40
|
+
Channels, Vault, Tasks, Boards, Instance (public URL, accent, notifications,
|
|
41
|
+
reload) and Security (master key, password, passkeys, signed-in devices).
|
|
42
|
+
|
|
43
|
+
## How the conversation works
|
|
44
|
+
|
|
45
|
+
The web chat is one conversation, Pier. Its current session answers,
|
|
46
|
+
remembers and dispatches; it runs in `~/.pier/home`, which holds
|
|
47
|
+
memory only: `MEMORY.md` (one-line facts) and daily notes in
|
|
48
|
+
`memory/YYYY-MM-DD.md`.
|
|
49
|
+
|
|
50
|
+
- Real work is delegated with `pier task run`: a worker for a small task, a
|
|
51
|
+
lead for a feature, which designs with you or builds with its own workers.
|
|
52
|
+
Each is its own session; its result comes back to Pier as a callback.
|
|
53
|
+
- After an idle hour, or past 60K tokens, the next message starts a fresh
|
|
54
|
+
session, seeded with the memory, the open items, recent runs and the last
|
|
55
|
+
exchanges. The old transcripts stay readable.
|
|
56
|
+
- `/status` lists what is open, `/new` starts a fresh session now, `/stop` stops
|
|
57
|
+
the reply in progress, `/skills` lists the skills. `%` works in place of
|
|
58
|
+
`/` (`%status`), because Slack's client eats an unregistered `/`.
|
|
59
|
+
- One IM DM, on Lark or Slack, can be the conversation: Console → Settings →
|
|
60
|
+
Channels, the DM's row, "This DM is the conversation". Pier then
|
|
61
|
+
answers there, from the phone; other chats keep one session per thread.
|
|
42
62
|
|
|
43
63
|
## Configure Pi
|
|
44
64
|
|
|
@@ -58,14 +78,15 @@ Console → Settings:
|
|
|
58
78
|
- **Models** — endpoints, API-key/OAuth login, pinned models. Credentials are
|
|
59
79
|
sealed in Pier's SQLite database, never written back to `models.json`.
|
|
60
80
|
**Test** sends one real request and shows the body as sent and the reply.
|
|
61
|
-
- **Agent** — edits `SYSTEM.md`, `AGENTS.md
|
|
62
|
-
|
|
63
|
-
`.pi/skills` and `.pi/extensions` listed).
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
- **Agent** — edits `SYSTEM.md`, `AGENTS.md` and the `models.json`
|
|
82
|
+
structure, globally or per project scope (with that project's
|
|
83
|
+
`.pi/skills` and `.pi/extensions` listed); `settings.json` is Pier's and
|
|
84
|
+
shown read-only. Changes apply when a session next opens; saving recycles
|
|
85
|
+
idle sessions; **Settings → Instance → Reload** does the same for files
|
|
86
|
+
changed elsewhere. Also here: packages (Pi's registry, **Add package**),
|
|
87
|
+
Pier's own skills as the built-in `pier` package (`pier web search|fetch`
|
|
88
|
+
reaches the web through the provider's hosted tools — Anthropic or OpenAI,
|
|
89
|
+
no key of your own), **Browse files**, and the managed CLI tools (`rtk`, `rg`, `fd`, `wt`, `jq`, or your own as a
|
|
69
90
|
[ubix](https://github.com/timqi/ubix) block), installed into
|
|
70
91
|
`~/.pier/tools/bin`, first on every session's PATH beside the `pier` shim
|
|
71
92
|
Pier writes there at start.
|
|
@@ -79,7 +100,7 @@ store and renamed `auth.json.imported`; literal keys in `models.json` likewise
|
|
|
79
100
|
(`models.json.imported`). The editor rejects plaintext keys or header values.
|
|
80
101
|
Pi's provider environment variables are inherited from the Pier process; a
|
|
81
102
|
systemd service does not inherit your shell, so put non-secret settings in a
|
|
82
|
-
unit override and API keys in
|
|
103
|
+
unit override and API keys in Settings → Models.
|
|
83
104
|
|
|
84
105
|
## Run it as a service
|
|
85
106
|
|
|
@@ -118,8 +139,9 @@ npm test # vitest
|
|
|
118
139
|
|
|
119
140
|
## Releases
|
|
120
141
|
|
|
121
|
-
Pier asks `registry.npmjs.org` at boot and every 30 minutes; the
|
|
122
|
-
|
|
142
|
+
Pier asks `registry.npmjs.org` at boot and every 30 minutes; the version in
|
|
143
|
+
the Settings header turns into a badge when a release is out (`Pier <version>
|
|
144
|
+
is out`) and opens a panel: source link,
|
|
123
145
|
**Update now**, **Update automatically** (idle instance only). Both drain first
|
|
124
146
|
and hand the install to the updater unit; off systemd the panel says `pier
|
|
125
147
|
update`. The updater writes `~/.pier/db/backups/pier.db.release-<version>.bak`
|
|
@@ -128,10 +150,10 @@ update`. The updater writes `~/.pier/db/backups/pier.db.release-<version>.bak`
|
|
|
128
150
|
|
|
129
151
|
`main` is the only development line. `just release [patch|minor|major]` checks,
|
|
130
152
|
tags and pushes; the tag builds and publishes to npm and a GitHub Release. The
|
|
131
|
-
|
|
153
|
+
version shown is `package.json`'s.
|
|
132
154
|
|
|
133
155
|
## License
|
|
134
156
|
|
|
135
157
|
[AGPL-3.0-only](LICENSE). Run it, change it, deploy it. If you offer a modified
|
|
136
158
|
Pier to other people over a network, they are entitled to your source — the
|
|
137
|
-
version in the
|
|
159
|
+
version in the Settings header links to this repository for exactly that reason.
|
package/dist/agent/config.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { promises as fs } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import {
|
|
7
|
+
import { isThinkingLevel } from "../core/types.js";
|
|
8
|
+
import { isProviderApi, SNAPSHOT_FILES, validateEndpoint, validateProviderSetup } from "./types.js";
|
|
8
9
|
import { pierPath } from "../paths.js";
|
|
9
10
|
import { mergeSnapshotProviders, normalizeAgentSnapshot, snapshotProviders } from "./config-sync.js";
|
|
10
11
|
const GLOBAL_FILES = ["SYSTEM.md", "AGENTS.md", "settings.json", "models.json"];
|
|
@@ -13,7 +14,6 @@ const PROJECT_FILES = ["AGENTS.md"];
|
|
|
13
14
|
// go through writeDefaults, and are the only fields a snapshot carries. The
|
|
14
15
|
// Console shows the file and never edits it; the rest of it is machine-local.
|
|
15
16
|
const READONLY_FILES = ["settings.json"];
|
|
16
|
-
const SNAPSHOT_FILES = ["SYSTEM.md", "AGENTS.md", "models.json", "settings.json"];
|
|
17
17
|
/** What a first boot writes (docs/deploy.md says why each key): the list the
|
|
18
18
|
* Console installs into, Pi's attribution headers off — a server is not a
|
|
19
19
|
* person to survey — and a retry budget of minutes, because an unattended
|
package/dist/agent/events.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Pure Pi-event → SessionEventPayload translation. Structurally typed: no Pi
|
|
2
2
|
// imports, so it is unit-testable without Pi and Pi types never leak past the
|
|
3
3
|
// seam. The golden table in events.test.ts is the mapping's spec.
|
|
4
|
-
import { isThinkingLevel, MAX_STEP_OUTPUT } from "../core/types.js";
|
|
4
|
+
import { isChatCommand, isThinkingLevel, MAX_STEP_OUTPUT } from "../core/types.js";
|
|
5
5
|
/** An assistant message that calls a tool is work in progress, not a reply. */
|
|
6
6
|
export const hasToolCalls = (message) => Array.isArray(message?.content) && message.content.some((part) => part.type === "toolCall");
|
|
7
7
|
export function textOf(content) {
|
|
@@ -20,14 +20,36 @@ function systemOrigin(message) {
|
|
|
20
20
|
const value = message.details;
|
|
21
21
|
if (!value || typeof value !== "object")
|
|
22
22
|
return null;
|
|
23
|
-
const { source: raw, ...origin } = value;
|
|
23
|
+
const { source: raw, cwd, ...origin } = value;
|
|
24
|
+
if (origin.kind === "session-seed") {
|
|
25
|
+
const { reason, previousSessionId } = origin;
|
|
26
|
+
return (reason === "first" || reason === "idle" || reason === "lost" || reason === "full" || reason === "new") &&
|
|
27
|
+
(previousSessionId === null || typeof previousSessionId === "string")
|
|
28
|
+
? { kind: "session-seed", reason, previousSessionId }
|
|
29
|
+
: null;
|
|
30
|
+
}
|
|
31
|
+
if (origin.kind === "chat-command") {
|
|
32
|
+
const { command, sessions } = origin;
|
|
33
|
+
if (!isChatCommand(command))
|
|
34
|
+
return null;
|
|
35
|
+
// A malformed map costs the card its links, not the card.
|
|
36
|
+
const links = sessions && typeof sessions === "object" && !Array.isArray(sessions) && Object.values(sessions).every((v) => typeof v === "string")
|
|
37
|
+
? { sessions: sessions }
|
|
38
|
+
: {};
|
|
39
|
+
return { kind: "chat-command", command, ...links };
|
|
40
|
+
}
|
|
24
41
|
if (typeof origin.taskId !== "string" ||
|
|
25
42
|
typeof origin.runId !== "string" ||
|
|
26
43
|
(origin.sourceSessionId !== null && typeof origin.sourceSessionId !== "string"))
|
|
27
44
|
return null;
|
|
28
45
|
// A half-valid `source` drawn by the card is an `undefined` in a chip.
|
|
29
46
|
const source = inputSource(raw);
|
|
30
|
-
|
|
47
|
+
// A callback's, and relative paths resolve against it, so only an absolute one is kept.
|
|
48
|
+
const shape = {
|
|
49
|
+
...origin,
|
|
50
|
+
...(source ? { source } : {}),
|
|
51
|
+
...(origin.kind === "task-callback" && typeof cwd === "string" && cwd.startsWith("/") ? { cwd } : {}),
|
|
52
|
+
};
|
|
31
53
|
if (origin.kind === "task-delegation" || origin.kind === "task-callback") {
|
|
32
54
|
return shape;
|
|
33
55
|
}
|
|
@@ -168,6 +190,13 @@ export function toChatTurns(messages) {
|
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
const text = textOf(m.content);
|
|
193
|
+
// Only the attempt that ended the turn: one Pi retried past is no failure.
|
|
194
|
+
if (m.role === "assistant" && m.stopReason === "error" && messages[i + 1]?.role !== "assistant") {
|
|
195
|
+
flush("assistant", hasTools ? "" : text, turnMetaAt(messages, i), undefined, m.timestamp);
|
|
196
|
+
turns[turns.length - 1].error = m.errorMessage || "unknown agent error";
|
|
197
|
+
candidate = undefined;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
171
200
|
// Tool-bearing messages are intermediate work, even when they include text.
|
|
172
201
|
if (!text || hasTools)
|
|
173
202
|
continue;
|
package/dist/agent/listing.js
CHANGED
|
@@ -80,16 +80,15 @@ function fold(acc, line, clean) {
|
|
|
80
80
|
}
|
|
81
81
|
/** A picker, not a results page. */
|
|
82
82
|
const SEARCH_LIMIT = 8;
|
|
83
|
-
/**
|
|
84
|
-
* of 64 tokens; the default 12 fits one word. */
|
|
83
|
+
/** Characters of context a snippet keeps around its first match. */
|
|
85
84
|
const SNIPPET_CHARS = 64;
|
|
86
85
|
/** Whitespace splits a query: a space is "and this too", not a character to
|
|
87
86
|
* find. A term is quoted where it has to be, so a phrase is still findable. */
|
|
88
87
|
const termsOf = (query) => query.split(/\s+/).filter(Boolean);
|
|
89
|
-
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
88
|
+
/** Cut here rather than by FTS `snippet()`, which can only cut the indexed
|
|
89
|
+
* text, markup and all. Each match delimited \u0001 … \u0002, `…` for a cut;
|
|
90
|
+
* every term is marked where it first appears inside the window. A match only
|
|
91
|
+
* inside stripped markup leaves the message's opening. */
|
|
93
92
|
function around(text, terms) {
|
|
94
93
|
const lower = text.toLowerCase();
|
|
95
94
|
const found = terms
|
|
@@ -98,7 +97,7 @@ function around(text, terms) {
|
|
|
98
97
|
.sort((a, b) => a.at - b.at);
|
|
99
98
|
const first = found[0];
|
|
100
99
|
if (!first)
|
|
101
|
-
return text.slice(0, SNIPPET_CHARS);
|
|
100
|
+
return text.length > SNIPPET_CHARS ? `${text.slice(0, SNIPPET_CHARS)}…` : text;
|
|
102
101
|
const start = Math.max(0, first.at - SNIPPET_CHARS / 2);
|
|
103
102
|
const end = Math.min(text.length, first.at + first.length + SNIPPET_CHARS / 2);
|
|
104
103
|
let out = start ? "…" : "";
|
|
@@ -115,14 +114,19 @@ function around(text, terms) {
|
|
|
115
114
|
export class IndexedListing {
|
|
116
115
|
dir;
|
|
117
116
|
clean;
|
|
117
|
+
plain;
|
|
118
118
|
#db;
|
|
119
119
|
#statements;
|
|
120
120
|
constructor(dir = join(defaultAgentDir(), "sessions"), db,
|
|
121
121
|
/** Handed in rather than imported: the header rule is core's, and agent/
|
|
122
122
|
* does not import core at runtime. */
|
|
123
|
-
clean = (text) => text
|
|
123
|
+
clean = (text) => text,
|
|
124
|
+
/** Chat markup off what was said, before a snippet is cut (core/reply.ts):
|
|
125
|
+
* applied at search time, so rows indexed before it are covered too. */
|
|
126
|
+
plain = (text) => text) {
|
|
124
127
|
this.dir = dir;
|
|
125
128
|
this.clean = clean;
|
|
129
|
+
this.plain = plain;
|
|
126
130
|
this.#db = db;
|
|
127
131
|
}
|
|
128
132
|
/** Opened on the first scan: building a factory must not open the database. */
|
|
@@ -231,7 +235,7 @@ export class IndexedListing {
|
|
|
231
235
|
return [];
|
|
232
236
|
const rows = terms.every((term) => [...term].length >= 3)
|
|
233
237
|
// Quoted: a term is a string to find, never FTS syntax.
|
|
234
|
-
? sql(`SELECT session_id, role, at,
|
|
238
|
+
? sql(`SELECT session_id, role, at, text
|
|
235
239
|
FROM session_fts WHERE text MATCH ? ORDER BY bm25(session_fts), at DESC`).iterate(terms.map((term) => `"${term.replaceAll('"', '""')}"`).join(" AND "))
|
|
236
240
|
: sql(`SELECT session_id, role, at, text FROM session_fts WHERE ${terms.map(() => "text LIKE ? ESCAPE '\\'").join(" AND ")} ORDER BY at DESC`).iterate(...terms.map((term) => `%${term.replaceAll(/[\\%_]/g, "\\$&")}%`));
|
|
237
241
|
const hits = [];
|
|
@@ -245,7 +249,7 @@ export class IndexedListing {
|
|
|
245
249
|
sessionId: row.session_id,
|
|
246
250
|
role: row.role,
|
|
247
251
|
at: row.at,
|
|
248
|
-
snippet:
|
|
252
|
+
snippet: around(this.plain(row.text), terms),
|
|
249
253
|
});
|
|
250
254
|
if (hits.length >= limit)
|
|
251
255
|
break;
|
package/dist/agent/packages.js
CHANGED
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
import { existsSync, promises as fs } from "node:fs";
|
|
6
6
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
7
7
|
import { DefaultPackageManager, loadSkills, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
import { PackageError, } from "
|
|
8
|
+
import { PackageError, } from "./types.js";
|
|
9
9
|
import { logger } from "../log.js";
|
|
10
10
|
import { defaultAgentDir } from "./config.js";
|
|
11
11
|
const log = logger("packages");
|
|
12
12
|
/** Set by how long a released fix may sit unnoticed, like update.ts. */
|
|
13
13
|
const CHECK_EVERY_MS = 24 * 60 * 60_000;
|
|
14
14
|
const KINDS = { extensions: "extension", skills: "skill" };
|
|
15
|
-
/** Written by `rtk init -g --agent pi` (the rtk block's `post_install` hook):
|
|
16
|
-
*
|
|
15
|
+
/** Written by `rtk init -g --agent pi` (the rtk block's `post_install` hook): the rtk tool's, so it
|
|
16
|
+
* is left out of `local` — the Console switches it as that tool, and a settings.json pattern would fight it. */
|
|
17
17
|
const RTK_FILE = join("extensions", "rtk.ts");
|
|
18
|
-
const RTK_STATE = "installed by the rtk tool";
|
|
19
18
|
export const kindOf = (source) => source.startsWith("npm:") ? "npm" : /^(git:|(https?|ssh|git):\/\/)/.test(source) ? "git" : "path";
|
|
20
19
|
/** A trailing `@ref` on an npm or git source; the leading `@` of a scope is not one. */
|
|
21
20
|
export const pinnedRef = (source) => {
|
|
@@ -120,13 +119,12 @@ export class PiPackageStore {
|
|
|
120
119
|
row("local", "local", "global", this.agentDir);
|
|
121
120
|
for (const key of Object.keys(KINDS)) {
|
|
122
121
|
for (const r of resolved[key]) {
|
|
122
|
+
if (r.path === rtkPath)
|
|
123
|
+
continue;
|
|
123
124
|
const own = r.metadata.origin === "top-level";
|
|
124
125
|
const scope = r.metadata.scope === "project" ? "project" : "global";
|
|
125
|
-
const resource = resourceRow(KINDS[key], r);
|
|
126
|
-
if (r.path === rtkPath)
|
|
127
|
-
Object.assign(resource, { state: RTK_STATE, locked: true });
|
|
128
126
|
row(own ? "local" : r.metadata.source, own ? "local" : kindOf(r.metadata.source), scope, r.metadata.baseDir ?? null)
|
|
129
|
-
.resources.push(
|
|
127
|
+
.resources.push(resourceRow(KINDS[key], r));
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
// Configured but unresolved (not installed, or empty): still a row.
|
|
@@ -277,8 +275,6 @@ export class PiPackageStore {
|
|
|
277
275
|
async setEnabled(change) {
|
|
278
276
|
const { source, kind, path, enabled, cwd } = change;
|
|
279
277
|
const { pkg, resource } = await this.#find(change);
|
|
280
|
-
if (resource.locked)
|
|
281
|
-
throw new PackageError("refused", `${resource.name} is ${resource.state} — its switch is under Tools`);
|
|
282
278
|
if (pkg.kind === "pier") {
|
|
283
279
|
this.pier.settings.setSkillsOff(withName(this.pier.settings.get().skillsOff, resource.name, !enabled));
|
|
284
280
|
}
|