t3code-cli 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -14
- package/dist/application.js +2 -2
- package/dist/auth.js +1 -1
- package/dist/bin.js +1556 -341
- package/dist/cli.js +1 -1
- package/dist/config.js +1 -1
- package/dist/connection.js +1 -11
- package/dist/index.js +1 -1
- package/dist/layout.js +1 -1
- package/dist/node.js +2 -0
- package/dist/orchestration.js +2 -2
- package/dist/rpc.js +2 -2
- package/dist/runtime.js +1 -1
- package/dist/scope.js +1 -1
- package/dist/shared.js +1734 -756
- package/dist/src/application/index.d.ts +3 -2
- package/dist/src/application/layer.d.ts +82 -841
- package/dist/src/application/model-selection.d.ts +15 -0
- package/dist/src/application/models.d.ts +1 -87
- package/dist/src/application/project-commands.d.ts +9 -0
- package/dist/src/application/projects.d.ts +13 -135
- package/dist/src/application/service.d.ts +84 -5
- package/dist/src/application/shell-sequence.d.ts +2 -12
- package/dist/src/application/terminals.d.ts +66 -0
- package/dist/src/application/thread-commands.d.ts +47 -0
- package/dist/src/application/thread-update.d.ts +8 -0
- package/dist/src/application/thread-wait.d.ts +3 -14
- package/dist/src/application/threads.d.ts +21 -50
- package/dist/src/cli/flags.d.ts +3 -0
- package/dist/src/cli/output-format.d.ts +2 -0
- package/dist/src/domain/error.d.ts +14 -1
- package/dist/src/domain/helpers.d.ts +0 -9
- package/dist/src/domain/model-config.d.ts +29 -3
- package/dist/src/domain/thread-lifecycle.d.ts +2 -10
- package/dist/src/node/index.d.ts +2 -0
- package/dist/src/orchestration/index.d.ts +1 -0
- package/dist/src/orchestration/layer.d.ts +177 -1032
- package/dist/src/orchestration/service.d.ts +1 -0
- package/dist/src/rpc/error.d.ts +4 -2
- package/dist/src/rpc/index.d.ts +5 -0
- package/dist/src/rpc/layer.d.ts +1097 -499
- package/dist/src/rpc/operation.d.ts +5752 -0
- package/dist/src/rpc/ws-group.d.ts +327 -147
- package/dist/src/runtime/layer.d.ts +1 -1
- package/dist/t3tools.js +2 -2
- package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +10 -33
- package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -6
- package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +1 -657
- package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -14
- package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +1 -175
- package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -492
- package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -45
- package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -21
- package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +3 -6
- package/package.json +6 -2
- package/src/application/index.ts +28 -2
- package/src/application/layer.ts +34 -5
- package/src/application/model-selection.ts +68 -2
- package/src/application/models.ts +14 -14
- package/src/application/project-commands.ts +15 -0
- package/src/application/projects.ts +43 -13
- package/src/application/service.ts +196 -77
- package/src/application/shell-sequence.ts +5 -7
- package/src/application/terminals.ts +207 -0
- package/src/application/thread-commands.test.ts +43 -0
- package/src/application/thread-commands.ts +81 -0
- package/src/application/thread-update.ts +72 -0
- package/src/application/thread-wait.ts +46 -47
- package/src/application/threads.test.ts +166 -0
- package/src/application/threads.ts +123 -40
- package/src/bin.ts +2 -0
- package/src/cli/app.ts +14 -0
- package/src/cli/confirm.ts +31 -0
- package/src/cli/error.ts +26 -3
- package/src/cli/flags.ts +15 -0
- package/src/cli/input/layer.ts +20 -0
- package/src/cli/input/service.ts +1 -0
- package/src/cli/output-format.ts +6 -2
- package/src/cli/project-format.ts +7 -0
- package/src/cli/project.ts +2 -1
- package/src/cli/projects/delete.ts +50 -0
- package/src/cli/self-action.ts +34 -0
- package/src/cli/terminal/attach.ts +32 -0
- package/src/cli/terminal/commands.test.ts +101 -0
- package/src/cli/terminal/create.ts +54 -0
- package/src/cli/terminal/destroy.ts +60 -0
- package/src/cli/terminal/encoding.test.ts +63 -0
- package/src/cli/terminal/encoding.ts +23 -0
- package/src/cli/terminal/error.ts +14 -0
- package/src/cli/terminal/io-node-layer.ts +82 -0
- package/src/cli/terminal/io-service.ts +25 -0
- package/src/cli/terminal/list.ts +35 -0
- package/src/cli/terminal/read.ts +102 -0
- package/src/cli/terminal/scope.ts +30 -0
- package/src/cli/terminal/shared.ts +250 -0
- package/src/cli/terminal/stream.ts +64 -0
- package/src/cli/terminal/wait.test.ts +146 -0
- package/src/cli/terminal/wait.ts +222 -0
- package/src/cli/terminal/write.ts +132 -0
- package/src/cli/terminal-format.ts +167 -0
- package/src/cli/terminal.ts +26 -0
- package/src/cli/thread-format.test.ts +32 -0
- package/src/cli/thread-format.ts +8 -1
- package/src/cli/thread.ts +9 -13
- package/src/cli/threads/archive.ts +11 -18
- package/src/cli/threads/delete.ts +61 -0
- package/src/cli/threads/interrupt.ts +52 -0
- package/src/cli/threads/list.test.ts +69 -0
- package/src/cli/threads/list.ts +15 -3
- package/src/cli/threads/messages.ts +3 -3
- package/src/cli/threads/send.ts +22 -2
- package/src/cli/threads/unarchive.ts +44 -0
- package/src/cli/threads/update.ts +150 -0
- package/src/domain/error.ts +20 -1
- package/src/domain/model-config.ts +4 -0
- package/src/domain/thread-lifecycle.test.ts +35 -0
- package/src/domain/thread-lifecycle.ts +29 -6
- package/src/node/index.ts +2 -0
- package/src/orchestration/index.ts +1 -0
- package/src/orchestration/layer.ts +33 -85
- package/src/orchestration/service.ts +4 -0
- package/src/rpc/error.ts +10 -0
- package/src/rpc/index.ts +5 -0
- package/src/rpc/operation.ts +83 -0
- package/src/rpc/ws-group.ts +24 -0
- package/src/runtime/layer.ts +7 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "vite-plus/test/config";
|
|
2
|
+
|
|
3
|
+
import { assert, describe, it } from "@effect/vitest";
|
|
4
|
+
import { fromPartial } from "@total-typescript/shoehorn";
|
|
5
|
+
import type { OrchestrationThreadShell } from "#t3tools/contracts";
|
|
6
|
+
|
|
7
|
+
import { formatThreadsHuman } from "./thread-format.ts";
|
|
8
|
+
|
|
9
|
+
describe("formatThreadsHuman", () => {
|
|
10
|
+
it("appends (archived) for archived threads", () => {
|
|
11
|
+
const threads: OrchestrationThreadShell[] = [
|
|
12
|
+
fromPartial({
|
|
13
|
+
id: "active-1",
|
|
14
|
+
title: "Active thread",
|
|
15
|
+
archivedAt: null,
|
|
16
|
+
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
17
|
+
session: null,
|
|
18
|
+
}),
|
|
19
|
+
fromPartial({
|
|
20
|
+
id: "archived-1",
|
|
21
|
+
title: "Archived thread",
|
|
22
|
+
archivedAt: "2026-01-02T00:00:00.000Z",
|
|
23
|
+
updatedAt: "2026-01-02T00:00:00.000Z",
|
|
24
|
+
session: null,
|
|
25
|
+
}),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const output = formatThreadsHuman(threads);
|
|
29
|
+
assert.include(output, "- Active thread\n");
|
|
30
|
+
assert.include(output, "- Archived thread (archived)\n");
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/cli/thread-format.ts
CHANGED
|
@@ -53,11 +53,18 @@ export function formatThreadsHuman(threads: ReadonlyArray<OrchestrationThreadShe
|
|
|
53
53
|
return threads
|
|
54
54
|
.map(
|
|
55
55
|
(thread) =>
|
|
56
|
-
`- ${thread.title}\n id: ${thread.id}\n status: ${threadStatus(thread)}\n updated: ${thread.updatedAt}\n`,
|
|
56
|
+
`- ${thread.title}${thread.archivedAt !== null ? " (archived)" : ""}\n id: ${thread.id}\n status: ${threadStatus(thread)}\n updated: ${thread.updatedAt}\n`,
|
|
57
57
|
)
|
|
58
58
|
.join("");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
export function formatThreadDeletedHuman(input: {
|
|
62
|
+
readonly threadId: string;
|
|
63
|
+
readonly dispatch: { readonly sequence: number };
|
|
64
|
+
}) {
|
|
65
|
+
return `thread deleted: ${input.threadId}\nsequence: ${input.dispatch.sequence}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
export function formatThreadStartedHuman(input: {
|
|
62
69
|
readonly thread: OrchestrationThread;
|
|
63
70
|
readonly sequence: number;
|
package/src/cli/thread.ts
CHANGED
|
@@ -3,27 +3,23 @@ import { Command } from "effect/unstable/cli";
|
|
|
3
3
|
import { approveThreadCommand } from "./threads/approve.ts";
|
|
4
4
|
import { archiveThreadCommand } from "./threads/archive.ts";
|
|
5
5
|
import { callbackThreadCommand } from "./threads/callback.ts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { deleteThreadCommand } from "./threads/delete.ts";
|
|
7
|
+
import { interruptThreadCommand } from "./threads/interrupt.ts";
|
|
8
8
|
import { respondThreadCommand } from "./threads/respond.ts";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { startThreadCommand } from "./threads/start.ts";
|
|
12
|
-
import { waitForThreadCommand } from "./threads/wait.ts";
|
|
9
|
+
import { unarchiveThreadCommand } from "./threads/unarchive.ts";
|
|
10
|
+
import { updateThreadCommand } from "./threads/update.ts";
|
|
13
11
|
|
|
14
12
|
export function createThreadCommand() {
|
|
15
13
|
return Command.make("thread").pipe(
|
|
16
|
-
Command.withDescription("thread commands"),
|
|
14
|
+
Command.withDescription("advanced thread commands"),
|
|
17
15
|
Command.withSubcommands([
|
|
18
|
-
listThreadsCommand,
|
|
19
|
-
startThreadCommand,
|
|
20
|
-
sendThreadCommand,
|
|
21
|
-
showThreadCommand,
|
|
22
16
|
approveThreadCommand,
|
|
23
17
|
respondThreadCommand,
|
|
24
18
|
archiveThreadCommand,
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
interruptThreadCommand,
|
|
20
|
+
unarchiveThreadCommand,
|
|
21
|
+
updateThreadCommand,
|
|
22
|
+
deleteThreadCommand,
|
|
27
23
|
callbackThreadCommand,
|
|
28
24
|
]),
|
|
29
25
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Option from "effect/Option";
|
|
3
|
-
import { Command
|
|
3
|
+
import { Command } from "effect/unstable/cli";
|
|
4
4
|
|
|
5
|
-
import { formatFlag, threadFlag } from "../flags.ts";
|
|
6
|
-
import { MissingThreadError
|
|
5
|
+
import { formatFlag, selfActionForceFlag, threadFlag } from "../flags.ts";
|
|
6
|
+
import { MissingThreadError } from "../error.ts";
|
|
7
|
+
import { requireSelfActionConfirmation } from "../self-action.ts";
|
|
7
8
|
import { resolveThreadId } from "../../scope/index.ts";
|
|
8
9
|
import { T3Application } from "../../application/service.ts";
|
|
9
10
|
import { Environment } from "../../environment/service.ts";
|
|
@@ -14,7 +15,7 @@ export const archiveThreadCommand = Command.make(
|
|
|
14
15
|
"archive",
|
|
15
16
|
{
|
|
16
17
|
thread: threadFlag,
|
|
17
|
-
force:
|
|
18
|
+
force: selfActionForceFlag,
|
|
18
19
|
format: formatFlag,
|
|
19
20
|
},
|
|
20
21
|
({ thread, force, format }) =>
|
|
@@ -33,20 +34,12 @@ export const archiveThreadCommand = Command.make(
|
|
|
33
34
|
}),
|
|
34
35
|
);
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
) {
|
|
43
|
-
return yield* Effect.fail(
|
|
44
|
-
new SelfArchiveError({
|
|
45
|
-
threadId,
|
|
46
|
-
message: `cannot archive thread ${threadId}: matches T3CODE_THREAD_ID (are you trying to archive yourself?). Use --force to override.`,
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
}
|
|
37
|
+
yield* requireSelfActionConfirmation({
|
|
38
|
+
threadId,
|
|
39
|
+
force,
|
|
40
|
+
environment,
|
|
41
|
+
action: "archive",
|
|
42
|
+
});
|
|
50
43
|
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
51
44
|
const dispatch = yield* application.archiveThread(threadId);
|
|
52
45
|
if (resolvedFormat === "json") {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { Command } from "effect/unstable/cli";
|
|
4
|
+
|
|
5
|
+
import { requireDestructiveConfirmation } from "../confirm.ts";
|
|
6
|
+
import { formatFlag, selfActionForceFlag, threadFlag, yesFlag } from "../flags.ts";
|
|
7
|
+
import { formatThreadDeletedHuman } from "../thread-format.ts";
|
|
8
|
+
import { MissingThreadError } from "../error.ts";
|
|
9
|
+
import { requireSelfActionConfirmation } from "../self-action.ts";
|
|
10
|
+
import { resolveThreadId } from "../../scope/index.ts";
|
|
11
|
+
import { T3Application } from "../../application/service.ts";
|
|
12
|
+
import { Environment } from "../../environment/service.ts";
|
|
13
|
+
import { resolveOutputFormat } from "../output-format.ts";
|
|
14
|
+
import { T3Output } from "../output/service.ts";
|
|
15
|
+
|
|
16
|
+
export const deleteThreadCommand = Command.make(
|
|
17
|
+
"delete",
|
|
18
|
+
{
|
|
19
|
+
thread: threadFlag,
|
|
20
|
+
force: selfActionForceFlag,
|
|
21
|
+
yes: yesFlag,
|
|
22
|
+
format: formatFlag,
|
|
23
|
+
},
|
|
24
|
+
({ thread, force, yes, format }) =>
|
|
25
|
+
Effect.gen(function* () {
|
|
26
|
+
const application = yield* T3Application;
|
|
27
|
+
const environment = yield* Environment;
|
|
28
|
+
const output = yield* T3Output;
|
|
29
|
+
const threadId = resolveThreadId({
|
|
30
|
+
value: Option.getOrUndefined(thread),
|
|
31
|
+
env: environment.env,
|
|
32
|
+
});
|
|
33
|
+
if (threadId === undefined) {
|
|
34
|
+
return yield* Effect.fail(
|
|
35
|
+
new MissingThreadError({
|
|
36
|
+
message: "thread id is required: pass --thread or set T3CODE_THREAD_ID",
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
yield* requireSelfActionConfirmation({
|
|
41
|
+
threadId,
|
|
42
|
+
force,
|
|
43
|
+
environment,
|
|
44
|
+
action: "delete",
|
|
45
|
+
});
|
|
46
|
+
yield* requireDestructiveConfirmation({
|
|
47
|
+
message: `Delete thread ${threadId}?`,
|
|
48
|
+
yes,
|
|
49
|
+
environment,
|
|
50
|
+
});
|
|
51
|
+
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
52
|
+
const result = yield* application.deleteThread(threadId);
|
|
53
|
+
if (resolvedFormat === "json") {
|
|
54
|
+
return yield* output.printJson({
|
|
55
|
+
threadId: result.threadId,
|
|
56
|
+
dispatch: result.dispatch,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return yield* output.printInfo(formatThreadDeletedHuman(result));
|
|
60
|
+
}),
|
|
61
|
+
).pipe(Command.withDescription("delete thread"));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { Command } from "effect/unstable/cli";
|
|
4
|
+
|
|
5
|
+
import { formatFlag, selfActionForceFlag, threadFlag } from "../flags.ts";
|
|
6
|
+
import { MissingThreadError } from "../error.ts";
|
|
7
|
+
import { requireSelfActionConfirmation } from "../self-action.ts";
|
|
8
|
+
import { resolveThreadId } from "../../scope/index.ts";
|
|
9
|
+
import { T3Application } from "../../application/service.ts";
|
|
10
|
+
import { Environment } from "../../environment/service.ts";
|
|
11
|
+
import { resolveOutputFormat } from "../output-format.ts";
|
|
12
|
+
import { T3Output } from "../output/service.ts";
|
|
13
|
+
|
|
14
|
+
export const interruptThreadCommand = Command.make(
|
|
15
|
+
"interrupt",
|
|
16
|
+
{
|
|
17
|
+
thread: threadFlag,
|
|
18
|
+
force: selfActionForceFlag,
|
|
19
|
+
format: formatFlag,
|
|
20
|
+
},
|
|
21
|
+
({ thread, force, format }) =>
|
|
22
|
+
Effect.gen(function* () {
|
|
23
|
+
const application = yield* T3Application;
|
|
24
|
+
const environment = yield* Environment;
|
|
25
|
+
const output = yield* T3Output;
|
|
26
|
+
const threadId = resolveThreadId({
|
|
27
|
+
value: Option.getOrUndefined(thread),
|
|
28
|
+
env: environment.env,
|
|
29
|
+
});
|
|
30
|
+
if (threadId === undefined) {
|
|
31
|
+
return yield* Effect.fail(
|
|
32
|
+
new MissingThreadError({
|
|
33
|
+
message: "thread id is required: pass --thread or set T3CODE_THREAD_ID",
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
yield* requireSelfActionConfirmation({
|
|
38
|
+
threadId,
|
|
39
|
+
force,
|
|
40
|
+
environment,
|
|
41
|
+
action: "interrupt",
|
|
42
|
+
});
|
|
43
|
+
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
44
|
+
const dispatch = yield* application.interruptThread(threadId);
|
|
45
|
+
if (resolvedFormat === "json") {
|
|
46
|
+
return yield* output.printJson({ threadId, dispatch });
|
|
47
|
+
}
|
|
48
|
+
return yield* output.printInfo(
|
|
49
|
+
`thread interrupted: ${threadId}\nsequence: ${dispatch.sequence}`,
|
|
50
|
+
);
|
|
51
|
+
}),
|
|
52
|
+
).pipe(Command.withDescription("interrupt running thread turn"));
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import "vite-plus/test/config";
|
|
2
|
+
|
|
3
|
+
import * as Cause from "effect/Cause";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Exit from "effect/Exit";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import { assert, describe, it } from "@effect/vitest";
|
|
10
|
+
import { Command } from "effect/unstable/cli";
|
|
11
|
+
import { fromPartial } from "@total-typescript/shoehorn";
|
|
12
|
+
|
|
13
|
+
import { T3Application } from "../../application/service.ts";
|
|
14
|
+
import { T3Config } from "../../config/service.ts";
|
|
15
|
+
import { NodeEnvironmentLive } from "../../environment/layer.ts";
|
|
16
|
+
import { InvalidFlagCombinationError } from "../error.ts";
|
|
17
|
+
import { T3Output } from "../output/service.ts";
|
|
18
|
+
import { listThreadsCommand } from "./list.ts";
|
|
19
|
+
|
|
20
|
+
const testLayer = Layer.mergeAll(
|
|
21
|
+
Layer.succeed(
|
|
22
|
+
T3Application,
|
|
23
|
+
fromPartial({
|
|
24
|
+
listThreads: () => Effect.die("listThreads should not be called"),
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
Layer.succeed(
|
|
28
|
+
T3Config,
|
|
29
|
+
fromPartial({
|
|
30
|
+
resolve: () =>
|
|
31
|
+
Effect.succeed({
|
|
32
|
+
url: "ws://localhost",
|
|
33
|
+
token: "token",
|
|
34
|
+
source: "config",
|
|
35
|
+
local: true,
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
),
|
|
39
|
+
Layer.succeed(T3Output, {
|
|
40
|
+
writeStdout: () => Effect.void,
|
|
41
|
+
writeStderr: () => Effect.void,
|
|
42
|
+
printJson: () => Effect.void,
|
|
43
|
+
printNdjson: () => Effect.void,
|
|
44
|
+
printInfo: () => Effect.void,
|
|
45
|
+
}),
|
|
46
|
+
NodeServices.layer,
|
|
47
|
+
NodeEnvironmentLive,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
describe("listThreadsCommand", () => {
|
|
51
|
+
it.layer(testLayer)("listThreadsCommand", (t) => {
|
|
52
|
+
t.effect("rejects --archived and --all together", () =>
|
|
53
|
+
Effect.gen(function* () {
|
|
54
|
+
const run = Command.runWith(listThreadsCommand, { version: "0.0.0-test" });
|
|
55
|
+
const exit = yield* run(["--archived", "--all", "--project", "proj-1"]).pipe(Effect.exit);
|
|
56
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
57
|
+
if (!Exit.isFailure(exit)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const error = Cause.findErrorOption(exit.cause);
|
|
61
|
+
assert.isTrue(Option.isSome(error));
|
|
62
|
+
if (Option.isSome(error)) {
|
|
63
|
+
assert.instanceOf(error.value, InvalidFlagCombinationError);
|
|
64
|
+
assert.equal(error.value.message, "--archived and --all are mutually exclusive");
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
});
|
package/src/cli/threads/list.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
-
import { Command } from "effect/unstable/cli";
|
|
2
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
3
3
|
|
|
4
|
+
import type { ListThreadsInclude } from "../../application/service.ts";
|
|
4
5
|
import { formatFlag, projectFlag } from "../flags.ts";
|
|
6
|
+
import { InvalidFlagCombinationError } from "../error.ts";
|
|
5
7
|
import { requireCommandProjectRef } from "../require.ts";
|
|
6
8
|
import { formatThreadsHuman } from "../thread-format.ts";
|
|
7
9
|
import { T3Application } from "../../application/service.ts";
|
|
@@ -13,20 +15,30 @@ export const listThreadsCommand = Command.make(
|
|
|
13
15
|
"list",
|
|
14
16
|
{
|
|
15
17
|
project: projectFlag,
|
|
18
|
+
archived: Flag.boolean("archived").pipe(Flag.withDescription("List archived threads only")),
|
|
19
|
+
all: Flag.boolean("all").pipe(Flag.withDescription("List active and archived threads")),
|
|
16
20
|
format: formatFlag,
|
|
17
21
|
},
|
|
18
|
-
({ project, format }) =>
|
|
22
|
+
({ project, archived, all, format }) =>
|
|
19
23
|
Effect.gen(function* () {
|
|
20
24
|
const application = yield* T3Application;
|
|
21
25
|
const environment = yield* Environment;
|
|
22
26
|
const output = yield* T3Output;
|
|
27
|
+
if (archived && all) {
|
|
28
|
+
yield* Effect.fail(
|
|
29
|
+
new InvalidFlagCombinationError({
|
|
30
|
+
message: "--archived and --all are mutually exclusive",
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const include: ListThreadsInclude = archived ? "archived" : all ? "all" : "active";
|
|
23
35
|
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
24
36
|
const projectRef = yield* requireCommandProjectRef({
|
|
25
37
|
project,
|
|
26
38
|
env: environment.env,
|
|
27
39
|
cwd: environment.cwd,
|
|
28
40
|
});
|
|
29
|
-
const result = yield* application.listThreads(projectRef);
|
|
41
|
+
const result = yield* application.listThreads(projectRef, { include });
|
|
30
42
|
if (resolvedFormat === "json") {
|
|
31
43
|
yield* output.printJson(result.threads);
|
|
32
44
|
} else {
|
|
@@ -12,8 +12,8 @@ import { Environment } from "../../environment/service.ts";
|
|
|
12
12
|
import { resolveOutputFormat } from "../output-format.ts";
|
|
13
13
|
import { T3Output } from "../output/service.ts";
|
|
14
14
|
|
|
15
|
-
export const
|
|
16
|
-
"
|
|
15
|
+
export const getThreadTranscriptCommand = Command.make(
|
|
16
|
+
"transcript",
|
|
17
17
|
{
|
|
18
18
|
thread: threadFlag,
|
|
19
19
|
limit: Flag.integer("limit").pipe(Flag.withDefault(20)),
|
|
@@ -48,4 +48,4 @@ export const getThreadMessagesCommand = Command.make(
|
|
|
48
48
|
}
|
|
49
49
|
return yield* output.writeStdout(formatThreadMessagesHuman(detail, limit));
|
|
50
50
|
}),
|
|
51
|
-
).pipe(Command.withDescription("get latest thread
|
|
51
|
+
).pipe(Command.withDescription("get latest thread transcript"));
|
package/src/cli/threads/send.ts
CHANGED
|
@@ -2,10 +2,11 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import * as Option from "effect/Option";
|
|
3
3
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
5
|
-
import { modelFlags, threadFlag, threadFormatFlag } from "../flags.ts";
|
|
5
|
+
import { modelFlags, selfActionForceFlag, threadFlag, threadFormatFlag } from "../flags.ts";
|
|
6
6
|
import { readInitialMessage } from "../message-input.ts";
|
|
7
7
|
import { buildModelOptions } from "../model-options.ts";
|
|
8
8
|
import { MissingThreadError } from "../error.ts";
|
|
9
|
+
import { requireSelfActionConfirmation } from "../self-action.ts";
|
|
9
10
|
import { resolveThreadId } from "../../scope/index.ts";
|
|
10
11
|
import { T3Application } from "../../application/service.ts";
|
|
11
12
|
import { Environment } from "../../environment/service.ts";
|
|
@@ -18,13 +19,26 @@ export const sendThreadCommand = Command.make(
|
|
|
18
19
|
"send",
|
|
19
20
|
{
|
|
20
21
|
thread: threadFlag,
|
|
22
|
+
force: selfActionForceFlag,
|
|
21
23
|
message: Argument.string("message").pipe(Argument.optional),
|
|
22
24
|
stdin: Flag.boolean("stdin"),
|
|
23
25
|
...modelFlags,
|
|
24
26
|
wait: Flag.boolean("wait"),
|
|
25
27
|
format: threadFormatFlag,
|
|
26
28
|
},
|
|
27
|
-
({
|
|
29
|
+
({
|
|
30
|
+
thread,
|
|
31
|
+
force,
|
|
32
|
+
message,
|
|
33
|
+
stdin,
|
|
34
|
+
option,
|
|
35
|
+
reasoningEffort,
|
|
36
|
+
effort,
|
|
37
|
+
fastMode,
|
|
38
|
+
thinking,
|
|
39
|
+
wait,
|
|
40
|
+
format,
|
|
41
|
+
}) =>
|
|
28
42
|
Effect.gen(function* () {
|
|
29
43
|
const inputService = yield* T3Input;
|
|
30
44
|
const text = yield* readInitialMessage({
|
|
@@ -53,6 +67,12 @@ export const sendThreadCommand = Command.make(
|
|
|
53
67
|
}),
|
|
54
68
|
);
|
|
55
69
|
}
|
|
70
|
+
yield* requireSelfActionConfirmation({
|
|
71
|
+
threadId,
|
|
72
|
+
force,
|
|
73
|
+
environment,
|
|
74
|
+
action: "send a message to",
|
|
75
|
+
});
|
|
56
76
|
const input = {
|
|
57
77
|
message: text,
|
|
58
78
|
threadId,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { Command } from "effect/unstable/cli";
|
|
4
|
+
|
|
5
|
+
import { formatFlag, threadFlag } from "../flags.ts";
|
|
6
|
+
import { MissingThreadError } from "../error.ts";
|
|
7
|
+
import { resolveThreadId } from "../../scope/index.ts";
|
|
8
|
+
import { T3Application } from "../../application/service.ts";
|
|
9
|
+
import { Environment } from "../../environment/service.ts";
|
|
10
|
+
import { resolveOutputFormat } from "../output-format.ts";
|
|
11
|
+
import { T3Output } from "../output/service.ts";
|
|
12
|
+
|
|
13
|
+
export const unarchiveThreadCommand = Command.make(
|
|
14
|
+
"unarchive",
|
|
15
|
+
{
|
|
16
|
+
thread: threadFlag,
|
|
17
|
+
format: formatFlag,
|
|
18
|
+
},
|
|
19
|
+
({ thread, format }) =>
|
|
20
|
+
Effect.gen(function* () {
|
|
21
|
+
const application = yield* T3Application;
|
|
22
|
+
const environment = yield* Environment;
|
|
23
|
+
const output = yield* T3Output;
|
|
24
|
+
const threadId = resolveThreadId({
|
|
25
|
+
value: Option.getOrUndefined(thread),
|
|
26
|
+
env: environment.env,
|
|
27
|
+
});
|
|
28
|
+
if (threadId === undefined) {
|
|
29
|
+
return yield* Effect.fail(
|
|
30
|
+
new MissingThreadError({
|
|
31
|
+
message: "thread id is required: pass --thread or set T3CODE_THREAD_ID",
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
36
|
+
const dispatch = yield* application.unarchiveThread(threadId);
|
|
37
|
+
if (resolvedFormat === "json") {
|
|
38
|
+
return yield* output.printJson(dispatch);
|
|
39
|
+
}
|
|
40
|
+
return yield* output.printInfo(
|
|
41
|
+
`thread unarchived: ${threadId}\nsequence: ${dispatch.sequence}`,
|
|
42
|
+
);
|
|
43
|
+
}),
|
|
44
|
+
).pipe(Command.withDescription("unarchive thread"));
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
|
+
|
|
5
|
+
import { formatFlag, modelFlags, selfActionForceFlag, threadFlag } from "../flags.ts";
|
|
6
|
+
import {
|
|
7
|
+
ConflictingUpdateFlagsError,
|
|
8
|
+
MissingThreadError,
|
|
9
|
+
MissingUpdateFieldsError,
|
|
10
|
+
} from "../error.ts";
|
|
11
|
+
import { requireSelfActionConfirmation } from "../self-action.ts";
|
|
12
|
+
import { buildModelOptions } from "../model-options.ts";
|
|
13
|
+
import { resolveThreadId } from "../../scope/index.ts";
|
|
14
|
+
import { T3Application } from "../../application/service.ts";
|
|
15
|
+
import { Environment } from "../../environment/service.ts";
|
|
16
|
+
import { resolveOutputFormat } from "../output-format.ts";
|
|
17
|
+
import { T3Output } from "../output/service.ts";
|
|
18
|
+
|
|
19
|
+
export const updateThreadCommand = Command.make(
|
|
20
|
+
"update",
|
|
21
|
+
{
|
|
22
|
+
thread: threadFlag,
|
|
23
|
+
force: selfActionForceFlag,
|
|
24
|
+
title: Flag.string("title").pipe(Flag.optional),
|
|
25
|
+
provider: Flag.string("provider").pipe(Flag.optional),
|
|
26
|
+
model: Flag.string("model").pipe(Flag.optional),
|
|
27
|
+
...modelFlags,
|
|
28
|
+
branch: Flag.string("branch").pipe(Flag.optional),
|
|
29
|
+
clearBranch: Flag.boolean("clear-branch").pipe(Flag.optional),
|
|
30
|
+
worktree: Flag.string("worktree").pipe(Flag.optional),
|
|
31
|
+
clearWorktree: Flag.boolean("clear-worktree").pipe(Flag.optional),
|
|
32
|
+
format: formatFlag,
|
|
33
|
+
},
|
|
34
|
+
({
|
|
35
|
+
thread,
|
|
36
|
+
force,
|
|
37
|
+
title,
|
|
38
|
+
provider,
|
|
39
|
+
model,
|
|
40
|
+
option,
|
|
41
|
+
reasoningEffort,
|
|
42
|
+
effort,
|
|
43
|
+
fastMode,
|
|
44
|
+
thinking,
|
|
45
|
+
branch,
|
|
46
|
+
clearBranch,
|
|
47
|
+
worktree,
|
|
48
|
+
clearWorktree,
|
|
49
|
+
format,
|
|
50
|
+
}) =>
|
|
51
|
+
Effect.gen(function* () {
|
|
52
|
+
const application = yield* T3Application;
|
|
53
|
+
const environment = yield* Environment;
|
|
54
|
+
const output = yield* T3Output;
|
|
55
|
+
const threadId = resolveThreadId({
|
|
56
|
+
value: Option.getOrUndefined(thread),
|
|
57
|
+
env: environment.env,
|
|
58
|
+
});
|
|
59
|
+
if (threadId === undefined) {
|
|
60
|
+
return yield* Effect.fail(
|
|
61
|
+
new MissingThreadError({
|
|
62
|
+
message: "thread id is required: pass --thread or set T3CODE_THREAD_ID",
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
yield* requireSelfActionConfirmation({
|
|
68
|
+
threadId,
|
|
69
|
+
force,
|
|
70
|
+
environment,
|
|
71
|
+
action: "update",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const titleValue = Option.getOrUndefined(title);
|
|
75
|
+
const providerValue = Option.getOrUndefined(provider);
|
|
76
|
+
const modelValue = Option.getOrUndefined(model);
|
|
77
|
+
const branchValue = Option.getOrUndefined(branch);
|
|
78
|
+
const worktreeValue = Option.getOrUndefined(worktree);
|
|
79
|
+
const clearBranchValue = Option.getOrUndefined(clearBranch);
|
|
80
|
+
const clearWorktreeValue = Option.getOrUndefined(clearWorktree);
|
|
81
|
+
const options = buildModelOptions({
|
|
82
|
+
option,
|
|
83
|
+
reasoningEffort,
|
|
84
|
+
effort,
|
|
85
|
+
fastMode,
|
|
86
|
+
thinking,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (clearBranchValue === true && branchValue !== undefined) {
|
|
90
|
+
return yield* Effect.fail(
|
|
91
|
+
new ConflictingUpdateFlagsError({
|
|
92
|
+
message: "--branch and --clear-branch are mutually exclusive",
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (clearWorktreeValue === true && worktreeValue !== undefined) {
|
|
97
|
+
return yield* Effect.fail(
|
|
98
|
+
new ConflictingUpdateFlagsError({
|
|
99
|
+
message: "--worktree and --clear-worktree are mutually exclusive",
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const hasTitle = titleValue !== undefined && titleValue.length > 0;
|
|
105
|
+
const hasProvider = providerValue !== undefined && providerValue.length > 0;
|
|
106
|
+
const hasModel = modelValue !== undefined && modelValue.length > 0;
|
|
107
|
+
const hasBranch = branchValue !== undefined && branchValue.length > 0;
|
|
108
|
+
const hasWorktree = worktreeValue !== undefined && worktreeValue.length > 0;
|
|
109
|
+
if (
|
|
110
|
+
!hasTitle &&
|
|
111
|
+
!hasProvider &&
|
|
112
|
+
!hasModel &&
|
|
113
|
+
options.length === 0 &&
|
|
114
|
+
!hasBranch &&
|
|
115
|
+
clearBranchValue !== true &&
|
|
116
|
+
!hasWorktree &&
|
|
117
|
+
clearWorktreeValue !== true
|
|
118
|
+
) {
|
|
119
|
+
return yield* Effect.fail(
|
|
120
|
+
new MissingUpdateFieldsError({
|
|
121
|
+
message:
|
|
122
|
+
"at least one update field is required: --title, model flags, --branch, --clear-branch, --worktree, or --clear-worktree",
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dispatch = yield* application.updateThread({
|
|
128
|
+
threadId,
|
|
129
|
+
...(hasTitle ? { title: titleValue } : {}),
|
|
130
|
+
...(hasProvider ? { provider: providerValue } : {}),
|
|
131
|
+
...(hasModel ? { model: modelValue } : {}),
|
|
132
|
+
...(options.length > 0 ? { options } : {}),
|
|
133
|
+
...(clearBranchValue === true
|
|
134
|
+
? { branch: null }
|
|
135
|
+
: hasBranch
|
|
136
|
+
? { branch: branchValue }
|
|
137
|
+
: {}),
|
|
138
|
+
...(clearWorktreeValue === true
|
|
139
|
+
? { worktreePath: null }
|
|
140
|
+
: hasWorktree
|
|
141
|
+
? { worktreePath: worktreeValue }
|
|
142
|
+
: {}),
|
|
143
|
+
});
|
|
144
|
+
const resolvedFormat = resolveOutputFormat(format, environment, "json");
|
|
145
|
+
if (resolvedFormat === "json") {
|
|
146
|
+
return yield* output.printJson(dispatch);
|
|
147
|
+
}
|
|
148
|
+
return yield* output.printInfo(`thread updated: ${threadId}\nsequence: ${dispatch.sequence}`);
|
|
149
|
+
}),
|
|
150
|
+
).pipe(Command.withDescription("update thread metadata"));
|
package/src/domain/error.ts
CHANGED
|
@@ -30,6 +30,14 @@ export class ThreadSessionError extends Schema.TaggedErrorClass<ThreadSessionErr
|
|
|
30
30
|
},
|
|
31
31
|
) {}
|
|
32
32
|
|
|
33
|
+
export class ThreadLookupError extends Schema.TaggedErrorClass<ThreadLookupError>()(
|
|
34
|
+
"ThreadLookupError",
|
|
35
|
+
{
|
|
36
|
+
message: Schema.String,
|
|
37
|
+
threadId: Schema.String,
|
|
38
|
+
},
|
|
39
|
+
) {}
|
|
40
|
+
|
|
33
41
|
export class ProjectCreateVisibilityError extends Schema.TaggedErrorClass<ProjectCreateVisibilityError>()(
|
|
34
42
|
"ProjectCreateVisibilityError",
|
|
35
43
|
{
|
|
@@ -38,9 +46,20 @@ export class ProjectCreateVisibilityError extends Schema.TaggedErrorClass<Projec
|
|
|
38
46
|
},
|
|
39
47
|
) {}
|
|
40
48
|
|
|
49
|
+
export class TerminalLookupError extends Schema.TaggedErrorClass<TerminalLookupError>()(
|
|
50
|
+
"TerminalLookupError",
|
|
51
|
+
{
|
|
52
|
+
message: Schema.String,
|
|
53
|
+
threadId: Schema.String,
|
|
54
|
+
terminalId: Schema.String,
|
|
55
|
+
},
|
|
56
|
+
) {}
|
|
57
|
+
|
|
41
58
|
export type DomainError =
|
|
42
59
|
| ProjectLookupError
|
|
43
60
|
| ModelSelectionError
|
|
44
61
|
| ThreadEventError
|
|
45
62
|
| ThreadSessionError
|
|
46
|
-
|
|
|
63
|
+
| ThreadLookupError
|
|
64
|
+
| ProjectCreateVisibilityError
|
|
65
|
+
| TerminalLookupError;
|