@gavin-hu/my-pi-agent 0.19.0 → 0.21.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.
Files changed (208) hide show
  1. package/README.md +19 -12
  2. package/README.zh-CN.md +15 -9
  3. package/docs/extensions/README.md +2 -1
  4. package/docs/extensions/board.md +329 -151
  5. package/docs/extensions/bytefly.md +427 -273
  6. package/docs/extensions/file-browse.md +46 -44
  7. package/docs/extensions/job.md +1 -2
  8. package/docs/extensions/plan.md +86 -24
  9. package/docs/extensions/serve.md +129 -0
  10. package/docs/extensions/session-bus.md +23 -10
  11. package/docs/extensions/team.md +56 -28
  12. package/docs/extensions/wechat.md +2 -1
  13. package/docs/extensions/worktree.md +30 -15
  14. package/extensions/board/commands.ts +48 -17
  15. package/extensions/board/format.ts +107 -9
  16. package/extensions/board/index.ts +14 -2
  17. package/extensions/board/join.ts +31 -10
  18. package/extensions/board/page.ts +202 -156
  19. package/extensions/board/result.ts +10 -24
  20. package/extensions/board/rows.ts +1 -1
  21. package/extensions/board/schema.ts +31 -10
  22. package/extensions/board/seat.ts +9 -10
  23. package/extensions/board/snapshot.ts +7 -5
  24. package/extensions/board/tools.ts +211 -34
  25. package/extensions/board/transport/router.ts +27 -110
  26. package/extensions/board/transport/server.ts +76 -56
  27. package/extensions/board/types.ts +57 -0
  28. package/extensions/board/view/client.ts +24 -13
  29. package/extensions/board/view/dialog.ts +43 -0
  30. package/extensions/board/view/keys.ts +28 -24
  31. package/extensions/board/view/live.ts +6 -178
  32. package/extensions/board/view/names.ts +91 -0
  33. package/extensions/board/view/page.ts +60 -89
  34. package/extensions/board/view/render.ts +228 -200
  35. package/extensions/board/view/styles.ts +72 -49
  36. package/extensions/board/view/team.ts +63 -41
  37. package/extensions/bytefly/footer/budget.ts +6 -5
  38. package/extensions/bytefly/footer/collect/entries.ts +12 -4
  39. package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
  40. package/extensions/bytefly/footer/config.ts +19 -11
  41. package/extensions/bytefly/footer/price/price.ts +30 -8
  42. package/extensions/bytefly/footer/register.ts +5 -5
  43. package/extensions/bytefly/footer/render/footer.ts +18 -4
  44. package/extensions/bytefly/footer/render/format.ts +18 -0
  45. package/extensions/bytefly/footer/render/key.ts +46 -0
  46. package/extensions/bytefly/footer/render/layout.ts +13 -3
  47. package/extensions/bytefly/footer/render/lines.ts +23 -7
  48. package/extensions/bytefly/footer/render/segments.ts +93 -31
  49. package/extensions/bytefly/footer/types.ts +29 -5
  50. package/extensions/bytefly/footer/unpriced.ts +42 -17
  51. package/extensions/bytefly/header/component.ts +4 -20
  52. package/extensions/bytefly/panel/commands.ts +28 -1
  53. package/extensions/bytefly/panel/filter.ts +68 -0
  54. package/extensions/bytefly/panel/format.ts +16 -15
  55. package/extensions/bytefly/panel/join.ts +56 -13
  56. package/extensions/bytefly/panel/prices.ts +43 -0
  57. package/extensions/bytefly/panel/settings.ts +72 -23
  58. package/extensions/bytefly/panel/tui/entries.ts +26 -8
  59. package/extensions/bytefly/panel/tui/frame.ts +130 -93
  60. package/extensions/bytefly/panel/tui/keys.ts +352 -0
  61. package/extensions/bytefly/panel/tui/list-view.ts +25 -46
  62. package/extensions/bytefly/panel/tui/panes.ts +76 -0
  63. package/extensions/bytefly/panel/tui/section-view.ts +126 -0
  64. package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
  65. package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
  66. package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
  67. package/extensions/bytefly/panel/tui.ts +306 -259
  68. package/extensions/check/commands.ts +2 -1
  69. package/extensions/check/render.ts +10 -1
  70. package/extensions/file-browse/config/config.ts +4 -19
  71. package/extensions/file-browse/files/classify.ts +136 -0
  72. package/extensions/file-browse/files/files.ts +8 -547
  73. package/extensions/file-browse/files/index.ts +90 -0
  74. package/extensions/file-browse/files/list.ts +261 -0
  75. package/extensions/file-browse/files/tree.ts +101 -0
  76. package/extensions/file-browse/index.ts +27 -48
  77. package/extensions/file-browse/live/live.ts +1 -1
  78. package/extensions/file-browse/transport/router.ts +12 -6
  79. package/extensions/file-browse/transport/server.ts +61 -28
  80. package/extensions/file-browse/view/chrome.ts +4 -4
  81. package/extensions/file-browse/view/client.ts +3 -1
  82. package/extensions/file-browse/view/emit.ts +51 -0
  83. package/extensions/file-browse/view/html.ts +78 -46
  84. package/extensions/file-browse/view/live-client.ts +2 -2
  85. package/extensions/file-browse/view/markdown.ts +23 -26
  86. package/extensions/file-browse/view/palette.ts +2 -2
  87. package/extensions/file-browse/view/raw-html.ts +27 -22
  88. package/extensions/file-browse/view/tree.ts +1 -1
  89. package/extensions/file-browse/view/urls.ts +20 -6
  90. package/extensions/goal/tui.ts +12 -2
  91. package/extensions/job/runtime.ts +4 -4
  92. package/extensions/job/tools.ts +19 -7
  93. package/extensions/job/tui.ts +2 -28
  94. package/extensions/plan/commands.ts +2 -2
  95. package/extensions/plan/feedback.ts +88 -18
  96. package/extensions/plan/format.ts +10 -3
  97. package/extensions/plan/index.ts +24 -2
  98. package/extensions/plan/plan-files.ts +88 -0
  99. package/extensions/plan/plan-text.ts +114 -0
  100. package/extensions/plan/plans.ts +19 -23
  101. package/extensions/plan/policy.ts +10 -5
  102. package/extensions/plan/runtime.ts +43 -4
  103. package/extensions/plan/todos.ts +51 -3
  104. package/extensions/plan/tools/exit.ts +10 -1
  105. package/extensions/plan/tools/todo-seed.ts +4 -2
  106. package/extensions/plan/tools/write.ts +12 -1
  107. package/extensions/plan/tools.ts +2 -2
  108. package/extensions/plan/types.ts +15 -0
  109. package/extensions/plan/web-blocks.ts +160 -0
  110. package/extensions/plan/web-chrome.ts +7 -5
  111. package/extensions/plan/web-client.ts +271 -325
  112. package/extensions/plan/web-collab.ts +7 -7
  113. package/extensions/plan/web-comments.ts +57 -16
  114. package/extensions/plan/web-composer.ts +91 -0
  115. package/extensions/plan/web-draft.ts +40 -0
  116. package/extensions/plan/web-keys.ts +258 -0
  117. package/extensions/plan/web-markdown.ts +1 -1
  118. package/extensions/plan/web-outline.ts +212 -0
  119. package/extensions/plan/web-page.ts +59 -86
  120. package/extensions/plan/web-payload.ts +188 -0
  121. package/extensions/plan/web-plan.ts +160 -0
  122. package/extensions/plan/web-router.ts +0 -1
  123. package/extensions/plan/web-settle.ts +201 -0
  124. package/extensions/plan/web-share-session.ts +24 -55
  125. package/extensions/plan/web-share.ts +19 -37
  126. package/extensions/plan/web-write.ts +112 -0
  127. package/extensions/plan/web.ts +77 -130
  128. package/extensions/rewind/commands/review.ts +2 -1
  129. package/extensions/serve/config.ts +37 -0
  130. package/extensions/serve/index.ts +71 -0
  131. package/extensions/serve/registry.ts +251 -0
  132. package/extensions/session-bus/join-request.ts +16 -9
  133. package/extensions/session-bus/result.ts +1 -27
  134. package/extensions/session-bus/rows.ts +7 -3
  135. package/extensions/session-bus/runtime.ts +100 -38
  136. package/extensions/session-bus/store.ts +14 -2
  137. package/extensions/session-bus/tools.ts +5 -4
  138. package/extensions/session-bus/tui.ts +1 -1
  139. package/extensions/session-bus/types.ts +10 -1
  140. package/extensions/subagent/commands.ts +2 -1
  141. package/extensions/team/commands.ts +44 -9
  142. package/extensions/team/format.ts +14 -0
  143. package/extensions/team/index.ts +2 -1
  144. package/extensions/team/tools.ts +30 -9
  145. package/extensions/team/types.ts +17 -0
  146. package/extensions/todo/index.ts +4 -1
  147. package/extensions/todo/nudge.ts +8 -2
  148. package/extensions/todo/tui.ts +21 -3
  149. package/extensions/wechat/bridge.ts +43 -172
  150. package/extensions/wechat/intake.ts +245 -0
  151. package/extensions/worktree/commands.ts +24 -17
  152. package/extensions/worktree/exit.ts +12 -6
  153. package/extensions/worktree/git.ts +22 -14
  154. package/extensions/worktree/guard.ts +15 -285
  155. package/extensions/worktree/handoff.ts +10 -2
  156. package/extensions/worktree/index.ts +2 -1
  157. package/extensions/worktree/lifecycle.ts +2 -0
  158. package/extensions/worktree/registry.ts +20 -11
  159. package/extensions/worktree/root-tools.ts +13 -3
  160. package/extensions/worktree/runtime.ts +8 -2
  161. package/extensions/worktree/screen.ts +15 -8
  162. package/extensions/worktree/shell.ts +303 -0
  163. package/extensions/worktree/state.ts +1 -1
  164. package/extensions/worktree/status.ts +42 -9
  165. package/extensions/worktree/tools.ts +19 -13
  166. package/lib/README.md +9 -7
  167. package/lib/board/card.ts +192 -3
  168. package/lib/board/links.ts +26 -2
  169. package/lib/board/page-pointer.ts +111 -32
  170. package/lib/board/round-text.ts +11 -2
  171. package/lib/board/round.ts +25 -20
  172. package/lib/board/rules.ts +144 -11
  173. package/lib/board/session.ts +84 -18
  174. package/lib/board/store.ts +268 -55
  175. package/lib/board/view.ts +114 -24
  176. package/lib/bus-request.ts +83 -0
  177. package/lib/format.ts +22 -0
  178. package/lib/http/index.ts +11 -7
  179. package/lib/http/mount.ts +142 -0
  180. package/lib/http/port.ts +25 -0
  181. package/lib/http/serve.ts +170 -43
  182. package/lib/http/server.ts +3 -3
  183. package/lib/http/sink.ts +34 -9
  184. package/lib/plan-entry.ts +20 -13
  185. package/lib/policy.ts +14 -1
  186. package/lib/requirements.ts +2 -2
  187. package/lib/session-label.ts +23 -7
  188. package/lib/session-roster.ts +74 -3
  189. package/lib/session-tool.ts +37 -21
  190. package/lib/spawn.ts +37 -3
  191. package/lib/stray-fields.ts +45 -0
  192. package/lib/tool-names.ts +13 -0
  193. package/lib/tui/README.md +6 -3
  194. package/lib/tui/glyphs.ts +3 -6
  195. package/lib/tui/render-cache.ts +29 -0
  196. package/lib/tui/tool.ts +30 -1
  197. package/lib/web/README.md +52 -2
  198. package/lib/web/client-runtime.ts +149 -0
  199. package/lib/web/index.ts +4 -2
  200. package/lib/web/markup.ts +106 -2
  201. package/lib/web/names.ts +42 -0
  202. package/package.json +3 -2
  203. package/extensions/board/config.ts +0 -36
  204. package/extensions/board/view/actions.ts +0 -306
  205. package/extensions/board/writes.ts +0 -99
  206. package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
  207. package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
  208. package/extensions/job/process.ts +0 -35
@@ -10,6 +10,7 @@
10
10
 
11
11
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
12
12
  import { resolveEffectiveCwd } from "../../lib/env.ts";
13
+ import { planModeRefusal } from "../../lib/plan-entry.ts";
13
14
  import { isPlanModeEnabled } from "../../lib/plan-state.ts";
14
15
  import { withRailsSuppressed } from "../../lib/rails.ts";
15
16
  import { formatCheckReport, formatCheckSummary } from "./format.ts";
@@ -107,7 +108,7 @@ export function registerCheckCommand(pi: ExtensionAPI, runtime: CheckRuntime): v
107
108
  },
108
109
  handler: async (args, ctx) => {
109
110
  if (isPlanModeEnabled(ctx.sessionManager.getBranch())) {
110
- ctx.ui.notify("Plan mode is read-only. Approve the plan before running checks.", "warning");
111
+ ctx.ui.notify(planModeRefusal("running checks"), "warning");
111
112
  return;
112
113
  }
113
114
  const target = parseTarget(args, ctx);
@@ -16,6 +16,7 @@ import type { Component } from "@earendil-works/pi-tui";
16
16
  import { sanitizeLine } from "../../lib/format.ts";
17
17
  import { clip } from "../../lib/tui/columns.ts";
18
18
  import { expandHint, GLYPHS, SEPARATORS } from "../../lib/tui/glyphs.ts";
19
+ import { RenderCache } from "../../lib/tui/render-cache.ts";
19
20
  import { listRow } from "../../lib/tui/rows.ts";
20
21
  import { paint, type TuiRole } from "../../lib/tui/tokens.ts";
21
22
  import { earlierHint } from "../../lib/tui/tool.ts";
@@ -134,6 +135,7 @@ export interface CheckResultInput {
134
135
  export class CheckResultBlock implements Component {
135
136
  private input: CheckResultInput;
136
137
  private theme: Theme;
138
+ private readonly cache = new RenderCache();
137
139
 
138
140
  constructor(input: CheckResultInput, theme: Theme) {
139
141
  this.input = input;
@@ -144,12 +146,19 @@ export class CheckResultBlock implements Component {
144
146
  setInput(input: CheckResultInput, theme: Theme): void {
145
147
  this.input = input;
146
148
  this.theme = theme;
149
+ this.cache.invalidate();
147
150
  }
148
151
 
149
- invalidate(): void {}
152
+ invalidate(): void {
153
+ this.cache.invalidate();
154
+ }
150
155
 
151
156
  render(width: number): string[] {
152
157
  const w = Math.max(1, width);
158
+ return this.cache.lines(w, undefined, () => this.build(w));
159
+ }
160
+
161
+ private build(w: number): string[] {
153
162
  const theme = this.theme;
154
163
  const { details, expanded } = this.input;
155
164
  const role = statusRole(details.status);
@@ -2,16 +2,14 @@
2
2
  * Configuration for the browse extension.
3
3
  *
4
4
  * The `file-browse` section of the shared `bytefly.json` (global then project,
5
- * project wins). Everything
6
- * is validated and clamped so a typo cannot produce a nonsensical server.
5
+ * project wins). Everything is validated and clamped so a typo cannot produce a
6
+ * nonsensical page. The port is not here: the session serves every page on one, and
7
+ * `serve.port` is where that is pinned.
7
8
  */
8
9
 
9
- import { randomInt } from "node:crypto";
10
10
  import { asBoolean, clampInteger, loadConfigSection } from "../../../lib/config.ts";
11
11
 
12
12
  export interface ServeConfig {
13
- /** TCP port on 127.0.0.1; `0` picks one random port fixed for the session, a positive value pins it. */
14
- port: number;
15
13
  /** Open the default browser when the server starts. */
16
14
  autoOpen: boolean;
17
15
  /** Files larger than this are not rendered inline. */
@@ -43,7 +41,6 @@ export interface ServeConfig {
43
41
  }
44
42
 
45
43
  export const DEFAULT_CONFIG: ServeConfig = {
46
- port: 0,
47
44
  autoOpen: true,
48
45
  maxFileBytes: 1024 * 1024,
49
46
  maxThumbBytes: 5 * 1024 * 1024,
@@ -60,12 +57,6 @@ export const DEFAULT_CONFIG: ServeConfig = {
60
57
  liveIntervalMs: 5000,
61
58
  };
62
59
 
63
- /** Base of the per-session port range. */
64
- export const PORT_BASE = 4780;
65
-
66
- /** Number of ports in the per-session range. */
67
- export const PORT_RANGE = 1000;
68
-
69
60
  /** Fastest live-status cadence a page may be given: short enough to watch, and a
70
61
  * floor so a page cannot outrun the git reads behind each frame. */
71
62
  export const LIVE_INTERVAL_MIN = 250;
@@ -76,7 +67,7 @@ export const LIVE_INTERVAL_MAX = 60_000;
76
67
  /**
77
68
  * How long the server keeps listening once the last page has gone. A reload or a
78
69
  * navigation detaches the stream and re-attaches within a moment, so the grace has
79
- * to outlast one; a closed window never re-attaches, and the port is released
70
+ * to outlast one; a closed window never re-attaches, and the prefix is given back
80
71
  * instead of listening for nobody until the session ends.
81
72
  */
82
73
  export const IDLE_STOP_MS = 10_000;
@@ -90,11 +81,6 @@ export const IDLE_STOP_MS = 10_000;
90
81
  */
91
82
  export const LIVE_MAX_CLIENTS = 8;
92
83
 
93
- /** A random port in the per-session range `PORT_BASE`–`PORT_BASE + PORT_RANGE - 1`. */
94
- export function randomPort(): number {
95
- return PORT_BASE + randomInt(PORT_RANGE);
96
- }
97
-
98
84
  /** Validate and clamp a raw config object over the defaults. */
99
85
  export function normalizeConfig(
100
86
  raw: Record<string, unknown> | undefined,
@@ -102,7 +88,6 @@ export function normalizeConfig(
102
88
  ): ServeConfig {
103
89
  if (!raw) return base;
104
90
  return {
105
- port: clampInteger(raw.port, base.port, 0, 65535),
106
91
  autoOpen: asBoolean(raw.autoOpen, base.autoOpen),
107
92
  maxFileBytes: clampInteger(raw.maxFileBytes, base.maxFileBytes, 0, 1024 * 1024 * 1024),
108
93
  maxThumbBytes: clampInteger(raw.maxThumbBytes, base.maxThumbBytes, 0, 1024 * 1024 * 1024),
@@ -0,0 +1,136 @@
1
+ /**
2
+ * What a file's kind is, from its name and — for an extensionless one — a byte sample.
3
+ *
4
+ * Every surface that names a kind (the listing's rows, the tree, a preview's routing) asks
5
+ * here, so "is this an image" has one answer.
6
+ */
7
+
8
+ import { basename, extname } from "node:path";
9
+
10
+ export type EntryKind = "directory" | "text" | "image" | "binary";
11
+
12
+ /** Image extension (no dot) to MIME type; the single source for image classification. */
13
+ const IMAGE_MIME: Record<string, string> = {
14
+ png: "image/png",
15
+ jpg: "image/jpeg",
16
+ jpeg: "image/jpeg",
17
+ gif: "image/gif",
18
+ webp: "image/webp",
19
+ svg: "image/svg+xml",
20
+ bmp: "image/bmp",
21
+ ico: "image/x-icon",
22
+ avif: "image/avif",
23
+ };
24
+
25
+ /** Image extensions without a leading dot. */
26
+ export const IMAGE_EXTENSIONS: ReadonlySet<string> = new Set(Object.keys(IMAGE_MIME));
27
+
28
+ const MARKDOWN_EXTS = new Set([".md", ".markdown", ".mdown", ".mkd", ".mkdn"]);
29
+ const TEXT_EXTS = new Set([
30
+ ".txt",
31
+ ".md",
32
+ ".markdown",
33
+ ".rst",
34
+ ".adoc",
35
+ ".org",
36
+ ".log",
37
+ ".json",
38
+ ".jsonc",
39
+ ".json5",
40
+ ".yaml",
41
+ ".yml",
42
+ ".toml",
43
+ ".ini",
44
+ ".cfg",
45
+ ".conf",
46
+ ".xml",
47
+ ".csv",
48
+ ".tsv",
49
+ ".ts",
50
+ ".tsx",
51
+ ".js",
52
+ ".jsx",
53
+ ".mjs",
54
+ ".cjs",
55
+ ".py",
56
+ ".go",
57
+ ".rs",
58
+ ".java",
59
+ ".c",
60
+ ".h",
61
+ ".cc",
62
+ ".cpp",
63
+ ".hpp",
64
+ ".cs",
65
+ ".rb",
66
+ ".php",
67
+ ".sh",
68
+ ".bash",
69
+ ".zsh",
70
+ ".ps1",
71
+ ".swift",
72
+ ".kt",
73
+ ".kts",
74
+ ".scala",
75
+ ".lua",
76
+ ".r",
77
+ ".pl",
78
+ ".sql",
79
+ ".css",
80
+ ".scss",
81
+ ".sass",
82
+ ".less",
83
+ ".html",
84
+ ".htm",
85
+ ".vue",
86
+ ".svelte",
87
+ ".astro",
88
+ ]);
89
+ const TEXT_NAMES = new Set([
90
+ "license",
91
+ "licence",
92
+ "makefile",
93
+ "dockerfile",
94
+ "readme",
95
+ "changelog",
96
+ "authors",
97
+ "notice",
98
+ ]);
99
+
100
+ export function isImageName(name: string): boolean {
101
+ return IMAGE_EXTENSIONS.has(extname(name).slice(1).toLowerCase());
102
+ }
103
+
104
+ /** Whether the name has a Markdown extension. */
105
+ export function isMarkdownName(name: string): boolean {
106
+ return MARKDOWN_EXTS.has(extname(name).toLowerCase());
107
+ }
108
+
109
+ /** Extension-based classification for listings (no file contents read). */
110
+ export function classifyByName(name: string): EntryKind {
111
+ const ext = extname(name).toLowerCase();
112
+ if (IMAGE_EXTENSIONS.has(ext.slice(1))) return "image";
113
+ if (TEXT_EXTS.has(ext)) return "text";
114
+ const lower = basename(name).toLowerCase();
115
+ if (TEXT_NAMES.has(lower)) return "text";
116
+ if (!ext && name.startsWith(".")) return "text";
117
+ return "binary";
118
+ }
119
+
120
+ /** True when the sample looks like binary data (NUL or many control bytes). */
121
+ export function isProbablyText(sample: Buffer): boolean {
122
+ if (sample.length === 0) return true;
123
+ if (sample.includes(0)) return false;
124
+ let suspicious = 0;
125
+ for (const byte of sample) {
126
+ if (byte === 9 || byte === 10 || byte === 13 || byte === 12 || byte === 8) continue;
127
+ if (byte < 32 || byte === 127) suspicious++;
128
+ }
129
+ return suspicious / sample.length < 0.3;
130
+ }
131
+
132
+ /** MIME type for a name; text is always served as plain text. */
133
+ export function contentTypeFor(name: string): string {
134
+ const ext = extname(name).slice(1).toLowerCase();
135
+ return IMAGE_MIME[ext] ?? "application/octet-stream";
136
+ }