@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,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-wizard
|
|
3
|
+
description: >-
|
|
4
|
+
Guides the user into test-driven development from the start: learns about
|
|
5
|
+
the project, takes an existing specification or writes one with the user
|
|
6
|
+
through questions about goals and scope, then develops the test cases with
|
|
7
|
+
them, generates the tests, validates them and writes them to the project.
|
|
8
|
+
Ends by offering to carry on with the implementation, test run and coverage
|
|
9
|
+
phases or to leave those to the user. Use when the user wants to start a
|
|
10
|
+
project or a feature test-first, asks for a TDD wizard, or wants a
|
|
11
|
+
specification turned into tests.
|
|
12
|
+
disabled: true
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Skill: TDD Wizard
|
|
16
|
+
|
|
17
|
+
Takes the user from an idea to a specification and a set of tests that describe it, ready for the code to be written against them. It is a guided conversation: the user decides, this skill asks, proposes, writes and checks.
|
|
18
|
+
|
|
19
|
+
Part of the TDD set: `tdd-gen` (implements from tests), `tdd-test` (runs them), `tdd-coverage` (finds what is untested). The wizard works when those are off.
|
|
20
|
+
|
|
21
|
+
## How to conduct it
|
|
22
|
+
|
|
23
|
+
- **One stage at a time, and stop at each gate.** Every stage ends with something the user confirms before the next begins. Do not run ahead.
|
|
24
|
+
- **Ask one question at a time**, or a small group about one topic. Where the likely answers are few, offer them as choices, with your recommendation first. Never send a questionnaire.
|
|
25
|
+
- **Do not ask what the repo can answer.** Read first.
|
|
26
|
+
- **Propose, do not interrogate.** When the user is unsure, suggest a concrete answer for them to accept or change.
|
|
27
|
+
- **Write no implementation code** in this skill. Its output is the specification and the tests.
|
|
28
|
+
- If the user wants to skip a stage or already has its result, take what they have and move on.
|
|
29
|
+
|
|
30
|
+
Say at the start, in two or three lines, what the stages are and that they can stop after any of them.
|
|
31
|
+
|
|
32
|
+
## Stage 1: The project
|
|
33
|
+
|
|
34
|
+
Read before asking: the README, the manifest (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `*.csproj`, ...), the folder layout, the existing tests and their config.
|
|
35
|
+
|
|
36
|
+
Establish:
|
|
37
|
+
|
|
38
|
+
- **new or existing:** an empty or near-empty repo, or a feature for a project that already has code;
|
|
39
|
+
- **language and test framework.** Existing project: the ones it uses, and how its tests are named, placed and written. New project: ask for the language, then propose the framework that is the default for it (Vitest for TypeScript or JavaScript, pytest for Python, the built-in runner for Go and Rust, xUnit for .NET, JUnit for Java);
|
|
40
|
+
- **where tests go**, by the project's convention or the framework's;
|
|
41
|
+
- **what this session is about:** the whole project, or one feature of it.
|
|
42
|
+
|
|
43
|
+
**Gate:** state what you found in a few lines and have the user confirm or correct it.
|
|
44
|
+
|
|
45
|
+
## Stage 2: The specification
|
|
46
|
+
|
|
47
|
+
Ask first: **is there a specification already?** A file in the repo, a document to paste, an issue or a ticket.
|
|
48
|
+
|
|
49
|
+
**There is one.** Read it. Check it against the list below and ask only about what is missing or ambiguous. Offer to save an updated copy; do not rewrite the user's document unasked.
|
|
50
|
+
|
|
51
|
+
**There is none.** Build it with the user, a topic at a time:
|
|
52
|
+
|
|
53
|
+
1. **Goal:** what this is for, in a sentence or two. Who or what uses it.
|
|
54
|
+
2. **Scope:** what it does. Get this as concrete behaviours ("a user can reset a password by email"), not qualities ("secure", "fast").
|
|
55
|
+
3. **Out of scope:** what it deliberately does not do. This is as valuable as the scope; ask for it explicitly.
|
|
56
|
+
4. **Interfaces:** how it is used: functions, a command line, HTTP endpoints, a screen. The inputs and outputs of each.
|
|
57
|
+
5. **Rules:** validation, limits, calculations, states and what moves between them.
|
|
58
|
+
6. **Errors:** what can go wrong, and what must happen when it does.
|
|
59
|
+
7. **Constraints:** platform, dependencies it must or must not use, performance or data limits that are real requirements.
|
|
60
|
+
|
|
61
|
+
Do not go deeper than the user's project needs: a small utility has a short specification.
|
|
62
|
+
|
|
63
|
+
Then write the specification file. Default path `docs/SPEC.md`, or beside the feature for a single feature; ask if the repo suggests another place. Use this shape:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
# <Project or feature>: Specification
|
|
67
|
+
|
|
68
|
+
## Goal
|
|
69
|
+
## Scope
|
|
70
|
+
## Out of scope
|
|
71
|
+
## Interfaces
|
|
72
|
+
## Requirements
|
|
73
|
+
- **R1** <one behaviour, stated so that a test can prove it>
|
|
74
|
+
- **R2** ...
|
|
75
|
+
## Errors
|
|
76
|
+
- **E1** <condition> → <what must happen>
|
|
77
|
+
## Constraints
|
|
78
|
+
## Open questions
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Each requirement:
|
|
82
|
+
|
|
83
|
+
- is one behaviour, not several joined by "and";
|
|
84
|
+
- is **testable**: it names an input or situation and an observable result. "Handles large files well" is not a requirement; "reads a 1 GB file without holding it in memory" is;
|
|
85
|
+
- has an id (`R1`, `E1`) that the tests will refer to.
|
|
86
|
+
|
|
87
|
+
Anything the user could not decide goes under **Open questions**, not into a requirement as a guess.
|
|
88
|
+
|
|
89
|
+
**Gate:** show the specification and have the user approve it or change it. Tests are written against the approved text.
|
|
90
|
+
|
|
91
|
+
## Stage 3: The test cases
|
|
92
|
+
|
|
93
|
+
Derive the cases from the specification, not from imagined code. For each requirement and each error:
|
|
94
|
+
|
|
95
|
+
- the **normal case**: typical input, the expected result;
|
|
96
|
+
- the **boundaries**: empty, zero, one, the maximum, the first and the last, exactly at a limit and just past it;
|
|
97
|
+
- the **invalid input** and the **error path** the specification names;
|
|
98
|
+
- for anything with state: the order of operations, and doing the same thing twice.
|
|
99
|
+
|
|
100
|
+
Present them as a table before writing any test code:
|
|
101
|
+
|
|
102
|
+
| ID | Requirement | Case | Input / situation | Expected |
|
|
103
|
+
|---|---|---|---|---|
|
|
104
|
+
| T1 | R1 | valid data | name and email given | user returned with an id |
|
|
105
|
+
| T2 | R1 | empty name | name is `""` | rejected: "Name is required" |
|
|
106
|
+
|
|
107
|
+
Then go through it with the user: is a case missing, is one wrong, is one not worth having? Ask in particular about expected values you had to assume, such as an exact error message, a rounding rule or an ordering. A specification gap found here goes back into the specification file.
|
|
108
|
+
|
|
109
|
+
Also settle with the user:
|
|
110
|
+
|
|
111
|
+
- **the interface the tests will call:** the names and signatures of the functions, classes or endpoints. The tests fix these, so they are a design decision; propose them and let the user adjust;
|
|
112
|
+
- **what is faked:** the clock, the network, the database, the file system. Prefer passing dependencies in over patching them.
|
|
113
|
+
|
|
114
|
+
**Gate:** the user approves the table and the interface.
|
|
115
|
+
|
|
116
|
+
## Stage 4: Generate the tests
|
|
117
|
+
|
|
118
|
+
Write the tests in the project's framework and in the style of its existing tests.
|
|
119
|
+
|
|
120
|
+
- One test per row of the table, named for the behaviour ("rejects an empty name"), with the requirement id in the name or in a comment so each test traces back to the specification.
|
|
121
|
+
- **Test behaviour through the public interface.** Assert on results and on what a caller can observe, never on private fields or on how the code is built inside.
|
|
122
|
+
- Arrange, act, assert; one behaviour per test. Rows that differ only in data become one table-driven or parameterised test.
|
|
123
|
+
- **Deterministic:** no sleeping, no real clock, no real network, no dependence on test order or on the machine.
|
|
124
|
+
- Import from where the implementation will live, following the project's layout. The file need not exist yet.
|
|
125
|
+
- Do not write the implementation, and do not write tests for behaviour the specification does not contain.
|
|
126
|
+
|
|
127
|
+
## Stage 5: Validate, then write
|
|
128
|
+
|
|
129
|
+
Before calling the tests done, check them:
|
|
130
|
+
|
|
131
|
+
1. **Trace:** every requirement and every error in the specification has at least one test; every test points to a requirement. List anything uncovered.
|
|
132
|
+
2. **They load:** run the test command (the `tdd-test` skill if it is enabled, else the framework's run-once form). The tests must be found and counted. A syntax error, a wrong import of the test framework itself or a config error is a defect in the tests: fix it.
|
|
133
|
+
3. **They fail for the right reason.** With no implementation, each test should fail because the code under test is missing or does not yet do the thing. That is the correct result here, and it is the "red" that test-driven development starts from.
|
|
134
|
+
4. **None passes already.** A test that passes with nothing implemented asserts nothing, or tests the wrong thing. Fix or remove it. (In an existing project a test may pass because the behaviour is already there; say which, and keep it.)
|
|
135
|
+
5. **Expected values match the specification**, row by row. Do not validate tests by adjusting them until they agree with something else.
|
|
136
|
+
|
|
137
|
+
In some languages the test file cannot even compile until the names it uses exist (Go, Rust, Java, C#, TypeScript with type-checking in the test run). Then say so, and ask before creating the minimum that lets it compile: the declarations with bodies that only raise "not implemented". That is scaffolding, not implementation.
|
|
138
|
+
|
|
139
|
+
If a framework has to be installed or configured first, say what will be added and ask before changing the project.
|
|
140
|
+
|
|
141
|
+
Write the test files, then report:
|
|
142
|
+
|
|
143
|
+
- the specification file;
|
|
144
|
+
- the test files, with the number of tests in each;
|
|
145
|
+
- the trace: requirements to tests, and anything left open;
|
|
146
|
+
- the result of the run: how many were found, and that they fail because the implementation is missing.
|
|
147
|
+
|
|
148
|
+
## Stage 6: What next
|
|
149
|
+
|
|
150
|
+
Offer the choice plainly:
|
|
151
|
+
|
|
152
|
+
1. **Continue now:** implement against these tests (`tdd-gen`), run the whole suite (`tdd-test`), then check coverage (`tdd-coverage`). Stop after each for the user to look, unless they say to go straight through.
|
|
153
|
+
2. **Take it from here themselves:** give the commands for each phase, so they can run them when they choose: implement from a test file with `tdd-gen` on that file, run tests with `tdd-test`, check coverage with `tdd-coverage`.
|
|
154
|
+
3. **Stop here:** the specification and the tests are the deliverable.
|
|
155
|
+
|
|
156
|
+
If the user continues and one of those skills is not enabled, do the phase directly by the same rules: write the least code that makes the tests pass **without changing the tests**, run them until green, tidy up on green, then measure coverage and propose tests for what is below 80%.
|
|
157
|
+
|
|
158
|
+
When requirements change later, the order stays the same: the specification first, then the tests, then the code.
|
|
159
|
+
|
|
160
|
+
## Credits
|
|
161
|
+
|
|
162
|
+
Written for Cerebriline, as the front end to the workflow described in "[Test-Driven Development with Claude Code: Practical Guide](https://aiskill.market/blog/tdd-with-claude-code)" by Duke Harewood (aiskill.market, January 28, 2026), whose `/tdd`, `/test` and `/coverage` commands are the basis of `tdd-gen`, `tdd-test` and `tdd-coverage`. The test-writing guidance in Stage 4 (behaviour over implementation, table-driven cases, injected dependencies, no timing) follows that article's patterns.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The skills that ship with the product.
|
|
3
|
+
*
|
|
4
|
+
* They live in core's `assets/skills/`, one folder per skill, and are found
|
|
5
|
+
* beside the bundle first, where the extension's build copies them as
|
|
6
|
+
* `bundled-skills/`; then beside the executable, which is where the CLI's
|
|
7
|
+
* build puts them, since a compiled binary's modules have no folder on disk;
|
|
8
|
+
* and then in core's own `assets/`, by walking up from this module, which is
|
|
9
|
+
* the case for tests and for core used from `node_modules`.
|
|
10
|
+
*
|
|
11
|
+
* A bundled skill is replaced by every update, so nothing is ever written to
|
|
12
|
+
* its file. Whether it is on is a setting (`bundledSkills` in the global
|
|
13
|
+
* settings); the `disabled` field in its frontmatter is only the default.
|
|
14
|
+
*/
|
|
15
|
+
/** The folder name beside a bundle. */
|
|
16
|
+
export declare const BUNDLED_SKILLS_DIRECTORY_NAME = "bundled-skills";
|
|
17
|
+
export declare function resolveBundledSkillsDirectory(besideDir?: string): string | undefined;
|
|
18
|
+
export declare function isBundledSkillPath(filePath: string, bundledDirectory?: string | undefined): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a bundled skill is on: the user's choice if they made one, and
|
|
21
|
+
* otherwise what the skill's own frontmatter says.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isBundledSkillEnabled(name: string, enabledByDefault: boolean): boolean;
|
|
24
|
+
export declare function setBundledSkillEnabled(name: string, enabled: boolean): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { getBuiltinPromptTemplateSource, getBuiltinPromptTemplates, } from "./builtin-templates";
|
|
2
|
+
export { BUNDLED_SKILLS_DIRECTORY_NAME, isBundledSkillPath, resolveBundledSkillsDirectory, setBundledSkillEnabled, } from "./bundled-skills";
|
|
2
3
|
export { auditCompactionSections, BUILTIN_COMPACTION_PROMPTS, buildCompactionTranslationRequest, type CompactionPromptSources, resolveCompactionPromptSources, stripCompactionSections, } from "./prompt-template-compaction";
|
|
3
4
|
export { createPromptTemplateHooks, type PromptTemplateHooksOptions, } from "./prompt-template-hooks";
|
|
4
5
|
export { loadPromptTemplates, loadPromptTemplatesFromDirectory, PROMPT_TEMPLATES_DIRECTORY_NAME, type PromptTemplateDirectory, type PromptTemplateFileWarnings, type PromptTemplateLoadError, type PromptTemplateLoadResult, resolvePromptTemplateDirectories, } from "./prompt-template-loader";
|
|
@@ -10,6 +11,6 @@ export { getShippedToolCallSignatures, HOST_TOOL_INPUT_SCHEMAS, } from "./shippe
|
|
|
10
11
|
export type { UnifiedConfigDefinition, UnifiedConfigFileCandidate, UnifiedConfigFileContext, UnifiedConfigRecord, UnifiedConfigWatcherEvent, UnifiedConfigWatcherOptions, } from "./unified-config-file-watcher";
|
|
11
12
|
export { UnifiedConfigFileWatcher } from "./unified-config-file-watcher";
|
|
12
13
|
export type { CreateInstructionWatcherOptions, CreateRulesConfigDefinitionOptions, CreateSkillsConfigDefinitionOptions, CreateWorkflowsConfigDefinitionOptions, ParseMarkdownFrontmatterResult, RuleConfig, SkillConfig, UserInstructionConfig, UserInstructionConfigType, WorkflowConfig, } from "./user-instruction-config-loader";
|
|
13
|
-
export { createRulesConfigDefinition, createSkillsConfigDefinition, createWorkflowsConfigDefinition, parseRuleConfigFromMarkdown, parseSkillConfigFromMarkdown, parseWorkflowConfigFromMarkdown, RULES_CONFIG_DIRECTORY_NAME, resolveRulesConfigSearchPaths, resolveSkillsConfigSearchPaths, resolveWorkflowsConfigSearchPaths, SKILLS_CONFIG_DIRECTORY_NAME, WORKFLOWS_CONFIG_DIRECTORY_NAME, } from "./user-instruction-config-loader";
|
|
14
|
+
export { createRulesConfigDefinition, createSkillsConfigDefinition, createWorkflowsConfigDefinition, parseRuleConfigFromMarkdown, parseSkillConfigFromMarkdown, parseWorkflowConfigFromMarkdown, RULES_CONFIG_DIRECTORY_NAME, resolveRulesConfigSearchPaths, resolveSkillsConfigSearchPaths, resolveWorkflowsConfigSearchPaths, SKILLS_CONFIG_DIRECTORY_NAME, setBundledSkillEnabledByPath, WORKFLOWS_CONFIG_DIRECTORY_NAME, } from "./user-instruction-config-loader";
|
|
14
15
|
export type { CreateUserInstructionConfigServiceOptions, UserInstructionConfigRecord, UserInstructionConfigService, } from "./user-instruction-service";
|
|
15
16
|
export { combineUserInstructionConfigServices, createUserInstructionConfigService, } from "./user-instruction-service";
|
|
@@ -13,6 +13,18 @@ export interface UnifiedConfigFileCandidate {
|
|
|
13
13
|
export interface UnifiedConfigDefinition<TType extends string = string, TItem = unknown> {
|
|
14
14
|
type: TType;
|
|
15
15
|
directories: ReadonlyArray<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Directories that are read and never watched, with everything under
|
|
18
|
+
* them: files that ship with the program and change only when it is
|
|
19
|
+
* replaced.
|
|
20
|
+
*
|
|
21
|
+
* A watcher is an open handle on the directory, and on Windows a folder
|
|
22
|
+
* with an open handle anywhere inside it cannot be renamed. The bundled
|
|
23
|
+
* skills live inside the VS Code extension's own folder, so watching them
|
|
24
|
+
* made every reinstall of the running version fail with `EPERM: rename`
|
|
25
|
+
* and "Please restart VS Code before reinstalling".
|
|
26
|
+
*/
|
|
27
|
+
unwatchedDirectories?: ReadonlyArray<string>;
|
|
16
28
|
discoverFiles?: (directoryPath: string) => Promise<ReadonlyArray<UnifiedConfigFileCandidate>>;
|
|
17
29
|
includeFile?: (fileName: string, filePath: string) => boolean;
|
|
18
30
|
parseFile: (context: UnifiedConfigFileContext<TType>) => TItem;
|
|
@@ -52,6 +64,8 @@ export declare class UnifiedConfigFileWatcher<TType extends string = string, TIt
|
|
|
52
64
|
private readonly baseTypesByDirectory;
|
|
53
65
|
private watchedTypesByDirectory;
|
|
54
66
|
private readonly discoveredDirectoriesByType;
|
|
67
|
+
/** Read, never watched: see `UnifiedConfigDefinition.unwatchedDirectories`. */
|
|
68
|
+
private readonly unwatchedRoots;
|
|
55
69
|
private readonly definitionsByType;
|
|
56
70
|
private readonly pendingTypes;
|
|
57
71
|
private flushTimer;
|
|
@@ -72,6 +86,7 @@ export declare class UnifiedConfigFileWatcher<TType extends string = string, TIt
|
|
|
72
86
|
private scheduleFlush;
|
|
73
87
|
private refreshTypeInternal;
|
|
74
88
|
private loadDefinition;
|
|
89
|
+
private isUnwatched;
|
|
75
90
|
private buildDesiredTypesByDirectory;
|
|
76
91
|
private readDirectoryFileCandidates;
|
|
77
92
|
}
|
|
@@ -14,6 +14,11 @@ export interface SkillConfig {
|
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
instructions: string;
|
|
16
16
|
frontmatter: Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Shipped with the product. Its file is never written: `disabled` here is
|
|
19
|
+
* the user's setting, or the frontmatter's default where they made none.
|
|
20
|
+
*/
|
|
21
|
+
bundled?: boolean;
|
|
17
22
|
source?: {
|
|
18
23
|
type: "agent-plugin";
|
|
19
24
|
pluginName: string;
|
|
@@ -45,6 +50,11 @@ export interface CreateInstructionWatcherOptions {
|
|
|
45
50
|
}
|
|
46
51
|
export interface CreateSkillsConfigDefinitionOptions {
|
|
47
52
|
directories?: ReadonlyArray<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Where the skills shipped with the product are. Undefined means core's
|
|
55
|
+
* own; `false` leaves them out. Not used when `directories` is given.
|
|
56
|
+
*/
|
|
57
|
+
bundledSkillsDirectory?: string | false;
|
|
48
58
|
workspacePath?: string;
|
|
49
59
|
includePluginSkills?: boolean;
|
|
50
60
|
pluginSkillDirectories?: ReadonlyArray<string>;
|
|
@@ -60,6 +70,11 @@ export interface CreateWorkflowsConfigDefinitionOptions {
|
|
|
60
70
|
directories?: ReadonlyArray<string>;
|
|
61
71
|
workspacePath?: string;
|
|
62
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Turn a bundled skill on or off, given its `SKILL.md`. False when the path
|
|
75
|
+
* is not a bundled skill's, so a caller can fall back to the frontmatter.
|
|
76
|
+
*/
|
|
77
|
+
export declare function setBundledSkillEnabledByPath(filePath: string, enabled: boolean): Promise<boolean>;
|
|
63
78
|
export declare function parseSkillConfigFromMarkdown(content: string, fallbackName: string): SkillConfig;
|
|
64
79
|
export declare function parseRuleConfigFromMarkdown(content: string, fallbackName: string): RuleConfig;
|
|
65
80
|
export declare function parseWorkflowConfigFromMarkdown(content: string, fallbackName: string): WorkflowConfig;
|
|
@@ -8,4 +8,14 @@
|
|
|
8
8
|
* `destroy()` deletes the directory.
|
|
9
9
|
*/
|
|
10
10
|
import type { DocumentFormat, DocumentReadResult, ReadOptions } from "./formats";
|
|
11
|
+
/**
|
|
12
|
+
* Why an EPUB cannot be opened as the ZIP it is, or nothing when it can.
|
|
13
|
+
*
|
|
14
|
+
* A ZIP's index is at its end, so a file cut short while it was copied,
|
|
15
|
+
* downloaded or unpacked has its first entries and no index. lingo-reader
|
|
16
|
+
* does not survive that: its loader drops the ZIP reader's error and the
|
|
17
|
+
* promise it returned never settles, so the read would wait until the tool's
|
|
18
|
+
* limit with nothing to say.
|
|
19
|
+
*/
|
|
20
|
+
export declare function epubZipFault(data: Uint8Array): string | undefined;
|
|
11
21
|
export declare function readEbook(filePath: string, data: Uint8Array, format: Extract<DocumentFormat, "epub" | "mobi" | "azw3" | "fb2">, options: ReadOptions): Promise<DocumentReadResult>;
|
|
@@ -36,6 +36,31 @@ export interface ReadOptions {
|
|
|
36
36
|
* recognition is off; resolves undefined for a page it did not read.
|
|
37
37
|
*/
|
|
38
38
|
recognize?: (page: number, images: readonly PageImage[]) => Promise<RecognizedText | undefined>;
|
|
39
|
+
/** Stops the read at the next page or chapter; the read throws. */
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
/** Called as the read moves: once a page or chapter, and around recognition. */
|
|
42
|
+
onProgress?: (progress: ReadProgress) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Everything the read left out, a line each: a picture that could not be
|
|
45
|
+
* taken out, and why. The reader appends; nothing is left out silently.
|
|
46
|
+
*/
|
|
47
|
+
problems?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Lines for the log, not for the reader of the document: what the PDF
|
|
50
|
+
* decoder warned about, a picture that needed asking for twice.
|
|
51
|
+
*/
|
|
52
|
+
onNote?: (line: string) => void;
|
|
53
|
+
}
|
|
54
|
+
/** Where a read is. */
|
|
55
|
+
export interface ReadProgress {
|
|
56
|
+
unit: UnitName;
|
|
57
|
+
/** The page or chapter being read, from 1. */
|
|
58
|
+
at: number;
|
|
59
|
+
total: number;
|
|
60
|
+
/** Pictures taken out so far. */
|
|
61
|
+
pictures: number;
|
|
62
|
+
/** What it is doing there, when that is more than reading: "recognizing text". */
|
|
63
|
+
activity?: string;
|
|
39
64
|
}
|
|
40
65
|
/** A picture a scanned page is drawn from, as a PNG. */
|
|
41
66
|
export interface PageImage {
|
|
@@ -57,6 +57,9 @@ export declare class OcrLanguageMissingError extends Error {
|
|
|
57
57
|
export declare class TesseractReader {
|
|
58
58
|
private readonly worker;
|
|
59
59
|
readonly languages: readonly string[];
|
|
60
|
+
private readonly failure;
|
|
61
|
+
/** Why the worker is gone, once it is: nothing more is asked of it. */
|
|
62
|
+
private dead;
|
|
60
63
|
private constructor();
|
|
61
64
|
static open(languages: readonly string[], dataDir?: string): Promise<TesseractReader>;
|
|
62
65
|
recognize(png: Uint8Array): Promise<{
|
|
@@ -35,6 +35,11 @@ export interface RecognitionPlan {
|
|
|
35
35
|
}[];
|
|
36
36
|
/** Sentences for the result header. */
|
|
37
37
|
notes(scannedPages: readonly number[], recognizedPages: readonly number[]): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Every scanned page that has no recognized text, with the reason, a line
|
|
40
|
+
* each. `notes` is the summary a reader skims; this is the whole account.
|
|
41
|
+
*/
|
|
42
|
+
problems(scannedPages: readonly number[], recognizedPages: readonly number[]): string[];
|
|
38
43
|
close(): Promise<void>;
|
|
39
44
|
}
|
|
40
45
|
interface PlanInput {
|
|
@@ -43,6 +48,12 @@ interface PlanInput {
|
|
|
43
48
|
settings: DocumentReaderSettings;
|
|
44
49
|
describeImages?: DescribeImages;
|
|
45
50
|
modelSupportsImages: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* How many scanned pages are recognized. A book kept in the Library is
|
|
53
|
+
* read whole: its call is long by design and reports as it goes.
|
|
54
|
+
* @default the engine's per-call limit
|
|
55
|
+
*/
|
|
56
|
+
pageLimit?: number;
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
48
59
|
* The recognition a call will do, ready to hand to the reader.
|
|
@@ -61,6 +72,11 @@ export interface PictureToDescribe {
|
|
|
61
72
|
* Descriptions of a document's pictures, one request each, in order.
|
|
62
73
|
* `undefined` for any the vision model could not describe.
|
|
63
74
|
*/
|
|
75
|
+
/**
|
|
76
|
+
* One picture described, or the reason it was not. For a caller that has to
|
|
77
|
+
* say why: `describePictures` keeps the answers and drops the reasons.
|
|
78
|
+
*/
|
|
79
|
+
export declare function describePicture(describeImages: DescribeImages, picture: PictureToDescribe, documentName: string): Promise<string | undefined>;
|
|
64
80
|
export declare function describePictures(describeImages: DescribeImages, pictures: readonly PictureToDescribe[], documentName: string): Promise<(string | undefined)[]>;
|
|
65
81
|
/** A description short enough for alt text: its first sentences, up to 300 characters. */
|
|
66
82
|
export declare function altTextOf(description: string): string;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { AgentOverlay } from "../../../runtime/sandbox/overlay-fs";
|
|
10
10
|
import type { ExtractDocumentExecutor } from "../types";
|
|
11
|
+
import { type DocumentFormat, type ReadProgress } from "./document/formats";
|
|
11
12
|
import type { DocumentReaderSettings } from "./document/ocr";
|
|
12
13
|
import { type DescribeImages } from "./document/recognition";
|
|
13
14
|
/** Where extractions go when the call names no `output_dir`. */
|
|
@@ -52,4 +53,94 @@ export declare function markdownToText(markdown: string): string;
|
|
|
52
53
|
* and all of it is tokens.
|
|
53
54
|
*/
|
|
54
55
|
export declare function tidyMarkdown(markdown: string): string;
|
|
56
|
+
export interface DocumentText {
|
|
57
|
+
/** The document as markdown. */
|
|
58
|
+
markdown: string;
|
|
59
|
+
format: DocumentFormat;
|
|
60
|
+
title?: string;
|
|
61
|
+
author?: string;
|
|
62
|
+
bytes: number;
|
|
63
|
+
/** What was skipped or could not be read, in sentences. */
|
|
64
|
+
notes: string[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A document's text, and nothing written anywhere: what the Library indexes.
|
|
68
|
+
*
|
|
69
|
+
* The same readers as the tool, without the pictures. Scanned pages are read
|
|
70
|
+
* when the settings name an engine that needs no model in the conversation
|
|
71
|
+
* (tesseract, or the vision model when one is given).
|
|
72
|
+
*/
|
|
73
|
+
export declare function readDocumentText(filePath: string, options: {
|
|
74
|
+
/** Removed afterwards. Not the system temp directory: a book's pages can be large. */
|
|
75
|
+
scratchDir: string;
|
|
76
|
+
maxFileSizeBytes?: number;
|
|
77
|
+
reader?: DocumentReaderSettings;
|
|
78
|
+
describeImages?: DescribeImages;
|
|
79
|
+
}): Promise<DocumentText>;
|
|
80
|
+
export interface BookDocument extends DocumentText {
|
|
81
|
+
/** The pictures inside it, with their bytes; the markdown links them as `images/<file>`. */
|
|
82
|
+
images: {
|
|
83
|
+
file: string;
|
|
84
|
+
data: Uint8Array;
|
|
85
|
+
mediaType: string;
|
|
86
|
+
width?: number;
|
|
87
|
+
height?: number;
|
|
88
|
+
origin?: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
/** `description` is a vision model's, not the caption the document gave. */
|
|
91
|
+
described?: boolean;
|
|
92
|
+
}[];
|
|
93
|
+
/** Pages or chapters it has, and how many were read. */
|
|
94
|
+
units?: {
|
|
95
|
+
name: string;
|
|
96
|
+
total: number;
|
|
97
|
+
read: number;
|
|
98
|
+
};
|
|
99
|
+
/** Scanned pages, and how many of them had their text recognized. */
|
|
100
|
+
scanned?: {
|
|
101
|
+
pages: number;
|
|
102
|
+
recognized: number;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Everything that was left out, a line each: a scanned page with no
|
|
106
|
+
* recognized text, a picture that could not be taken out. Empty when the
|
|
107
|
+
* whole document was read.
|
|
108
|
+
*/
|
|
109
|
+
problems: string[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A document as the Library keeps a book: its text and its pictures.
|
|
113
|
+
* Nothing is written anywhere; the caller puts the pictures where the book
|
|
114
|
+
* is kept, and `describeBookPictures` has a vision model look at them.
|
|
115
|
+
*/
|
|
116
|
+
export declare function readDocumentForBook(filePath: string, options: {
|
|
117
|
+
scratchDir: string;
|
|
118
|
+
maxFileSizeBytes?: number;
|
|
119
|
+
reader?: DocumentReaderSettings;
|
|
120
|
+
describeImages?: DescribeImages;
|
|
121
|
+
/**
|
|
122
|
+
* Take the pictures out as well. Off for a read that only compares
|
|
123
|
+
* text: a scanned book is a picture a page, a gigabyte of them decoded.
|
|
124
|
+
* @default true
|
|
125
|
+
*/
|
|
126
|
+
pictures?: boolean;
|
|
127
|
+
/** Stops the read at the next page or chapter; the read throws. */
|
|
128
|
+
signal?: AbortSignal;
|
|
129
|
+
onProgress?: (progress: ReadProgress) => void;
|
|
130
|
+
/** Lines for the log: what the decoder warned about, a picture asked for twice. */
|
|
131
|
+
onNote?: (line: string) => void;
|
|
132
|
+
}): Promise<BookDocument>;
|
|
133
|
+
/**
|
|
134
|
+
* Have the vision model describe a book's pictures: each description is kept
|
|
135
|
+
* with its picture and becomes the alt text where the text shows it, so a
|
|
136
|
+
* search for what a figure shows finds the page it is on. Resolves with how
|
|
137
|
+
* many were described and how many could have been.
|
|
138
|
+
*/
|
|
139
|
+
export declare function describeBookPictures(book: BookDocument, describeImages: DescribeImages, options: {
|
|
140
|
+
documentName: string;
|
|
141
|
+
limit?: number;
|
|
142
|
+
}): Promise<{
|
|
143
|
+
described: number;
|
|
144
|
+
candidates: number;
|
|
145
|
+
}>;
|
|
55
146
|
export declare function createDocumentExtractExecutor(options?: DocumentExtractExecutorOptions): ExtractDocumentExecutor;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The librarian's tools: everything that changes the Library.
|
|
3
|
+
*
|
|
4
|
+
* They come with the `librarian` skill, which says how the work is done:
|
|
5
|
+
* check whether a book is already here before adding it, ask the user when
|
|
6
|
+
* it is another edition, put it on the shelf it belongs on. The tools keep
|
|
7
|
+
* the rules that must hold whoever is asking: nothing is added over a book
|
|
8
|
+
* that looks the same without being told to, and nothing is deleted, only
|
|
9
|
+
* moved to the trash.
|
|
10
|
+
*/
|
|
11
|
+
import { type AgentTool } from "@cerebriline/shared";
|
|
12
|
+
import { type BookDocument } from "./executors/document-extract";
|
|
13
|
+
import { type CreateLibraryToolsOptions } from "./library-tools";
|
|
14
|
+
export declare const LIBRARIAN_TOOL_NAMES: readonly ["library_check", "library_add", "library_organize", "library_transfer", "web_scrape", "library_web_book"];
|
|
15
|
+
export interface ReadSource {
|
|
16
|
+
file: string;
|
|
17
|
+
name: string;
|
|
18
|
+
sha256: string;
|
|
19
|
+
document: BookDocument;
|
|
20
|
+
isbn?: string;
|
|
21
|
+
fingerprint: number[];
|
|
22
|
+
}
|
|
23
|
+
/** An ISBN printed in the first pages, when there is one. */
|
|
24
|
+
export declare function findIsbn(markdown: string): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* What the files of one batch have to do with each other: a folder of ebooks
|
|
27
|
+
* often holds the same book twice, and the Library cannot say so about files
|
|
28
|
+
* it does not hold yet.
|
|
29
|
+
*/
|
|
30
|
+
export declare function compareBatch(sources: readonly ReadSource[]): Map<string, string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* The librarian's tools. The two web tools are there only when a scraping
|
|
33
|
+
* endpoint is configured and the session's profile allows it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createLibrarianTools(options: CreateLibraryToolsOptions): AgentTool[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One librarian call that reads files, as the person watching sees it.
|
|
3
|
+
*
|
|
4
|
+
* Reading a book is minutes: every page of a PDF, every scanned page through
|
|
5
|
+
* recognition. A row that sits unchanged for that long reads as a hang, and
|
|
6
|
+
* a real hang looks the same. So the call says where it is, file by file,
|
|
7
|
+
* and says so when a file has stopped moving; it can be cancelled from its
|
|
8
|
+
* row; and however it ends, the model is told what was read, what was left
|
|
9
|
+
* out and why, a line for each.
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentToolContext } from "@cerebriline/shared";
|
|
12
|
+
import type { ReadProgress } from "./executors/document/formats";
|
|
13
|
+
/**
|
|
14
|
+
* Cancels every librarian call that is reading files. Each one ends as a
|
|
15
|
+
* failed tool call whose error is its full report.
|
|
16
|
+
* @returns how many were cancelled
|
|
17
|
+
*/
|
|
18
|
+
export declare function cancelLibraryImports(): number;
|
|
19
|
+
/** Whether a librarian call is reading files right now. */
|
|
20
|
+
export declare function hasActiveLibraryImports(): boolean;
|
|
21
|
+
export declare class LibraryImportCancelled extends Error {
|
|
22
|
+
constructor(reason: string);
|
|
23
|
+
}
|
|
24
|
+
export declare class LibraryImportRun {
|
|
25
|
+
private readonly what;
|
|
26
|
+
private readonly context;
|
|
27
|
+
private readonly log?;
|
|
28
|
+
private readonly entries;
|
|
29
|
+
private readonly controller;
|
|
30
|
+
private readonly startedAt;
|
|
31
|
+
private readonly timer;
|
|
32
|
+
private readonly onOuterAbort;
|
|
33
|
+
private lastEmit;
|
|
34
|
+
private lastLog;
|
|
35
|
+
private pendingEmit;
|
|
36
|
+
private cancelReason;
|
|
37
|
+
private closed;
|
|
38
|
+
private rejectOnCancel;
|
|
39
|
+
private readonly cancelled;
|
|
40
|
+
constructor(what: string, files: readonly string[], context: AgentToolContext | undefined, log?: ((message: string) => void) | undefined,
|
|
41
|
+
/** What an item is called on its line. @default the file's name */
|
|
42
|
+
label?: (item: string) => string);
|
|
43
|
+
/** Handed to the reader: it stops at the next page or chapter. */
|
|
44
|
+
get signal(): AbortSignal;
|
|
45
|
+
get isCancelled(): boolean;
|
|
46
|
+
/** @returns false when it was already cancelled or over */
|
|
47
|
+
cancel(reason: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The work, or the cancellation, whichever is first. A reader notices a
|
|
50
|
+
* cancellation at its next page; one that is waiting on something that
|
|
51
|
+
* never answers does not, and the call must still end.
|
|
52
|
+
*/
|
|
53
|
+
guard<T>(work: Promise<T>): Promise<T>;
|
|
54
|
+
private entry;
|
|
55
|
+
start(file: string): void;
|
|
56
|
+
progress(file: string, progress: ReadProgress): void;
|
|
57
|
+
/** A line about a file for the log alone. */
|
|
58
|
+
note(file: string, line: string): void;
|
|
59
|
+
/** What a file is doing after it was read: "describing pictures". */
|
|
60
|
+
stage(file: string, stage: string | undefined): void;
|
|
61
|
+
done(file: string, outcome: string, problems?: readonly string[]): void;
|
|
62
|
+
fail(file: string, why: string): void;
|
|
63
|
+
private line;
|
|
64
|
+
private counts;
|
|
65
|
+
/** The row's text: a line of totals, then a line a file. */
|
|
66
|
+
statusText(): string;
|
|
67
|
+
/** Where the files in progress are, for the log and for a cancellation. */
|
|
68
|
+
private standing;
|
|
69
|
+
private tick;
|
|
70
|
+
private emit;
|
|
71
|
+
/**
|
|
72
|
+
* What happened to every file, for the model: read or not, how far, and
|
|
73
|
+
* every page and picture that was left out, with the reason.
|
|
74
|
+
*/
|
|
75
|
+
report(): string[];
|
|
76
|
+
/** The error a cancelled call ends with: why, and the whole report. */
|
|
77
|
+
cancellation(extra?: readonly string[]): Error;
|
|
78
|
+
close(): void;
|
|
79
|
+
}
|