clanka 0.7.5 → 0.7.7

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 (46) hide show
  1. package/dist/Acp.d.ts +21 -2
  2. package/dist/Acp.d.ts.map +1 -1
  3. package/dist/Acp.js +66 -13
  4. package/dist/Acp.js.map +1 -1
  5. package/dist/Acp.test.js +56 -10
  6. package/dist/Acp.test.js.map +1 -1
  7. package/dist/AcpImage.test.js +1 -0
  8. package/dist/AcpImage.test.js.map +1 -1
  9. package/dist/Agent.d.ts.map +1 -1
  10. package/dist/Agent.js +1 -2
  11. package/dist/Agent.js.map +1 -1
  12. package/dist/AgentExecutor.d.ts +0 -172
  13. package/dist/AgentExecutor.d.ts.map +1 -1
  14. package/dist/AgentTools.d.ts +0 -123
  15. package/dist/AgentTools.d.ts.map +1 -1
  16. package/dist/AgentTools.js +22 -60
  17. package/dist/AgentTools.js.map +1 -1
  18. package/dist/AgentTools.test.d.ts +2 -0
  19. package/dist/AgentTools.test.d.ts.map +1 -0
  20. package/dist/AgentTools.test.js +63 -0
  21. package/dist/AgentTools.test.js.map +1 -0
  22. package/dist/bin/{Effect-DdkIoNCD.mjs → Effect-CeAMoJlg.mjs} +7 -2
  23. package/dist/bin/Effect-CeAMoJlg.mjs.map +1 -0
  24. package/dist/bin/{McpClient-CJ0Vugoi.mjs → McpClient-zQibhsnA.mjs} +3 -3
  25. package/dist/bin/{McpClient-CJ0Vugoi.mjs.map → McpClient-zQibhsnA.mjs.map} +1 -1
  26. package/dist/bin/{OutputFormatter-DN8OGwuK.mjs → OutputFormatter-C7Awn0DS.mjs} +3 -3
  27. package/dist/bin/{OutputFormatter-DN8OGwuK.mjs.map → OutputFormatter-C7Awn0DS.mjs.map} +1 -1
  28. package/dist/bin/{Schema-BALIXGp7.mjs → Schema-Bc06DxcV.mjs} +3 -6
  29. package/dist/bin/{Schema-BALIXGp7.mjs.map → Schema-Bc06DxcV.mjs.map} +1 -1
  30. package/dist/bin/{SemanticSearch-O6PZBRfd.mjs → SemanticSearch-BPxQoRSC.mjs} +4 -4
  31. package/dist/bin/{SemanticSearch-O6PZBRfd.mjs.map → SemanticSearch-BPxQoRSC.mjs.map} +1 -1
  32. package/dist/bin/{Stream-B5ebASRi.mjs → Stream-DlGXvw0L.mjs} +2 -2
  33. package/dist/bin/{Stream-B5ebASRi.mjs.map → Stream-DlGXvw0L.mjs.map} +1 -1
  34. package/dist/bin/cli.mjs +95 -91
  35. package/dist/bin/cli.mjs.map +1 -1
  36. package/dist/cli.js +17 -8
  37. package/dist/cli.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/Acp.test.ts +86 -9
  40. package/src/Acp.ts +90 -17
  41. package/src/AcpImage.test.ts +1 -0
  42. package/src/Agent.ts +1 -2
  43. package/src/AgentTools.test.ts +116 -0
  44. package/src/AgentTools.ts +32 -76
  45. package/src/cli.ts +22 -9
  46. package/dist/bin/Effect-DdkIoNCD.mjs.map +0 -1
package/src/AgentTools.ts CHANGED
@@ -82,6 +82,12 @@ export class ImageAttacher extends Context.Service<
82
82
  (image: ImageAttachment) => Effect.Effect<void>
83
83
  >()("clanka/AgentTools/ImageAttacher") {}
84
84
 
85
+ const globNamesDirectory = (glob: string) =>
86
+ glob
87
+ .split("/")
88
+ .slice(0, -1)
89
+ .some((segment) => segment.length > 0 && !/[*?[\]{}]/.test(segment))
90
+
85
91
  /**
86
92
  * @since 1.0.0
87
93
  * @category Context
@@ -94,14 +100,6 @@ export const makeContextNoop = (cwd?: string) =>
94
100
  Context.add(ImageAttacher, () => Effect.void),
95
101
  )
96
102
 
97
- class TodoItem extends Schema.Opaque<TodoItem>()(
98
- Schema.Struct({
99
- id: Schema.Number,
100
- text: Schema.String,
101
- completed: Schema.Boolean,
102
- }),
103
- ) {}
104
-
105
103
  /**
106
104
  * @since 1.0.0
107
105
  * @category Toolkit
@@ -169,29 +167,6 @@ export const AgentTools = Toolkit.make(
169
167
  success: Schema.String,
170
168
  dependencies: [CurrentDirectory],
171
169
  }),
172
- Tool.make("listTodos", {
173
- description: "Read your todo list",
174
- parameters: Schema.Void,
175
- success: Schema.Array(TodoItem),
176
- }),
177
- Tool.make("addTodo", {
178
- description: "Add an item to your todo list",
179
- parameters: Schema.String.annotate({
180
- identifier: "text",
181
- }),
182
- }),
183
- Tool.make("updateTodo", {
184
- description: "Update an item in your todo list",
185
- parameters: Schema.Struct({
186
- id: Schema.Number,
187
- text: Schema.optional(Schema.String),
188
- completed: Schema.optional(Schema.Boolean),
189
- }),
190
- }),
191
- Tool.make("clearTodos", {
192
- description: "Clear all items in your todo list",
193
- parameters: Schema.Void,
194
- }),
195
170
  Tool.make("bash", {
196
171
  description: "Run a bash command and return the output",
197
172
  parameters: Schema.Struct({
@@ -305,9 +280,6 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
305
280
  const pathService = yield* Path.Path
306
281
  const webSearch = yield* ExaSearch.ExaSearch
307
282
  const fetchMarkdown = yield* WebToMarkdown.WebToMarkdown
308
- const todoMap = new Map<number, TodoItem>()
309
- let todoIdCounter = 0
310
-
311
283
  const execute = Effect.fn(function* (command: ChildProcess.Command) {
312
284
  const handle = yield* spawner.spawn(command)
313
285
  return yield* handle.all.pipe(
@@ -474,24 +446,38 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
474
446
  if (options.filesOnly) {
475
447
  args.push("--files-with-matches")
476
448
  }
449
+ const searchesIgnoredFiles =
450
+ options.glob !== undefined && !options.glob.startsWith("*")
477
451
  if (options.glob) {
478
452
  args.push("--glob", options.glob)
479
- if (!options.glob.startsWith("*")) {
453
+ if (searchesIgnoredFiles) {
480
454
  args.push("-uu")
481
455
  }
482
456
  }
483
457
  args.push(options.pattern)
484
- let stream = spawner.streamLines(
485
- ChildProcess.make("rg", args, {
486
- cwd,
487
- stdin: "ignore",
488
- }),
489
- )
490
- stream = Stream.take(stream, options.maxLines ?? 500)
491
- return yield* Stream.runCollect(stream).pipe(
492
- Effect.map(Array.join("\n")),
493
- Effect.orDie,
494
- )
458
+ const run = Effect.fnUntraced(function* (args: Array<string>) {
459
+ let stream = spawner.streamLines(
460
+ ChildProcess.make("rg", args, {
461
+ cwd,
462
+ stdin: "ignore",
463
+ }),
464
+ )
465
+ stream = Stream.take(stream, options.maxLines ?? 500)
466
+ return yield* Stream.runCollect(stream).pipe(
467
+ Effect.map(Array.join("\n")),
468
+ Effect.orDie,
469
+ )
470
+ })
471
+ const output = yield* run(args)
472
+ if (
473
+ output.length > 0 ||
474
+ searchesIgnoredFiles ||
475
+ options.glob === undefined ||
476
+ !globNamesDirectory(options.glob)
477
+ ) {
478
+ return output
479
+ }
480
+ return yield* run(args.slice(0, -1).concat("-uu", options.pattern))
495
481
  }),
496
482
  glob: Effect.fn("AgentTools.glob")(function* (pattern) {
497
483
  yield* Effect.logInfo(`Calling "glob"`).pipe(
@@ -502,36 +488,6 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
502
488
  import("glob").then((Glob) => Glob.glob(pattern, { cwd })),
503
489
  )
504
490
  }),
505
- listTodos: Effect.fn("AgentTools.listTodos")(function* () {
506
- yield* Effect.logInfo(`Calling "listTodos"`)
507
- return Array.fromIterable(todoMap.values())
508
- }),
509
- addTodo: Effect.fn("AgentTools.addTodo")(function* (text) {
510
- yield* Effect.logInfo(`Calling "addTodo"`).pipe(
511
- Effect.annotateLogs({ text }),
512
- )
513
- const id = ++todoIdCounter
514
- const item = { id, text, completed: false }
515
- todoMap.set(id, item)
516
- }),
517
- updateTodo: Effect.fn("AgentTools.updateTodo")(function* (options) {
518
- yield* Effect.logInfo(`Calling "updateTodo"`).pipe(
519
- Effect.annotateLogs(options),
520
- )
521
- const item = todoMap.get(options.id)
522
- if (item === undefined) {
523
- return yield* Effect.die(`Todo item ${options.id} not found`)
524
- }
525
- todoMap.set(item.id, {
526
- ...item,
527
- text: options.text ?? item.text,
528
- completed: options.completed ?? item.completed,
529
- })
530
- }),
531
- clearTodos: Effect.fn("AgentTools.clearTodos")(function* () {
532
- yield* Effect.logInfo(`Calling "clearTodos"`)
533
- todoMap.clear()
534
- }),
535
491
  bash: Effect.fn("AgentTools.bash")(function* (options) {
536
492
  const timeoutMs = Math.min(options.timeoutMs ?? 120_000, 240_000)
537
493
  yield* Effect.logInfo(`Calling "bash"`).pipe(
package/src/cli.ts CHANGED
@@ -64,14 +64,25 @@ const modelLayer = (provider: Provider, model: string, effort: string) =>
64
64
  Layer.provide(Copilot.layerClient),
65
65
  )
66
66
 
67
+ const isProvider = (provider: string | undefined): provider is Provider =>
68
+ providers.includes(provider as Provider)
69
+
70
+ // `<provider>/<model>/<effort>` as accepted by the `acp` --model flag.
67
71
  const parseModelId = (modelId: string) => {
68
72
  const [provider, model, effort, ...rest] = modelId.split("/")
69
- return provider !== undefined &&
70
- providers.includes(provider as Provider) &&
73
+ return isProvider(provider) &&
71
74
  model !== undefined &&
72
- effort !== undefined &&
75
+ Schema.is(Acp.ThoughtLevel)(effort) &&
73
76
  rest.length === 0
74
- ? Option.some({ provider: provider as Provider, model, effort })
77
+ ? Option.some({ model: `${provider}:${model}`, thoughtLevel: effort })
78
+ : Option.none()
79
+ }
80
+
81
+ // `<provider>:<model>` as advertised over ACP.
82
+ const parseAcpModelId = (modelId: string) => {
83
+ const [provider, model, ...rest] = modelId.split(":")
84
+ return isProvider(provider) && model !== undefined && rest.length === 0
85
+ ? Option.some({ provider, model })
75
86
  : Option.none()
76
87
  }
77
88
 
@@ -80,7 +91,7 @@ const ModelId = Schema.String.pipe(
80
91
  Schema.makeFilter(
81
92
  (modelId: string) =>
82
93
  Option.isSome(parseModelId(modelId)) ||
83
- `Invalid model "${modelId}", expected <provider>/<model>/<effort>`,
94
+ `Invalid model "${modelId}", expected <provider>/<model>/<effort> with effort one of ${Acp.ThoughtLevel.literals.join(", ")}`,
84
95
  ),
85
96
  ),
86
97
  )
@@ -192,6 +203,7 @@ const acpModel = Flag.String("model").pipe(
192
203
  ),
193
204
  Flag.withSchema(ModelId),
194
205
  Flag.withDefault("openai/gpt-6-astra/medium"),
206
+ Flag.map((modelId) => Option.getOrThrow(parseModelId(modelId))),
195
207
  )
196
208
 
197
209
  const acp = Command.make("acp", { model: acpModel, compaction }).pipe(
@@ -201,14 +213,15 @@ const acp = Command.make("acp", { model: acpModel, compaction }).pipe(
201
213
  Command.withHandler(({ model }) =>
202
214
  Acp.runStdio({
203
215
  version,
204
- defaultModel: model,
216
+ defaultModel: model.model,
217
+ defaultThoughtLevel: model.thoughtLevel,
205
218
  makeAgent: (cwd) =>
206
219
  Agent.make.pipe(
207
220
  Effect.provide(AgentExecutor.layerLocal({ directory: cwd })),
208
221
  ),
209
- makeModel: (modelId) =>
210
- Option.map(parseModelId(modelId), ({ provider, model, effort }) =>
211
- modelLayer(provider, model, effort),
222
+ makeModel: (modelId, thoughtLevel) =>
223
+ Option.map(parseAcpModelId(modelId), ({ provider, model }) =>
224
+ modelLayer(provider, model, thoughtLevel),
212
225
  ),
213
226
  }),
214
227
  ),