@cerebriline/core 4.100.240 → 4.100.241
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/assets/skills/README.md +14 -0
- package/assets/skills/build-project/SKILL.md +103 -0
- package/assets/skills/docker-compose-deploy/SKILL.md +239 -0
- package/assets/skills/librarian/SKILL.md +177 -0
- package/assets/skills/mandatory-qa-prompt-and-issue-resolution/SKILL.md +58 -0
- package/assets/skills/run-project/SKILL.md +103 -0
- package/assets/skills/sdd-discuss/SKILL.md +129 -0
- package/assets/skills/sdd-execute/SKILL.md +146 -0
- package/assets/skills/sdd-plan/SKILL.md +140 -0
- package/assets/skills/sdd-quick/SKILL.md +40 -0
- package/assets/skills/sdd-status/SKILL.md +75 -0
- package/assets/skills/sdd-verify/SKILL.md +122 -0
- package/assets/skills/sdd-wizard/SKILL.md +105 -0
- package/assets/skills/tdd-coverage/SKILL.md +88 -0
- package/assets/skills/tdd-gen/SKILL.md +82 -0
- package/assets/skills/tdd-test/SKILL.md +61 -0
- package/assets/skills/tdd-wizard/SKILL.md +162 -0
- package/dist/extensions/config/bundled-skills.d.ts +24 -0
- package/dist/extensions/config/index.d.ts +2 -1
- package/dist/extensions/config/unified-config-file-watcher.d.ts +15 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts +15 -0
- package/dist/extensions/tools/executors/document/ebook.d.ts +10 -0
- package/dist/extensions/tools/executors/document/formats.d.ts +25 -0
- package/dist/extensions/tools/executors/document/ocr.d.ts +3 -0
- package/dist/extensions/tools/executors/document/recognition.d.ts +16 -0
- package/dist/extensions/tools/executors/document-extract.d.ts +91 -0
- package/dist/extensions/tools/librarian-tools.d.ts +35 -0
- package/dist/extensions/tools/library-import-run.d.ts +79 -0
- package/dist/extensions/tools/library-tools.d.ts +88 -0
- package/dist/extensions/tools/memory-tools.d.ts +41 -0
- package/dist/extensions/tools/sdd-tool.d.ts +215 -0
- package/dist/extensions/tools/team/create-agent-tool.d.ts +5 -4
- package/dist/hub/daemon/entry.js +547 -545
- package/dist/hub/index.js +531 -529
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1070 -689
- package/dist/services/global-settings.d.ts +9 -0
- package/dist/services/llms/provider-settings.d.ts +15 -15
- package/dist/services/retrieval/catalogue.d.ts +370 -0
- package/dist/services/retrieval/chunker.d.ts +44 -0
- package/dist/services/retrieval/embedding-client.d.ts +78 -0
- package/dist/services/retrieval/fingerprint.d.ts +21 -0
- package/dist/services/retrieval/firecrawl.d.ts +77 -0
- package/dist/services/retrieval/lancedb-runtime.d.ts +84 -0
- package/dist/services/retrieval/lancedb-runtime.manifest.d.ts +17 -0
- package/dist/services/retrieval/library-store.d.ts +148 -0
- package/dist/services/retrieval/library.d.ts +139 -0
- package/dist/services/retrieval/memory-recall.d.ts +97 -0
- package/dist/services/retrieval/memory.d.ts +184 -0
- package/dist/services/retrieval/retrieve.d.ts +85 -0
- package/dist/services/retrieval/tar-extract.d.ts +14 -0
- package/dist/services/retrieval/tar-write.d.ts +20 -0
- package/dist/services/retrieval/vector-index.d.ts +82 -0
- package/dist/services/sdd/sdd-engine.d.ts +248 -0
- package/dist/services/telemetry/index.js +1 -1
- package/dist/types/config.d.ts +7 -0
- package/dist/types/session.d.ts +2 -0
- package/package.json +4 -4
- package/dist/extensions/tools/code-intel.d.ts +0 -131
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory: short notes the model keeps between tasks.
|
|
3
|
+
*
|
|
4
|
+
* A note is a fact worth having next time: a decision and its reason, how
|
|
5
|
+
* this project is built and tested, a preference of the user, a trap that
|
|
6
|
+
* cost an hour. It is kept by the retrieval core the Library uses, in a
|
|
7
|
+
* store of its own, so it is searched the same way: by keyword always, and
|
|
8
|
+
* by meaning when an embedding model is set.
|
|
9
|
+
*
|
|
10
|
+
* There are any number of memories, all in the one store: the main one,
|
|
11
|
+
* which every workspace starts on, and whichever others the user makes --
|
|
12
|
+
* one for a workspace, one for a client, one imported from a colleague. A
|
|
13
|
+
* session keeps new notes in exactly one of them and searches as many as
|
|
14
|
+
* the user allows it; which, is the user's choice per workspace and not
|
|
15
|
+
* something the model can widen.
|
|
16
|
+
*/
|
|
17
|
+
import { type MemorySettings } from "@cerebriline/shared";
|
|
18
|
+
import type { RetrievalEndpoint } from "./embedding-client";
|
|
19
|
+
import { type EmbedPendingResult, type LibraryOptions, type LibraryVectorSet } from "./library";
|
|
20
|
+
export interface MemoryItem {
|
|
21
|
+
/** `m12`: what `forget` takes. */
|
|
22
|
+
id: string;
|
|
23
|
+
text: string;
|
|
24
|
+
/** The memory it is kept in. */
|
|
25
|
+
memory: string;
|
|
26
|
+
tags: string[];
|
|
27
|
+
createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface RecalledMemory extends MemoryItem {
|
|
30
|
+
/** The reranker's score, 0 to 1, when there was one. */
|
|
31
|
+
relevance?: number;
|
|
32
|
+
/** Cosine similarity to the query, when the note was found by meaning. */
|
|
33
|
+
similarity?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface MemoryEndpoints {
|
|
36
|
+
embedding?: RetrievalEndpoint;
|
|
37
|
+
reranker?: RetrievalEndpoint;
|
|
38
|
+
}
|
|
39
|
+
/** One of the memories, as the settings panel lists it. */
|
|
40
|
+
export interface MemoryInfo {
|
|
41
|
+
name: string;
|
|
42
|
+
/** The main memory: always there, never deleted. */
|
|
43
|
+
main: boolean;
|
|
44
|
+
notes: number;
|
|
45
|
+
/** The workspace it was made for, when it was made for one. */
|
|
46
|
+
workspace?: string;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
}
|
|
49
|
+
/** A memory written to a file, to be read back here or on another machine. */
|
|
50
|
+
export interface MemoryExport {
|
|
51
|
+
format: typeof MEMORY_EXPORT_FORMAT;
|
|
52
|
+
version: 1;
|
|
53
|
+
name: string;
|
|
54
|
+
workspace?: string;
|
|
55
|
+
exportedAt: string;
|
|
56
|
+
notes: Array<{
|
|
57
|
+
text: string;
|
|
58
|
+
tags: string[];
|
|
59
|
+
createdAt: string;
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
62
|
+
export declare const MEMORY_EXPORT_FORMAT = "cerebriline-memory";
|
|
63
|
+
/** A memory is a note, not a document: longer than this belongs in the Library. */
|
|
64
|
+
export declare const MEMORY_MAX_CHARS = 4000;
|
|
65
|
+
export declare const MEMORY_NAME_MAX_CHARS = 60;
|
|
66
|
+
export declare function resolveMemoryDirectory(): string;
|
|
67
|
+
/** A name as it is kept: trimmed, one line, of a length a list can show. */
|
|
68
|
+
export declare function normalizeMemoryName(name: string): string;
|
|
69
|
+
export declare class Memory {
|
|
70
|
+
private readonly library;
|
|
71
|
+
private readonly directory;
|
|
72
|
+
private migrated;
|
|
73
|
+
constructor(options?: LibraryOptions);
|
|
74
|
+
/** Which workspace each memory was made for. Not in the database: it is about the user's folders, not the notes. */
|
|
75
|
+
private get registryPath();
|
|
76
|
+
private readRegistry;
|
|
77
|
+
private writeRegistry;
|
|
78
|
+
/**
|
|
79
|
+
* Bring a store made by the first build to this one's names, once: its
|
|
80
|
+
* `global` collection is the main memory, and each `project:<path>` is a
|
|
81
|
+
* memory named after the folder, made for that workspace.
|
|
82
|
+
*/
|
|
83
|
+
private migrate;
|
|
84
|
+
private freeName;
|
|
85
|
+
/** Every memory in the store, the main one first. */
|
|
86
|
+
listMemories(): MemoryInfo[];
|
|
87
|
+
/** Make a memory. A name already taken is refused, not reused. */
|
|
88
|
+
createMemory(input: {
|
|
89
|
+
name: string;
|
|
90
|
+
workspace?: string;
|
|
91
|
+
}): MemoryInfo;
|
|
92
|
+
/**
|
|
93
|
+
* Give a memory another name. Its notes, their ids and their vectors stay
|
|
94
|
+
* as they are. The main memory keeps its name: everything starts on it.
|
|
95
|
+
*/
|
|
96
|
+
renameMemory(from: string, to: string): MemoryInfo;
|
|
97
|
+
/** Delete a memory and every note in it. The main memory stays. */
|
|
98
|
+
deleteMemory(name: string): Promise<number>;
|
|
99
|
+
/** The collections of these memories; a name that is not a memory is left out. */
|
|
100
|
+
private collections;
|
|
101
|
+
private toItem;
|
|
102
|
+
/** Keep a note. The same text in the same memory is kept once. */
|
|
103
|
+
remember(input: {
|
|
104
|
+
text: string;
|
|
105
|
+
/** @default the main memory */
|
|
106
|
+
memory?: string;
|
|
107
|
+
tags?: readonly string[];
|
|
108
|
+
/** For an imported note: when it was first noted. */
|
|
109
|
+
createdAt?: string;
|
|
110
|
+
endpoints?: MemoryEndpoints;
|
|
111
|
+
signal?: AbortSignal;
|
|
112
|
+
}): Promise<{
|
|
113
|
+
item: MemoryItem;
|
|
114
|
+
outcome: "added" | "unchanged";
|
|
115
|
+
note?: string;
|
|
116
|
+
}>;
|
|
117
|
+
/** The notes that best match, best first, from the memories named. */
|
|
118
|
+
recall(query: string, options?: {
|
|
119
|
+
/** @default the main memory */
|
|
120
|
+
memories?: readonly string[];
|
|
121
|
+
settings?: MemorySettings;
|
|
122
|
+
endpoints?: MemoryEndpoints;
|
|
123
|
+
limit?: number;
|
|
124
|
+
signal?: AbortSignal;
|
|
125
|
+
}): Promise<{
|
|
126
|
+
items: RecalledMemory[];
|
|
127
|
+
notes: string[];
|
|
128
|
+
}>;
|
|
129
|
+
/** Every note of the memories named, newest first. */
|
|
130
|
+
list(options?: {
|
|
131
|
+
memories?: readonly string[];
|
|
132
|
+
}): MemoryItem[];
|
|
133
|
+
/** Remove a note. False when the memories named hold none with that id. */
|
|
134
|
+
forget(id: string, options?: {
|
|
135
|
+
memories?: readonly string[];
|
|
136
|
+
}): Promise<boolean>;
|
|
137
|
+
/** A memory as a file's content. The notes only: vectors are made again where it is read. */
|
|
138
|
+
exportMemory(name: string): MemoryExport;
|
|
139
|
+
/**
|
|
140
|
+
* Read an exported memory in. Into the memory named, or into one of the
|
|
141
|
+
* file's own name, made if it is not there. A note already there is left
|
|
142
|
+
* alone, so reading the same file twice adds nothing.
|
|
143
|
+
*/
|
|
144
|
+
importMemory(data: unknown, options?: {
|
|
145
|
+
into?: string;
|
|
146
|
+
endpoints?: MemoryEndpoints;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
memory: string;
|
|
149
|
+
created: boolean;
|
|
150
|
+
added: number;
|
|
151
|
+
unchanged: number;
|
|
152
|
+
skipped: number;
|
|
153
|
+
}>;
|
|
154
|
+
/** How many notes there are, and how many have vectors for this embedding model. */
|
|
155
|
+
counts(model?: string): {
|
|
156
|
+
notes: number;
|
|
157
|
+
embeddedNotes: number;
|
|
158
|
+
};
|
|
159
|
+
/** Give vectors to every note that has none for this embedding model. */
|
|
160
|
+
embedPending(options: {
|
|
161
|
+
embedding: RetrievalEndpoint;
|
|
162
|
+
onProgress?: (progress: {
|
|
163
|
+
done: number;
|
|
164
|
+
total: number;
|
|
165
|
+
}) => void;
|
|
166
|
+
probe?: boolean;
|
|
167
|
+
signal?: AbortSignal;
|
|
168
|
+
}): Promise<EmbedPendingResult>;
|
|
169
|
+
/** The sets of vectors the notes have, one per embedding model and size. */
|
|
170
|
+
vectorSets(current?: string): Promise<LibraryVectorSet[]>;
|
|
171
|
+
deleteVectorSet(table: string): Promise<boolean>;
|
|
172
|
+
/** Record the vector size a model returned, as a search would. */
|
|
173
|
+
noteDimension(model: string, dimension: number): void;
|
|
174
|
+
/** Whether notes can be found by meaning here now, and if not, why. */
|
|
175
|
+
vectorState(): {
|
|
176
|
+
installed: boolean;
|
|
177
|
+
unsupported?: string;
|
|
178
|
+
};
|
|
179
|
+
close(): Promise<void>;
|
|
180
|
+
}
|
|
181
|
+
/** Closes every Memory this process opened. For shutdown, like `closeSharedLibraries`. */
|
|
182
|
+
export declare function closeSharedMemories(): Promise<void>;
|
|
183
|
+
/** The one Memory of a folder in this process. */
|
|
184
|
+
export declare function sharedMemory(options?: LibraryOptions): Memory;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieval over the Library: keywords, vectors, or both, then a reranker.
|
|
3
|
+
*
|
|
4
|
+
* The shape follows open-webui's, which works well in practice: take
|
|
5
|
+
* candidates from the keyword index and from the vector index, merge the two
|
|
6
|
+
* rankings by weighted reciprocal rank (the "BM25 weight" slides between
|
|
7
|
+
* semantic and lexical), keep the top K, and let a reranking model score
|
|
8
|
+
* those against the query and keep the best few above a threshold.
|
|
9
|
+
*
|
|
10
|
+
* Every stage past the keywords is optional. With no embedding model the
|
|
11
|
+
* search is keywords alone; when an embedder or reranker cannot be reached,
|
|
12
|
+
* the stages that did run still answer and the result says what was skipped.
|
|
13
|
+
*/
|
|
14
|
+
import { type RetrievalEndpoint } from "./embedding-client";
|
|
15
|
+
import type { LibraryHit, LibraryStore } from "./library-store";
|
|
16
|
+
import type { VectorSearcher } from "./vector-index";
|
|
17
|
+
export interface RetrieveOptions {
|
|
18
|
+
store: LibraryStore;
|
|
19
|
+
collectionIds?: readonly number[];
|
|
20
|
+
/** Where vectors are searched. Left out, the search is keywords alone. */
|
|
21
|
+
vectors?: VectorSearcher;
|
|
22
|
+
embedding?: RetrievalEndpoint & {
|
|
23
|
+
/** Put in front of the query, for embedders trained with an instruction. */
|
|
24
|
+
queryPrefix?: string;
|
|
25
|
+
};
|
|
26
|
+
reranker?: RetrievalEndpoint & {
|
|
27
|
+
batchSize?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Use the keyword index together with the vectors. Off, a search with
|
|
31
|
+
* vectors available is semantic alone. Default on.
|
|
32
|
+
*/
|
|
33
|
+
hybrid?: boolean;
|
|
34
|
+
/** 0 is semantic alone, 1 lexical alone. Default 0.5. */
|
|
35
|
+
bm25Weight?: number;
|
|
36
|
+
/** Also match file names, titles and section headers by keyword. Default on. */
|
|
37
|
+
enrich?: boolean;
|
|
38
|
+
/** Chunks kept after merging. Default 5. */
|
|
39
|
+
topK?: number;
|
|
40
|
+
/** Chunks kept after reranking. Default 3. */
|
|
41
|
+
topKReranker?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Chunks scoring below this are dropped: the reranker's score (0 to 1)
|
|
44
|
+
* when there is one, the cosine similarity of a semantic-only search
|
|
45
|
+
* otherwise. A merged ranking has no score to hold against it. Default 0.
|
|
46
|
+
*/
|
|
47
|
+
relevanceThreshold?: number;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
fetch?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
export interface RetrievedHit extends LibraryHit {
|
|
52
|
+
/** Position in the keyword ranking, from 1, when it was found there. */
|
|
53
|
+
keywordRank?: number;
|
|
54
|
+
/** Position in the vector ranking, from 1, when it was found there. */
|
|
55
|
+
vectorRank?: number;
|
|
56
|
+
similarity?: number;
|
|
57
|
+
/** The reranker's score, 0 to 1. */
|
|
58
|
+
rerankScore?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface RetrieveResult {
|
|
61
|
+
hits: RetrievedHit[];
|
|
62
|
+
/** Which rankings the hits came from. */
|
|
63
|
+
mode: "keyword" | "vector" | "hybrid";
|
|
64
|
+
reranked: boolean;
|
|
65
|
+
/** Stages that were asked for and did not run, and why. */
|
|
66
|
+
notes: string[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Rerankers answer on two scales: a probability, or the raw logit behind it
|
|
70
|
+
* (llama.cpp). Anything outside 0..1 marks the second, and is put through
|
|
71
|
+
* the logistic function so that one threshold means the same on both.
|
|
72
|
+
*/
|
|
73
|
+
export declare function normalizeRerankScores(scores: readonly number[]): number[];
|
|
74
|
+
/**
|
|
75
|
+
* Merge rankings by weighted reciprocal rank. Each ranking is a list of ids,
|
|
76
|
+
* best first; the result is the ids with their merged score, best first.
|
|
77
|
+
*/
|
|
78
|
+
export declare function fuseRankings(rankings: readonly {
|
|
79
|
+
ids: readonly number[];
|
|
80
|
+
weight: number;
|
|
81
|
+
}[]): {
|
|
82
|
+
id: number;
|
|
83
|
+
score: number;
|
|
84
|
+
}[];
|
|
85
|
+
export declare function retrieve(query: string, options: RetrieveOptions): Promise<RetrieveResult>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unpacks an npm tarball (gzip over tar) as it streams in, without holding
|
|
3
|
+
* it in memory: the native library the Library downloads is 200 MB and more.
|
|
4
|
+
*
|
|
5
|
+
* Only what npm tarballs contain is handled: regular files and directories,
|
|
6
|
+
* with PAX and GNU long names. Links and devices are skipped. The first path
|
|
7
|
+
* component (`package/`) is dropped, and a path that would leave the target
|
|
8
|
+
* folder is refused.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Unpack the gzipped tar read from `source` into `root`. Resolves with the
|
|
12
|
+
* number of files written.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractTarGz(source: AsyncIterable<Uint8Array>, root: string): Promise<number>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes a gzipped tar file by file, without holding it in memory: an export
|
|
3
|
+
* of the Library carries the books' own files, and a shelf of them is large.
|
|
4
|
+
*
|
|
5
|
+
* What `extractTarGz` reads back: regular files, with a PAX record for a name
|
|
6
|
+
* past the header's 100 bytes.
|
|
7
|
+
*/
|
|
8
|
+
export interface TarEntry {
|
|
9
|
+
/** The path inside the archive, with forward slashes. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** A file to copy in, */
|
|
12
|
+
path?: string;
|
|
13
|
+
/** or the content itself. */
|
|
14
|
+
data?: string | Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Write `entries` to `target` as a gzipped tar. Resolves with the number of
|
|
18
|
+
* files written.
|
|
19
|
+
*/
|
|
20
|
+
export declare function writeTarGz(target: string, entries: Iterable<TarEntry> | AsyncIterable<TarEntry>): Promise<number>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Library's vectors, in LanceDB.
|
|
3
|
+
*
|
|
4
|
+
* SQLite holds the text and answers keyword queries; this holds one vector
|
|
5
|
+
* per chunk and answers "what is close to this". The two are joined on the
|
|
6
|
+
* chunk id. Vectors from different embedding models are not comparable, so
|
|
7
|
+
* each model and dimension has a table of its own: changing the model leaves
|
|
8
|
+
* the old vectors where they are and starts a new table.
|
|
9
|
+
*/
|
|
10
|
+
import type { LanceDbRuntime } from "./lancedb-runtime";
|
|
11
|
+
export interface VectorRow {
|
|
12
|
+
chunkId: number;
|
|
13
|
+
collectionId: number;
|
|
14
|
+
documentId: number;
|
|
15
|
+
vector: Float32Array;
|
|
16
|
+
}
|
|
17
|
+
export interface VectorHit {
|
|
18
|
+
chunkId: number;
|
|
19
|
+
/** Cosine similarity, 1 for the same direction. */
|
|
20
|
+
similarity: number;
|
|
21
|
+
}
|
|
22
|
+
export interface VectorSearchOptions {
|
|
23
|
+
collectionIds?: readonly number[];
|
|
24
|
+
/** Default 10. */
|
|
25
|
+
limit?: number;
|
|
26
|
+
}
|
|
27
|
+
/** What hybrid retrieval needs from a vector index. */
|
|
28
|
+
export interface VectorSearcher {
|
|
29
|
+
search(model: string, vector: Float32Array, options?: VectorSearchOptions): Promise<VectorHit[]>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Below this many rows a table is scanned: exact, and fast enough. Above it
|
|
33
|
+
* an approximate index is built, which is what keeps a library of gigabytes
|
|
34
|
+
* answering in milliseconds.
|
|
35
|
+
*/
|
|
36
|
+
export declare const VECTOR_INDEX_MIN_ROWS = 50000;
|
|
37
|
+
export declare function vectorTableName(model: string, dimension: number): string;
|
|
38
|
+
export declare class VectorIndex implements VectorSearcher {
|
|
39
|
+
private readonly runtime;
|
|
40
|
+
private readonly db;
|
|
41
|
+
private readonly tables;
|
|
42
|
+
private constructor();
|
|
43
|
+
static open(runtime: LanceDbRuntime, directory: string): Promise<VectorIndex>;
|
|
44
|
+
/** The tables there are, as `{model slug, dimension}` names. */
|
|
45
|
+
tableNames(): Promise<string[]>;
|
|
46
|
+
private table;
|
|
47
|
+
private createTable;
|
|
48
|
+
/**
|
|
49
|
+
* Add vectors. The caller removes a document's old vectors first
|
|
50
|
+
* (`deleteDocuments`): chunk ids are new each time a document is replaced.
|
|
51
|
+
*/
|
|
52
|
+
add(model: string, rows: readonly VectorRow[]): Promise<void>;
|
|
53
|
+
search(model: string, vector: Float32Array, options?: VectorSearchOptions): Promise<VectorHit[]>;
|
|
54
|
+
count(model: string, dimension?: number): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Remove these documents' vectors from one model's table only. For
|
|
57
|
+
* embedding a document again: its vectors for other models are not this
|
|
58
|
+
* run's to touch, and deleting them empties a set the store still counts.
|
|
59
|
+
*/
|
|
60
|
+
deleteDocumentsIn(model: string, dimension: number, documentIds: readonly number[]): Promise<void>;
|
|
61
|
+
/** Every set of vectors there is: its table, the size of its vectors, and how many. */
|
|
62
|
+
sets(): Promise<{
|
|
63
|
+
table: string;
|
|
64
|
+
dimension: number;
|
|
65
|
+
rows: number;
|
|
66
|
+
}[]>;
|
|
67
|
+
/** Delete a whole set of vectors. */
|
|
68
|
+
dropSet(table: string): Promise<boolean>;
|
|
69
|
+
/** Remove the vectors of these documents, in every table. */
|
|
70
|
+
deleteDocuments(documentIds: readonly number[]): Promise<void>;
|
|
71
|
+
deleteCollection(collectionId: number): Promise<void>;
|
|
72
|
+
private deleteWhere;
|
|
73
|
+
/**
|
|
74
|
+
* Housekeeping after a large ingest: compact what the adds wrote, and build
|
|
75
|
+
* the approximate index once the table is large enough to need one.
|
|
76
|
+
* Returns whether the table has an index on its vectors afterwards.
|
|
77
|
+
*/
|
|
78
|
+
optimize(model: string, dimension: number, options?: {
|
|
79
|
+
minRows?: number;
|
|
80
|
+
}): Promise<boolean>;
|
|
81
|
+
close(): void;
|
|
82
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine behind the spec-driven skills.
|
|
3
|
+
*
|
|
4
|
+
* The plan of a piece of software — what is wanted, the milestones, their
|
|
5
|
+
* slices, each slice's tasks, and what was proved along the way — is kept in
|
|
6
|
+
* one SQLite file, `.sdd/sdd.db` in the project. The database is the truth.
|
|
7
|
+
* The markdown beside it is written from it, for people to read and to
|
|
8
|
+
* review in a diff; nothing is read back from those files.
|
|
9
|
+
*
|
|
10
|
+
* Why a database and not the files: a model that keeps its place by reading
|
|
11
|
+
* and ticking markdown has to get every read and every edit right, and a
|
|
12
|
+
* small model does not. Here the order of the work is the engine's. It
|
|
13
|
+
* refuses a step that is out of turn and says why, and `next()` answers
|
|
14
|
+
* "what do I do now" with the one step that is due and everything that step
|
|
15
|
+
* needs.
|
|
16
|
+
*
|
|
17
|
+
* The hierarchy and the order of the phases are those of Get Shit Done
|
|
18
|
+
* (GSD 2, https://getshitdone.help/), which also keeps its state in a
|
|
19
|
+
* database and dispatches each step itself.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SDD_DIRECTORY = ".sdd";
|
|
22
|
+
export declare const SDD_DATABASE = "sdd.db";
|
|
23
|
+
export type RequirementStatus = "active" | "validated" | "deferred" | "out_of_scope";
|
|
24
|
+
export type MilestoneStatus = "open" | "complete";
|
|
25
|
+
export type SliceStatus = "pending" | "done";
|
|
26
|
+
export type TaskStatus = "pending" | "in_progress" | "done" | "blocked";
|
|
27
|
+
export type Verdict = "pass" | "needs_remediation";
|
|
28
|
+
export type UatResult = "pass" | "fail" | "needs_human";
|
|
29
|
+
export interface SddRequirement {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
description: string;
|
|
33
|
+
status: RequirementStatus;
|
|
34
|
+
/** The slice that owns it, as `M001/S01`, once a roadmap names one. */
|
|
35
|
+
owner?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface SddMilestone {
|
|
38
|
+
id: string;
|
|
39
|
+
title: string;
|
|
40
|
+
context: string;
|
|
41
|
+
status: MilestoneStatus;
|
|
42
|
+
requirementsApproved: boolean;
|
|
43
|
+
roadmapApproved: boolean;
|
|
44
|
+
verdict?: Verdict;
|
|
45
|
+
validation?: string;
|
|
46
|
+
summary?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface SddSlice {
|
|
49
|
+
milestone: string;
|
|
50
|
+
id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
goal: string;
|
|
53
|
+
demo: string;
|
|
54
|
+
depends: string[];
|
|
55
|
+
status: SliceStatus;
|
|
56
|
+
research?: string;
|
|
57
|
+
summary?: string;
|
|
58
|
+
replans: number;
|
|
59
|
+
uat: {
|
|
60
|
+
check: string;
|
|
61
|
+
result: UatResult;
|
|
62
|
+
note?: string;
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
export interface SddTask {
|
|
66
|
+
milestone: string;
|
|
67
|
+
slice: string;
|
|
68
|
+
id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
steps: string;
|
|
71
|
+
files: string[];
|
|
72
|
+
verify: string;
|
|
73
|
+
expect?: string;
|
|
74
|
+
status: TaskStatus;
|
|
75
|
+
summary?: string;
|
|
76
|
+
evidence?: string;
|
|
77
|
+
blocker?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SliceInput {
|
|
80
|
+
title: string;
|
|
81
|
+
goal?: string;
|
|
82
|
+
demo?: string;
|
|
83
|
+
depends?: string[];
|
|
84
|
+
requirements?: string[];
|
|
85
|
+
}
|
|
86
|
+
export interface TaskInput {
|
|
87
|
+
title: string;
|
|
88
|
+
steps: string;
|
|
89
|
+
files?: string[];
|
|
90
|
+
verify: string;
|
|
91
|
+
expect?: string;
|
|
92
|
+
}
|
|
93
|
+
export type SddStep = "discuss_project" | "discuss_milestone" | "approve_requirements" | "plan_milestone" | "approve_roadmap" | "plan_slice" | "execute_task" | "resume_task" | "replan_slice" | "close_slice" | "validate_milestone" | "complete_milestone" | "stuck";
|
|
94
|
+
export interface SddNext {
|
|
95
|
+
step: SddStep;
|
|
96
|
+
/** `M001`, `M001/S01` or `M001/S01/T01`: what the step is about. */
|
|
97
|
+
ref?: string;
|
|
98
|
+
/** Whether the step is the user's to answer, so auto mode stops here. */
|
|
99
|
+
needsUser: boolean;
|
|
100
|
+
/** What to do, with everything the step needs. */
|
|
101
|
+
instruction: string;
|
|
102
|
+
}
|
|
103
|
+
/** A step asked for out of turn, or with something missing. The message says what to do instead. */
|
|
104
|
+
export declare class SddRuleError extends Error {
|
|
105
|
+
constructor(message: string);
|
|
106
|
+
}
|
|
107
|
+
export declare const milestoneId: (n: number) => string;
|
|
108
|
+
export declare const sliceId: (n: number) => string;
|
|
109
|
+
export declare const taskId: (n: number) => string;
|
|
110
|
+
export declare class SddEngine {
|
|
111
|
+
private readonly db;
|
|
112
|
+
/** The `.sdd` folder. */
|
|
113
|
+
readonly directory: string;
|
|
114
|
+
constructor(projectRoot: string);
|
|
115
|
+
close(): void;
|
|
116
|
+
private all;
|
|
117
|
+
private one;
|
|
118
|
+
private run;
|
|
119
|
+
private change;
|
|
120
|
+
project(): {
|
|
121
|
+
name: string;
|
|
122
|
+
description: string;
|
|
123
|
+
agreements: string;
|
|
124
|
+
} | undefined;
|
|
125
|
+
requirements(): SddRequirement[];
|
|
126
|
+
decisions(): {
|
|
127
|
+
id: string;
|
|
128
|
+
title: string;
|
|
129
|
+
choice: string;
|
|
130
|
+
why: string;
|
|
131
|
+
}[];
|
|
132
|
+
knowledge(): string[];
|
|
133
|
+
captures(): string[];
|
|
134
|
+
private toMilestone;
|
|
135
|
+
milestones(): SddMilestone[];
|
|
136
|
+
/** The milestone being worked on: the first that is not complete. */
|
|
137
|
+
activeMilestone(): SddMilestone | undefined;
|
|
138
|
+
private toSlice;
|
|
139
|
+
slices(milestone: string): SddSlice[];
|
|
140
|
+
private toTask;
|
|
141
|
+
tasks(milestone: string, slice: string): SddTask[];
|
|
142
|
+
journal(limit?: number): {
|
|
143
|
+
at: string;
|
|
144
|
+
action: string;
|
|
145
|
+
ref?: string;
|
|
146
|
+
text: string;
|
|
147
|
+
}[];
|
|
148
|
+
private milestoneOf;
|
|
149
|
+
/** `S02`, `M001/S02` or nothing (the slice that is due). */
|
|
150
|
+
private sliceOf;
|
|
151
|
+
private taskOf;
|
|
152
|
+
/** The first slice not done whose dependencies are all done. */
|
|
153
|
+
private dueSlice;
|
|
154
|
+
setProject(input: {
|
|
155
|
+
name?: string;
|
|
156
|
+
description?: string;
|
|
157
|
+
agreements?: string;
|
|
158
|
+
}): void;
|
|
159
|
+
addRequirement(input: {
|
|
160
|
+
title: string;
|
|
161
|
+
description?: string;
|
|
162
|
+
status?: RequirementStatus;
|
|
163
|
+
}): string;
|
|
164
|
+
updateRequirement(id: string, patch: {
|
|
165
|
+
status?: RequirementStatus;
|
|
166
|
+
title?: string;
|
|
167
|
+
description?: string;
|
|
168
|
+
}): void;
|
|
169
|
+
addDecision(input: {
|
|
170
|
+
title: string;
|
|
171
|
+
choice: string;
|
|
172
|
+
why?: string;
|
|
173
|
+
}): string;
|
|
174
|
+
addKnowledge(note: string): void;
|
|
175
|
+
/** A thought that is not for now, kept so it is not lost. */
|
|
176
|
+
capture(note: string): void;
|
|
177
|
+
quick(input: {
|
|
178
|
+
title: string;
|
|
179
|
+
summary: string;
|
|
180
|
+
}): string;
|
|
181
|
+
addMilestone(input: {
|
|
182
|
+
title: string;
|
|
183
|
+
context: string;
|
|
184
|
+
}): string;
|
|
185
|
+
/** Change the open milestone's title or context, when the user changes course. */
|
|
186
|
+
updateMilestone(input: {
|
|
187
|
+
title?: string;
|
|
188
|
+
context?: string;
|
|
189
|
+
}): void;
|
|
190
|
+
/** Record that the user approved the requirements or the roadmap. */
|
|
191
|
+
approve(what: "requirements" | "roadmap"): void;
|
|
192
|
+
private insertSlices;
|
|
193
|
+
/** The roadmap: the milestone cut into slices. Replaces an unapproved roadmap. */
|
|
194
|
+
planMilestone(slices: readonly SliceInput[], ref?: string): string[];
|
|
195
|
+
/** Slices added to an approved roadmap: remediation, or new scope the user agreed to. */
|
|
196
|
+
addSlices(slices: readonly SliceInput[], ref?: string): string[];
|
|
197
|
+
/** Take a slice that has not produced anything off the roadmap. */
|
|
198
|
+
removeSlice(input: {
|
|
199
|
+
slice: string;
|
|
200
|
+
reason: string;
|
|
201
|
+
}): void;
|
|
202
|
+
private insertTasks;
|
|
203
|
+
planSlice(input: {
|
|
204
|
+
slice?: string;
|
|
205
|
+
research?: string;
|
|
206
|
+
tasks: readonly TaskInput[];
|
|
207
|
+
}): string[];
|
|
208
|
+
/** Replace the tasks that are not done, after a blocker or a plan that proved wrong. */
|
|
209
|
+
replanSlice(input: {
|
|
210
|
+
slice?: string;
|
|
211
|
+
reason: string;
|
|
212
|
+
tasks: readonly TaskInput[];
|
|
213
|
+
}): string[];
|
|
214
|
+
private setTask;
|
|
215
|
+
startTask(ref?: string): SddTask;
|
|
216
|
+
completeTask(input: {
|
|
217
|
+
task?: string;
|
|
218
|
+
summary: string;
|
|
219
|
+
evidence: string;
|
|
220
|
+
}): void;
|
|
221
|
+
blockTask(input: {
|
|
222
|
+
task?: string;
|
|
223
|
+
reason: string;
|
|
224
|
+
}): void;
|
|
225
|
+
/** Close a slice: its summary and how its acceptance checks went. */
|
|
226
|
+
completeSlice(input: {
|
|
227
|
+
slice?: string;
|
|
228
|
+
summary: string;
|
|
229
|
+
uat: readonly {
|
|
230
|
+
check: string;
|
|
231
|
+
result: UatResult;
|
|
232
|
+
note?: string;
|
|
233
|
+
}[];
|
|
234
|
+
}): void;
|
|
235
|
+
validateMilestone(input: {
|
|
236
|
+
verdict: Verdict;
|
|
237
|
+
findings: string;
|
|
238
|
+
remediation?: readonly SliceInput[];
|
|
239
|
+
}): string[];
|
|
240
|
+
completeMilestone(summary: string): void;
|
|
241
|
+
next(): SddNext;
|
|
242
|
+
status(): string;
|
|
243
|
+
/**
|
|
244
|
+
* Write the markdown from the database. Called after every change. The
|
|
245
|
+
* files are for reading and for review; nothing reads them back.
|
|
246
|
+
*/
|
|
247
|
+
render(): void;
|
|
248
|
+
}
|