@gavin-hu/my-pi-agent 0.18.0 → 0.19.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 +52 -44
- package/README.zh-CN.md +46 -41
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +344 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +446 -404
- package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
- package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
- package/docs/extensions/file-browse.md +554 -0
- package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
- package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
- package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
- package/{extensions/job/README.md → docs/extensions/job.md} +61 -50
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +390 -317
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
- package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
- package/docs/extensions/session-bus.md +285 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +226 -0
- package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
- package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +258 -162
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +115 -72
- package/extensions/board/commands.ts +87 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +72 -0
- package/extensions/board/index.ts +40 -0
- package/extensions/board/input.ts +54 -0
- package/extensions/board/join.ts +42 -0
- package/extensions/board/page.ts +312 -0
- package/extensions/board/result.ts +99 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +75 -0
- package/extensions/board/seat.ts +29 -0
- package/extensions/board/snapshot.ts +41 -0
- package/extensions/board/tools.ts +279 -0
- package/extensions/board/transport/router.ts +172 -0
- package/extensions/board/transport/server.ts +149 -0
- package/extensions/board/types.ts +54 -0
- package/extensions/board/view/actions.ts +306 -0
- package/extensions/board/view/client.ts +41 -0
- package/extensions/board/view/keys.ts +121 -0
- package/extensions/board/view/live.ts +397 -0
- package/extensions/board/view/page.ts +158 -0
- package/extensions/board/view/render.ts +496 -0
- package/extensions/board/view/styles.ts +211 -0
- package/extensions/board/view/team.ts +305 -0
- package/extensions/board/writes.ts +99 -0
- package/extensions/bytefly/panel/commands.ts +40 -7
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +3 -20
- package/extensions/bytefly/panel/tui/settings-view.ts +1 -1
- package/extensions/bytefly/picker/discovery.ts +0 -2
- package/extensions/bytefly/picker/types.ts +0 -2
- package/extensions/bytefly/update/check.ts +39 -0
- package/extensions/bytefly/update/format.ts +15 -0
- package/extensions/bytefly/update/register.ts +73 -34
- package/extensions/bytefly/update/state.ts +6 -3
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/{files.ts → files/files.ts} +1 -14
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
- package/extensions/file-browse/index.ts +4 -4
- package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
- package/extensions/file-browse/{router.ts → transport/router.ts} +21 -15
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
- package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
- package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
- package/extensions/file-browse/{html.ts → view/html.ts} +14 -12
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +7 -7
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +1 -1
- package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
- package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
- package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
- package/extensions/file-read/schema.ts +2 -1
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +21 -16
- package/extensions/plan/feedback.ts +147 -38
- package/extensions/plan/index.ts +6 -4
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +8 -11
- package/extensions/plan/policy.ts +20 -12
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +21 -9
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/tools/exit.ts +11 -95
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +0 -8
- package/extensions/plan/web-client.ts +1 -1
- package/extensions/plan/web-page.ts +0 -7
- package/extensions/plan/web-router.ts +3 -57
- package/extensions/plan/web-share-session.ts +24 -30
- package/extensions/plan/web-streams.ts +14 -14
- package/extensions/plan/web-tokens.ts +2 -2
- package/extensions/plan/web.ts +66 -120
- package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
- package/extensions/rewind/{review.ts → commands/review.ts} +13 -13
- package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
- package/extensions/rewind/{config.ts → config/config.ts} +1 -1
- package/extensions/rewind/index.ts +3 -3
- package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
- package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
- package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
- package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
- package/extensions/rewind/{format.ts → text/format.ts} +3 -3
- package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
- package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
- package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
- package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
- package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
- package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
- package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
- package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
- package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
- package/extensions/rewind/{status.ts → view/status.ts} +4 -4
- package/extensions/session-bus/commands.ts +17 -69
- package/extensions/session-bus/format.ts +22 -45
- package/extensions/session-bus/index.ts +13 -7
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +32 -4
- package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
- package/extensions/session-bus/protocol.ts +19 -49
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +16 -51
- package/extensions/session-bus/rows.ts +4 -2
- package/extensions/session-bus/runtime.ts +129 -238
- package/extensions/session-bus/store.ts +42 -6
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +4 -4
- package/extensions/session-bus/types.ts +13 -13
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +112 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +105 -0
- package/extensions/team/index.ts +33 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +246 -0
- package/extensions/team/types.ts +48 -0
- package/extensions/web-access/fetch/page.ts +4 -1
- package/extensions/web-access/fetch/structured.ts +447 -0
- package/extensions/web-access/fetch/tool.ts +2 -1
- package/extensions/web-access/search/tool.ts +2 -1
- package/extensions/wechat/bridge.ts +76 -92
- package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
- package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
- package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
- package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
- package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
- package/extensions/wechat/dialog/channels.ts +136 -0
- package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
- package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
- package/extensions/wechat/index.ts +10 -10
- package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
- package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
- package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
- package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
- package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
- package/extensions/wechat/{state.ts → state/state.ts} +14 -2
- package/extensions/wechat/{format.ts → text/format.ts} +3 -3
- package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
- package/extensions/wechat/tools.ts +29 -12
- package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
- package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
- package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
- package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
- package/extensions/wechat/transport/outbound.ts +142 -0
- package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
- package/extensions/wechat/types.ts +10 -1
- package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
- package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
- package/extensions/worktree/guard.ts +3 -5
- package/lib/README.md +8 -6
- package/lib/board/card.ts +290 -0
- package/lib/board/links.ts +117 -0
- package/lib/board/page-pointer.ts +256 -0
- package/lib/board/round-text.ts +72 -0
- package/lib/board/round.ts +89 -0
- package/lib/board/rules.ts +304 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +310 -0
- package/lib/board/view.ts +150 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +13 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +234 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +110 -0
- package/lib/http/token.ts +38 -0
- package/lib/policy.ts +12 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +130 -0
- package/lib/session-tool.ts +184 -0
- package/lib/tool-names.ts +25 -2
- package/lib/tui/glyphs.ts +2 -0
- package/lib/web/README.md +5 -2
- package/lib/web/components.ts +5 -0
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +26 -14
- package/skills/next/SKILL.md +4 -3
- package/skills/project-docs/SKILL.md +133 -0
- package/skills/project-docs/references/audit.md +131 -0
- package/skills/project-docs/references/changelog.md +105 -0
- package/skills/project-docs/references/discovery.md +127 -0
- package/skills/project-docs/references/readme.md +80 -0
- package/skills/project-docs/references/release.md +84 -0
- package/skills/project-docs/references/roadmap.md +93 -0
- package/skills/project-docs/scripts/state.ts +316 -0
- package/skills/refactor/SKILL.md +100 -125
- package/skills/refactor/references/handoff.md +35 -0
- package/skills/refactor/references/signals.md +54 -0
- package/skills/refactor/scripts/inventory.ts +587 -0
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/plan/split.ts +0 -87
- package/extensions/session-bus/README.md +0 -247
- package/extensions/session-bus/team.ts +0 -102
- package/extensions/wechat/outbound.ts +0 -100
- package/lib/plans/sections.ts +0 -172
- package/lib/plans/setting.ts +0 -52
- package/lib/plans/split.ts +0 -215
- package/lib/plans/store.ts +0 -19
- package/lib/plans/team.ts +0 -151
- package/skills/changelog/SKILL.md +0 -148
- package/skills/parallel-dev/SKILL.md +0 -150
- /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
- /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
- /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
- /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
- /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
- /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
- /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
- /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
- /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
- /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
- /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `session` tool, as the extensions that are not `session-bus` call it.
|
|
3
|
+
*
|
|
4
|
+
* `board` and `team` are the user's surface for the team; the bus is the transport
|
|
5
|
+
* underneath, and neither may import it (`test/structure.test.ts`). So the one place
|
|
6
|
+
* that turns a call into a typed answer lives here, next to the tool's name in
|
|
7
|
+
* `lib/tool-names.ts` — two copies would be two copies of the details-shape
|
|
8
|
+
* knowledge, and a bus that renamed a field would be broken in one of them.
|
|
9
|
+
*
|
|
10
|
+
* There are two transports, and the difference is the context, not the caller's
|
|
11
|
+
* choice: `join` travels as an in-process request on `pi.events` because a *command*
|
|
12
|
+
* context has no `ctx.executeTool` — the same constraint `lib/plan-entry.ts` exists
|
|
13
|
+
* for — while `peers` and `send` are nested tool calls, which only ever run inside a
|
|
14
|
+
* tool. One function either way, so a caller never has to know which it is holding.
|
|
15
|
+
*
|
|
16
|
+
* Every function returns a result and none throws: "the bus is not loaded", "a plan
|
|
17
|
+
* is open", and "that label is taken" are all answers a caller reports, not reasons
|
|
18
|
+
* to abandon a board write or a card operation that has nothing to do with the bus.
|
|
19
|
+
*
|
|
20
|
+
* This module is the *caller* side only. Reading the bus's records from disk is
|
|
21
|
+
* `session-roster.ts`, which is what works when the bus is not loaded at all.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { ExtensionAPI, ExtensionContext, ExtensionToolContext } from "@earendil-works/pi-coding-agent";
|
|
25
|
+
import { messageOf } from "./errors.ts";
|
|
26
|
+
import { watchingOf } from "./session-roster.ts";
|
|
27
|
+
import { SESSION_TOOL } from "./tool-names.ts";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The `session send` text limit, which is also the longest handoff or offer.
|
|
31
|
+
*
|
|
32
|
+
* It is copied from the bus's protocol rather than imported — the dependency runs one
|
|
33
|
+
* way — and it lives here so `team` and `board` clip to one number instead of two.
|
|
34
|
+
*/
|
|
35
|
+
export const MAX_MESSAGE_TEXT = 4_000;
|
|
36
|
+
|
|
37
|
+
/** Whether a session is listening for mail, and whether it raises a turn. */
|
|
38
|
+
export interface Watching {
|
|
39
|
+
armed: boolean;
|
|
40
|
+
wake: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** One session the bus lists. */
|
|
44
|
+
export interface Peer {
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
/** Absent when the record does not say, which is not the same as "not listening". */
|
|
48
|
+
listening?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** What a join did: the label it landed on, and whether mail will reach the session. */
|
|
52
|
+
export type JoinResult = { ok: true; label: string; watching: Watching } | { ok: false; error: string };
|
|
53
|
+
|
|
54
|
+
/** EventBus channel on which a caller asks `session-bus` to join the team for it. */
|
|
55
|
+
export const TEAM_JOIN_REQUEST = "my-pi-agent/team-join/request";
|
|
56
|
+
|
|
57
|
+
/** What a caller sends on {@link TEAM_JOIN_REQUEST}. */
|
|
58
|
+
export interface TeamJoinRequest {
|
|
59
|
+
/** The caller's context; the bus starts and names through it. */
|
|
60
|
+
ctx: ExtensionContext;
|
|
61
|
+
/** What to join as and how to listen. */
|
|
62
|
+
input: { label?: string; wake?: boolean; listen?: boolean };
|
|
63
|
+
/** Called once, when the bus has answered. */
|
|
64
|
+
respond(result: JoinResult): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** What a send did. */
|
|
68
|
+
export type SendResult = { ok: true; delivered: string[] } | { ok: false; error: string };
|
|
69
|
+
|
|
70
|
+
/** One nested call's answer: the details on success, the sentence on failure. */
|
|
71
|
+
type Answer = { details: Record<string, unknown> } | { error: string };
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Call the `session` tool once.
|
|
75
|
+
*
|
|
76
|
+
* A nested call reports failure in its result rather than by rejecting; a rejection
|
|
77
|
+
* is still caught, because a bus that is not registered at all throws before the
|
|
78
|
+
* tool runs and the caller deserves the sentence rather than an exception.
|
|
79
|
+
*/
|
|
80
|
+
async function call(ctx: ExtensionToolContext, args: Record<string, unknown>): Promise<Answer> {
|
|
81
|
+
const outcome = await ctx.executeTool(SESSION_TOOL, args).catch((error: unknown) => ({
|
|
82
|
+
isError: true as const,
|
|
83
|
+
result: { content: [{ type: "text", text: messageOf(error) }], details: undefined },
|
|
84
|
+
}));
|
|
85
|
+
if (outcome.isError) return { error: nestedMessage(outcome.result) };
|
|
86
|
+
const details = outcome.result.details;
|
|
87
|
+
return { details: typeof details === "object" && details !== null ? (details as Record<string, unknown>) : {} };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The message a failed nested call carries, or a sentence that says only that it failed. */
|
|
91
|
+
function nestedMessage(result: { content?: Array<{ type: string; text?: string }> } | undefined): string {
|
|
92
|
+
const text = result?.content?.find((block) => block.type === "text")?.text;
|
|
93
|
+
return text !== undefined && text !== "" ? text.replace(/^Error:\s*/, "") : "the session bus did not answer.";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* How an extension joins the team, whatever kind of context it is holding.
|
|
98
|
+
*
|
|
99
|
+
* Two extensions ask — `board` when the page opens or a card is worked, `team` for
|
|
100
|
+
* `/team join` — and both hold a command or a tool context, so the request is the one
|
|
101
|
+
* transport that works from either.
|
|
102
|
+
*/
|
|
103
|
+
export interface TeamJoin {
|
|
104
|
+
/** Join the team, naming this session when `label` is given. Never throws. */
|
|
105
|
+
join(ctx: ExtensionContext, label?: string): Promise<JoinResult>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The production join, bound to one extension's `pi`. */
|
|
109
|
+
export function createTeamJoin(pi: Pick<ExtensionAPI, "events" | "getAllTools">): TeamJoin {
|
|
110
|
+
return {
|
|
111
|
+
join: (ctx, label) => join(pi, ctx, label === undefined ? {} : { label }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Join the team: on the bus, named when a label is given, and listening for mail.
|
|
117
|
+
*
|
|
118
|
+
* One call for all three, because that is one act — see `join` in
|
|
119
|
+
* `extensions/session-bus` — and one transport for both kinds of caller: the request
|
|
120
|
+
* travels on `pi.events` and the bus answers it.
|
|
121
|
+
*
|
|
122
|
+
* With no bus loaded there is no listener, so the answer is the sentence the caller
|
|
123
|
+
* reports rather than a promise that never settles: the `session` tool's presence is
|
|
124
|
+
* the same check `lib/plan-entry.ts` makes, and the listener always answers.
|
|
125
|
+
*/
|
|
126
|
+
export async function join(
|
|
127
|
+
pi: Pick<ExtensionAPI, "events" | "getAllTools">,
|
|
128
|
+
ctx: ExtensionContext,
|
|
129
|
+
input: { label?: string; wake?: boolean; listen?: boolean } = {},
|
|
130
|
+
): Promise<JoinResult> {
|
|
131
|
+
if (!pi.getAllTools().some((tool) => tool.name === SESSION_TOOL)) {
|
|
132
|
+
return { ok: false, error: "the session bus is not loaded" };
|
|
133
|
+
}
|
|
134
|
+
return new Promise<JoinResult>((resolve) => {
|
|
135
|
+
let settled = false;
|
|
136
|
+
const respond = (result: JoinResult): void => {
|
|
137
|
+
if (settled) return;
|
|
138
|
+
settled = true;
|
|
139
|
+
resolve(result);
|
|
140
|
+
};
|
|
141
|
+
pi.events.emit(TEAM_JOIN_REQUEST, { ctx, input, respond } satisfies TeamJoinRequest);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Every session the bus lists, or `undefined` when it cannot answer.
|
|
147
|
+
*
|
|
148
|
+
* Named `listPeers` rather than `peers` so it cannot be mistaken for a caller's own
|
|
149
|
+
* formatting helper — `wechat`'s doctor has one — and a caller reads the verb.
|
|
150
|
+
*
|
|
151
|
+
* A caller renders "the record does not say" differently from "nobody is here", so a
|
|
152
|
+
* failed read is not an empty list.
|
|
153
|
+
*/
|
|
154
|
+
export async function listPeers(ctx: ExtensionToolContext): Promise<Peer[] | undefined> {
|
|
155
|
+
const answer = await call(ctx, { action: "status" });
|
|
156
|
+
if ("error" in answer) return undefined;
|
|
157
|
+
const status = answer.details.status;
|
|
158
|
+
if (typeof status !== "object" || status === null) return undefined;
|
|
159
|
+
const sessions = (status as { sessions?: unknown }).sessions;
|
|
160
|
+
if (!Array.isArray(sessions)) return undefined;
|
|
161
|
+
const peers: Peer[] = [];
|
|
162
|
+
for (const entry of sessions) {
|
|
163
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
164
|
+
const { id, label, watching } = entry as Record<string, unknown>;
|
|
165
|
+
if (typeof id !== "string" || typeof label !== "string") continue;
|
|
166
|
+
const state = watchingOf(watching);
|
|
167
|
+
peers.push({ id, label, ...(state === undefined ? {} : { listening: state.armed }) });
|
|
168
|
+
}
|
|
169
|
+
return peers;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Send one message, and report the labels a message file was written for. */
|
|
173
|
+
export async function send(
|
|
174
|
+
ctx: ExtensionToolContext,
|
|
175
|
+
input: { kind: "handoff" | "event"; to: string; text: string },
|
|
176
|
+
): Promise<SendResult> {
|
|
177
|
+
const answer = await call(ctx, { action: "send", kind: input.kind, to: input.to, text: input.text });
|
|
178
|
+
if ("error" in answer) return { ok: false, error: answer.error };
|
|
179
|
+
const delivered = answer.details.delivered;
|
|
180
|
+
return {
|
|
181
|
+
ok: true,
|
|
182
|
+
delivered: Array.isArray(delivered) ? delivered.filter((label): label is string => typeof label === "string") : [],
|
|
183
|
+
};
|
|
184
|
+
}
|
package/lib/tool-names.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Tool names are otherwise owned by the extension that registers the tool, and
|
|
5
5
|
* no extension imports another's modules (see this directory's README). When an
|
|
6
6
|
* orchestrating tool calls another extension's tool through `ctx.executeTool`,
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* reads another extension's registered parameter schema, lists the name in a
|
|
8
|
+
* read-only policy, or passes it on a child's `--tools` allowlist, the shared
|
|
9
|
+
* name lives here so a rename breaks the build on both sides instead of silently
|
|
9
10
|
* disabling the integration.
|
|
10
11
|
*/
|
|
11
12
|
|
|
@@ -31,9 +32,31 @@ export const SUBAGENT_TOOL = "subagent";
|
|
|
31
32
|
*/
|
|
32
33
|
export const ASK_USER_QUESTION_TOOL = "ask_user_question";
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* The `session` tool. `session-bus` registers it, and `team` calls it through
|
|
37
|
+
* `ctx.executeTool` to send a handoff, so a rename breaks both sides.
|
|
38
|
+
*/
|
|
39
|
+
export const SESSION_TOOL = "session";
|
|
40
|
+
|
|
34
41
|
/**
|
|
35
42
|
* Parameter on the `subagent` tool that forces every spawned agent to a
|
|
36
43
|
* read-only tool set. Plan mode sets it on every call while planning, and
|
|
37
44
|
* refuses a `subagent` whose schema does not declare it.
|
|
38
45
|
*/
|
|
39
46
|
export const SUBAGENT_READ_ONLY_PARAM = "readOnly";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The `read_doc` tool. `file-read` registers it, and the plan, rewind, and
|
|
50
|
+
* subagent reader lists carry the name, so the document reader is one reader
|
|
51
|
+
* everywhere instead of three spellings.
|
|
52
|
+
*/
|
|
53
|
+
export const READ_DOC_TOOL = "read_doc";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The `web_search` tool. `web-access` registers it; plan's and subagent's reader
|
|
57
|
+
* lists carry the name, so a rename cannot silently drop the web reader.
|
|
58
|
+
*/
|
|
59
|
+
export const WEB_SEARCH_TOOL = "web_search";
|
|
60
|
+
|
|
61
|
+
/** The `web_fetch` tool, shared the same way as {@link WEB_SEARCH_TOOL}. */
|
|
62
|
+
export const WEB_FETCH_TOOL = "web_fetch";
|
package/lib/tui/glyphs.ts
CHANGED
|
@@ -30,6 +30,7 @@ export const GLYPHS = {
|
|
|
30
30
|
jobsFailure: "✗",
|
|
31
31
|
stopped: "⊘",
|
|
32
32
|
browse: "◉",
|
|
33
|
+
board: "▤",
|
|
33
34
|
delegation: "↳",
|
|
34
35
|
wechat: "✆",
|
|
35
36
|
gaugeFull: "▰",
|
|
@@ -71,6 +72,7 @@ export const STATUS_KEYS = {
|
|
|
71
72
|
watch: "watch",
|
|
72
73
|
watchMatches: "watch-matches",
|
|
73
74
|
browse: "browse",
|
|
75
|
+
board: "board",
|
|
74
76
|
rewind: "rewind",
|
|
75
77
|
agents: "agents",
|
|
76
78
|
wechat: "wechat",
|
package/lib/web/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The tokens, base rules, app shell, `bf-*` components, HTML atoms, and client
|
|
4
4
|
runtime the two loopback pages draw with: the plan review
|
|
5
5
|
([`../../extensions/plan/web-page.ts`](../../extensions/plan/web-page.ts)) and
|
|
6
|
-
file browse ([`../../extensions/file-browse/styles.ts`](../../extensions/file-browse/styles.ts)).
|
|
6
|
+
file browse ([`../../extensions/file-browse/view/styles.ts`](../../extensions/file-browse/view/styles.ts)).
|
|
7
7
|
The module-by-module inventory is the `web/` row of [`../README.md`](../README.md);
|
|
8
8
|
this file is the conventions those modules exist to make easy.
|
|
9
9
|
|
|
@@ -34,7 +34,10 @@ are computed rather than mapped:
|
|
|
34
34
|
- `--scrim` and `--shadow` — per appearance, because a dimming layer and a lifted
|
|
35
35
|
panel need their own alpha rather than a palette entry.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
`--raised` is the one derived neutral: `--surface` mixed 6% (dark) or 3% (light)
|
|
38
|
+
toward `--text`, for a block that has to read as a plane above the page rather than
|
|
39
|
+
as an outline — the light step is smaller because a darker accent on a lighter step
|
|
40
|
+
falls below AA. Components read tokens only: no literal hex or `rgba()` in `components.ts`, and a
|
|
38
41
|
test fails one. Contrast is asserted, not assumed — text, muted, and accent at
|
|
39
42
|
4.5:1 on both surfaces, the primary label at 4.5:1 on the accent fill, `--dim` at
|
|
40
43
|
3:1 because it carries the line-number gutter, and the status colours at 3:1.
|
package/lib/web/components.ts
CHANGED
|
@@ -18,6 +18,8 @@ export const WEB_COMPONENTS_CSS = `.bf-brand{display:inline-flex;align-items:cen
|
|
|
18
18
|
.bf-btn:hover{border-color:var(--accent);color:var(--accent);text-decoration:none}
|
|
19
19
|
.bf-btn[aria-pressed="true"]{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
|
|
20
20
|
.bf-btn--primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
|
|
21
|
+
.bf-btn--danger{border-color:var(--danger);color:var(--danger)}
|
|
22
|
+
.bf-btn--danger:hover{border-color:var(--danger);background:var(--danger);color:var(--on-accent)}
|
|
21
23
|
.bf-btn--primary:hover{filter:brightness(1.06);color:var(--on-accent)}
|
|
22
24
|
.bf-btn--ghost{border-color:transparent;background:none;padding:0;font:inherit;color:inherit}
|
|
23
25
|
.bf-btn--ghost:hover{color:var(--accent)}
|
|
@@ -36,6 +38,9 @@ export const WEB_COMPONENTS_CSS = `.bf-brand{display:inline-flex;align-items:cen
|
|
|
36
38
|
.bf-segmented__item[aria-current="page"],.bf-segmented__item[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent)}
|
|
37
39
|
.bf-input{border:0;background:transparent;color:var(--text);font:var(--fs-sm) var(--font-mono);width:100%;outline:none}
|
|
38
40
|
.bf-field{display:flex;align-items:center;gap:var(--space-1);background:var(--canvas);border:1px solid var(--border);border-radius:var(--radius-md);padding:var(--space-1) var(--space-2);color:var(--muted);font-size:var(--fs-sm)}
|
|
41
|
+
/* The field is the control: ringing the input inside it drew a border within a border. */
|
|
42
|
+
.bf-field:focus-within{outline:var(--focus-ring) solid var(--accent);outline-offset:2px}
|
|
43
|
+
.bf-field .bf-input:focus-visible{outline:none}
|
|
39
44
|
.bf-textarea{width:100%;min-height:72px;padding:var(--space-2);font:inherit;color:var(--text);background:var(--canvas);border:1px solid var(--border);border-radius:var(--radius-md);resize:vertical}
|
|
40
45
|
.bf-panel{min-width:0;overflow:auto;overscroll-behavior:contain}
|
|
41
46
|
.bf-panel__summary{display:none}
|
package/lib/web/runtime.ts
CHANGED
|
@@ -123,9 +123,15 @@ export function dialogScript(options: DialogOptions): string {
|
|
|
123
123
|
// A button inside a closed dialog has no boxes, so the rects are the test that
|
|
124
124
|
// survives a hidden attribute, a collapsed panel, and a stylesheet swap alike.
|
|
125
125
|
function focusables() {
|
|
126
|
-
|
|
126
|
+
var nodes = Array.prototype.filter.call(dialog.querySelectorAll('${FOCUSABLE}'), function (node) {
|
|
127
127
|
return node.getClientRects && node.getClientRects().length > 0;
|
|
128
128
|
});
|
|
129
|
+
// A panel that names itself focusable is where focus lands on open, so the close
|
|
130
|
+
// button is not the first thing a dialog says with its ring. It stays first in the
|
|
131
|
+
// list, which is what keeps Shift+Tab wrapping inside instead of escaping the scrim.
|
|
132
|
+
var panel = dialog.querySelector('[role="dialog"][tabindex]');
|
|
133
|
+
if (panel && panel.getClientRects && panel.getClientRects().length > 0) nodes.unshift(panel);
|
|
134
|
+
return nodes;
|
|
129
135
|
}
|
|
130
136
|
function toggle(force) {
|
|
131
137
|
var open = force === undefined ? dialog.hidden : force;
|
package/lib/web/tokens.ts
CHANGED
|
@@ -21,8 +21,8 @@ interface ThemeJson {
|
|
|
21
21
|
const DARK = darkTheme as unknown as ThemeJson;
|
|
22
22
|
const LIGHT = lightTheme as unknown as ThemeJson;
|
|
23
23
|
|
|
24
|
-
/** Web custom property -> Bytefly theme var. `--accent-soft`, `--on-accent`,
|
|
25
|
-
* and `--shadow` are derived, so no appearance-only colour enters the kit. */
|
|
24
|
+
/** Web custom property -> Bytefly theme var. `--accent-soft`, `--raised`, `--on-accent`,
|
|
25
|
+
* `--scrim`, and `--shadow` are derived, so no appearance-only colour enters the kit. */
|
|
26
26
|
const VAR_TOKENS: ReadonlyArray<readonly [string, string]> = [
|
|
27
27
|
["--canvas", "canvas"],
|
|
28
28
|
["--surface", "canvasSubtle"],
|
|
@@ -73,6 +73,16 @@ function resolve(theme: ThemeJson, value: string | number): string {
|
|
|
73
73
|
return theme.vars[value] ?? value;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** `#rrggbb` mixed toward another colour: `t` of the way from `a` to `b`. */
|
|
77
|
+
function mix(a: string, b: string, t: number): string {
|
|
78
|
+
const channel = (hex: string, at: number): number => Number.parseInt(hex.replace("#", "").slice(at, at + 2), 16);
|
|
79
|
+
const between = (x: number, y: number): number => Math.round(x + (y - x) * t);
|
|
80
|
+
const r = between(channel(a, 0), channel(b, 0));
|
|
81
|
+
const g = between(channel(a, 2), channel(b, 2));
|
|
82
|
+
const bl = between(channel(a, 4), channel(b, 4));
|
|
83
|
+
return `#${[r, g, bl].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
/** `#rrggbb` (or `#rgb`) plus alpha -> `rgba(r,g,b,a)`. */
|
|
77
87
|
function rgba(hex: string, alpha: number): string {
|
|
78
88
|
const raw = hex.replace("#", "");
|
|
@@ -94,6 +104,15 @@ function rgba(hex: string, alpha: number): string {
|
|
|
94
104
|
* lifts a panel off it, so each appearance needs its own alpha rather than the
|
|
95
105
|
* theme's colours.
|
|
96
106
|
*/
|
|
107
|
+
/**
|
|
108
|
+
* How far `--raised` sits above `--surface`: enough to see, not enough to shout.
|
|
109
|
+
*
|
|
110
|
+
* The light theme takes a smaller step because its accent is darker: at the dark
|
|
111
|
+
* step, `--accent` on `--raised` falls to 4.34:1 and the owner chips on a raised row
|
|
112
|
+
* stop being readable.
|
|
113
|
+
*/
|
|
114
|
+
const RAISED_STEP: Record<"dark" | "light", number> = { dark: 0.06, light: 0.03 };
|
|
115
|
+
|
|
97
116
|
const OVERLAYS: Record<"dark" | "light", { scrim: string; shadow: string }> = {
|
|
98
117
|
dark: { scrim: "rgba(0,0,0,.5)", shadow: "0 8px 24px rgba(0,0,0,.4)" },
|
|
99
118
|
light: { scrim: "rgba(15,23,42,.3)", shadow: "0 8px 24px rgba(15,23,42,.14)" },
|
|
@@ -104,6 +123,10 @@ function paletteOf(theme: ThemeJson, appearance: "dark" | "light", accentAlpha:
|
|
|
104
123
|
const palette: Record<string, string> = {};
|
|
105
124
|
for (const [token, name] of VAR_TOKENS) palette[token] = theme.vars[name];
|
|
106
125
|
palette["--accent-soft"] = rgba(palette["--accent"] ?? "#000000", accentAlpha);
|
|
126
|
+
// One step above `--surface`, for a block that must read as a plane rather than an
|
|
127
|
+
// outline: the theme offers only two neutral fills, and a surface one hairline
|
|
128
|
+
// lighter than the page is what made a dashboard look like a wireframe.
|
|
129
|
+
palette["--raised"] = mix(palette["--surface"] ?? "#000000", palette["--text"] ?? "#ffffff", RAISED_STEP[appearance]);
|
|
107
130
|
// A label on the accent fill is the canvas colour: a light blue button takes dark
|
|
108
131
|
// text, and the same derivation gives white on the light theme's darker blue.
|
|
109
132
|
palette["--on-accent"] = palette["--canvas"];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gavin-hu/my-pi-agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Personal Pi customizations, packaged as a Pi package: worktree isolation and rewind snapshots, question, todo, goal, plan, subagent, job, watch, web-access, file-browse, file-open, file-read, check, memory, context, session-bus, wechat, and bytefly.",
|
|
3
|
+
"version": "0.19.0",
|
|
4
|
+
"description": "Personal Pi customizations, packaged as a Pi package: worktree isolation and rewind snapshots, question, todo, goal, plan, subagent, job, watch, web-access, file-browse, file-open, file-read, check, memory, context, session-bus, board, team, wechat, and bytefly.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi",
|
|
@@ -49,6 +49,12 @@
|
|
|
49
49
|
"excel",
|
|
50
50
|
"read_doc",
|
|
51
51
|
"memory",
|
|
52
|
+
"board",
|
|
53
|
+
"cards",
|
|
54
|
+
"backlog",
|
|
55
|
+
"team",
|
|
56
|
+
"handoff",
|
|
57
|
+
"assign",
|
|
52
58
|
"cross-session",
|
|
53
59
|
"context",
|
|
54
60
|
"compaction",
|
|
@@ -74,6 +80,7 @@
|
|
|
74
80
|
},
|
|
75
81
|
"files": [
|
|
76
82
|
"extensions/**",
|
|
83
|
+
"docs/extensions/**",
|
|
77
84
|
"lib/**",
|
|
78
85
|
"skills/**",
|
|
79
86
|
"themes/**",
|
|
@@ -101,6 +108,8 @@
|
|
|
101
108
|
"./extensions/file-open/index.ts",
|
|
102
109
|
"./extensions/memory/index.ts",
|
|
103
110
|
"./extensions/context/index.ts",
|
|
111
|
+
"./extensions/board/index.ts",
|
|
112
|
+
"./extensions/team/index.ts",
|
|
104
113
|
"./extensions/session-bus/index.ts",
|
|
105
114
|
"./extensions/wechat/index.ts",
|
|
106
115
|
"./extensions/bytefly/index.ts"
|
|
@@ -111,7 +120,7 @@
|
|
|
111
120
|
"themes": [
|
|
112
121
|
"./themes/*.json"
|
|
113
122
|
],
|
|
114
|
-
"image": "https://raw.githubusercontent.com/gavin-hu/my-pi-agent/main/assets/chrome.png"
|
|
123
|
+
"image": "https://raw.githubusercontent.com/gavin-hu/my-pi-agent/main/assets/screens/chrome.png"
|
|
115
124
|
},
|
|
116
125
|
"scripts": {
|
|
117
126
|
"test": "bun test --parallel=4 --timeout 30000",
|
|
@@ -127,12 +136,15 @@
|
|
|
127
136
|
"config:migrate": "bun run scripts/migrate-config.ts",
|
|
128
137
|
"compat:versions": "bun run scripts/peer-versions.ts",
|
|
129
138
|
"bench:render": "bun run scripts/render-bench.ts",
|
|
130
|
-
"screenshot:
|
|
131
|
-
"screenshot:
|
|
132
|
-
"screenshot:
|
|
133
|
-
"screenshot:
|
|
134
|
-
"screenshot:
|
|
135
|
-
"
|
|
139
|
+
"screenshot:board": "bun run scripts/shots/board.ts",
|
|
140
|
+
"screenshot:cards": "bun run scripts/shots/cards.ts",
|
|
141
|
+
"screenshot:file-browse": "bun run scripts/shots/file-browse.ts",
|
|
142
|
+
"screenshot:plan": "bun run scripts/shots/plan-review.ts",
|
|
143
|
+
"screenshot:tools": "bun run tools:render gallery assets/screens/tool-renderings.png",
|
|
144
|
+
"screenshot:tool-pngs": "bun run tools:render tools assets/tools",
|
|
145
|
+
"probe:wechat": "bun run scripts/wechat-poll.ts",
|
|
146
|
+
"screenshots": "bun run scripts/screenshots.ts",
|
|
147
|
+
"tools:render": "bun run scripts/tools/index.ts"
|
|
136
148
|
},
|
|
137
149
|
"dependencies": {
|
|
138
150
|
"exceljs": "^4.4.0",
|
|
@@ -153,15 +165,15 @@
|
|
|
153
165
|
},
|
|
154
166
|
"devDependencies": {
|
|
155
167
|
"@biomejs/biome": "^2.5.15",
|
|
156
|
-
"@earendil-works/pi-agent-core": "^1.0
|
|
157
|
-
"@earendil-works/pi-ai": "^1.0
|
|
158
|
-
"@earendil-works/pi-coding-agent": "^1.0
|
|
159
|
-
"@earendil-works/pi-tui": "^1.0
|
|
168
|
+
"@earendil-works/pi-agent-core": "^1.4.0",
|
|
169
|
+
"@earendil-works/pi-ai": "^1.4.0",
|
|
170
|
+
"@earendil-works/pi-coding-agent": "^1.4.0",
|
|
171
|
+
"@earendil-works/pi-tui": "^1.4.0",
|
|
160
172
|
"@types/bun": "latest",
|
|
161
173
|
"@types/qrcode": "^1.5.5",
|
|
162
174
|
"get-east-asian-width": "^1.6.0",
|
|
163
175
|
"playwright-core": "^1.64.1",
|
|
164
|
-
"typebox": "^1.3
|
|
176
|
+
"typebox": "^1.4.3",
|
|
165
177
|
"typescript": "^5.6.0"
|
|
166
178
|
}
|
|
167
179
|
}
|
package/skills/next/SKILL.md
CHANGED
|
@@ -16,9 +16,10 @@ The conversation decides where the candidates come from (section 3); the request
|
|
|
16
16
|
decides only what to ask and where to look.
|
|
17
17
|
|
|
18
18
|
Two sibling skills, where they are installed, own a narrower question: an audit
|
|
19
|
-
of *where to refactor* is `refactor`, and *
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
of *where to refactor* is `refactor`, and *whether a document still tells the
|
|
20
|
+
truth* — the changelog, the roadmap, the front door — is `project-docs`. When the
|
|
21
|
+
request is already scoped to one of those, name the skill that answers it and
|
|
22
|
+
hand over, instead of returning a generic list.
|
|
22
23
|
|
|
23
24
|
A next-steps block that the host draws itself is not this skill's output: that is
|
|
24
25
|
the reply's suggested follow-up, a turn-level hint. This skill inspects state, so
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: project-docs
|
|
3
|
+
description: "Keep a project's documents true to its tree: a read-only audit of the front door, the roadmap, the contributor path, and the changelog against the code and against each other, then one mode per act that writes — record a changelog bullet in the same commit as the change, curate a release section from the project's own rules, delete a shipped roadmap item and renumber, and sync the README front door. Use when a doc claim looks stale, before a release, or when a change lands owing an entry; prioritisation stays with `next` and structure with `refactor`."
|
|
4
|
+
compatibility: "Bun for scripts/state.ts; git for the roadmap and the audit."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Keep the docs true to the tree
|
|
8
|
+
|
|
9
|
+
Goal: the documents a reader trusts — the front door, the plan of record, the record
|
|
10
|
+
of what shipped, and the contributor path — so a claim the tree no longer meets is
|
|
11
|
+
found, and each act that needs writing has one owner. This is a procedure, not a
|
|
12
|
+
second policy: the project's instructions and release document win, and where they
|
|
13
|
+
are silent this file says ask.
|
|
14
|
+
|
|
15
|
+
A bare `/skill:project-docs` reads and reports. Nothing is written until a mode is
|
|
16
|
+
named.
|
|
17
|
+
|
|
18
|
+
## Pick the mode
|
|
19
|
+
|
|
20
|
+
| The words after `/skill:project-docs` | Mode | Owns | Writes | Follow |
|
|
21
|
+
|---|---|---|---|---|
|
|
22
|
+
| nothing | audit | every document, read-only | no | [`references/audit.md`](./references/audit.md) |
|
|
23
|
+
| `changelog` | changelog | the unreleased window of the changelog | yes | [`references/changelog.md`](./references/changelog.md) |
|
|
24
|
+
| `release <version>` | release | one version's section and link refs | yes | [`references/release.md`](./references/release.md) |
|
|
25
|
+
| `roadmap` | roadmap | items, ids, and buckets | yes | [`references/roadmap.md`](./references/roadmap.md) |
|
|
26
|
+
| `readme` | readme | the front door and its mirror | yes | [`references/readme.md`](./references/readme.md) |
|
|
27
|
+
| anything else | refuse, and name the five | — | no | — |
|
|
28
|
+
|
|
29
|
+
Audit is the default because the skill spans four documents: a bare invocation that
|
|
30
|
+
writes would edit a document nobody asked about.
|
|
31
|
+
|
|
32
|
+
## Start here
|
|
33
|
+
|
|
34
|
+
Read [`references/discovery.md`](./references/discovery.md) first, in every mode. It
|
|
35
|
+
finds the project's documents, the version's single source, the changelog's local
|
|
36
|
+
conventions, the roadmap's rules, the check command, and the guards by name. State
|
|
37
|
+
that profile in one paragraph before writing anything; it is how a misread is caught
|
|
38
|
+
in a line instead of in a diff.
|
|
39
|
+
|
|
40
|
+
For the changelog's mechanical half, run the state script, resolved against this
|
|
41
|
+
skill's own directory:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bun <skill>/scripts/state.ts <project-root>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
It prints the changelog file it found, the last tag and the commits since it, the
|
|
48
|
+
`[Unreleased]` categories with counts and line numbers, the empty and duplicate
|
|
49
|
+
headings, the scope prefixes in use, and the link refs. That is the state; read the
|
|
50
|
+
diffs for judgement, not for counting. When it finds no changelog, no repository, or no
|
|
51
|
+
Bun to run it with, say so and fall back to discovery by hand. The git commands these
|
|
52
|
+
references give are git's; use the project's VCS where it differs.
|
|
53
|
+
|
|
54
|
+
## Non-negotiables
|
|
55
|
+
|
|
56
|
+
1. **Discover, don't impose.** The project's document set, category spelling, scope
|
|
57
|
+
prefix, bullet shape, item ids, bucket caps, and release procedure are the rules.
|
|
58
|
+
State the profile in one paragraph, and ask when two sources disagree.
|
|
59
|
+
2. **One bullet per user-visible outcome**, in the same commit as the change. A
|
|
60
|
+
feature and the bug it fixed before release are one bullet; two observable
|
|
61
|
+
changes are two.
|
|
62
|
+
3. **A shipped item is deleted here, with its reason, never ticked.** A roadmap that
|
|
63
|
+
keeps what already landed describes a tree that no longer exists.
|
|
64
|
+
4. **One id per thing, across every document.** A README row, a roadmap item, and a
|
|
65
|
+
changelog entry that name the same feature use the same name. A mismatch is
|
|
66
|
+
reported, never renamed.
|
|
67
|
+
5. **A judgement is not a finding.** Positioning, priority, dates, non-goals, and
|
|
68
|
+
contribution policy are the maintainer's: list them and ask.
|
|
69
|
+
6. **One owner per artefact.** The changelog belongs here; what to do next belongs to
|
|
70
|
+
the `next` skill; structure belongs to `refactor`.
|
|
71
|
+
|
|
72
|
+
## Modes
|
|
73
|
+
|
|
74
|
+
- **Audit** — read-only, five faces: the tree against the docs, the docs against each
|
|
75
|
+
other, the changelog's commit window, the roadmap's own ids and caps, and the front
|
|
76
|
+
door's examples. Every finding names the mode that fixes it.
|
|
77
|
+
- **Changelog** — one bullet under the project's category for one user-visible
|
|
78
|
+
outcome, re-reading the file first. It never decides whether something has already
|
|
79
|
+
shipped.
|
|
80
|
+
- **Release** — only for a version the user named, and only after the project's
|
|
81
|
+
release document has been read; everything after moving the entries follows it.
|
|
82
|
+
- **Roadmap** — re-derived from the commits since the last tag rather than from the
|
|
83
|
+
last tag: shipped items deleted, ids renumbered, every citation and link fixed.
|
|
84
|
+
- **Readme** — the front door's catalog rows, counts, examples, and mirror, kept equal
|
|
85
|
+
to the tree; deep detail stays in the project's own guides.
|
|
86
|
+
|
|
87
|
+
## Handover
|
|
88
|
+
|
|
89
|
+
- **A changelog entry, an audit, or a release** is this skill: do not look for
|
|
90
|
+
another one.
|
|
91
|
+
- **What to do next** is `next`, where it is installed: it triages loose work and
|
|
92
|
+
broken promises across the project, and its candidates are not a doc audit's findings.
|
|
93
|
+
- **A structural change to the docs or the code** is `refactor`, where it is installed.
|
|
94
|
+
- **Who may contribute, and how** is a decision for the maintainer: ask, and edit
|
|
95
|
+
nothing until it is answered.
|
|
96
|
+
|
|
97
|
+
## Report
|
|
98
|
+
|
|
99
|
+
Open with the profile in one paragraph, then:
|
|
100
|
+
|
|
101
|
+
- **Stale** — the claim, `path:line`, the evidence in the tree, and the mode that
|
|
102
|
+
fixes it.
|
|
103
|
+
- **Missing** — a document, a row, an entry, or an item the project's own rules call
|
|
104
|
+
for and the tree does not have.
|
|
105
|
+
- **Suggested edit** — the diff or the exact replacement, for the mechanical items
|
|
106
|
+
only, and never applied until the user says so.
|
|
107
|
+
- **Needs a decision** — one line each: the question, and what it changes.
|
|
108
|
+
|
|
109
|
+
Nothing found is a valid result, and the whole report may be empty. Say what was
|
|
110
|
+
checked and stop; an audit that invents four entries to look thorough is worse than
|
|
111
|
+
one that finds nothing.
|
|
112
|
+
|
|
113
|
+
## Quality gate
|
|
114
|
+
|
|
115
|
+
- The project's own check passes, including any guard on the release window, the
|
|
116
|
+
documentation's parity, or the roadmap's ids.
|
|
117
|
+
- Every edit names the document and the line, and every bullet names a scope in the
|
|
118
|
+
project's spelling and a user outcome.
|
|
119
|
+
- No judgement is smuggled into a diff, and the changelog entry is in the change's
|
|
120
|
+
own commit.
|
|
121
|
+
|
|
122
|
+
## Stop conditions
|
|
123
|
+
|
|
124
|
+
- The project has only a README, no roadmap, and no changelog: say so. Whether to
|
|
125
|
+
adopt one is the maintainer's decision, not an edit.
|
|
126
|
+
- The changelog is generated — by `semantic-release`, `changesets`, `git-cliff`, or a
|
|
127
|
+
step in the release workflow: the entry lands upstream, in the commit message, a
|
|
128
|
+
changeset fragment, or the generator's config. Never hand-edit the file.
|
|
129
|
+
- Two sources disagree — two changelogs, a version in two places, a roadmap item the
|
|
130
|
+
changelog contradicts: ask instead of averaging them.
|
|
131
|
+
- The change would reverse a documented non-goal: it is a decision, and the item is
|
|
132
|
+
listed rather than written.
|
|
133
|
+
- The request is really triage or a refactor: name the sibling skill and stop.
|