@tyvm/knowhow 0.0.109-dev.e88af1e → 0.0.110

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 (82) hide show
  1. package/autodoc/README.md +324 -0
  2. package/autodoc/chat-guide.md +268 -365
  3. package/autodoc/cli-reference.md +399 -473
  4. package/autodoc/config-reference.md +431 -330
  5. package/autodoc/embeddings-guide.md +223 -322
  6. package/autodoc/generate-guide.md +261 -301
  7. package/autodoc/language-plugin-guide.md +221 -247
  8. package/autodoc/modules-guide.md +242 -215
  9. package/autodoc/plugins-guide.md +470 -469
  10. package/autodoc/quickstart-guide.md +67 -70
  11. package/autodoc/skills-guide.md +455 -339
  12. package/autodoc/worker-guide.md +301 -308
  13. package/package.json +1 -1
  14. package/src/agents/tools/list.ts +2 -2
  15. package/src/ai.ts +81 -37
  16. package/src/chat/CliChatService.ts +1 -1
  17. package/src/chat/modules/SystemModule.ts +2 -2
  18. package/src/clients/anthropic.ts +1 -1
  19. package/src/clients/index.ts +25 -6
  20. package/src/clients/openai.ts +8 -5
  21. package/src/clients/types.ts +29 -6
  22. package/src/clients/withRetry.ts +89 -0
  23. package/src/commands/agent.ts +30 -0
  24. package/src/commands/modules.ts +365 -30
  25. package/src/config.ts +1 -1
  26. package/src/hashes.ts +8 -9
  27. package/src/index.ts +4 -2
  28. package/src/processors/Base64ImageDetector.ts +73 -0
  29. package/src/services/MediaProcessorService.ts +79 -10
  30. package/src/services/modules/index.ts +24 -19
  31. package/tests/processors/Base64ImageDetector.test.ts +160 -0
  32. package/tests/unit/clients/AIClient.test.ts +446 -0
  33. package/tests/unit/clients/withRetry.test.ts +319 -0
  34. package/tests/unit/commands/github-credentials.test.ts +1 -2
  35. package/ts_build/package.json +1 -1
  36. package/ts_build/src/agents/tools/list.js +2 -2
  37. package/ts_build/src/agents/tools/list.js.map +1 -1
  38. package/ts_build/src/ai.d.ts +3 -3
  39. package/ts_build/src/ai.js +51 -23
  40. package/ts_build/src/ai.js.map +1 -1
  41. package/ts_build/src/chat/CliChatService.js +1 -1
  42. package/ts_build/src/chat/CliChatService.js.map +1 -1
  43. package/ts_build/src/chat/modules/SystemModule.js +2 -2
  44. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  45. package/ts_build/src/clients/anthropic.js +1 -1
  46. package/ts_build/src/clients/anthropic.js.map +1 -1
  47. package/ts_build/src/clients/index.js +7 -6
  48. package/ts_build/src/clients/index.js.map +1 -1
  49. package/ts_build/src/clients/openai.js +4 -4
  50. package/ts_build/src/clients/openai.js.map +1 -1
  51. package/ts_build/src/clients/types.d.ts +12 -6
  52. package/ts_build/src/clients/withRetry.d.ts +2 -0
  53. package/ts_build/src/clients/withRetry.js +60 -0
  54. package/ts_build/src/clients/withRetry.js.map +1 -0
  55. package/ts_build/src/commands/agent.js +25 -0
  56. package/ts_build/src/commands/agent.js.map +1 -1
  57. package/ts_build/src/commands/modules.js +297 -17
  58. package/ts_build/src/commands/modules.js.map +1 -1
  59. package/ts_build/src/config.js +1 -1
  60. package/ts_build/src/config.js.map +1 -1
  61. package/ts_build/src/hashes.js +5 -7
  62. package/ts_build/src/hashes.js.map +1 -1
  63. package/ts_build/src/index.js +1 -1
  64. package/ts_build/src/index.js.map +1 -1
  65. package/ts_build/src/processors/Base64ImageDetector.d.ts +3 -0
  66. package/ts_build/src/processors/Base64ImageDetector.js +42 -0
  67. package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
  68. package/ts_build/src/services/MediaProcessorService.d.ts +5 -4
  69. package/ts_build/src/services/MediaProcessorService.js +53 -8
  70. package/ts_build/src/services/MediaProcessorService.js.map +1 -1
  71. package/ts_build/src/services/modules/index.js +17 -13
  72. package/ts_build/src/services/modules/index.js.map +1 -1
  73. package/ts_build/tests/processors/Base64ImageDetector.test.js +111 -0
  74. package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -1
  75. package/ts_build/tests/unit/clients/AIClient.test.d.ts +1 -0
  76. package/ts_build/tests/unit/clients/AIClient.test.js +339 -0
  77. package/ts_build/tests/unit/clients/AIClient.test.js.map +1 -0
  78. package/ts_build/tests/unit/clients/withRetry.test.d.ts +1 -0
  79. package/ts_build/tests/unit/clients/withRetry.test.js +225 -0
  80. package/ts_build/tests/unit/clients/withRetry.test.js.map +1 -0
  81. package/ts_build/tests/unit/commands/github-credentials.test.js +1 -2
  82. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -1
@@ -1,720 +1,721 @@
1
- # Plugins Guide (Knowhow CLI)
1
+ # Plugins Guide
2
2
 
3
- Knowhow **plugins** are modular “context providers” and helpers that can enrich an agent session with additional information and tooling. Depending on the plugin, it can:
3
+ Knowhow plugins extend the agent with **extra context sources**, **URL/file resolution**, **semantic retrieval**, and **IDE/session awareness**. Plugins can be enabled/disabled in `knowhow.json` and can also be added as **custom npm modules**.
4
4
 
5
- - **Expand shorthand terms / hotkeys** into files, snippets, and/or URLs (`language`)
6
- - **Load live editor/session context** (e.g., open Vim buffers) (`vim`)
7
- - **Perform semantic retrieval** using embeddings (`embeddings`)
8
- - **Resolve work-item references** (GitHub, Asana, Jira, Linear) into readable context (`github`, `asana`, `jira`, `linear`)
9
- - **Load rich external content** (web pages, downloads/transcripts, design docs) (`url`, `download`, `figma`, `notion`)
10
- - **Add codebase signals** (git diff/log) (`git`)
11
- - **Run safety/quality tooling** after edits (linting) (`linter`)
12
- - **Incorporate terminal/session state** (`tmux`)
13
- - **Load local guidance docs** (`agents-md`, `skills`)
14
- - **Execute commands for context** (`exec`)
5
+ ---
15
6
 
16
- Internally, Knowhow maintains a **plugin registry** and can call plugins by their **plugin key** (e.g., `github`, `language`). Plugins can also support **embeddings** via an `embed()`-style capability.
7
+ ## 1) What plugins are
17
8
 
18
- ---
9
+ A **plugin** is a module registered with the Knowhow plugin system. Each plugin is identified by a **plugin key** (e.g. `language`, `url`) and exposes:
10
+
11
+ - `call(userInput?: string): Promise<string>`
12
+ - `callMany(userInput?: string): Promise<string>`
13
+ - `embed(userInput?: string): Promise<MinimalEmbedding[]>`
14
+ - `enable() / disable() / isEnabled()`
15
+ - `meta` (key/name/description + optional `requires` env vars)
19
16
 
20
- ## 1) What plugins are (how they provide context)
17
+ Internally, `PluginService` keeps a map of plugin instances and invokes them by key. Before calling a plugin, Knowhow checks whether the plugin is enabled and its required environment variables are present.
21
18
 
22
- ### 1.1 Plugin “capabilities”
23
- Most plugins fall into one or more of these roles:
19
+ ### Common ways plugins provide value
24
20
 
25
- - **Context generation (`call`)**
26
- Reads input (prompt content, URLs, IDs, etc.) and returns text/markdown context.
21
+ #### A) Context expansions (especially for “language terms”)
22
+ The `language` plugin expands configured “terms” into sources (files/text/URLs and optionally other plugins).
27
23
 
28
- - **Batch processing (`callMany`)**
29
- Used when multiple values must be resolved at once (e.g., many URLs).
24
+ - It loads term sources of `kind: "file"` and reads the file contents
25
+ - It loads `kind: "text"` sources directly
26
+ - It can also route sources to **other plugins**:
27
+ - For each enabled plugin key `p`, if a language term source has `kind === p`, Knowhow calls that plugin with the source data.
30
28
 
31
- - **Embeddings (`embed`)**
32
- Returns `MinimalEmbedding[]` items so Knowhow can do semantic retrieval over fetched/constructed content.
29
+ This happens in `LanguagePlugin.resolveSources()`.
33
30
 
34
- ### 1.2 Common patterns
35
- - **URL / identifier resolution**
36
- A plugin detects an identifier in the user prompt and fetches/normalizes it into agent-ready context.
37
- Examples: GitHub PR/issue, Jira ticket, Linear issue, Notion page.
31
+ #### B) Event-driven context
32
+ Some plugins register handlers on Knowhow events (via `context.Events`). For example:
38
33
 
39
- - **Language-term expansion** (`language`)
40
- A plugin mapping turns configured tokens/hotkeys into:
41
- - local file contents (`kind: "file"`)
42
- - literal text snippets (`kind: "text"`)
43
- - URLs or references (often via `kind: "url"` or a service plugin key like `github`)
34
+ - `language` listens to configured events and triggers context expansions
35
+ - `linter` listens to `file:post-edit`
36
+ - `embeddings` listens to `file:post-edit` and starts embedding generation
37
+ - `git` listens to `file:post-edit`, `agent:newTask`, `agent:taskComplete`, and `linter:*`
44
38
 
45
- - **Local environment context**
46
- - `vim`: injects currently open Vim buffers
47
- - `git`: injects git diff/log context
48
- - `tmux`: injects terminal pane/session context
39
+ #### C) Semantic context via embeddings
40
+ Plugins like `embeddings` (semantic search) can return relevant IDs/documents based on the user prompt.
49
41
 
50
- - **Post-edit automation**
51
- - `linter`: runs lint after file edits
52
- - `exec`: runs commands for context (powerful; see security note)
42
+ #### D) Tool/assistant awareness
43
+ Plugins like `vim`, `tmux`, `agents-md`, and `skills` expose “what’s going on right now” (open buffers, terminal sessions, local agent instructions, reusable skills).
53
44
 
54
45
  ---
55
46
 
56
- ## 2) Enable / disable plugins (`knowhow.json`)
47
+ ## 2) Enabling / disabling plugins
57
48
 
58
- Plugins are controlled in `knowhow.json` using:
49
+ Plugins are controlled via `knowhow.json` under `plugins.enabled` and `plugins.disabled`.
59
50
 
60
- - `plugins.enabled`: array of plugin keys to enable
61
- - `plugins.disabled`: array of plugin keys to disable
51
+ > The code excerpt shows plugin enable/disable methods (`PluginService.enablePlugin()` / `disablePlugin()`), and plugins are considered enabled only if:
52
+ > 1) they are not manually disabled, and
53
+ > 2) their required environment variables are set (via `meta.requires` in `PluginBase.isEnabled()`).
62
54
 
63
- ### 2.1 Enable a subset
64
- ```jsonc
65
- {
66
- "plugins": {
67
- "enabled": ["language", "github", "embeddings", "git", "url", "linter"],
68
- "disabled": []
69
- }
70
- }
71
- ```
55
+ ### Example `knowhow.json`
72
56
 
73
- ### 2.2 Disable specific plugins
74
- ```jsonc
57
+ ```json
75
58
  {
76
59
  "plugins": {
77
- "enabled": ["language", "vim", "tmux", "github", "url", "download"],
78
- "disabled": ["exec", "tmux"]
60
+ "enabled": ["language", "git", "linter", "url", "skills"],
61
+ "disabled": ["embeddings"]
79
62
  }
80
63
  }
81
64
  ```
82
65
 
83
- > **Tip:** Avoid putting the same plugin in both lists. Use one source of truth in your config.
66
+ **Rules of thumb**
67
+ - Use `disabled` to quickly turn off a plugin.
68
+ - Use `enabled` to explicitly choose which built-ins to run.
69
+ - A plugin may still refuse to run if its `meta.requires` env vars are missing.
84
70
 
85
71
  ---
86
72
 
87
- ## 3) Built-in plugins (keys + config examples)
88
-
89
- Built-in plugins are identified by these keys:
90
-
91
- - `language`
92
- - `vim`
93
- - `embeddings`
94
- - `github`
95
- - `git`
96
- - `asana`
97
- - `jira`
98
- - `linear`
99
- - `figma`
100
- - `notion`
101
- - `download`
102
- - `url`
103
- - `linter`
104
- - `tmux`
105
- - `agents-md`
106
- - `exec`
107
- - `skills`
108
-
109
- Below are practical configuration examples for each plugin key. Some plugin config fields are implementation-specific; the examples show **typical / commonly used** fields and where you’d place plugin-specific options.
73
+ ## 3) Built-in plugins
74
+
75
+ Below are the built-in plugins and how to configure them.
76
+
77
+ > For some plugins, implementation details aren’t included in the provided source excerpt. Where that happens, configuration examples are based on the plugin’s purpose and its typical API surface (key name, tokens, and expected sources). When in doubt, check the plugin’s own `meta.requires` and configuration getters in your repository.
78
+
79
+ ### Quick reference table
80
+
81
+ | Plugin key | Purpose |
82
+ |---|---|
83
+ | `language` | Expand configured language terms into files/text/URLs and plugin-sourced context |
84
+ | `vim` | Load currently open Vim swap files (`*.swp`) as context |
85
+ | `embeddings` | Semantic search over an embeddings knowledgebase |
86
+ | `github` | Resolve GitHub PRs/issues/code (token-based) |
87
+ | `git` | Provide `.knowhow/.git` tracking context (diff/log + auto-commit behavior) |
88
+ | `asana` | Resolve Asana tasks (token-based) |
89
+ | `jira` | Resolve Jira issues (token-based) |
90
+ | `linear` | Resolve Linear issues (token-based) |
91
+ | `figma` | Resolve Figma design file context (token-based) |
92
+ | `notion` | Resolve Notion page context (token-based) |
93
+ | `download` | Download / transcribe URLs and handle YouTube videos |
94
+ | `url` | Fetch and parse web pages from URLs found in text |
95
+ | `linter` | Run background lint on file edits |
96
+ | `tmux` | Provide tmux session/window/pane context |
97
+ | `agents-md` | Detect nearby `agents.md` and alert the agent |
98
+ | `exec` | Execute shell commands for context (triggered via `language` “exec” sources) |
99
+ | `skills` | Load reusable `SKILL.md` instructions from configured directories |
110
100
 
111
101
  ---
112
102
 
113
- ### 3.1 `language` — language terms / hotkeys expansion
103
+ ### `language` plugin
104
+
105
+ **Key:** `language`
106
+ **What it does:** Looks for configured “terms” inside prompts/events. When matches occur, it **expands** them into configured sources:
107
+ - `kind: "file"` → reads file contents
108
+ - `kind: "text"` → injects literal text
109
+ - `kind: <pluginKey>` → calls that plugin with the source data (only if the target plugin is enabled)
110
+
111
+ **Event support:** Each term can define an `events` list. The plugin registers handlers for all `file*` events separately from other events, and emits an `agent:msg` containing the resolved sources.
114
112
 
115
- **What it does**
116
- - Detects configured **language terms / hotkeys**
117
- - Expands them into context sources like:
118
- - local files
119
- - literal snippets
120
- - plugin-backed resolutions (e.g., `github`)
121
- - URLs
113
+ #### Example config: language terms
122
114
 
123
- **Typical config shape**
124
- ```jsonc
115
+ Your repository contains `getLanguageConfig()` / `getLanguageConfig` in `src/config`; the excerpt implies this shape:
116
+
117
+ ```json
125
118
  {
126
- "plugins": {
127
- "enabled": ["language"]
128
- },
129
119
  "language": {
130
- "terms": {
131
- "@spec": {
132
- "events": ["file:read", "agent:msg"],
133
- "sources": [
134
- { "kind": "file", "data": ["./docs/spec.md"] }
135
- ]
136
- },
137
- "@pr": {
138
- "events": ["agent:msg"],
139
- "sources": [
140
- { "kind": "github", "data": ["owner/repo#123"] }
141
- ]
142
- }
120
+ "hotkey1": {
121
+ "events": ["file:post-edit", "agent:msg"],
122
+ "sources": [
123
+ { "kind": "file", "data": ["./docs/hotkey1.md"] },
124
+ { "kind": "text", "data": ["Use the Makefile targets to reproduce the issue."] },
125
+ { "kind": "url", "data": ["https://example.com/runbook"] }
126
+ ]
143
127
  },
144
- "hotkeys": {
145
- ":runbook": {
146
- "events": ["agent:msg"],
147
- "sources": [
148
- { "kind": "file", "data": ["./RUNBOOKS/security.md"] }
149
- ]
150
- }
128
+
129
+ "ABC-123, #ticket, ticket*": {
130
+ "events": ["agent:msg"],
131
+ "sources": [
132
+ { "kind": "jira", "data": ["ABC-123"] }
133
+ ]
151
134
  }
152
135
  }
153
136
  }
154
137
  ```
155
138
 
156
- **Env vars**
157
- - Usually none.
139
+ **Notes**
140
+ - Terms can be a comma-separated list of patterns, as `language` splits `term.split(",")`.
141
+ - Pattern matching:
142
+ - If a term pattern contains `*`, it uses glob matching (`minimatch`)
143
+ - Otherwise it checks `userPrompt.toLowerCase().includes(pattern)`
158
144
 
159
145
  ---
160
146
 
161
- ### 3.2 `vim` — loads currently open vim buffers as context
147
+ ### `vim` plugin
148
+
149
+ **Key:** `vim`
150
+ **What it does:** Finds Vim swap files `./**/*.swp` (including dotfiles), then maps swap files back to their likely source file paths and reads content (with safeguards for size).
151
+
152
+ #### Behavior (from code)
153
+ - `call()` returns a message listing the swap files it finds.
154
+ - It resolves swap file paths by stripping the swap suffix and checking for either:
155
+ - the non-dot file, or
156
+ - the dotfile variant in the same directory.
162
157
 
163
- **What it does**
164
- - Reads the contents of **open Vim buffers**
165
- - Supplies them to the agent as context
158
+ #### Example config
166
159
 
167
- **Example config**
168
- ```jsonc
160
+ ```json
169
161
  {
170
162
  "plugins": {
171
163
  "enabled": ["vim"]
172
- },
173
- "vim": {
174
- "maxBuffers": 10,
175
- "maxBytesPerBuffer": 200000
176
164
  }
177
165
  }
178
166
  ```
179
167
 
180
- **Env vars**
181
- - Usually none.
168
+ No additional plugin-specific config is visible in the excerpt.
182
169
 
183
170
  ---
184
171
 
185
- ### 3.3 `embeddings` — semantic search over embeddings
172
+ ### `embeddings` plugin
173
+
174
+ **Key:** `embeddings`
175
+ **What it does:**
176
+ 1. On `file:post-edit`, it starts a background `knowhow embed` process to refresh embeddings.
177
+ 2. `call()` runs semantic search (`queryEmbedding`) and returns the **IDs** for the top results (after pruning vector/metadata).
186
178
 
187
- **What it does**
188
- - Provides embedding-based retrieval (semantic search)
189
- - In the provided embedding-plugin behavior:
190
- - subscribes to file lifecycle events (e.g., post-edit)
191
- - may run `knowhow embed` asynchronously in the background after edits
192
- - returns top results (implementation chooses the final limit)
179
+ #### What it needs
180
+ - Embeddings must be configured via “configured embeddings” returned by `getConfiguredEmbeddings()` (not shown in excerpt).
181
+ - `config.embeddingModel` is used in `queryEmbedding(...)`.
193
182
 
194
- **Example config**
195
- ```jsonc
183
+ #### Example config (typical)
184
+
185
+ ```json
196
186
  {
187
+ "embeddingModel": "text-embedding-model-name",
188
+ "embeddings": {
189
+ "stores": [
190
+ {
191
+ "type": "local",
192
+ "path": "./.knowhow/embeddings"
193
+ }
194
+ ]
195
+ },
197
196
  "plugins": {
198
197
  "enabled": ["embeddings"]
199
- },
200
- "embeddings": {
201
- "embeddingModel": "text-embedding-3-small",
202
- "topK": 7,
203
- "indexDirs": ["./docs", "./src", "./skills"]
204
198
  }
205
199
  }
206
200
  ```
207
201
 
208
- **Env vars**
209
- - Depends on embedding provider/model backend (commonly an OpenAI/compatible API key). Verify the plugin’s `meta.requires` for your build.
202
+ If you don’t have embeddings set up, the plugin returns:
203
+
204
+ > “EMBEDDING PLUGIN: No embeddings configured. Run 'knowhow embed' to generate embeddings.”
210
205
 
211
206
  ---
212
207
 
213
- ### 3.4 `github` — resolves GitHub PRs, issues, code
208
+ ### `github` plugin
209
+
210
+ **Key:** `github`
211
+ **What it does (expected):**
212
+ - Resolve GitHub PRs/issues/code and return relevant context for the agent.
213
+ - Likely triggered by `language` term sources like `{ "kind": "github", "data": ["owner/repo#123"] }`.
214
214
 
215
- **What it does**
216
- - Resolves GitHub identifiers / URLs found in prompts into context:
217
- - PRs
218
- - issues
219
- - (often) code changes/diffs and structured summaries
215
+ #### Example config
220
216
 
221
- **Example config**
222
- ```jsonc
217
+ ```json
223
218
  {
224
219
  "plugins": {
225
- "enabled": ["github"]
220
+ "enabled": ["github", "language"]
226
221
  },
227
- "github": {
228
- "repos": ["my-org/my-repo", "my-org/another-repo"],
229
- "includeDiff": true,
230
- "maxItems": 10
222
+ "language": {
223
+ "owner/repo#*": {
224
+ "events": ["agent:msg"],
225
+ "sources": [
226
+ { "kind": "github", "data": ["$MATCH"] }
227
+ ]
228
+ }
231
229
  }
232
230
  }
233
231
  ```
234
232
 
235
- **Env vars**
236
- - `GITHUB_TOKEN` (commonly required)
233
+ > Exact term/source formatting depends on how your `language` config is processed in your codebase. The excerpt only shows that `language` passes joined `data` strings directly to `Plugins.call(plugin, data)`.
237
234
 
238
235
  ---
239
236
 
240
- ### 3.5 `git` — git diff/log context
237
+ ### `git` plugin
241
238
 
242
- **What it does**
243
- - Adds codebase history context from git:
244
- - diff outputs
245
- - commit history / logs
246
- - related change signals
239
+ **Key:** `git`
240
+ **What it does:**
241
+ - Provides project git status (via `git status --porcelain`)
242
+ - Tracks agent edits in a separate git repo at:
243
+ - `.knowhow/.git`
244
+ - Auto-commits on `file:post-edit` and creates task branches on `agent:newTask`, and squashes on `agent:taskComplete`.
247
245
 
248
- **Example config**
249
- ```jsonc
246
+ #### Example config
247
+
248
+ ```json
250
249
  {
251
250
  "plugins": {
252
- "enabled": ["git"]
253
- },
254
- "git": {
255
- "mode": "diff",
256
- "logDepth": 20,
257
- "maxChars": 20000
251
+ "enabled": ["git", "linter"]
258
252
  }
259
253
  }
260
254
  ```
261
255
 
262
- **Env vars**
263
- - Usually none.
256
+ **Where it stores data**
257
+ - `.knowhow/.git` (inside your current working directory)
264
258
 
265
259
  ---
266
260
 
267
- ### 3.6 `asana` — Asana task context
261
+ ### `asana` plugin
268
262
 
269
- **What it does**
270
- - Finds Asana task/list URLs in prompts
271
- - Fetches task details and formats them for the agent
263
+ **Key:** `asana`
264
+ **What it does (expected):**
265
+ - Resolve Asana tasks into agent context (likely based on a task URL or ID).
266
+ - Typically used via `language` term expansions.
272
267
 
273
- **Example config**
274
- ```jsonc
268
+ #### Example config
269
+
270
+ ```json
275
271
  {
276
- "plugins": {
277
- "enabled": ["asana"]
278
- },
279
- "asana": {
280
- "workspaceId": "1234567890",
281
- "includeSubtasks": true,
282
- "maxTasks": 5
272
+ "plugins": { "enabled": ["asana", "language"] },
273
+ "language": {
274
+ "asana:*": {
275
+ "events": ["agent:msg"],
276
+ "sources": [
277
+ { "kind": "asana", "data": ["asana:1234567890"] }
278
+ ]
279
+ }
283
280
  }
284
281
  }
285
282
  ```
286
283
 
287
- **Env vars**
288
- - `ASANA_TOKEN` (required/expected)
289
-
290
284
  ---
291
285
 
292
- ### 3.7 `jira` — Jira issue context
286
+ ### `jira` plugin
287
+
288
+ **Key:** `jira`
289
+ **What it does (expected):**
290
+ - Resolve Jira issues (e.g. `ABC-123`) into context.
293
291
 
294
- **What it does**
295
- - Detects Jira issue keys/URLs
296
- - Fetches ticket context and formats it for the agent
292
+ #### Example config
297
293
 
298
- **Example config**
299
- ```jsonc
294
+ ```json
300
295
  {
301
- "plugins": {
302
- "enabled": ["jira"]
303
- },
304
- "jira": {
305
- "site": "https://your-company.atlassian.net",
306
- "includeComments": true,
307
- "maxIssues": 5
296
+ "plugins": { "enabled": ["jira", "language"] },
297
+ "language": {
298
+ "ABC-*, #*": {
299
+ "events": ["agent:msg"],
300
+ "sources": [
301
+ { "kind": "jira", "data": ["ABC-123"] }
302
+ ]
303
+ }
308
304
  }
309
305
  }
310
306
  ```
311
307
 
312
- **Env vars**
313
- - Commonly one or more of:
314
- - `JIRA_TOKEN`
315
- - (sometimes) `JIRA_EMAIL`, `JIRA_BASE_URL`
316
- - Check your plugin’s `meta.requires` list for exact names.
317
-
318
308
  ---
319
309
 
320
- ### 3.8 `linear` — Linear issue context
310
+ ### `linear` plugin
321
311
 
322
- **What it does**
323
- - Resolves Linear issue URLs/keys and fetches issue context
312
+ **Key:** `linear`
313
+ **What it does (expected):**
314
+ - Resolve Linear issues (team/issue IDs or URLs) into context.
324
315
 
325
- **Example config**
326
- ```jsonc
316
+ #### Example config
317
+
318
+ ```json
327
319
  {
328
- "plugins": {
329
- "enabled": ["linear"]
330
- },
331
- "linear": {
332
- "includeComments": false,
333
- "maxIssues": 5
320
+ "plugins": { "enabled": ["linear", "language"] },
321
+ "language": {
322
+ "LIN-*, linear:*": {
323
+ "events": ["agent:msg"],
324
+ "sources": [
325
+ { "kind": "linear", "data": ["LIN-456"] }
326
+ ]
327
+ }
334
328
  }
335
329
  }
336
330
  ```
337
331
 
338
- **Env vars**
339
- - `LINEAR_TOKEN` (commonly required)
340
-
341
332
  ---
342
333
 
343
- ### 3.9 `figma` — Figma design file context
334
+ ### `figma` plugin
335
+
336
+ **Key:** `figma`
337
+ **What it does (expected):**
338
+ - Fetch Figma file/design references and provide context (frames, metadata, etc.).
344
339
 
345
- **What it does**
346
- - Accepts Figma file links (often with optional `node-id` targets)
347
- - Uses Figma API to fetch frame/node images or metadata
348
- - Can use vision/LLM to describe relevant nodes (implementation-dependent)
340
+ #### Example config
349
341
 
350
- **Example config**
351
- ```jsonc
342
+ ```json
352
343
  {
353
- "plugins": {
354
- "enabled": ["figma"]
355
- },
356
- "figma": {
357
- "maxNodes": 20
344
+ "plugins": { "enabled": ["figma", "language"] },
345
+ "language": {
346
+ "figma:*": {
347
+ "events": ["agent:msg"],
348
+ "sources": [
349
+ { "kind": "figma", "data": ["figma:FILEKEY#node-id"] }
350
+ ]
351
+ }
358
352
  }
359
353
  }
360
354
  ```
361
355
 
362
- **Env vars**
363
- - `FIGMA_API_KEY` (or `FIGMA_TOKEN`, depending on your plugin implementation)
364
- - Verify against the plugin’s `meta.requires`.
365
-
366
356
  ---
367
357
 
368
- ### 3.10 `notion` — Notion page context
358
+ ### `notion` plugin
369
359
 
370
- **What it does**
371
- - Extracts Notion URLs from prompts
372
- - Retrieves page blocks/content and produces context for the agent
373
- - Often supports recursive traversal up to a configured depth
360
+ **Key:** `notion`
361
+ **What it does (expected):**
362
+ - Load Notion pages (block/page text) as context.
374
363
 
375
- **Example config**
376
- ```jsonc
364
+ #### Example config
365
+
366
+ ```json
377
367
  {
378
- "plugins": {
379
- "enabled": ["notion"]
380
- },
381
- "notion": {
382
- "maxDepth": 2,
383
- "maxBlocks": 500
368
+ "plugins": { "enabled": ["notion", "language"] },
369
+ "language": {
370
+ "notion:*": {
371
+ "events": ["agent:msg"],
372
+ "sources": [
373
+ { "kind": "notion", "data": ["notion:xxxxxxxxxxxxxxxxxxxxxxxxxxxx"] }
374
+ ]
375
+ }
384
376
  }
385
377
  }
386
378
  ```
387
379
 
388
- **Env vars**
389
- - `NOTION_TOKEN` (commonly required)
390
- - Some implementations may also need database/page identifiers (config-based).
391
-
392
380
  ---
393
381
 
394
- ### 3.11 `download` — download/transcribe URLs, YouTube videos
382
+ ### `download` plugin
383
+
384
+ **Key:** `download`
385
+ **What it does (expected):**
386
+ - Download content from URLs and optionally transcribe (especially for YouTube).
395
387
 
396
- **What it does**
397
- - Downloads and/or transcribes content from URLs (including video sources like YouTube)
398
- - Converts to text context for the agent
399
- - May use chunking + transcription + optional vision keyframe descriptions (implementation-dependent)
388
+ #### Example config
400
389
 
401
- **Example config**
402
- ```jsonc
390
+ ```json
403
391
  {
404
- "plugins": {
405
- "enabled": ["download"]
406
- },
407
- "download": {
408
- "outputRoot": ".knowhow/downloads",
409
- "transcribe": true,
410
- "transcriptionLanguage": "en",
411
- "reuseCachedTranscripts": true
392
+ "plugins": { "enabled": ["download", "language"] },
393
+ "language": {
394
+ "https://*youtube.com/*": {
395
+ "events": ["agent:msg"],
396
+ "sources": [
397
+ { "kind": "download", "data": ["$MATCH"] }
398
+ ]
399
+ }
412
400
  }
413
401
  }
414
402
  ```
415
403
 
416
- **Env vars**
417
- - Often depends on the transcription/vision provider used by the plugin
418
- - Commonly an OpenAI/compatible API key, but verify the plugin’s `meta.requires`.
419
-
420
404
  ---
421
405
 
422
- ### 3.12 `url` — load web pages as context
406
+ ### `url` plugin
423
407
 
424
- **What it does**
425
- - Detects URLs in prompts
426
- - Fetches webpage contents and returns context
427
- - Can support embedding retrieval from the fetched page text
408
+ **Key:** `url`
409
+ **What it does:**
410
+ - Extracts URLs from the user prompt using regex: `/(https?:\/\/[^\s]+)/g`
411
+ - Fetches each URL (with a browser-like user agent)
412
+ - Strips HTML tags (simple conversion) and returns parsed text
413
+ - In `call()`, it limits to **10 URLs** max.
428
414
 
429
- **Example config**
430
- ```jsonc
415
+ #### Example config
416
+
417
+ ```json
431
418
  {
432
419
  "plugins": {
433
420
  "enabled": ["url"]
434
- },
435
- "url": {
436
- "maxUrls": 10,
437
- "maxCharsPerPage": 120000,
438
- "followRedirects": true
439
421
  }
440
422
  }
441
423
  ```
442
424
 
443
- **Env vars**
444
- - Usually none.
425
+ You typically don’t need to wire `url` via `language`; it can fetch URLs found directly in prompts. If you *do* want to drive it via `language`, you can.
426
+
427
+ #### Example language config using `url`
428
+
429
+ ```json
430
+ {
431
+ "language": {
432
+ "runbook: url:*": {
433
+ "events": ["agent:msg"],
434
+ "sources": [
435
+ { "kind": "url", "data": ["https://example.com/runbook"] }
436
+ ]
437
+ }
438
+ }
439
+ }
440
+ ```
445
441
 
446
442
  ---
447
443
 
448
- ### 3.13 `linter` — runs lint after file edits
444
+ ### `linter` plugin
445
+
446
+ **Key:** `linter`
447
+ **What it does (from code):**
448
+ - Listens for `file:post-edit`
449
+ - Looks up a per-extension lint command in `config.lintCommands`
450
+ - Runs it in the background (spawn with `shell: true`)
451
+ - Emits:
452
+ - `linter:started`
453
+ - `linter:finished`
454
+ - If lint fails (determined by **any stderr output**), it notifies the agent with the output.
455
+
456
+ #### Required config: `lintCommands`
449
457
 
450
- **What it does**
451
- - Watches for file lifecycle events after the agent edits files
452
- - Runs lint commands (by extension or configured command list)
453
- - Emits lint failures/results into the agent context
458
+ `config.lintCommands` should be a mapping of extension → command.
459
+ If the command includes `$1`, it is replaced with the edited `filePath`.
454
460
 
455
- **Example config**
456
- ```jsonc
461
+ Example:
462
+
463
+ ```json
457
464
  {
458
- "plugins": {
459
- "enabled": ["linter"]
465
+ "lintCommands": {
466
+ "ts": "eslint $1",
467
+ "js": "eslint $1",
468
+ "py": "python -m compileall $1"
460
469
  },
461
- "linter": {
462
- "commands": {
463
- ".ts": "npm run lint --silent -- $1",
464
- ".js": "npm run lint --silent -- $1",
465
- ".md": "markdownlint $1"
466
- },
467
- "failOnError": false
470
+ "plugins": {
471
+ "enabled": ["linter", "git"]
468
472
  }
469
473
  }
470
474
  ```
471
475
 
472
- **Env vars**
473
- - Usually none.
474
-
475
476
  ---
476
477
 
477
- ### 3.14 `tmux` — tmux session context
478
+ ### `tmux` plugin
478
479
 
479
- **What it does**
480
- - Detects tmux environment (e.g., using `$TMUX`)
481
- - Reads session/window/pane context and makes it available to the agent
480
+ **Key:** `tmux`
481
+ **What it does:**
482
+ - Checks if the current process is inside tmux by evaluating `echo $TMUX`
483
+ - If in tmux, it runs:
484
+ - `tmux display-message -p '#{session_name}:#{window_index}:#{window_name}'`
485
+ - `tmux list-sessions`
486
+ - `tmux list-windows`
487
+ - `tmux list-panes -a ...`
488
+ - Produces a structured overview and a small “useful commands” section.
482
489
 
483
- **Example config**
484
- ```jsonc
490
+ #### Example config
491
+
492
+ ```json
485
493
  {
486
494
  "plugins": {
487
495
  "enabled": ["tmux"]
488
- },
489
- "tmux": {
490
- "includePaneHistory": true,
491
- "maxCharsPerPane": 20000
492
496
  }
493
497
  }
494
498
  ```
495
499
 
496
- **Env vars**
497
- - Usually none (but requires tmux availability in your environment).
498
-
499
500
  ---
500
501
 
501
- ### 3.15 `agents-md` — loads AGENTS.md files
502
+ ### `agents-md` plugin
503
+
504
+ **Key:** `agents-md`
505
+ **What it does (from code):**
506
+ - Traverses upward from the edited file to find the nearest `agents.md`
507
+ - Alerts the agent via `agent:msg` when it finds one
508
+ - It listens to these events:
509
+ - `file:pre-write`, `file:post-write`, `file:write`, `file:edit`
502
510
 
503
- **What it does**
504
- - Loads repository/directory guidance files (commonly `AGENTS.md`)
505
- - Intended for agent behavior instructions, conventions, constraints, etc.
511
+ #### Example config
506
512
 
507
- **Example config**
508
- ```jsonc
513
+ ```json
509
514
  {
510
515
  "plugins": {
511
516
  "enabled": ["agents-md"]
512
- },
513
- "agents-md": {
514
- "glob": "**/AGENTS.md",
515
- "maxFiles": 30
516
517
  }
517
518
  }
518
519
  ```
519
520
 
520
- **Env vars**
521
- - Usually none.
522
-
523
521
  ---
524
522
 
525
- ### 3.16 `exec` — execute commands for context
523
+ ### `exec` plugin
526
524
 
527
- **What it does**
528
- - Executes shell commands to generate context
529
- - In the provided implementation behavior:
530
- - `callMany(input)` only dispatches when input starts with `!` or `/!`
531
- - `call(input)` runs the command string and returns stdout/stderr formatted context
532
- - **Security risk:** can run arbitrary commands.
525
+ **Key:** `exec`
526
+ **What it does:**
527
+ - Executes shell commands synchronously via `execSync(command, ...)`.
528
+ - `callMany()` is special: it only executes when `input` starts with `!` or `/!`.
529
+ - `call()` executes any non-empty trimmed input.
533
530
 
534
- **Example config**
535
- ```jsonc
536
- {
537
- "plugins": {
538
- "enabled": ["exec"]
539
- }
540
- }
541
- ```
531
+ This is designed to be triggered from `language` expansions where a source routes to the `exec` plugin.
532
+
533
+ #### Example language config to run commands
542
534
 
543
- **Example usage with `language` trigger**
544
- ```jsonc
535
+ ```json
545
536
  {
546
- "plugins": {
547
- "enabled": ["language", "exec"]
548
- },
549
537
  "language": {
550
- "terms": {
551
- "!now": {
552
- "events": ["agent:msg"],
553
- "sources": [
554
- { "kind": "text", "data": "!date" }
555
- ]
556
- }
538
+ "ls: command": {
539
+ "events": ["agent:msg"],
540
+ "sources": [
541
+ { "kind": "exec", "data": ["ls -la"] }
542
+ ]
557
543
  }
544
+ },
545
+ "plugins": {
546
+ "enabled": ["language", "exec"]
558
547
  }
559
548
  }
560
549
  ```
561
550
 
562
- **Env vars**
563
- - None inherent to the plugin, but your executed commands may depend on your environment.
564
-
565
551
  ---
566
552
 
567
- ### 3.17 `skills` — loads SKILL.md files from configured directories
553
+ ### `skills` plugin
568
554
 
569
- **What it does (as implemented by the Skills plugin)**
570
- - Configures an array of directories to scan
571
- - Recursively finds `SKILL.md`
572
- - Parses YAML-like frontmatter at the top:
555
+ **Key:** `skills`
556
+ **What it does (from code):**
557
+ - Reads `SKILL.md` files from configured directories (`config.skills`)
558
+ - Each `SKILL.md` must contain YAML-like frontmatter between `---` blocks with at least:
573
559
  - `name`
574
- - `description`
575
- - If a skill name appears in the user prompt (case-insensitive substring match):
576
- - returns the matched skill file content
577
- - If no skill names match:
578
- - returns a discovery list of available skills and how to reference them
579
-
580
- **Example config**
581
- ```jsonc
560
+ - optionally `description`
561
+
562
+ When `call()` / `embed()` runs:
563
+ - It checks which skill names appear in the user prompt (case-insensitive)
564
+ - If matches exist, it loads full content and returns embeddings for each
565
+ - Otherwise, it returns a “skills discovery summary” with all available skills
566
+
567
+ #### Required config: `skills`
568
+
569
+ ```json
582
570
  {
571
+ "skills": [
572
+ "~/knowhow/skills",
573
+ "./.knowhow/skills"
574
+ ],
583
575
  "plugins": {
584
576
  "enabled": ["skills"]
585
- },
586
- "skills": [
587
- "./skills",
588
- "./docs/skills"
589
- ]
577
+ }
590
578
  }
591
579
  ```
592
580
 
593
- **Example `SKILL.md`**
581
+ #### Example `SKILL.md`
582
+
594
583
  ```md
595
584
  ---
596
- name: sql-tuning
597
- description: Optimize SQL queries with indexes and query plans
585
+ name: "React: component patterns"
586
+ description: "Preferred patterns for component composition and state handling."
598
587
  ---
599
588
 
600
- # sql-tuning
589
+ # React: component patterns
601
590
 
602
- When tuning a query:
603
- 1. Inspect `EXPLAIN`
604
- 2. Find missing indexes
605
- 3. Rewrite joins/filters
591
+ Use function components...
606
592
  ```
607
593
 
608
- **Env vars**
609
- - Usually none.
610
-
611
594
  ---
612
595
 
613
- ## 4) Custom plugins via `pluginPackages` (npm packages)
596
+ ## 4) Custom plugins via `modules`
614
597
 
615
- Knowhow can load **custom plugins** from npm packages listed under `pluginPackages` in `knowhow.json`.
598
+ Knowhow can also load **custom plugins** as npm packages (dynamic import). The plugin loader supports ESM import specifiers (e.g. `"my-knowhow-plugin"` or `"./plugins/foo"`).
616
599
 
617
- ### 4.1 Register a custom plugin package
600
+ Although the configuration loader code isn’t included in the excerpt, the presence of:
618
601
 
619
- ```jsonc
620
- {
621
- "plugins": {
622
- "enabled": ["my-custom-plugin"]
623
- },
624
- "pluginPackages": {
625
- "my-custom-plugin": "@your-scope/knowhow-my-custom-plugin"
602
+ - `PluginService.loadPlugin(spec: string)` which:
603
+ - `import(spec)` and expects a **default export** class
604
+ - instantiates it as `new PluginCtor(this)`
605
+ - registers it under `instance.meta.key`
606
+
607
+ implies the `modules` config should list import specifiers to load.
608
+
609
+ ### Writing a custom plugin package
610
+
611
+ Your plugin module must:
612
+
613
+ 1. Default-export a class that implements the Knowhow `Plugin` contract (or extends `PluginBase`)
614
+ 2. Provide `static meta` or an instance `meta` with:
615
+ - `meta.key` (plugin key string)
616
+ - `meta.name`
617
+ - optionally `meta.requires` (env vars)
618
+
619
+ #### Minimal example (TypeScript)
620
+
621
+ ```ts
622
+ // src/index.ts
623
+ import { PluginBase } from "knowhow/plugins/PluginBase";
624
+ import { PluginMeta } from "knowhow/plugins/PluginBase";
625
+ import { PluginContext } from "knowhow/plugins/types";
626
+ import { MinimalEmbedding } from "knowhow/types";
627
+
628
+ export default class MyPlugin extends PluginBase {
629
+ static readonly meta: PluginMeta = {
630
+ key: "my-plugin",
631
+ name: "My Plugin",
632
+ requires: ["MY_PLUGIN_TOKEN"]
633
+ };
634
+
635
+ meta = MyPlugin.meta;
636
+
637
+ constructor(context: PluginContext) {
638
+ super(context);
639
+ }
640
+
641
+ async call(input?: string): Promise<string> {
642
+ return `MyPlugin saw: ${input ?? ""}`;
626
643
  }
627
- }
628
- ```
629
644
 
630
- > In this model, the **key** in `pluginPackages` should map to the plugin key you enable in `plugins.enabled`.
645
+ async callMany(input?: string): Promise<string> {
646
+ return this.call(input);
647
+ }
631
648
 
632
- ### 4.2 What a custom plugin must provide
633
- A custom plugin package should export a plugin constructor/class that Knowhow can instantiate (typically with a plugin context/service). The plugin instance should include metadata, especially:
649
+ async embed(_input: string): Promise<MinimalEmbedding[]> {
650
+ return [];
651
+ }
652
+ }
653
+ ```
634
654
 
635
- - `meta.key` (must match the key you enable)
636
- - `meta.requires` (optional; env vars needed for auth/integration)
655
+ ### Registering the plugin in `knowhow.json`
637
656
 
638
- And implement one or more plugin methods, commonly:
639
- - `call(input?)`
640
- - `callMany(input?)`
641
- - `embed(input)`
657
+ A typical pattern is:
642
658
 
643
- ### 4.3 Example `knowhow.json` using multiple plugins
644
- ```jsonc
659
+ ```json
645
660
  {
661
+ "modules": ["my-knowhow-plugin"],
646
662
  "plugins": {
647
- "enabled": ["language", "my-custom-plugin", "embeddings"],
648
- "disabled": []
649
- },
650
- "pluginPackages": {
651
- "my-custom-plugin": "@acme/knowhow-my-custom-plugin"
663
+ "enabled": ["my-plugin"]
652
664
  }
653
665
  }
654
666
  ```
655
667
 
668
+ > If your local development build exports a file path instead of a package name, you can use an ESM specifier like `"./plugins/my-plugin/index.js"`.
669
+
656
670
  ---
657
671
 
658
- ## 5) Required environment variables (per plugin)
672
+ ## 5) Required environment variables per plugin
673
+
674
+ Knowhow decides plugin enablement using `PluginBase.isEnabled()`:
675
+
676
+ - If `plugin.meta.requires` is set, every env var listed must exist and be non-empty.
677
+ - Manual `plugins.disabled` also turns it off.
659
678
 
660
- Knowhow uses each plugin’s metadata (typically `meta.requires`) to decide whether the plugin is enabled. If env vars are missing, the plugin may be skipped.
679
+ In the provided excerpt, the following plugin env requirements are explicitly not defined (their `meta.requires` is empty or omitted):
680
+ **`language`, `vim`, `embeddings` (not shown as requiring), `url`, `git`, `linter`, `tmux`, `agents-md`, `exec`, `skills`, `skills`**.
661
681
 
662
- Below are the **expected** environment variables for the standard external-service integrations:
682
+ For the token-based services, plugin implementations commonly require API tokens. Configure these environment variables to ensure the plugins can activate.
663
683
 
664
- | Plugin key | Environment variables (expected) |
684
+ ### Recommended env vars (by plugin purpose)
685
+
686
+ | Plugin key | Common env vars (typical) |
665
687
  |---|---|
666
688
  | `github` | `GITHUB_TOKEN` |
667
689
  | `asana` | `ASANA_TOKEN` |
668
- | `jira` | `JIRA_TOKEN` (or provider-specific equivalents); often also `JIRA_BASE_URL` / `JIRA_EMAIL` depending on setup |
690
+ | `jira` | `JIRA_TOKEN` (or `JIRA_API_TOKEN` / `JIRA_EMAIL`, depending on implementation) |
669
691
  | `linear` | `LINEAR_TOKEN` |
670
- | `figma` | `FIGMA_API_KEY` or `FIGMA_TOKEN` (verify exact `meta.requires`) |
671
- | `notion` | `NOTION_TOKEN` |
672
- | `embeddings` | depends on embedding provider (commonly an API key like `OPENAI_API_KEY`, but verify in plugin code/config) |
673
- | `download` | depends on transcription/vision provider (often an API key; verify in plugin code/config) |
674
- | `language`, `vim`, `git`, `url`, `linter`, `tmux`, `agents-md`, `exec`, `skills` | typically none required for auth (but `exec` depends on your system tools) |
692
+ | `figma` | `FIGMA_TOKEN` |
693
+ | `notion` | `NOTION_TOKEN` (often also `NOTION_DATABASE_ID` or similar) |
694
+ | `download` | (usually none, unless a transcription backend requires credentials) |
695
+
696
+ ### Example `.env` / environment
675
697
 
676
- ### Example: setting environment variables
677
698
  ```bash
678
- export GITHUB_TOKEN="ghp_xxx"
679
- export ASANA_TOKEN="asca_xxx"
680
- export JIRA_TOKEN="xxx"
681
- export LINEAR_TOKEN="linear_xxx"
682
- export FIGMA_API_KEY="figma_xxx"
683
- export NOTION_TOKEN="secret_notion_xxx"
684
- export OPENAI_API_KEY="sk_xxx"
699
+ export GITHUB_TOKEN="ghp_..."
700
+ export ASANA_TOKEN="..."
701
+ export LINEAR_TOKEN="..."
702
+ export FIGMA_TOKEN="..."
703
+ export NOTION_TOKEN="..."
685
704
  ```
686
705
 
687
- ---
706
+ If you want, paste the repo files for `src/plugins/github.ts`, `src/plugins/asana.ts`, `src/plugins/jira.ts`, `src/plugins/linear.ts`, `src/plugins/figma.ts`, `src/plugins/notion.ts`, and `src/plugins/download.ts`, and I can replace the “typical” env vars with the exact ones listed in each plugin’s `meta.requires`.
688
707
 
689
- ## Recommended “starter” config
708
+ ---
690
709
 
691
- A safe, useful baseline that covers local context, issue resolution, and retrieval:
710
+ ## Summary
692
711
 
693
- ```jsonc
694
- {
695
- "plugins": {
696
- "enabled": [
697
- "language",
698
- "skills",
699
- "agents-md",
700
- "github",
701
- "jira",
702
- "linear",
703
- "asana",
704
- "notion",
705
- "url",
706
- "embeddings",
707
- "git",
708
- "linter"
709
- ],
710
- "disabled": ["exec", "download", "vim", "tmux"]
711
- },
712
- "pluginPackages": {}
713
- }
714
- ```
715
-
716
- > Enable `exec`/`download` only if you trust prompts and external content sources you’ll process.
717
-
718
- ---
712
+ - Use **`language`** to expand terms into contextual sources.
713
+ - Turn plugins on/off with **`knowhow.json -> plugins.enabled/disabled`**.
714
+ - Configure plugin-specific settings like:
715
+ - `language` term sources
716
+ - `lintCommands`
717
+ - `skills` directories
718
+ - embedding store/model settings
719
+ - Add new capabilities with **custom plugins** loaded via `modules`.
719
720
 
720
- If you paste your repo’s `knowhow.json` schema (or the plugin `meta.requires` and config types for each built-in plugin), I can revise this guide so **every config block uses the exact field names** your Knowhow version supports.
721
+ If you share your current `knowhow.json` (redact secrets) I can propose an end-to-end plugin setup tailored to your workflow.