@selesai/code 0.1.19 → 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.
- package/dist/agents/architect.md +4 -3
- package/dist/agents/builder.md +5 -4
- package/dist/agents/commentator.md +3 -2
- package/dist/agents/explorer.md +4 -4
- package/dist/agents/recapper.md +4 -3
- package/dist/config.d.ts +6 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -9
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +3 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +18 -24
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +8 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +25 -11
- package/dist/core/skills.js.map +1 -1
- package/dist/defaults/models.json +143 -39
- package/dist/defaults/settings.json +2 -29
- package/dist/extensions/package.json +5 -4
- package/dist/extensions/pi-intercom/index.ts +12 -5
- package/dist/extensions/pi-powerline-footer/index.ts +4 -1
- package/dist/extensions/pi-powerline-footer/tps.ts +286 -0
- package/dist/extensions/pi-powerline-footer/welcome.ts +47 -46
- package/dist/extensions/pi-subagents/README.md +12 -5
- package/dist/extensions/pi-subagents/agents/context-builder.md +2 -2
- package/dist/extensions/pi-subagents/agents/researcher.md +4 -5
- package/dist/extensions/pi-subagents/package.json +8 -5
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +1 -1
- package/dist/extensions/pi-subagents/src/agents/agents.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +1 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +1 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +29 -2
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +72 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +85 -41
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +1 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +46 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -4
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +6 -6
- package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -3
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/render.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +45 -0
- package/dist/extensions/pi-subagents/tsconfig.runner.json +17 -0
- package/dist/extensions/pi-web-agent/package.json +31 -0
- package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -0
- package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -0
- package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -0
- package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -0
- package/dist/extensions/pi-web-agent/src/changelog-notice.ts +133 -0
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -0
- package/dist/extensions/pi-web-agent/src/extension.ts +126 -0
- package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -0
- package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -0
- package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -0
- package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -0
- package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -0
- package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -0
- package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -0
- package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -0
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -0
- package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -0
- package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -0
- package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -0
- package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -0
- package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -0
- package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -0
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +208 -0
- package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -0
- package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -0
- package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -0
- package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -0
- package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -0
- package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -0
- package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -0
- package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -0
- package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -0
- package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -0
- package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -0
- package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -0
- package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -0
- package/dist/extensions/pi-web-agent/src/types.ts +88 -0
- package/dist/extensions/ponytail/index.js +189 -0
- package/dist/extensions/ponytail/package.json +8 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +122 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +94 -0
- package/dist/extensions/ponytail/test/extension.test.js +167 -0
- package/dist/extensions/ponytail/test/helpers.test.js +92 -0
- package/dist/extensions/question/constants.ts +38 -0
- package/dist/extensions/question/dialog-adapter.ts +74 -0
- package/dist/extensions/question/helpers.ts +102 -0
- package/dist/extensions/question/index.ts +671 -0
- package/dist/extensions/question/navigation.ts +14 -0
- package/dist/extensions/question/package.json +18 -0
- package/dist/extensions/question/question-list.ts +498 -0
- package/dist/extensions/question/row-layout.ts +162 -0
- package/dist/extensions/question/schemas.ts +69 -0
- package/dist/extensions/question/selection-mode.ts +49 -0
- package/dist/extensions/question/shortcuts.ts +53 -0
- package/dist/extensions/question/tests/helpers.test.ts +178 -0
- package/dist/extensions/question/tests/question-list.test.ts +230 -0
- package/dist/extensions/question/tests/row-layout.test.ts +136 -0
- package/dist/extensions/question/tests/shortcuts.test.ts +85 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +143 -0
- package/dist/extensions/question/tui-adapter.ts +33 -0
- package/dist/extensions/question/types.ts +102 -0
- package/dist/extensions/question/ui-protocol.ts +46 -0
- package/dist/extensions/tokenin-onboarding.ts +265 -2
- package/dist/extensions/web-agent-onboarding.ts +222 -0
- package/dist/extensions/workflow/adapter.ts +588 -0
- package/dist/extensions/workflow/extension.ts +24 -0
- package/dist/extensions/workflow/modes/prototype.ts +156 -0
- package/dist/extensions/workflow/modes/quick.ts +138 -0
- package/dist/extensions/workflow/package.json +17 -0
- package/dist/extensions/workflow/state-machine.ts +386 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +57 -13
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +24 -1
- package/dist/utils/mime.js.map +1 -1
- package/docs/docs.json +4 -0
- package/docs/workflows.md +206 -0
- package/package.json +5 -1
- package/README.md +0 -198
package/dist/utils/mime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAc9E;
|
|
1
|
+
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAc9E;AAiBD,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBnG","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\treturn null;\n}\n\nconst IMAGE_EXTENSION_MIME_TYPES: Record<string, string> = {\n\tpng: \"image/png\",\n\tjpg: \"image/jpeg\",\n\tjpeg: \"image/jpeg\",\n\tgif: \"image/gif\",\n\twebp: \"image/webp\",\n\tbmp: \"image/bmp\",\n\ttiff: \"image/tiff\",\n\ttif: \"image/tiff\",\n\tico: \"image/x-icon\",\n\theic: \"image/heic\",\n\theif: \"image/heif\",\n\tavif: \"image/avif\",\n};\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\tconst detected = detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t\tif (detected) return detected;\n\t\t// ponytail: magic-byte sniffer rejects APNG/JPEG2000 and ignores BMP/TIFF/etc.\n\t\t// Fall back to the file extension so image files are routed to the image path\n\t\t// (resize will no-op to an \"omitted\" note) instead of being read as text and\n\t\t// flooding the prompt with base64 garbage. SVG is intentionally excluded: it is\n\t\t// text and is correctly handled by the text branch.\n\t\tconst ext = filePath.toLowerCase().split(\".\").pop();\n\t\treturn ext ? (IMAGE_EXTENSION_MIME_TYPES[ext] ?? null) : null;\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
|
package/dist/utils/mime.js
CHANGED
|
@@ -16,12 +16,35 @@ export function detectSupportedImageMimeType(buffer) {
|
|
|
16
16
|
}
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
|
+
const IMAGE_EXTENSION_MIME_TYPES = {
|
|
20
|
+
png: "image/png",
|
|
21
|
+
jpg: "image/jpeg",
|
|
22
|
+
jpeg: "image/jpeg",
|
|
23
|
+
gif: "image/gif",
|
|
24
|
+
webp: "image/webp",
|
|
25
|
+
bmp: "image/bmp",
|
|
26
|
+
tiff: "image/tiff",
|
|
27
|
+
tif: "image/tiff",
|
|
28
|
+
ico: "image/x-icon",
|
|
29
|
+
heic: "image/heic",
|
|
30
|
+
heif: "image/heif",
|
|
31
|
+
avif: "image/avif",
|
|
32
|
+
};
|
|
19
33
|
export async function detectSupportedImageMimeTypeFromFile(filePath) {
|
|
20
34
|
const fileHandle = await open(filePath, "r");
|
|
21
35
|
try {
|
|
22
36
|
const buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);
|
|
23
37
|
const { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);
|
|
24
|
-
|
|
38
|
+
const detected = detectSupportedImageMimeType(buffer.subarray(0, bytesRead));
|
|
39
|
+
if (detected)
|
|
40
|
+
return detected;
|
|
41
|
+
// ponytail: magic-byte sniffer rejects APNG/JPEG2000 and ignores BMP/TIFF/etc.
|
|
42
|
+
// Fall back to the file extension so image files are routed to the image path
|
|
43
|
+
// (resize will no-op to an "omitted" note) instead of being read as text and
|
|
44
|
+
// flooding the prompt with base64 garbage. SVG is intentionally excluded: it is
|
|
45
|
+
// text and is correctly handled by the text branch.
|
|
46
|
+
const ext = filePath.toLowerCase().split(".").pop();
|
|
47
|
+
return ext ? (IMAGE_EXTENSION_MIME_TYPES[ext] ?? null) : null;
|
|
25
48
|
}
|
|
26
49
|
finally {
|
|
27
50
|
await fileHandle.close();
|
package/dist/utils/mime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvE,MAAM,UAAU,4BAA4B,CAAC,MAAkB,EAAiB;IAC/E,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC;IACjD,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,WAAW,CAAC;IACpB,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;QAC9E,OAAO,YAAY,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,CAAC,KAAK,UAAU,oCAAoC,CAAC,QAAgB,EAA0B;IACpG,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACpD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC;QAClF,
|
|
1
|
+
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvE,MAAM,UAAU,4BAA4B,CAAC,MAAkB,EAAiB;IAC/E,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC;IACjD,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,WAAW,CAAC;IACpB,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;QAC9E,OAAO,YAAY,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,0BAA0B,GAA2B;IAC1D,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,cAAc;IACnB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;CAClB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,oCAAoC,CAAC,QAAgB,EAA0B;IACpG,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACpD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,4BAA4B,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7E,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,+EAA+E;QAC/E,8EAA8E;QAC9E,6EAA6E;QAC7E,gFAAgF;QAChF,oDAAoD;QACpD,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QACpD,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/D,CAAC;YAAS,CAAC;QACV,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;AAAA,CACD;AAED,SAAS,KAAK,CAAC,MAAkB,EAAW;IAC3C,OAAO,CACN,MAAM,CAAC,MAAM,IAAI,EAAE,IAAI,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,eAAe,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAC/G,CAAC;AAAA,CACF;AAED,SAAS,aAAa,CAAC,MAAkB,EAAW;IACnD,IAAI,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC;IAClC,OAAO,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,CAAC;QACnC,IAAI,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QAClE,IAAI,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAEnE,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC;QAChD,IAAI,UAAU,IAAI,MAAM,IAAI,UAAU,GAAG,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACrE,MAAM,GAAG,UAAU,CAAC;IACrB,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,YAAY,CAAC,MAAkB,EAAE,MAAc,EAAU;IACjE,OAAO,CACN,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS;QACjC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CACzB,CAAC;AAAA,CACF;AAED,SAAS,UAAU,CAAC,MAAkB,EAAE,KAAe,EAAW;IACjE,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AAAA,CAC5D;AAED,SAAS,eAAe,CAAC,MAAkB,EAAE,MAAc,EAAE,IAAY,EAAW;IACnF,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACvD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\treturn null;\n}\n\nconst IMAGE_EXTENSION_MIME_TYPES: Record<string, string> = {\n\tpng: \"image/png\",\n\tjpg: \"image/jpeg\",\n\tjpeg: \"image/jpeg\",\n\tgif: \"image/gif\",\n\twebp: \"image/webp\",\n\tbmp: \"image/bmp\",\n\ttiff: \"image/tiff\",\n\ttif: \"image/tiff\",\n\tico: \"image/x-icon\",\n\theic: \"image/heic\",\n\theif: \"image/heif\",\n\tavif: \"image/avif\",\n};\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\tconst detected = detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t\tif (detected) return detected;\n\t\t// ponytail: magic-byte sniffer rejects APNG/JPEG2000 and ignores BMP/TIFF/etc.\n\t\t// Fall back to the file extension so image files are routed to the image path\n\t\t// (resize will no-op to an \"omitted\" note) instead of being read as text and\n\t\t// flooding the prompt with base64 garbage. SVG is intentionally excluded: it is\n\t\t// text and is correctly handled by the text branch.\n\t\tconst ext = filePath.toLowerCase().split(\".\").pop();\n\t\treturn ext ? (IMAGE_EXTENSION_MIME_TYPES[ext] ?? null) : null;\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
|
package/docs/docs.json
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Workflows
|
|
2
|
+
|
|
3
|
+
Selesai ships a workflow engine under `src/extensions/workflow/`. It powers the built-in `prototype` and `quick` workflows and is designed so you can add a new workflow mode as a thin config file — no engine changes.
|
|
4
|
+
|
|
5
|
+
## How it fits together
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/extensions/workflow/
|
|
9
|
+
package.json pi package manifest; lists each mode as a pi extension
|
|
10
|
+
state-machine.ts pure phase state machine (no fs, no pi API)
|
|
11
|
+
adapter.ts pi wiring: tools, commands, events, fs, agent continuation
|
|
12
|
+
modes/
|
|
13
|
+
prototype.ts config → export default createWorkflowExtension(config, options)
|
|
14
|
+
quick.ts config → export default createWorkflowExtension(config, options)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **`state-machine.ts`** is the deep module. It owns the phase graph, artifact gating, skip rules, the terminal close gate, and the reentrancy guard. It imports nothing external — no `node:fs`, no pi API, no `pi-tui`, no `typebox`. Every method returns a `WorkflowEffect` (a discriminated union in domain vocabulary) that the adapter pattern-matches on.
|
|
18
|
+
- **`adapter.ts`** is the thin glue. One state-machine method per call site (`start` tool, `next` tool, `end` tool, `tool_result` auto-advance, `/<command>`) plus one `applyEffect` switch. It owns all pi/fs wiring and the git-based `reuse` skip predicate.
|
|
19
|
+
- **A mode file** is pure data: the phase list, the per-phase artifact filenames, the per-phase prompt generators, the terminal close artifacts, and identity strings (tool names, command name, status key, entry type). Prompts are functions that receive `{ artifactDir, userPrompt }` and return a string.
|
|
20
|
+
|
|
21
|
+
## To add a future mode
|
|
22
|
+
|
|
23
|
+
Copy `modes/quick.ts` (the smaller one) and change the config. That's the whole change — the engine never needs editing.
|
|
24
|
+
|
|
25
|
+
### 1. Create the mode file
|
|
26
|
+
|
|
27
|
+
`src/extensions/workflow/modes/rigorous.ts`:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import type {
|
|
31
|
+
Phase,
|
|
32
|
+
PromptContext,
|
|
33
|
+
WorkflowConfig,
|
|
34
|
+
} from "../state-machine.ts";
|
|
35
|
+
import { createWorkflowExtension } from "../adapter.ts";
|
|
36
|
+
|
|
37
|
+
const phases: Phase[] = [
|
|
38
|
+
"grilling",
|
|
39
|
+
"spec", // ← new phase, not in the built-in set
|
|
40
|
+
"research",
|
|
41
|
+
"plan",
|
|
42
|
+
"reuse",
|
|
43
|
+
"handoff",
|
|
44
|
+
"loop",
|
|
45
|
+
"audit",
|
|
46
|
+
"sign-off", // ← new terminal phase
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const prompts: Partial<Record<Phase, (ctx: PromptContext) => string>> = {
|
|
50
|
+
grilling: ({ artifactDir, userPrompt }) => `…grilling prompt…`,
|
|
51
|
+
spec: ({ artifactDir }) => `…spec prompt…`,
|
|
52
|
+
research: ({ artifactDir }) => `…research prompt…`,
|
|
53
|
+
plan: ({ artifactDir }) => `…plan prompt…`,
|
|
54
|
+
reuse: ({ artifactDir }) => `…reuse prompt…`,
|
|
55
|
+
handoff: ({ artifactDir }) => `…handoff prompt…`,
|
|
56
|
+
loop: ({ artifactDir }) => `…loop prompt…`,
|
|
57
|
+
audit: ({ artifactDir }) => `…audit prompt…`,
|
|
58
|
+
"sign-off": ({ artifactDir }) => `…sign-off prompt…`,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const config: WorkflowConfig = {
|
|
62
|
+
mode: "rigorous",
|
|
63
|
+
phases,
|
|
64
|
+
phaseArtifacts: {
|
|
65
|
+
grilling: "requirements.md",
|
|
66
|
+
spec: "spec.md",
|
|
67
|
+
research: "research.md",
|
|
68
|
+
plan: "plan.md",
|
|
69
|
+
reuse: "reuse.md",
|
|
70
|
+
handoff: "handoff.md",
|
|
71
|
+
loop: "loop-complete.md",
|
|
72
|
+
audit: "review.md",
|
|
73
|
+
"sign-off": "acceptance.md",
|
|
74
|
+
},
|
|
75
|
+
prompts,
|
|
76
|
+
// Files that must exist before end() can close the workflow.
|
|
77
|
+
// Config-owned — declare whatever your terminal phase requires.
|
|
78
|
+
closeArtifacts: ["acceptance.md", "sign-off-report.md"],
|
|
79
|
+
statusKey: "rigorous",
|
|
80
|
+
entryType: "rigorous-phase",
|
|
81
|
+
footerLabel: "rigorous",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default createWorkflowExtension(config, {
|
|
85
|
+
commandName: "rigorous",
|
|
86
|
+
commandDescription:
|
|
87
|
+
"Run the rigorous workflow (grill → spec → research → plan → reuse → handoff → loop → audit → sign-off)",
|
|
88
|
+
toolNames: {
|
|
89
|
+
start: "start_rigorous_workflow",
|
|
90
|
+
next: "next_rigorous_step",
|
|
91
|
+
end: "end_rigorous_workflow",
|
|
92
|
+
},
|
|
93
|
+
toolLabels: {
|
|
94
|
+
start: "Start Rigorous Workflow",
|
|
95
|
+
next: "Next Rigorous Step",
|
|
96
|
+
end: "End Rigorous Workflow",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. Register it in the package manifest
|
|
102
|
+
|
|
103
|
+
Add the mode to `src/extensions/workflow/package.json` under `pi.extensions`:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"pi": {
|
|
108
|
+
"extensions": [
|
|
109
|
+
"./modes/prototype.ts",
|
|
110
|
+
"./modes/quick.ts",
|
|
111
|
+
"./modes/rigorous.ts"
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
That's it. The loader picks it up at boot; the three tools (`start_rigorous_workflow`, `next_rigorous_step`, `end_rigorous_workflow`) and the `/rigorous` command are registered automatically.
|
|
118
|
+
|
|
119
|
+
## Config reference
|
|
120
|
+
|
|
121
|
+
| Field | Type | Description |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `mode` | `string` | Mode name, echoed in entry payloads and messages. |
|
|
124
|
+
| `phases` | `Phase[]` | Ordered phase list. `Phase` is `string` — new phase names are allowed. |
|
|
125
|
+
| `phaseArtifacts` | `Partial<Record<Phase, string>>` | The artifact file each phase must produce before advancing. Omit a phase to skip its gate. |
|
|
126
|
+
| `prompts` | `Partial<Record<Phase, (ctx) => string>>` | Prompt generator per phase. `ctx = { artifactDir, userPrompt }`. |
|
|
127
|
+
| `closeArtifacts` | `string[]` | Files that must exist before `end()` succeeds. Config-owned, no built-in default. |
|
|
128
|
+
| `skipRules?` | `{ phase, shouldSkip }[]` | Optional per-phase skip rules. `shouldSkip` is a boolean predicate; when true the engine skips to the next phase. Omit to use the adapter's default (skip `reuse` on empty projects). |
|
|
129
|
+
| `statusKey` | `string` | Footer status key. |
|
|
130
|
+
| `entryType` | `string` | Persisted entry custom-type, used for session resume. |
|
|
131
|
+
| `footerLabel` | `string` | Label shown in the footer (`● label · step/total phase`). |
|
|
132
|
+
|
|
133
|
+
### Adapter options
|
|
134
|
+
|
|
135
|
+
The second argument to `createWorkflowExtension`:
|
|
136
|
+
|
|
137
|
+
| Field | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `toolNames` | `{ start, next, end }` — the three registered tool names. |
|
|
140
|
+
| `toolLabels` | Human-readable labels for the tools. |
|
|
141
|
+
| `commandName` | The `/<command>` name users type to kick off the workflow. |
|
|
142
|
+
| `commandDescription` | Description shown in the command list. |
|
|
143
|
+
|
|
144
|
+
## How transitions work
|
|
145
|
+
|
|
146
|
+
Every state-machine method returns a `WorkflowEffect` — a discriminated union the adapter switches on:
|
|
147
|
+
|
|
148
|
+
| Effect | Meaning |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `started` | `start()` succeeded; first phase prompt + entry + footer. |
|
|
151
|
+
| `alreadyActive` | `start()` called while a workflow is active. |
|
|
152
|
+
| `advanced` | Phase moved forward (optionally `skipped` a phase). |
|
|
153
|
+
| `blocked` | Current phase's artifact is missing. |
|
|
154
|
+
| `terminalNeedsArtifacts` | At the last phase; a close artifact is missing. |
|
|
155
|
+
| `terminalReady` | At the last phase; all close artifacts present — call `end()`. |
|
|
156
|
+
| `closed` | `end()` succeeded; workflow finished. |
|
|
157
|
+
| `endBlocked` | `end()` called from the wrong phase or with close artifacts missing. |
|
|
158
|
+
| `idle` | No active workflow. |
|
|
159
|
+
| `noOp` | Auto-advance checked, nothing to do (not active, not armed, artifact not present, or already advancing). |
|
|
160
|
+
|
|
161
|
+
The `tool_result` auto-advance hook is one line:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const eff = await sm.onArtifactMaybe(deps);
|
|
165
|
+
applyEffect(pi, ctx, config, eff);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The reentrancy guard lives inside `onArtifactMaybe` — concurrent calls return `noOp`, so a double `write` in one turn cannot double-advance the phase.
|
|
169
|
+
|
|
170
|
+
## Skip rules
|
|
171
|
+
|
|
172
|
+
By default the adapter skips the `reuse` phase when the project has no git history. To override, supply `skipRules` in your config:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
skipRules: [
|
|
176
|
+
{ phase: "research", shouldSkip: async () => isWellUnderstoodDomain() },
|
|
177
|
+
{ phase: "reuse", shouldSkip: async () => isEmptyProject() },
|
|
178
|
+
],
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`shouldSkip` is a boolean predicate. When it returns `true`, the engine skips to the next phase in `phases` — the mode owns the transition graph, not the adapter.
|
|
182
|
+
|
|
183
|
+
## Testing a mode
|
|
184
|
+
|
|
185
|
+
The state machine is tested directly with in-memory stubs — no filesystem, no pi mock, no events:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { WorkflowStateMachine } from "../extensions/workflow/state-machine.ts";
|
|
189
|
+
|
|
190
|
+
const files = new Set<string>();
|
|
191
|
+
const deps = {
|
|
192
|
+
async artifactExists(phase, dir) {
|
|
193
|
+
const file = config.phaseArtifacts[phase];
|
|
194
|
+
return file ? files.has(`${dir}/${file}`) : true;
|
|
195
|
+
},
|
|
196
|
+
async fileExists(path) { return files.has(path); },
|
|
197
|
+
async mkdirArtifactDir() {},
|
|
198
|
+
artifactPathFor: (goal) => `/fake/${goal}`,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const sm = new WorkflowStateMachine(config);
|
|
202
|
+
const eff = await sm.start("build X", deps);
|
|
203
|
+
expect(eff.kind).toBe("started");
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
See `src/__tests__/state-machine.test.ts` for the full set of transition, skip, terminal, rehydrate, and validation tests.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selesai/code",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Selesai coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
"chalk": "5.6.2",
|
|
39
39
|
"cross-spawn": "7.0.6",
|
|
40
40
|
"diff": "8.0.4",
|
|
41
|
+
"@mozilla/readability": "^0.6.0",
|
|
42
|
+
"cheerio": "^1.1.0",
|
|
43
|
+
"jsdom": "^26.0.0",
|
|
44
|
+
"playwright": "^1.60.0",
|
|
41
45
|
"glob": "13.0.6",
|
|
42
46
|
"highlight.js": "10.7.3",
|
|
43
47
|
"hosted-git-info": "9.0.3",
|
package/README.md
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
# @selesai/code
|
|
2
|
-
|
|
3
|
-
Selesai coding agent — a terminal-based AI coding agent built on the
|
|
4
|
-
[`pi`](https://github.com/earendil-works/pi-coding-agent) runtime.
|
|
5
|
-
|
|
6
|
-
This repo is the Selesai-branded fork/repackage. It builds the same TypeScript
|
|
7
|
-
source and ships a `selesai` CLI binary plus an importable SDK.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Prerequisites
|
|
12
|
-
|
|
13
|
-
- **Node.js ≥ 20** (ESM, `Node16` module resolution)
|
|
14
|
-
- **npm ≥ 10**
|
|
15
|
-
- A POSIX shell (`shx` handles cross-platform file ops in build scripts)
|
|
16
|
-
- No native compilation required for the agent itself; one dependency
|
|
17
|
-
(`@silvia-odwyer/photon-node`) ships prebuilt native binaries via npm
|
|
18
|
-
optionalDependencies, so just `npm install`.
|
|
19
|
-
|
|
20
|
-
## Install (from source)
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
git clone git@github.com:SelesaiInTech/selesai-code.git
|
|
24
|
-
cd selesai-code
|
|
25
|
-
npm install
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Build
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm run build
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
What it does:
|
|
35
|
-
|
|
36
|
-
1. `tsgo -p tsconfig.build.json` — compiles `src/**/*.ts` → `dist/` with
|
|
37
|
-
declarations, source maps, and rewritten `.ts`→`.js` import extensions.
|
|
38
|
-
2. `chmod +x dist/cli.js` — makes the CLI entrypoint executable.
|
|
39
|
-
3. `copy-assets` — copies non-TS assets into `dist/`:
|
|
40
|
-
- `src/modes/interactive/theme/*.json`
|
|
41
|
-
- `src/modes/interactive/assets/*.png`
|
|
42
|
-
- `src/core/export-html/{template.html,template.css,template.js}`
|
|
43
|
-
- `src/core/export-html/vendor/*.js`
|
|
44
|
-
|
|
45
|
-
Clean rebuild:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
npm run clean && npm run build
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Run locally
|
|
52
|
-
|
|
53
|
-
After building, invoke the CLI directly:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
./dist/cli.js
|
|
57
|
-
# or link it globally:
|
|
58
|
-
npm link
|
|
59
|
-
selesai
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Or run from source without linking (TypeScript via `jiti` is a runtime dep):
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
node --experimental-strip-types src/cli.ts
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
> Note: the `bin` field maps `selesai` → `dist/cli.js`, so the published
|
|
69
|
-
> package exposes a `selesai` command once installed.
|
|
70
|
-
|
|
71
|
-
## Project layout
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
src/
|
|
75
|
-
cli.ts CLI entrypoint
|
|
76
|
-
index.ts public SDK exports (main/types)
|
|
77
|
-
cli/startup-ui.ts pre-session TUI prompts (first-run setup, selectors)
|
|
78
|
-
modes/ interactive, non-interactive, headless modes
|
|
79
|
-
core/ agent loop, settings, package manager, export-html
|
|
80
|
-
config.ts app name, config dir, paths
|
|
81
|
-
docs/ user + contributor documentation
|
|
82
|
-
examples/ extension / SDK examples
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Config dir on disk is `~/.selesai` (see `piConfig.configDir` in
|
|
86
|
-
`package.json`).
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Publish to npm
|
|
91
|
-
|
|
92
|
-
The package is published as **`@selesai/code`** (scoped). There is **no
|
|
93
|
-
`prepare`/`prepublishOnly` script** — you must build before publishing so
|
|
94
|
-
`dist/` exists and is included by the `files` allowlist.
|
|
95
|
-
|
|
96
|
-
### 1. Authenticate
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# one-time: log in to npm under the @selesai scope owner account
|
|
100
|
-
npm login
|
|
101
|
-
npm whoami # confirm you're authenticated
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
> Publishing a scoped package publicly requires the Selesai org (or your
|
|
105
|
-
> account) to own the `@selesai` scope. If the scope isn't created yet:
|
|
106
|
-
> ```bash
|
|
107
|
-
> npm org create selesai <your-npm-username>
|
|
108
|
-
> ```
|
|
109
|
-
|
|
110
|
-
### 2. Bump version
|
|
111
|
-
|
|
112
|
-
There's no `version` script wired up; use the standard flow:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
npm version patch # 0.1.0 -> 0.1.1 (bugfix)
|
|
116
|
-
npm version minor # 0.1.0 -> 0.2.0 (backward-compatible feature)
|
|
117
|
-
npm version major # 0.1.0 -> 1.0.0 (breaking)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
This updates `package.json`, commits, and tags. Update `CHANGELOG.md`
|
|
121
|
-
in the same commit if you maintain one (it's in the `files` allowlist).
|
|
122
|
-
|
|
123
|
-
### 3. Build, verify pack contents, publish
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
# build dist/ (REQUIRED — npm publishes whatever is on disk)
|
|
127
|
-
npm run build
|
|
128
|
-
|
|
129
|
-
# dry-run: inspect exactly what gets published (must NOT include src/)
|
|
130
|
-
npm pack --dry-run
|
|
131
|
-
|
|
132
|
-
# publish to the public registry
|
|
133
|
-
npm publish --access public
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
`--access public` is mandatory for scoped packages unless you intend a paid
|
|
137
|
-
private package; without it the publish fails by default.
|
|
138
|
-
|
|
139
|
-
### 4. Verify
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
npm view @selesai/code version
|
|
143
|
-
# or in a clean sandbox:
|
|
144
|
-
npx @selesai/code@latest --help
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Continuous publishing (CI)
|
|
148
|
-
|
|
149
|
-
Minimal GitHub Actions snippet — adapt to your runner auth (e.g. an
|
|
150
|
-
`NPM_TOKEN` secret with publish rights on `@selesai`):
|
|
151
|
-
|
|
152
|
-
```yaml
|
|
153
|
-
# .github/workflows/publish.yml
|
|
154
|
-
name: publish
|
|
155
|
-
on:
|
|
156
|
-
release:
|
|
157
|
-
types: [published]
|
|
158
|
-
jobs:
|
|
159
|
-
publish:
|
|
160
|
-
runs-on: ubuntu-latest
|
|
161
|
-
permissions:
|
|
162
|
-
contents: read
|
|
163
|
-
steps:
|
|
164
|
-
- uses: actions/checkout@v4
|
|
165
|
-
- uses: actions/setup-node@v4
|
|
166
|
-
with:
|
|
167
|
-
node-version: 20
|
|
168
|
-
registry-url: https://registry.npmjs.org
|
|
169
|
-
- run: npm ci
|
|
170
|
-
- run: npm run build
|
|
171
|
-
- run: npm publish --access public
|
|
172
|
-
env:
|
|
173
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Publishing checklist
|
|
177
|
-
|
|
178
|
-
- [ ] `npm run clean && npm run build` succeeds with no errors
|
|
179
|
-
- [ ] `npm pack --dry-run` shows only `dist/`, `docs/`, `examples/`,
|
|
180
|
-
`CHANGELOG.md`, `package.json`, `README.md`, `LICENSE` — **never `src/`**
|
|
181
|
-
- [ ] Version bumped and committed
|
|
182
|
-
- [ ] Logged in as an account with publish rights on `@selesai`
|
|
183
|
-
- [ ] `--access public` passed (scoped package)
|
|
184
|
-
- [ ] `npm view @selesai/code version` matches the version you intended
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## Notes & gotchas
|
|
189
|
-
|
|
190
|
-
- **No `prepublishOnly` hook.** Forgetting `npm run build` before
|
|
191
|
-
`npm publish` will publish a stale (or empty) `dist/`. Always build.
|
|
192
|
-
- **`tsgo`** is `@typescript/native-preview` (the Go-backed TS compiler
|
|
193
|
-
preview) — it's a devDependency, so CI/`npm ci` installs it automatically.
|
|
194
|
-
- **Asset copy is manual**, not a bundler step. If you add new JSON/PNG/HTML
|
|
195
|
-
themes or vendor JS, update the `copy-assets` script in `package.json`.
|
|
196
|
-
- **Scoped publish.** `@selesai/code` is under a scope; first-time publish
|
|
197
|
-
needs `--access public` (public visibility is free, private costs money).
|
|
198
|
-
- **Binary name.** Users get a `selesai` command after `npm i -g @selesai/code`.
|