vibes-diy 2.6.1 → 2.6.2

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 (35) hide show
  1. package/LICENSE.md +19 -50
  2. package/README.md +27 -19
  3. package/cli/cmds/chat-response-render.d.ts +10 -0
  4. package/cli/cmds/chat-response-render.js +94 -0
  5. package/cli/cmds/chat-response-render.js.map +1 -0
  6. package/cli/cmds/chat-response-render.test.d.ts +1 -0
  7. package/cli/cmds/chat-response-render.test.js +140 -0
  8. package/cli/cmds/chat-response-render.test.js.map +1 -0
  9. package/cli/cmds/chats-cmd.d.ts +25 -1
  10. package/cli/cmds/chats-cmd.js +107 -3
  11. package/cli/cmds/chats-cmd.js.map +1 -1
  12. package/cli/cmds/chats-cmd.test.js +23 -0
  13. package/cli/cmds/chats-cmd.test.js.map +1 -1
  14. package/cli/cmds/db/subscribe-cmd.js +1 -1
  15. package/cli/cmds/db/subscribe-cmd.js.map +1 -1
  16. package/cli/cmds/db/subscribe-cmd.test.js +5 -1
  17. package/cli/cmds/db/subscribe-cmd.test.js.map +1 -1
  18. package/cli/cmds/edit-cmd.js +4 -3
  19. package/cli/cmds/edit-cmd.js.map +1 -1
  20. package/cli/cmds/generate-cmd.js +7 -4
  21. package/cli/cmds/generate-cmd.js.map +1 -1
  22. package/cli/cmds/generate-cmd.test.js +140 -1
  23. package/cli/cmds/generate-cmd.test.js.map +1 -1
  24. package/cli/cmds/push-from-dir.d.ts +2 -0
  25. package/cli/cmds/push-from-dir.js +1 -0
  26. package/cli/cmds/push-from-dir.js.map +1 -1
  27. package/cli/cmds/push-from-dir.test.js +22 -0
  28. package/cli/cmds/push-from-dir.test.js.map +1 -1
  29. package/cli/cmds/resolved-model.js +6 -1
  30. package/cli/cmds/resolved-model.js.map +1 -1
  31. package/cli/cmds/resolved-model.test.js +27 -0
  32. package/cli/cmds/resolved-model.test.js.map +1 -1
  33. package/cli/main.js +5 -1
  34. package/cli/main.js.map +1 -1
  35. package/package.json +6 -6
package/LICENSE.md CHANGED
@@ -1,32 +1,3 @@
1
- The contents of this repository are Copyright (c) corresponding authors and
2
- contributors, licensed under the `Permissive License Stack` meaning either of:
3
-
4
- - Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0
5
- ([...4tr2kfsq](https://dweb.link/ipfs/bafkreiankqxazcae4onkp436wag2lj3ccso4nawxqkkfckd6cg4tr2kfsq))
6
-
7
- - MIT Software License: https://opensource.org/licenses/MIT
8
- ([...vljevcba](https://dweb.link/ipfs/bafkreiepofszg4gfe2gzuhojmksgemsub2h4uy2gewdnr35kswvljevcba))
9
-
10
- You may not use the contents of this repository except in compliance
11
- with one of the listed Licenses. For an extended clarification of the
12
- intent behind the choice of Licensing please refer to
13
- https://protocol.ai/blog/announcing-the-permissive-license-stack/
14
-
15
- Unless required by applicable law or agreed to in writing, software
16
- distributed under the terms listed in this notice is distributed on
17
- an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
18
- either express or implied. See each License for the specific language
19
- governing permissions and limitations under that License.
20
-
21
- <!--- SPDX-License-Identifier: Apache-2.0 OR MIT -->
22
-
23
- `SPDX-License-Identifier: Apache-2.0 OR MIT`
24
-
25
- Verbatim copies of both licenses are included below:
26
-
27
- <details><summary>Apache-2.0 Software License</summary>
28
-
29
- ```
30
1
  Apache License
31
2
  Version 2.0, January 2004
32
3
  http://www.apache.org/licenses/
@@ -203,30 +174,28 @@ Verbatim copies of both licenses are included below:
203
174
  of your accepting any such warranty or additional liability.
204
175
 
205
176
  END OF TERMS AND CONDITIONS
206
- ```
207
177
 
208
- </details>
178
+ APPENDIX: How to apply the Apache License to your work.
209
179
 
210
- <details><summary>MIT Software License</summary>
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
211
188
 
212
- ```
213
- Permission is hereby granted, free of charge, to any person obtaining a copy
214
- of this software and associated documentation files (the "Software"), to deal
215
- in the Software without restriction, including without limitation the rights
216
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
217
- copies of the Software, and to permit persons to whom the Software is
218
- furnished to do so, subject to the following conditions:
189
+ Copyright [yyyy] [name of copyright owner]
219
190
 
220
- The above copyright notice and this permission notice shall be included in
221
- all copies or substantial portions of the Software.
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
222
194
 
223
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
224
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
225
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
226
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
227
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
228
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
229
- THE SOFTWARE.
230
- ```
195
+ http://www.apache.org/licenses/LICENSE-2.0
231
196
 
232
- </details>
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md CHANGED
@@ -1,23 +1,38 @@
1
1
  [![Join our
2
- Discord](https://discord-badge.selem.workers.dev/i/weSgWt684k.svg)](https://discord.gg/weSgWt684k)
2
+ Discord](https://discord-badge.selem.workers.dev/i/vnpWycj4Ta.svg)](https://discord.gg/vnpWycj4Ta)
3
3
 
4
- # ✨ Build Mini Apps with AI Magic
4
+ # ✨ Vibes DIY make apps with your friends
5
5
 
6
- Turn your **ideas** into **interactive apps** instantly. [Try it now](https://vibes.diy/) or [fork on GitHub](https://github.com/VibesDIY/vibes.diy) to customize with your own AI account.
6
+ Software is getting weird again. Describe what you want and get a real, live app — fun, done, and alive. [Try it now](https://vibes.diy/) or [fork on GitHub](https://github.com/VibesDIY/vibes.diy).
7
7
 
8
- Create beautiful, interactive mini apps with zero setup. Your creations are automatically saved and can be shared instantly.
8
+ Make apps with your friends, so easy even AI can do it. Describe your app in plain words, it builds instantly, and you keep changing it just by talking to it. Share the link and your friends join right away — no setup, no "wait, let me send you the invite."
9
9
 
10
- ## Features
10
+ ## Why it's different
11
11
 
12
- - Create interactive mini apps using AI
13
- - Browse and return to your previous creations
14
- - Auto-save your work securely
15
- - See your app changes in real-time
12
+ - **Fun** build for any reason, or no reason at all.
13
+ - **Done** it's a real app at its own live link, not a prototype. State is saved automatically.
14
+ - **Alive** keep talking to it and it keeps changing. So can anyone you share it with.
15
+
16
+ Seeing a vibe makes you want to change it. Start from someone else's, tweak it to fit you, share it, and the people you share with remix their own version. You're not a user anymore — you're a maker.
17
+
18
+ ## What people make
19
+
20
+ A character bot that answers in your voice. A neighborhood resource tracker you share at the next town meeting. A flashcard app remixed into collaborative trivia your students change live. A workout tracker a whole gym swaps routines on. Every one is the same move: start, tweak, share, remix.
21
+
22
+ ## Yours to control
23
+
24
+ Private by default — or open to the world. You choose. Keep an app to the people you invite, or publish it so anyone can use and remix it. You set who can read, write, and open it.
25
+
26
+ > Your app's data lives in a centralized cloud database. "Private" means access-gated from other people — not encrypted, and not hidden from the platform.
27
+
28
+ ## Open source
29
+
30
+ The whole platform is open source under Apache 2.0. Read the code, open an issue, send a PR.
16
31
 
17
32
  ## Quick Start
18
33
 
19
- 1. [Try the demo](https://vibes.diy/) - No setup required!
20
- 2. Or set up your own:
34
+ 1. [Try the demo](https://vibes.diy/) no setup required.
35
+ 2. Or run it yourself:
21
36
  - Clone the repository
22
37
  - Install with `pnpm i`
23
38
  - Run `pnpm dev`
@@ -28,11 +43,4 @@ We are committed to providing a welcoming and inclusive environment for all cont
28
43
 
29
44
  Interested in contributing? Check out our [Contributing Guide](CONTRIBUTING.md) to get started.
30
45
 
31
- ## Your Work is Always Safe
32
-
33
- Every app you create is automatically saved, so you can:
34
-
35
- - Browse your creations in the sidebar
36
- - Return to previous work anytime
37
- - Keep track of your building history
38
- - Save screenshots of your apps
46
+ Find us on [Discord](https://discord.gg/vnpWycj4Ta), [Substack](https://vibesdiy.substack.com/), [YouTube](https://www.youtube.com/@VibesDIY), and [Bluesky](https://bsky.app/profile/vibes.diy).
@@ -0,0 +1,10 @@
1
+ import { Result } from "@adviser/cement";
2
+ import type { PromptAndBlockMsgs, ResChatResponseTurn, SectionEvent } from "@vibes.diy/api-types";
3
+ import { type ResolveSectionStreamResult } from "./resolve-section-stream.js";
4
+ export declare function turnBlocks(turn: ResChatResponseTurn): PromptAndBlockMsgs[];
5
+ export declare function reconstructVerbatim(blocks: readonly PromptAndBlockMsgs[]): string;
6
+ export declare function renderJsonl(blocks: readonly PromptAndBlockMsgs[]): string;
7
+ export declare function extractRawText(blocks: readonly PromptAndBlockMsgs[]): string | undefined;
8
+ export declare function extractUserPrompts(blocks: readonly PromptAndBlockMsgs[]): string[];
9
+ export declare function buildSectionStream(turn: ResChatResponseTurn): ReadableStream<SectionEvent>;
10
+ export declare function resolveTurnFiles(turns: readonly ResChatResponseTurn[], selectedPromptId: string): Promise<Result<ResolveSectionStreamResult>>;
@@ -0,0 +1,94 @@
1
+ import { Result } from "@adviser/cement";
2
+ import { isCodeBegin, isCodeEnd, isCodeLine, isToplevelLine } from "@vibes.diy/call-ai-v2";
3
+ import { isPromptRaw, isPromptReq } from "@vibes.diy/api-types";
4
+ import { resolveSectionStream } from "./resolve-section-stream.js";
5
+ export function turnBlocks(turn) {
6
+ return turn.sections.flatMap((s) => s.blocks);
7
+ }
8
+ export function reconstructVerbatim(blocks) {
9
+ const out = [];
10
+ for (const b of blocks) {
11
+ if (isToplevelLine(b)) {
12
+ out.push(b.line);
13
+ }
14
+ else if (isCodeBegin(b)) {
15
+ const info = b.path ? `${b.lang} ${b.path}` : b.lang;
16
+ out.push("```" + info);
17
+ }
18
+ else if (isCodeLine(b)) {
19
+ out.push(b.line);
20
+ }
21
+ else if (isCodeEnd(b)) {
22
+ out.push("```");
23
+ }
24
+ }
25
+ return out.join("\n");
26
+ }
27
+ export function renderJsonl(blocks) {
28
+ return blocks.map((b) => JSON.stringify(b)).join("\n");
29
+ }
30
+ export function extractRawText(blocks) {
31
+ const parts = blocks.filter(isPromptRaw).map((b) => b.text);
32
+ return parts.length === 0 ? undefined : parts.join("");
33
+ }
34
+ export function extractUserPrompts(blocks) {
35
+ const prompts = [];
36
+ for (const b of blocks) {
37
+ if (!isPromptReq(b))
38
+ continue;
39
+ for (const m of b.request.messages) {
40
+ if (m.role !== "user")
41
+ continue;
42
+ const text = m.content
43
+ .filter((c) => c.type === "text")
44
+ .map((c) => c.text)
45
+ .join("\n");
46
+ if (text !== "")
47
+ prompts.push(text);
48
+ }
49
+ }
50
+ return prompts;
51
+ }
52
+ export function buildSectionStream(turn) {
53
+ const events = turn.sections.map((s) => ({
54
+ type: "vibes.diy.section-event",
55
+ chatId: turn.chatId,
56
+ promptId: turn.promptId,
57
+ blockSeq: s.blockSeq,
58
+ timestamp: new Date(0),
59
+ blocks: s.blocks,
60
+ }));
61
+ return new ReadableStream({
62
+ start(controller) {
63
+ for (const e of events)
64
+ controller.enqueue(e);
65
+ controller.close();
66
+ },
67
+ });
68
+ }
69
+ export async function resolveTurnFiles(turns, selectedPromptId) {
70
+ const selected = turns.find((t) => t.promptId === selectedPromptId);
71
+ if (selected === undefined) {
72
+ return Result.Err(new Error(`No turn with promptId ${selectedPromptId}`));
73
+ }
74
+ const chain = turns
75
+ .filter((t) => t.chatId === selected.chatId)
76
+ .slice()
77
+ .sort((a, b) => (a.created < b.created ? -1 : a.created > b.created ? 1 : 0));
78
+ let seed = new Map();
79
+ let last;
80
+ for (const t of chain) {
81
+ const r = await resolveSectionStream({ sectionStream: buildSectionStream(t), streamId: t.promptId, seed });
82
+ if (r.isErr())
83
+ return Result.Err(r.Err());
84
+ last = r.Ok();
85
+ seed = new Map(Object.entries(last.files));
86
+ if (t.promptId === selected.promptId)
87
+ break;
88
+ }
89
+ if (last === undefined) {
90
+ return Result.Ok({ files: {}, errors: [], snapshotCount: 0, applyErrorCount: 0, turnEndSeen: false, promptErrors: [] });
91
+ }
92
+ return Result.Ok(last);
93
+ }
94
+ //# sourceMappingURL=chat-response-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-response-render.js","sourceRoot":"","sources":["../../../jsr/cli/cmds/chat-response-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,140 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildSectionStream, extractRawText, extractUserPrompts, reconstructVerbatim, renderJsonl, turnBlocks, } from "./chat-response-render.js";
3
+ import { resolveSectionStream } from "./resolve-section-stream.js";
4
+ const base = (seq) => ({
5
+ blockId: "blk",
6
+ streamId: "stream-1",
7
+ seq,
8
+ blockNr: 0,
9
+ timestamp: new Date(0),
10
+ });
11
+ function toplevelLine(seq, line) {
12
+ return { type: "block.toplevel.line", sectionId: "s", lineNr: seq, line, ...base(seq) };
13
+ }
14
+ function codeBegin(seq, lang, path) {
15
+ return { type: "block.code.begin", sectionId: "s", lang, path, ...base(seq) };
16
+ }
17
+ function codeLine(seq, lang, path, line) {
18
+ return { type: "block.code.line", sectionId: "s", lang, path, lineNr: seq, line, ...base(seq) };
19
+ }
20
+ function codeEnd(seq, lang, path) {
21
+ return {
22
+ type: "block.code.end",
23
+ sectionId: "s",
24
+ lang,
25
+ path,
26
+ stats: { lines: 1, bytes: 1 },
27
+ ...base(seq),
28
+ };
29
+ }
30
+ describe("reconstructVerbatim", () => {
31
+ it("renders prose, a fenced code block with its bound path, and close fence", () => {
32
+ const blocks = [
33
+ toplevelLine(0, "Here is the app:"),
34
+ codeBegin(1, "jsx", "App.jsx"),
35
+ codeLine(2, "jsx", "App.jsx", "export default function App() {}"),
36
+ codeEnd(3, "jsx", "App.jsx"),
37
+ ];
38
+ expect(reconstructVerbatim(blocks)).toBe(["Here is the app:", "```jsx App.jsx", "export default function App() {}", "```"].join("\n"));
39
+ });
40
+ it("surfaces a path clobber: an orphaned label as prose, then a fence bound to the wrong path", () => {
41
+ const blocks = [
42
+ codeBegin(0, "jsx", "App.jsx"),
43
+ codeLine(1, "jsx", "App.jsx", "const a = 1;"),
44
+ codeEnd(2, "jsx", "App.jsx"),
45
+ toplevelLine(3, "access.js"),
46
+ toplevelLine(4, ""),
47
+ codeBegin(5, "js", "App.jsx"),
48
+ codeLine(6, "js", "App.jsx", "export const x = 1;"),
49
+ codeEnd(7, "js", "App.jsx"),
50
+ ];
51
+ const out = reconstructVerbatim(blocks);
52
+ expect(out).toContain("access.js");
53
+ expect(out.match(/```js App\.jsx/g)).toHaveLength(1);
54
+ expect(out.match(/```jsx App\.jsx/g)).toHaveLength(1);
55
+ });
56
+ it("omits the path from the info-string when a fence carries none", () => {
57
+ const blocks = [{ type: "block.code.begin", sectionId: "s", lang: "txt", ...base(0) }];
58
+ expect(reconstructVerbatim(blocks)).toBe("```txt");
59
+ });
60
+ });
61
+ describe("renderJsonl", () => {
62
+ it("emits one JSON object per line", () => {
63
+ const blocks = [toplevelLine(0, "a"), codeBegin(1, "js", "x.js")];
64
+ const lines = renderJsonl(blocks).split("\n");
65
+ expect(lines).toHaveLength(2);
66
+ expect(JSON.parse(lines[0]).type).toBe("block.toplevel.line");
67
+ expect(JSON.parse(lines[1]).type).toBe("block.code.begin");
68
+ });
69
+ });
70
+ describe("extractRawText", () => {
71
+ const rawBlock = (text) => ({
72
+ type: "prompt.raw",
73
+ streamId: "stream-1",
74
+ chatId: "c",
75
+ seq: 9,
76
+ timestamp: new Date(0),
77
+ text,
78
+ });
79
+ it("returns the byte-faithful text from a prompt.raw block", () => {
80
+ const blocks = [toplevelLine(0, "App.jsx"), rawBlock("App.jsx\n\n```jsx\nconst a=1;\n```\n")];
81
+ expect(extractRawText(blocks)).toBe("App.jsx\n\n```jsx\nconst a=1;\n```\n");
82
+ });
83
+ it("returns undefined when no prompt.raw block is present (un-backfilled turn)", () => {
84
+ expect(extractRawText([toplevelLine(0, "hi"), codeBegin(1, "jsx", "App.jsx")])).toBeUndefined();
85
+ });
86
+ });
87
+ describe("extractUserPrompts", () => {
88
+ it("pulls user message text from prompt.req blocks", () => {
89
+ const promptReq = {
90
+ type: "prompt.req",
91
+ streamId: "stream-1",
92
+ chatId: "c",
93
+ seq: 0,
94
+ timestamp: new Date(0),
95
+ request: {
96
+ messages: [
97
+ { role: "system", content: [{ type: "text", text: "ignore me" }] },
98
+ { role: "user", content: [{ type: "text", text: "build a todo app" }] },
99
+ ],
100
+ },
101
+ };
102
+ expect(extractUserPrompts([promptReq])).toEqual(["build a todo app"]);
103
+ });
104
+ });
105
+ describe("buildSectionStream + resolveSectionStream", () => {
106
+ it("resolves files from a stored turn via the shared generate/edit resolver", async () => {
107
+ const blocks = [
108
+ { type: "block.begin", ...base(0) },
109
+ codeBegin(0, "jsx", "App.jsx"),
110
+ codeLine(1, "jsx", "App.jsx", "export default function App() { return null; }"),
111
+ codeEnd(2, "jsx", "App.jsx"),
112
+ {
113
+ type: "block.end",
114
+ stats: {
115
+ toplevel: { lines: 0, bytes: 0 },
116
+ code: { lines: 1, bytes: 1 },
117
+ image: { lines: 0, bytes: 0 },
118
+ total: { lines: 1, bytes: 1 },
119
+ },
120
+ usage: { given: [], calculated: {} },
121
+ ...base(3),
122
+ },
123
+ ];
124
+ const turn = {
125
+ chatId: "c",
126
+ promptId: "stream-1",
127
+ created: "2026-01-01T00:00:00Z",
128
+ sections: [{ blockSeq: 0, blocks }],
129
+ };
130
+ expect(turnBlocks(turn)).toHaveLength(blocks.length);
131
+ const rResolved = await resolveSectionStream({
132
+ sectionStream: buildSectionStream(turn),
133
+ streamId: "stream-1",
134
+ });
135
+ expect(rResolved.isOk()).toBe(true);
136
+ const files = rResolved.Ok().files;
137
+ expect(files["App.jsx"]).toContain("export default function App()");
138
+ });
139
+ });
140
+ //# sourceMappingURL=chat-response-render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-response-render.test.js","sourceRoot":"","sources":["../../../jsr/cli/cmds/chat-response-render.test.ts"],"names":[],"mappings":"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"}
@@ -7,9 +7,21 @@ export declare const ReqChats: import("arktype/internal/variants/object.ts").Obj
7
7
  ownerHandle: string;
8
8
  chatId?: string | undefined;
9
9
  apiUrl: string;
10
+ response?: boolean | undefined;
11
+ raw?: boolean | undefined;
12
+ files?: boolean | undefined;
13
+ jsonl?: boolean | undefined;
14
+ user?: boolean | undefined;
15
+ promptId?: string | undefined;
10
16
  }, {}>;
11
17
  export type ReqChats = typeof ReqChats.infer;
12
18
  export declare function isReqChats(obj: unknown): obj is ReqChats;
19
+ export declare const ResChatResponse: import("arktype/internal/variants/object.ts").ObjectType<{
20
+ type: "vibes-diy.cli.res-chat-response";
21
+ output: string;
22
+ }, {}>;
23
+ export type ResChatResponse = typeof ResChatResponse.infer;
24
+ export declare function isResChatResponse(obj: unknown): obj is ResChatResponse;
13
25
  export declare const ResChatsList: import("arktype/internal/variants/object.ts").ObjectType<{
14
26
  type: "vibes-diy.cli.res-chats-list";
15
27
  items: {
@@ -34,7 +46,7 @@ export declare const ResChatDetail: import("arktype/internal/variants/object.ts"
34
46
  }, {}>;
35
47
  export type ResChatDetail = typeof ResChatDetail.infer;
36
48
  export declare function isResChatDetail(obj: unknown): obj is ResChatDetail;
37
- type ResChats = ResChatsList | ResChatDetail;
49
+ type ResChats = ResChatsList | ResChatDetail | ResChatResponse;
38
50
  export declare const chatsEvento: EventoHandler<WrapCmdTSMsg<unknown>, ReqChats, ResChats>;
39
51
  export declare function chatsCmd(ctx: CliCtx): Partial<import("cmd-ts/dist/cjs/argparser.js").Register> & {
40
52
  parse(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
@@ -42,6 +54,12 @@ export declare function chatsCmd(ctx: CliCtx): Partial<import("cmd-ts/dist/cjs/a
42
54
  chatId: string | undefined;
43
55
  vibe: string;
44
56
  handle: string;
57
+ response: boolean;
58
+ raw: boolean;
59
+ files: boolean;
60
+ jsonl: boolean;
61
+ user: boolean;
62
+ turn: string | undefined;
45
63
  apiUrl: string;
46
64
  json: boolean;
47
65
  text: boolean;
@@ -51,6 +69,12 @@ export declare function chatsCmd(ctx: CliCtx): Partial<import("cmd-ts/dist/cjs/a
51
69
  chatId: string | undefined;
52
70
  vibe: string;
53
71
  handle: string;
72
+ response: boolean;
73
+ raw: boolean;
74
+ files: boolean;
75
+ jsonl: boolean;
76
+ user: boolean;
77
+ turn: string | undefined;
54
78
  apiUrl: string;
55
79
  json: boolean;
56
80
  text: boolean;
@@ -1,4 +1,4 @@
1
- import { command, option, optional, positional, string } from "cmd-ts";
1
+ import { command, flag, option, optional, positional, string } from "cmd-ts";
2
2
  import { Result, Option } from "@adviser/cement";
3
3
  import { type } from "arktype";
4
4
  import { resListApplicationChatsItem } from "@vibes.diy/api-types";
@@ -7,16 +7,30 @@ import { sendMsg } from "../cmd-evento.js";
7
7
  import { resolveHandle } from "../resolve-handle.js";
8
8
  import { formatErr } from "./format-err.js";
9
9
  import { resolveVibePositionals } from "../parse-vibe.js";
10
+ import { extractRawText, extractUserPrompts, reconstructVerbatim, renderJsonl, resolveTurnFiles, turnBlocks, } from "./chat-response-render.js";
10
11
  export const ReqChats = type({
11
12
  type: "'vibes-diy.cli.chats'",
12
13
  appSlug: "string",
13
14
  ownerHandle: "string",
14
15
  "chatId?": "string",
15
16
  apiUrl: "string",
17
+ "response?": "boolean",
18
+ "raw?": "boolean",
19
+ "files?": "boolean",
20
+ "jsonl?": "boolean",
21
+ "user?": "boolean",
22
+ "promptId?": "string",
16
23
  });
17
24
  export function isReqChats(obj) {
18
25
  return !(ReqChats(obj) instanceof type.errors);
19
26
  }
27
+ export const ResChatResponse = type({
28
+ type: "'vibes-diy.cli.res-chat-response'",
29
+ output: "string",
30
+ });
31
+ export function isResChatResponse(obj) {
32
+ return !(ResChatResponse(obj) instanceof type.errors);
33
+ }
20
34
  export const ResChatsList = type({
21
35
  type: "'vibes-diy.cli.res-chats-list'",
22
36
  items: resListApplicationChatsItem.array(),
@@ -54,6 +68,69 @@ export const chatsEvento = {
54
68
  const args = ctx.validated;
55
69
  const api = ectx.vibesDiyApiFactory(args.apiUrl);
56
70
  const ownerHandle = await resolveHandle(api, args.ownerHandle === "" ? undefined : args.ownerHandle);
71
+ const wantResponse = args.response === true || args.raw === true || args.files === true || args.jsonl === true || args.user === true;
72
+ if (wantResponse) {
73
+ if (ownerHandle === undefined) {
74
+ return Result.Err("Could not resolve handle. Pass --handle or run 'vibes-diy login'.");
75
+ }
76
+ const rResp = await api.getChatResponse({
77
+ ownerHandle,
78
+ appSlug: args.appSlug,
79
+ ...(args.chatId !== undefined ? { chatId: args.chatId } : {}),
80
+ });
81
+ if (rResp.isErr()) {
82
+ return Result.Err(formatErr(rResp.Err()));
83
+ }
84
+ const turns = rResp.Ok().turns;
85
+ if (turns.length === 0) {
86
+ return sendMsg(ctx, {
87
+ type: "vibes-diy.cli.res-chat-response",
88
+ output: "(no response found for this chat)",
89
+ });
90
+ }
91
+ let turn = turns[0];
92
+ if (args.promptId !== undefined) {
93
+ turn = turns.find((t) => t.promptId === args.promptId);
94
+ if (turn === undefined) {
95
+ return Result.Err(`No turn with promptId ${args.promptId} in this chat.`);
96
+ }
97
+ }
98
+ else if (turns.length > 1) {
99
+ ectx.output.stderr(`Showing newest turn ${turn.promptId} (${turns.length} turns total — use --turn <promptId> to pick another).\n`);
100
+ }
101
+ const blocks = turnBlocks(turn);
102
+ let body;
103
+ if (args.raw === true) {
104
+ const raw = extractRawText(blocks);
105
+ body =
106
+ raw ??
107
+ "(no raw capture for this turn — byte-faithful text is only stored for generations after raw capture shipped; try --jsonl or the default block-faithful view)";
108
+ }
109
+ else if (args.jsonl === true) {
110
+ body = renderJsonl(blocks);
111
+ }
112
+ else if (args.files === true) {
113
+ const rResolved = await resolveTurnFiles(turns, turn.promptId);
114
+ if (rResolved.isErr()) {
115
+ return Result.Err(`Failed to resolve stored stream: ${rResolved.Err().message}`);
116
+ }
117
+ body = JSON.stringify(rResolved.Ok().files, null, 2);
118
+ }
119
+ else {
120
+ body = reconstructVerbatim(blocks);
121
+ }
122
+ let output = body;
123
+ if (args.user === true) {
124
+ const userBlock = extractUserPrompts(blocks)
125
+ .map((p) => p
126
+ .split("\n")
127
+ .map((l) => `> ${l}`)
128
+ .join("\n"))
129
+ .join("\n>\n");
130
+ output = userBlock === "" ? body : `${userBlock}\n\n${body}`;
131
+ }
132
+ return sendMsg(ctx, { type: "vibes-diy.cli.res-chat-response", output });
133
+ }
57
134
  switch (true) {
58
135
  case args.chatId !== undefined: {
59
136
  if (ownerHandle === undefined) {
@@ -128,8 +205,34 @@ export function chatsCmd(ctx) {
128
205
  defaultValue: () => "",
129
206
  defaultValueIsSerializable: true,
130
207
  }),
208
+ response: flag({
209
+ long: "response",
210
+ short: "r",
211
+ description: "Show the model's reply, block-faithfully reconstructed from stored block events, instead of the user prompt",
212
+ }),
213
+ raw: flag({
214
+ long: "raw",
215
+ description: "With --response: byte-faithful raw model text captured upstream of the parser (preserves consumed labels & blank lines; new generations only)",
216
+ }),
217
+ files: flag({
218
+ long: "files",
219
+ description: "With --response: the resolved path→content map (via the generate/edit resolver)",
220
+ }),
221
+ jsonl: flag({
222
+ long: "jsonl",
223
+ description: "With --response: the raw block events, one JSON object per line",
224
+ }),
225
+ user: flag({
226
+ long: "user",
227
+ description: "With --response: also print the user prompt(s) so the full transcript reads top-down",
228
+ }),
229
+ turn: option({
230
+ long: "turn",
231
+ description: "With --response: select a specific turn by promptId (default: newest)",
232
+ type: optional(string),
233
+ }),
131
234
  },
132
- handler: ctx.cliStream.enqueue(({ handle, chatId, vibe, appSlug, ...rest }) => {
235
+ handler: ctx.cliStream.enqueue(({ handle, chatId, vibe, appSlug, turn, ...rest }) => {
133
236
  const resolved = resolveVibePositionals({ vibe, handle, positionals: [appSlug, chatId] });
134
237
  const resolvedChatId = resolved.trailing[0];
135
238
  const base = {
@@ -138,7 +241,8 @@ export function chatsCmd(ctx) {
138
241
  appSlug: resolved.appSlug,
139
242
  ownerHandle: resolved.handle,
140
243
  };
141
- return resolvedChatId === undefined ? base : { ...base, chatId: resolvedChatId };
244
+ const withChat = resolvedChatId === undefined ? base : { ...base, chatId: resolvedChatId };
245
+ return turn === undefined ? withChat : { ...withChat, promptId: turn };
142
246
  }),
143
247
  });
144
248
  }