@huseynsnmz/pi-suite 0.1.0 → 0.2.1

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 (98) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +2 -1
  3. package/package.json +6 -7
  4. package/packages/board/README.md +1 -1
  5. package/packages/board/package.json +1 -3
  6. package/packages/board/src/core/rows.mjs +21 -5
  7. package/packages/board/src/ui/dashboard.ts +36 -20
  8. package/packages/commands/LICENSE +21 -0
  9. package/packages/commands/README.md +20 -0
  10. package/packages/commands/package.json +22 -0
  11. package/packages/commands/src/index.ts +43 -0
  12. package/packages/commands/src/security-review.ts +141 -0
  13. package/packages/frame/README.md +2 -2
  14. package/packages/frame/package.json +1 -4
  15. package/packages/frame/src/data/github.ts +1 -1
  16. package/packages/frame/src/data/mode.ts +28 -2
  17. package/packages/frame/src/data/snapshot.ts +1 -1
  18. package/packages/frame/src/data/start-notes.ts +1 -1
  19. package/packages/frame/src/data/totals.ts +2 -2
  20. package/packages/frame/src/data/visits.ts +1 -1
  21. package/packages/frame/src/index.ts +25 -3
  22. package/packages/frame/src/kit/LICENSE +21 -0
  23. package/packages/frame/src/kit/app/attribution.ts +30 -0
  24. package/packages/frame/src/kit/app/builder.ts +186 -0
  25. package/packages/frame/src/kit/app/claim.ts +27 -0
  26. package/packages/frame/src/kit/app/feature.ts +91 -0
  27. package/packages/frame/src/kit/app/host.ts +214 -0
  28. package/packages/frame/src/kit/app/renderers.ts +62 -0
  29. package/packages/frame/src/kit/app/scope.ts +125 -0
  30. package/packages/frame/src/kit/board/model.ts +337 -0
  31. package/packages/frame/src/kit/contract.ts +79 -0
  32. package/packages/frame/src/kit/contracts/accounts.ts +33 -0
  33. package/packages/frame/src/kit/contracts/ask.ts +120 -0
  34. package/packages/frame/src/kit/contracts/board.ts +6 -0
  35. package/packages/frame/src/kit/contracts/by.ts +22 -0
  36. package/packages/frame/src/kit/contracts/calls.ts +46 -0
  37. package/packages/frame/src/kit/contracts/events.ts +89 -0
  38. package/packages/frame/src/kit/contracts/mode.ts +26 -0
  39. package/packages/frame/src/kit/contracts/names.ts +12 -0
  40. package/packages/frame/src/kit/contracts/peers.ts +77 -0
  41. package/packages/frame/src/kit/contracts/prompts.ts +8 -0
  42. package/packages/frame/src/kit/contracts/screen.ts +75 -0
  43. package/packages/frame/src/kit/contracts/skills.ts +81 -0
  44. package/packages/frame/src/kit/contracts/sources.ts +81 -0
  45. package/packages/frame/src/kit/contracts/views.ts +42 -0
  46. package/packages/frame/src/kit/control.ts +59 -0
  47. package/packages/frame/src/kit/dark-theme.ts +41 -0
  48. package/packages/frame/src/kit/decode.ts +210 -0
  49. package/packages/frame/src/kit/events/log.ts +313 -0
  50. package/packages/frame/src/kit/format.ts +25 -0
  51. package/packages/frame/src/kit/host.ts +4 -0
  52. package/packages/frame/src/kit/index.ts +25 -0
  53. package/packages/frame/src/kit/legacy-tasks.ts +39 -0
  54. package/packages/frame/src/kit/overlay-images.ts +36 -0
  55. package/packages/frame/src/kit/paths.ts +16 -0
  56. package/packages/frame/src/kit/screen/client.ts +74 -0
  57. package/packages/frame/src/kit/screen/feature.ts +64 -0
  58. package/packages/frame/src/kit/screen/model.ts +182 -0
  59. package/packages/frame/src/kit/screen/view.ts +838 -0
  60. package/packages/frame/src/kit/settings/files.ts +90 -0
  61. package/packages/frame/src/kit/settings/setting.ts +57 -0
  62. package/packages/frame/src/kit/settings/store.ts +192 -0
  63. package/packages/frame/src/kit/sources/file.ts +129 -0
  64. package/packages/frame/src/kit/sources/follow.ts +155 -0
  65. package/packages/frame/src/kit/testing.ts +260 -0
  66. package/packages/frame/src/kit/totals.ts +72 -0
  67. package/packages/frame/src/kit/ui/entry.ts +75 -0
  68. package/packages/frame/src/kit/ui/frame.ts +212 -0
  69. package/packages/frame/src/kit/ui/glyphs.ts +132 -0
  70. package/packages/frame/src/kit/ui/scroll.ts +14 -0
  71. package/packages/frame/src/render/diff.ts +55 -2
  72. package/packages/frame/src/render/fit.ts +1 -1
  73. package/packages/frame/src/render/format.ts +1 -1
  74. package/packages/frame/src/render/segments.ts +1 -1
  75. package/packages/frame/src/render/streaming.ts +1 -1
  76. package/packages/frame/src/render/tools.ts +1 -1
  77. package/packages/frame/src/settings.ts +1 -1
  78. package/packages/frame/src/ui/editor.ts +1 -1
  79. package/packages/frame/src/ui/effort.ts +1 -1
  80. package/packages/frame/src/ui/header.ts +1 -1
  81. package/packages/frame/src/ui/outputs.ts +1 -1
  82. package/packages/frame/src/ui/pi-entries.ts +28 -6
  83. package/packages/frame/src/ui/previews.ts +1 -1
  84. package/packages/frame/src/ui/queue.ts +1 -1
  85. package/packages/frame/src/ui/todos.ts +1 -1
  86. package/packages/frame/src/ui/tool-frame.ts +1 -1
  87. package/packages/frame/src/view/state.ts +1 -1
  88. package/packages/frame/src/view/transcript.ts +1 -1
  89. package/packages/frame/src/view/view.ts +1 -1
  90. package/packages/memory/README.md +1 -1
  91. package/packages/modes/README.md +1 -1
  92. package/packages/router/README.md +1 -1
  93. package/packages/web/README.md +1 -1
  94. package/packages/web/package.json +1 -3
  95. package/packages/web/src/extract.ts +2 -2
  96. package/packages/web/src/markdown.ts +156 -0
  97. package/src/index.ts +1 -1
  98. package/src/settings.ts +1 -0
@@ -0,0 +1,156 @@
1
+ /** Page HTML to Markdown. The page is hostile, so this only ever emits text, and escapes what would read as Markdown. */
2
+ import { parseHTML } from "linkedom";
3
+
4
+ interface DomNode {
5
+ nodeType: number;
6
+ tagName?: string;
7
+ textContent: string | null;
8
+ childNodes: Iterable<DomNode>;
9
+ getAttribute?(name: string): string | null;
10
+ }
11
+
12
+ const TEXT = 3;
13
+ const ELEMENT = 1;
14
+ const NEVER = new Set(["script", "style", "noscript", "template", "head", "title", "meta", "link", "iframe", "object", "embed", "svg", "canvas", "form", "input", "textarea", "select", "button", "img", "picture", "video", "audio"]);
15
+ const BLOCK = new Set(["p", "div", "section", "article", "main", "aside", "header", "footer", "nav", "figure", "figcaption", "address", "details", "summary", "dl", "dt", "dd", "center"]);
16
+
17
+ function escaped(text: string): string {
18
+ return text
19
+ .replace(/\\/g, "\\\\")
20
+ .replace(/[*`[\]_]/g, "\\$&")
21
+ .replace(/^(\s*)([-+=>]|#|~~~)/, "$1\\$2")
22
+ .replace(/^(\s*\d+)\./, "$1\\.");
23
+ }
24
+
25
+ /** Text with backticks in it needs a longer run of them around it. */
26
+ function fenceFor(text: string, shortest: number): string {
27
+ let longest = 0;
28
+ for (const run of text.matchAll(/`+/g)) longest = Math.max(longest, run[0].length);
29
+ return "`".repeat(Math.max(shortest, longest + 1));
30
+ }
31
+
32
+ function inside(node: DomNode): string {
33
+ return [...node.childNodes].map(render).join("");
34
+ }
35
+
36
+ function list(node: DomNode, ordered: boolean): string {
37
+ let number = Number.parseInt(node.getAttribute?.("start") ?? "", 10);
38
+ if (!Number.isSafeInteger(number)) number = 1;
39
+ const items: string[] = [];
40
+ for (const child of node.childNodes) {
41
+ if (child.tagName?.toLowerCase() !== "li") {
42
+ // Not valid HTML, but browsers show it, so it's part of the page.
43
+ const stray = render(child).trim();
44
+ if (stray !== "") items.push(` ${stray.replace(/\n/g, "\n ")}`);
45
+ continue;
46
+ }
47
+ const marker = ordered ? `${number++}. ` : "- ";
48
+ const body = inside(child).trim().replace(/\n{3,}/g, "\n\n");
49
+ items.push(marker + body.replace(/\n/g, `\n${" ".repeat(marker.length)}`));
50
+ }
51
+ return `\n\n${items.join("\n")}\n\n`;
52
+ }
53
+
54
+ /** Marks go right around the words, and the spaces beside them stay outside, or the words run together. */
55
+ function wrapped(text: string, wrap: (words: string) => string): string {
56
+ const words = text.trim();
57
+ if (words === "") return text;
58
+ const at = text.indexOf(words);
59
+ return text.slice(0, at) + wrap(words) + text.slice(at + words.length);
60
+ }
61
+
62
+ /** Only these can be followed from a page. The characters that could end a Markdown link are encoded. */
63
+ const LINKABLE = /^(?:https?|mailto):/i;
64
+ function linkTarget(href: string): string {
65
+ return href.replace(/[()\s]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`);
66
+ }
67
+
68
+ function render(node: DomNode): string {
69
+ if (node.nodeType === TEXT) return escaped((node.textContent ?? "").replace(/\s+/g, " "));
70
+ // Comments are never shown, so they don't count as the page's text.
71
+ if (node.nodeType !== ELEMENT) return "";
72
+ const tag = (node.tagName ?? "").toLowerCase();
73
+ if (NEVER.has(tag)) return "";
74
+ const heading = /^h([1-6])$/.exec(tag)?.[1];
75
+ if (heading !== undefined) {
76
+ const text = inside(node).trim();
77
+ return text === "" ? "" : `\n\n${"#".repeat(Number(heading))} ${text.replace(/\s*\n\s*/g, " ")}\n\n`;
78
+ }
79
+ if (BLOCK.has(tag)) return `\n\n${inside(node)}\n\n`;
80
+ switch (tag) {
81
+ case "br":
82
+ return "\n";
83
+ case "hr":
84
+ return "\n\n---\n\n";
85
+ case "ul":
86
+ case "ol":
87
+ return list(node, tag === "ol");
88
+ case "li":
89
+ return `\n- ${inside(node).trim()}\n`;
90
+ case "blockquote": {
91
+ const text = inside(node).trim().replace(/\n{3,}/g, "\n\n");
92
+ return text === "" ? "" : `\n\n${text.replace(/^/gm, "> ")}\n\n`;
93
+ }
94
+ case "pre": {
95
+ const text = (node.textContent ?? "").replace(/\n$/, "");
96
+ const fence = fenceFor(text, 3);
97
+ const code = [...node.childNodes].find((child) => child.tagName?.toLowerCase() === "code");
98
+ const language = /language-([\w+-]+)/.exec(code?.getAttribute?.("class") ?? "")?.[1] ?? "";
99
+ return `\n\n${fence}${language}\n${text}\n${fence}\n\n`;
100
+ }
101
+ case "code": {
102
+ const text = (node.textContent ?? "").replace(/\s+/g, " ");
103
+ if (text.trim() === "") return "";
104
+ const fence = fenceFor(text, 1);
105
+ const pad = text.startsWith("`") || text.endsWith("`") ? " " : "";
106
+ return `${fence}${pad}${text}${pad}${fence}`;
107
+ }
108
+ case "strong":
109
+ case "b":
110
+ case "em":
111
+ case "i": {
112
+ const mark = tag === "strong" || tag === "b" ? "**" : "_";
113
+ return wrapped(inside(node), (words) => `${mark}${words}${mark}`);
114
+ }
115
+ case "a": {
116
+ const text = inside(node).replace(/\n+/g, " ");
117
+ const href = node.getAttribute?.("href");
118
+ if (!href || !LINKABLE.test(href)) return text;
119
+ return wrapped(text, (words) => `[${words}](${linkTarget(href)})`);
120
+ }
121
+ case "table":
122
+ return `\n\n${[...node.childNodes].map(render).join("").trim()}\n\n`;
123
+ case "thead":
124
+ case "tbody":
125
+ case "tfoot":
126
+ return [...node.childNodes].filter((child) => child.nodeType === ELEMENT).map(render).join("");
127
+ case "tr":
128
+ return `${[...node.childNodes].map((cell) => render(cell).trim()).filter(Boolean).join(" | ")}\n`;
129
+ default:
130
+ return inside(node);
131
+ }
132
+ }
133
+
134
+ /** Removes trailing spaces and extra blank lines, a line at a time so a page of spaces costs nothing, and leaves code blocks exactly as they are. */
135
+ function tidied(markdown: string): string {
136
+ const out: string[] = [];
137
+ let fence: string | undefined;
138
+ for (const line of markdown.split("\n")) {
139
+ if (fence !== undefined) {
140
+ out.push(line);
141
+ if (line === fence) fence = undefined;
142
+ continue;
143
+ }
144
+ // Page text has its backticks escaped, so only a real code block can start one of these.
145
+ const opening = /^(`{3,})[\w+-]*$/.exec(line)?.[1];
146
+ if (opening !== undefined) fence = opening;
147
+ const tidy = line.trimEnd();
148
+ if (tidy !== "" || (out.length > 0 && out.at(-1) !== "")) out.push(tidy);
149
+ }
150
+ return out.join("\n").trim();
151
+ }
152
+
153
+ export function markdownOf(html: string): string {
154
+ const { document } = parseHTML(`<!doctype html><html><body>${html}</body></html>`);
155
+ return tidied(render(document.body as unknown as DomNode));
156
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // One package, six parts. A part that's off isn't even imported, so it adds no tools, prompts or start-up time.
1
+ // One package, seven parts. A part that's off isn't even imported, so it adds no tools, prompts or start-up time.
2
2
  import { readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
package/src/settings.ts CHANGED
@@ -9,6 +9,7 @@ export const PARTS = {
9
9
  memory: "Memory the agent keeps between sessions",
10
10
  frame: "The start card, framed editor, tool boxes, footer and todo list",
11
11
  board: "The board of agent sessions",
12
+ commands: "/clear and /security-review",
12
13
  } as const;
13
14
 
14
15
  export type Part = keyof typeof PARTS;