clanka 0.1.22 → 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.
@@ -10,12 +10,18 @@ import { pipe } from "effect/Function";
10
10
  import * as EmbeddingModel from "effect/unstable/ai/EmbeddingModel";
11
11
  import * as RequestResolver from "effect/RequestResolver";
12
12
  import * as Option from "effect/Option";
13
+ import * as Path from "effect/Path";
13
14
  import * as ServiceMap from "effect/ServiceMap";
14
15
  import * as Fiber from "effect/Fiber";
15
16
  import * as Duration from "effect/Duration";
16
17
  import * as FiberHandle from "effect/FiberHandle";
17
18
  import { SqliteLayer } from "./Sqlite.js";
18
19
  import * as Console from "effect/Console";
20
+ const normalizePath = (path) => path.replace(/\\/g, "/");
21
+ const chunkConfig = {
22
+ chunkSize: 20,
23
+ chunkOverlap: 5,
24
+ };
19
25
  /**
20
26
  * @since 1.0.0
21
27
  * @category Services
@@ -30,48 +36,68 @@ export const layer = (options) => Layer.effect(SemanticSearch, Effect.gen(functi
30
36
  const chunker = yield* CodeChunker.CodeChunker;
31
37
  const repo = yield* ChunkRepo.ChunkRepo;
32
38
  const embeddings = yield* EmbeddingModel.EmbeddingModel;
39
+ const pathService = yield* Path.Path;
40
+ const root = pathService.resolve(options.directory);
33
41
  const resolver = embeddings.resolver.pipe(RequestResolver.setDelay(options.embeddingBatchSize ?? Duration.millis(50)), RequestResolver.batchN(options.embeddingBatchSize ?? 500));
42
+ const concurrency = options.concurrency ?? 2000;
34
43
  const indexHandle = yield* FiberHandle.make();
35
44
  const console = yield* Console.Console;
36
- const index = Effect.gen(function* () {
37
- const syncId = ChunkRepo.SyncId.makeUnsafe(crypto.randomUUID());
38
- yield* Effect.logInfo("Starting SemanticSearch index");
39
- yield* pipe(chunker.chunkCodebase({
40
- root: options.directory,
41
- chunkSize: 20,
42
- chunkOverlap: 5,
43
- }), Stream.tap(Effect.fnUntraced(function* (chunk) {
45
+ const resolveIndexedPath = (path) => {
46
+ const absolutePath = pathService.resolve(root, path);
47
+ const relativePath = normalizePath(pathService.relative(root, absolutePath));
48
+ if (relativePath.length === 0 ||
49
+ relativePath === ".." ||
50
+ relativePath.startsWith("../")) {
51
+ return Option.none();
52
+ }
53
+ return Option.some(relativePath);
54
+ };
55
+ const processChunk = Effect.fnUntraced(function* (options) {
56
+ if (options.checkExisting) {
44
57
  const id = yield* repo.exists({
45
- path: chunk.path,
46
- startLine: chunk.startLine,
47
- hash: chunk.contentHash,
58
+ path: options.chunk.path,
59
+ startLine: options.chunk.startLine,
60
+ hash: options.chunk.contentHash,
48
61
  });
49
62
  if (Option.isSome(id)) {
50
- yield* repo.setSyncId(id.value, syncId);
63
+ yield* repo.setSyncId(id.value, options.syncId);
51
64
  return;
52
65
  }
53
- const result = yield* Effect.request(new EmbeddingModel.EmbeddingRequest({
54
- input: `File: ${chunk.path}
55
- Lines: ${chunk.startLine}-${chunk.endLine}
66
+ }
67
+ const result = yield* Effect.request(new EmbeddingModel.EmbeddingRequest({
68
+ input: `File: ${options.chunk.path}
69
+ Lines: ${options.chunk.startLine}-${options.chunk.endLine}
56
70
 
57
- ${chunk.content}`,
58
- }), resolver);
59
- const vector = new Float32Array(result.vector);
60
- yield* repo.insert(ChunkRepo.Chunk.insert.makeUnsafe({
61
- path: chunk.path,
62
- startLine: chunk.startLine,
63
- endLine: chunk.endLine,
64
- hash: chunk.contentHash,
65
- content: chunk.content,
66
- vector,
67
- syncId,
68
- }));
69
- }, Effect.ignore({
70
- log: "Warn",
71
- message: "Failed to process chunk for embedding",
72
- }), (effect, chunk) => Effect.annotateLogs(effect, {
73
- chunk: `${chunk.path}/${chunk.startLine}`,
74
- })), { concurrency: options.concurrency ?? 2000 }), Stream.runDrain);
71
+ ${options.chunk.content}`,
72
+ }), resolver);
73
+ const vector = new Float32Array(result.vector);
74
+ yield* repo.insert(ChunkRepo.Chunk.insert.makeUnsafe({
75
+ path: options.chunk.path,
76
+ startLine: options.chunk.startLine,
77
+ endLine: options.chunk.endLine,
78
+ hash: options.chunk.contentHash,
79
+ content: options.chunk.content,
80
+ vector,
81
+ syncId: options.syncId,
82
+ }));
83
+ }, Effect.ignore({
84
+ log: "Warn",
85
+ message: "Failed to process chunk for embedding",
86
+ }), (effect, options) => Effect.annotateLogs(effect, {
87
+ chunk: `${options.chunk.path}/${options.chunk.startLine}`,
88
+ }));
89
+ const index = Effect.gen(function* () {
90
+ const syncId = ChunkRepo.SyncId.makeUnsafe(crypto.randomUUID());
91
+ yield* Effect.logInfo("Starting SemanticSearch index");
92
+ yield* pipe(chunker.chunkCodebase({
93
+ root,
94
+ ...chunkConfig,
95
+ }), Stream.tap((chunk) => processChunk({
96
+ chunk,
97
+ syncId,
98
+ checkExisting: true,
99
+ }), { concurrency }), Stream.runDrain);
100
+ yield* repo.deleteForSyncId(syncId);
75
101
  yield* Effect.logInfo("Finished SemanticSearch index");
76
102
  }).pipe(Effect.withSpan("SemanticSearch.index"), Effect.withLogSpan("SemanticSearch.index"), Effect.provideService(Console.Console, console));
77
103
  const runIndex = FiberHandle.run(indexHandle, index, {
@@ -90,7 +116,36 @@ ${chunk.content}`,
90
116
  });
91
117
  return results.map((r) => r.format()).join("\n\n");
92
118
  }, Effect.orDie),
93
- reindex: Effect.asVoid(runIndex),
119
+ updateFile: Effect.fn("SemanticSearch.updateFile")(function* (path) {
120
+ yield* Fiber.join(initialIndex);
121
+ const indexedPath = resolveIndexedPath(path);
122
+ if (Option.isNone(indexedPath)) {
123
+ return;
124
+ }
125
+ yield* repo.deleteByPath(indexedPath.value);
126
+ const chunks = yield* chunker.chunkFile({
127
+ root,
128
+ path: indexedPath.value,
129
+ ...chunkConfig,
130
+ });
131
+ if (chunks.length === 0) {
132
+ return;
133
+ }
134
+ const syncId = ChunkRepo.SyncId.makeUnsafe(crypto.randomUUID());
135
+ yield* pipe(Stream.fromArray(chunks), Stream.tap((chunk) => processChunk({
136
+ chunk,
137
+ syncId,
138
+ checkExisting: false,
139
+ }), { concurrency }), Stream.runDrain);
140
+ }, Effect.orDie),
141
+ removeFile: Effect.fn("SemanticSearch.removeFile")(function* (path) {
142
+ yield* Fiber.join(initialIndex);
143
+ const indexedPath = resolveIndexedPath(path);
144
+ if (Option.isNone(indexedPath)) {
145
+ return;
146
+ }
147
+ yield* repo.deleteByPath(indexedPath.value);
148
+ }, Effect.orDie),
94
149
  });
95
150
  })).pipe(Layer.provide([
96
151
  CodeChunker.layer,
@@ -100,8 +155,16 @@ ${chunk.content}`,
100
155
  * @since 1.0.0
101
156
  * @category Utils
102
157
  */
103
- export const maybeReindex = Effect.serviceOption(SemanticSearch).pipe(Effect.flatMap(Option.match({
158
+ export const maybeUpdateFile = (path) => Effect.serviceOption(SemanticSearch).pipe(Effect.flatMap(Option.match({
159
+ onNone: () => Effect.void,
160
+ onSome: (service) => service.updateFile(path),
161
+ })));
162
+ /**
163
+ * @since 1.0.0
164
+ * @category Utils
165
+ */
166
+ export const maybeRemoveFile = (path) => Effect.serviceOption(SemanticSearch).pipe(Effect.flatMap(Option.match({
104
167
  onNone: () => Effect.void,
105
- onSome: (service) => service.reindex,
168
+ onSome: (service) => service.removeFile(path),
106
169
  })));
107
170
  //# sourceMappingURL=SemanticSearch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SemanticSearch.js","sourceRoot":"","sources":["../src/SemanticSearch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SemanticSearch.js","sourceRoot":"","sources":["../src/SemanticSearch.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clanka",
3
3
  "type": "module",
4
- "version": "0.1.22",
4
+ "version": "0.2.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
package/src/Agent.ts CHANGED
@@ -166,6 +166,7 @@ ${content}
166
166
  let finalSummary = Option.none<string>()
167
167
 
168
168
  const output = yield* Queue.make<Output, AgentFinished | AiError.AiError>()
169
+ let inputTokens = 0
169
170
  let outputTokens = 0
170
171
  const prompt = opts.disableHistory ? MutableRef.make(Prompt.empty) : history
171
172
 
@@ -376,10 +377,12 @@ ${content}
376
377
  outputTokens += usage.outputTokens.total
377
378
  }
378
379
  if (usage.inputTokens.total !== undefined) {
380
+ inputTokens += usage.inputTokens.total
379
381
  maybeSend({
380
382
  agentId,
381
383
  part: new Usage({
382
- inputTokens: usage.inputTokens.total,
384
+ contextTokens: usage.inputTokens.total,
385
+ inputTokens,
383
386
  outputTokens,
384
387
  }),
385
388
  })
@@ -704,6 +707,7 @@ export class ScriptEnd extends Schema.TaggedClass<ScriptEnd>()(
704
707
  * @category Output
705
708
  */
706
709
  export class Usage extends Schema.TaggedClass<Usage>()("Usage", {
710
+ contextTokens: Schema.Number,
707
711
  inputTokens: Schema.Number,
708
712
  outputTokens: Schema.Number,
709
713
  }) {}
package/src/AgentTools.ts CHANGED
@@ -212,7 +212,9 @@ const SearchTool = Toolkit.make(
212
212
  Tool.make("search", {
213
213
  description: "Semantic code search",
214
214
  parameters: Schema.Struct({
215
- query: Schema.String,
215
+ query: Schema.String.annotate({
216
+ documentation: "Describe what you are looking for",
217
+ }),
216
218
  limit: Schema.optional(Schema.Finite).annotate({
217
219
  documentation: "Number of results (defaults to 5, max 10)",
218
220
  }),
@@ -295,14 +297,18 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
295
297
  recursive: true,
296
298
  })
297
299
  yield* fs.writeFileString(path, options.content)
298
- yield* SemanticSearch.maybeReindex
300
+ yield* SemanticSearch.maybeUpdateFile(pathService.relative(cwd, path))
299
301
  }, Effect.orDie),
300
302
  removeFile: Effect.fn("AgentTools.removeFile")(function* (path) {
301
303
  yield* Effect.logInfo(`Calling "removeFile"`).pipe(
302
304
  Effect.annotateLogs({ path }),
303
305
  )
304
306
  const cwd = yield* CurrentDirectory
305
- return yield* fs.remove(pathService.resolve(cwd, path), { force: true })
307
+ const absolutePath = pathService.resolve(cwd, path)
308
+ yield* fs.remove(absolutePath, { force: true })
309
+ yield* SemanticSearch.maybeRemoveFile(
310
+ pathService.relative(cwd, absolutePath),
311
+ )
306
312
  }, Effect.orDie),
307
313
  renameFile: Effect.fn("AgentTools.renameFile")(function* (options) {
308
314
  yield* Effect.logInfo(`Calling "renameFile"`).pipe(
@@ -314,7 +320,9 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
314
320
  yield* fs.makeDirectory(pathService.dirname(to), {
315
321
  recursive: true,
316
322
  })
317
- return yield* fs.rename(from, to)
323
+ yield* fs.rename(from, to)
324
+ yield* SemanticSearch.maybeRemoveFile(pathService.relative(cwd, from))
325
+ yield* SemanticSearch.maybeUpdateFile(pathService.relative(cwd, to))
318
326
  }, Effect.orDie),
319
327
  mkdir: Effect.fn("AgentTools.mkdir")(function* (path) {
320
328
  yield* Effect.logInfo(`Calling "mkdir"`).pipe(
@@ -458,8 +466,7 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
458
466
  }
459
467
  >
460
468
  const out = [] as Array<string>
461
- const rel = (path: string) =>
462
- pathService.relative(cwd, path).replaceAll("\\", "/")
469
+ const rel = (path: string) => pathService.relative(cwd, path)
463
470
  const load = Effect.fn("AgentTools.applyPatch.load")(function* (
464
471
  path: string,
465
472
  reason: "delete" | "update",
@@ -545,6 +552,7 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
545
552
  recursive: true,
546
553
  })
547
554
  yield* fs.writeFileString(step.path, step.next)
555
+ yield* SemanticSearch.maybeUpdateFile(rel(step.path))
548
556
  break
549
557
  }
550
558
  case "move": {
@@ -553,17 +561,18 @@ export const AgentToolHandlersNoDeps = AgentToolsWithSearch.toLayer(
553
561
  })
554
562
  yield* fs.writeFileString(step.movePath, step.next)
555
563
  yield* fs.remove(step.path)
564
+ yield* SemanticSearch.maybeRemoveFile(rel(step.path))
565
+ yield* SemanticSearch.maybeUpdateFile(rel(step.movePath))
556
566
  break
557
567
  }
558
568
  case "delete": {
559
569
  yield* fs.remove(step.path)
570
+ yield* SemanticSearch.maybeRemoveFile(rel(step.path))
560
571
  break
561
572
  }
562
573
  }
563
574
  }
564
575
 
565
- yield* SemanticSearch.maybeReindex
566
-
567
576
  return `Success. Updated the following files:\n${out.join("\n")}`
568
577
  }, Effect.orDie),
569
578
  delegate: Effect.fn("AgentTools.delegate")(function* (prompt) {
package/src/ChunkRepo.ts CHANGED
@@ -126,6 +126,7 @@ export class ChunkRepo extends ServiceMap.Service<
126
126
  chunkId: ChunkId,
127
127
  syncId: SyncId,
128
128
  ): Effect.Effect<void, ChunkRepoError>
129
+ deleteByPath(path: string): Effect.Effect<void, ChunkRepoError>
129
130
  deleteForSyncId(syncId: SyncId): Effect.Effect<void, ChunkRepoError>
130
131
  }
131
132
  >()("clanka/ChunkRepo") {}
@@ -223,6 +224,10 @@ export const layer = Layer.effect(
223
224
  sql`update chunks set syncId = ${syncId} where id = ${chunkId}`.pipe(
224
225
  Effect.mapError((reason) => new ChunkRepoError({ reason })),
225
226
  ),
227
+ deleteByPath: (path) =>
228
+ sql`delete from chunks where path = ${path}`.pipe(
229
+ Effect.mapError((reason) => new ChunkRepoError({ reason })),
230
+ ),
226
231
  deleteForSyncId: (syncId) =>
227
232
  sql`delete from chunks where syncId != ${syncId}`.pipe(
228
233
  Effect.mapError((reason) => new ChunkRepoError({ reason })),
@@ -36,6 +36,18 @@ export class CodeChunker extends ServiceMap.Service<
36
36
  readonly root: string
37
37
  readonly maxFileSize?: string | undefined
38
38
  }): Effect.Effect<ReadonlyArray<string>>
39
+ chunkFile(options: {
40
+ readonly root: string
41
+ readonly path: string
42
+ readonly chunkSize: number
43
+ readonly chunkOverlap: number
44
+ }): Effect.Effect<ReadonlyArray<CodeChunk>>
45
+ chunkFiles(options: {
46
+ readonly root: string
47
+ readonly paths: ReadonlyArray<string>
48
+ readonly chunkSize: number
49
+ readonly chunkOverlap: number
50
+ }): Stream.Stream<CodeChunk>
39
51
  chunkCodebase(options: {
40
52
  readonly root: string
41
53
  readonly maxFileSize?: string | undefined
@@ -346,6 +358,46 @@ export const layer: Layer.Layer<
346
358
  )
347
359
  })
348
360
 
361
+ const chunkFile: CodeChunker["Service"]["chunkFile"] = Effect.fn(
362
+ "CodeChunker.chunkFile",
363
+ )(function* (options): Effect.fn.Return<ReadonlyArray<CodeChunk>> {
364
+ const root = pathService.resolve(options.root)
365
+ const absolutePath = pathService.resolve(root, options.path)
366
+ const path = normalizePath(pathService.relative(root, absolutePath))
367
+
368
+ if (
369
+ path.length === 0 ||
370
+ path === ".." ||
371
+ path.startsWith("../") ||
372
+ !isMeaningfulFile(path)
373
+ ) {
374
+ return []
375
+ }
376
+
377
+ return yield* pipe(
378
+ fs.readFileString(absolutePath),
379
+ Effect.map((content) => chunkFileContent(path, content, options)),
380
+ Effect.catch(() => Effect.succeed([])),
381
+ )
382
+ })
383
+
384
+ const chunkFiles: CodeChunker["Service"]["chunkFiles"] = (options) =>
385
+ Stream.fromArray(options.paths).pipe(
386
+ Stream.flatMap(
387
+ (path) =>
388
+ pipe(
389
+ chunkFile({
390
+ root: options.root,
391
+ path,
392
+ chunkSize: options.chunkSize,
393
+ chunkOverlap: options.chunkOverlap,
394
+ }),
395
+ Stream.fromArrayEffect,
396
+ ),
397
+ { concurrency: 5 },
398
+ ),
399
+ )
400
+
349
401
  const chunkCodebase: CodeChunker["Service"]["chunkCodebase"] =
350
402
  Effect.fnUntraced(function* (options) {
351
403
  const root = pathService.resolve(options.root)
@@ -356,26 +408,18 @@ export const layer: Layer.Layer<
356
408
  : { maxFileSize: options.maxFileSize }),
357
409
  })
358
410
 
359
- return Stream.fromArray(files).pipe(
360
- Stream.flatMap(
361
- (path) => {
362
- const absolutePath = pathService.resolve(root, path)
363
- return pipe(
364
- fs.readFileString(absolutePath),
365
- Effect.map((content) =>
366
- chunkFileContent(path, content, options),
367
- ),
368
- Effect.catch(() => Effect.succeed([])),
369
- Stream.fromArrayEffect,
370
- )
371
- },
372
- { concurrency: 5 },
373
- ),
374
- )
411
+ return chunkFiles({
412
+ root,
413
+ paths: files,
414
+ chunkSize: options.chunkSize,
415
+ chunkOverlap: options.chunkOverlap,
416
+ })
375
417
  }, Stream.unwrap)
376
418
 
377
419
  return CodeChunker.of({
378
420
  listFiles,
421
+ chunkFile,
422
+ chunkFiles,
379
423
  chunkCodebase,
380
424
  })
381
425
  }),
@@ -78,7 +78,7 @@ ${output.summary}\n\n`
78
78
  return `${prefix}${chalk.red(`Error: ${output.error.reason._tag}. Retrying...`)}\n\n${chalk.dim(Cause.pretty(Cause.fail(output.error)))}\n\n`
79
79
  }
80
80
  case "Usage": {
81
- return `${prefix}${chalkInfoHeading(`${infoIcon} Usage:`)} ${numberFormat.format(output.inputTokens)} input / ${numberFormat.format(output.outputTokens)} output\n\n`
81
+ return `${prefix}${chalkInfoHeading(`${infoIcon} Usage:`)} ${numberFormat.format(output.inputTokens)} context / ${numberFormat.format(output.inputTokens)} input / ${numberFormat.format(output.outputTokens)} output\n\n`
82
82
  }
83
83
  }
84
84
  }),