@kolisachint/hoocode-agent 0.5.41 → 0.5.43
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/CHANGELOG.md +111 -0
- package/dist/core/builtin-skills.d.ts +11 -0
- package/dist/core/builtin-skills.d.ts.map +1 -1
- package/dist/core/builtin-skills.js +31 -0
- package/dist/core/builtin-skills.js.map +1 -1
- package/dist/core/canvas/scaffold.d.ts +1 -1
- package/dist/core/canvas/scaffold.d.ts.map +1 -1
- package/dist/core/canvas/scaffold.js +100 -5
- package/dist/core/canvas/scaffold.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +1 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +14 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +3 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/webtools-shared.d.ts +53 -2
- package/dist/core/tools/webtools-shared.d.ts.map +1 -1
- package/dist/core/tools/webtools-shared.js +43 -2
- package/dist/core/tools/webtools-shared.js.map +1 -1
- package/dist/extensions/core/canvas.d.ts.map +1 -1
- package/dist/extensions/core/canvas.js +2 -1
- package/dist/extensions/core/canvas.js.map +1 -1
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +4 -0
- package/dist/init-templates.generated.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +11 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +16 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/websearch-warning.d.ts +35 -0
- package/dist/modes/interactive/websearch-warning.d.ts.map +1 -0
- package/dist/modes/interactive/websearch-warning.js +40 -0
- package/dist/modes/interactive/websearch-warning.js.map +1 -0
- package/docs/settings.md +52 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/templates/agents/code-review.md +61 -0
- package/templates/agents/security-review.md +69 -0
- package/templates/skills/artifact-design/SKILL.md +191 -0
- package/templates/skills/canvas-design/SKILL.md +117 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "websearch has no API key" startup notice.
|
|
3
|
+
*
|
|
4
|
+
* `websearch` works with no configuration at all, but the keyless default is
|
|
5
|
+
* scraped DuckDuckGo HTML: it is rate-limited hard and can fail outright, and
|
|
6
|
+
* the failure surfaces as a bad search rather than as a setup problem. So when
|
|
7
|
+
* the tool is active and no keyed backend is configured, the session says so
|
|
8
|
+
* once — the same shape as the Anthropic extra-usage notice, including the
|
|
9
|
+
* `/settings` switch that turns it off.
|
|
10
|
+
*/
|
|
11
|
+
import type { WarningSettings } from "../../core/settings-types.js";
|
|
12
|
+
import { type WebtoolsSearchSettings } from "../../core/tools/webtools-shared.js";
|
|
13
|
+
export declare const WEBSEARCH_API_KEY_TITLE = "Web search has no API key";
|
|
14
|
+
export interface WebSearchApiKeyNoticeInput {
|
|
15
|
+
/** Tools the session actually exposes; the notice is pointless without `websearch`. */
|
|
16
|
+
activeToolNames: readonly string[];
|
|
17
|
+
/** Warning switches from settings (`warnings.websearchApiKey`). */
|
|
18
|
+
warnings: WarningSettings;
|
|
19
|
+
/** The `webtools.search` block, as the binary would read it. */
|
|
20
|
+
search?: WebtoolsSearchSettings;
|
|
21
|
+
}
|
|
22
|
+
export interface WebSearchApiKeyNotice {
|
|
23
|
+
title: string;
|
|
24
|
+
body: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The notice to show, or undefined when there is nothing to say: the tool is
|
|
28
|
+
* off, a keyed backend is configured, the user pinned the keyless backend on
|
|
29
|
+
* purpose, or the warning is switched off.
|
|
30
|
+
*
|
|
31
|
+
* Pure, so the decision is testable without a TUI; the caller owns the
|
|
32
|
+
* once-per-session latch.
|
|
33
|
+
*/
|
|
34
|
+
export declare function websearchApiKeyNotice(input: WebSearchApiKeyNoticeInput): WebSearchApiKeyNotice | undefined;
|
|
35
|
+
//# sourceMappingURL=websearch-warning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websearch-warning.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/websearch-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAA+B,KAAK,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAE/G,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE,MAAM,WAAW,0BAA0B;IAC1C,uFAAuF;IACvF,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,mEAAmE;IACnE,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,qBAAqB,GAAG,SAAS,CAiB1G","sourcesContent":["/**\n * The \"websearch has no API key\" startup notice.\n *\n * `websearch` works with no configuration at all, but the keyless default is\n * scraped DuckDuckGo HTML: it is rate-limited hard and can fail outright, and\n * the failure surfaces as a bad search rather than as a setup problem. So when\n * the tool is active and no keyed backend is configured, the session says so\n * once — the same shape as the Anthropic extra-usage notice, including the\n * `/settings` switch that turns it off.\n */\n\nimport type { WarningSettings } from \"../../core/settings-types.js\";\nimport { resolveWebSearchCredentials, type WebtoolsSearchSettings } from \"../../core/tools/webtools-shared.js\";\n\nexport const WEBSEARCH_API_KEY_TITLE = \"Web search has no API key\";\n\nexport interface WebSearchApiKeyNoticeInput {\n\t/** Tools the session actually exposes; the notice is pointless without `websearch`. */\n\tactiveToolNames: readonly string[];\n\t/** Warning switches from settings (`warnings.websearchApiKey`). */\n\twarnings: WarningSettings;\n\t/** The `webtools.search` block, as the binary would read it. */\n\tsearch?: WebtoolsSearchSettings;\n}\n\nexport interface WebSearchApiKeyNotice {\n\ttitle: string;\n\tbody: string[];\n}\n\n/**\n * The notice to show, or undefined when there is nothing to say: the tool is\n * off, a keyed backend is configured, the user pinned the keyless backend on\n * purpose, or the warning is switched off.\n *\n * Pure, so the decision is testable without a TUI; the caller owns the\n * once-per-session latch.\n */\nexport function websearchApiKeyNotice(input: WebSearchApiKeyNoticeInput): WebSearchApiKeyNotice | undefined {\n\tif (input.warnings.websearchApiKey === false) return undefined;\n\tif (!input.activeToolNames.includes(\"websearch\")) return undefined;\n\n\tconst credentials = resolveWebSearchCredentials(input.search);\n\tif (credentials.configured || credentials.explicitKeyless) return undefined;\n\n\treturn {\n\t\ttitle: WEBSEARCH_API_KEY_TITLE,\n\t\t// Two lines, like the Anthropic notice: what is wrong and the one setting\n\t\t// that fixes it. The other backends and the settings.json block are in\n\t\t// the docs — a startup notice is not the place to list them.\n\t\tbody: [\n\t\t\t\"Searching keyless DuckDuckGo, which is rate-limited. Set BRAVE_API_KEY or TAVILY_API_KEY.\",\n\t\t\t\"Turn off in /settings → Web search API key.\",\n\t\t],\n\t};\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "websearch has no API key" startup notice.
|
|
3
|
+
*
|
|
4
|
+
* `websearch` works with no configuration at all, but the keyless default is
|
|
5
|
+
* scraped DuckDuckGo HTML: it is rate-limited hard and can fail outright, and
|
|
6
|
+
* the failure surfaces as a bad search rather than as a setup problem. So when
|
|
7
|
+
* the tool is active and no keyed backend is configured, the session says so
|
|
8
|
+
* once — the same shape as the Anthropic extra-usage notice, including the
|
|
9
|
+
* `/settings` switch that turns it off.
|
|
10
|
+
*/
|
|
11
|
+
import { resolveWebSearchCredentials } from "../../core/tools/webtools-shared.js";
|
|
12
|
+
export const WEBSEARCH_API_KEY_TITLE = "Web search has no API key";
|
|
13
|
+
/**
|
|
14
|
+
* The notice to show, or undefined when there is nothing to say: the tool is
|
|
15
|
+
* off, a keyed backend is configured, the user pinned the keyless backend on
|
|
16
|
+
* purpose, or the warning is switched off.
|
|
17
|
+
*
|
|
18
|
+
* Pure, so the decision is testable without a TUI; the caller owns the
|
|
19
|
+
* once-per-session latch.
|
|
20
|
+
*/
|
|
21
|
+
export function websearchApiKeyNotice(input) {
|
|
22
|
+
if (input.warnings.websearchApiKey === false)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (!input.activeToolNames.includes("websearch"))
|
|
25
|
+
return undefined;
|
|
26
|
+
const credentials = resolveWebSearchCredentials(input.search);
|
|
27
|
+
if (credentials.configured || credentials.explicitKeyless)
|
|
28
|
+
return undefined;
|
|
29
|
+
return {
|
|
30
|
+
title: WEBSEARCH_API_KEY_TITLE,
|
|
31
|
+
// Two lines, like the Anthropic notice: what is wrong and the one setting
|
|
32
|
+
// that fixes it. The other backends and the settings.json block are in
|
|
33
|
+
// the docs — a startup notice is not the place to list them.
|
|
34
|
+
body: [
|
|
35
|
+
"Searching keyless DuckDuckGo, which is rate-limited. Set BRAVE_API_KEY or TAVILY_API_KEY.",
|
|
36
|
+
"Turn off in /settings → Web search API key.",
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=websearch-warning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websearch-warning.js","sourceRoot":"","sources":["../../../src/modes/interactive/websearch-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,2BAA2B,EAA+B,MAAM,qCAAqC,CAAC;AAE/G,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAgBnE;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiC,EAAqC;IAC3G,IAAI,KAAK,CAAC,QAAQ,CAAC,eAAe,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IAC/D,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IAEnE,MAAM,WAAW,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,WAAW,CAAC,UAAU,IAAI,WAAW,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC;IAE5E,OAAO;QACN,KAAK,EAAE,uBAAuB;QAC9B,0EAA0E;QAC1E,uEAAuE;QACvE,+DAA6D;QAC7D,IAAI,EAAE;YACL,2FAA2F;YAC3F,+CAA6C;SAC7C;KACD,CAAC;AAAA,CACF","sourcesContent":["/**\n * The \"websearch has no API key\" startup notice.\n *\n * `websearch` works with no configuration at all, but the keyless default is\n * scraped DuckDuckGo HTML: it is rate-limited hard and can fail outright, and\n * the failure surfaces as a bad search rather than as a setup problem. So when\n * the tool is active and no keyed backend is configured, the session says so\n * once — the same shape as the Anthropic extra-usage notice, including the\n * `/settings` switch that turns it off.\n */\n\nimport type { WarningSettings } from \"../../core/settings-types.js\";\nimport { resolveWebSearchCredentials, type WebtoolsSearchSettings } from \"../../core/tools/webtools-shared.js\";\n\nexport const WEBSEARCH_API_KEY_TITLE = \"Web search has no API key\";\n\nexport interface WebSearchApiKeyNoticeInput {\n\t/** Tools the session actually exposes; the notice is pointless without `websearch`. */\n\tactiveToolNames: readonly string[];\n\t/** Warning switches from settings (`warnings.websearchApiKey`). */\n\twarnings: WarningSettings;\n\t/** The `webtools.search` block, as the binary would read it. */\n\tsearch?: WebtoolsSearchSettings;\n}\n\nexport interface WebSearchApiKeyNotice {\n\ttitle: string;\n\tbody: string[];\n}\n\n/**\n * The notice to show, or undefined when there is nothing to say: the tool is\n * off, a keyed backend is configured, the user pinned the keyless backend on\n * purpose, or the warning is switched off.\n *\n * Pure, so the decision is testable without a TUI; the caller owns the\n * once-per-session latch.\n */\nexport function websearchApiKeyNotice(input: WebSearchApiKeyNoticeInput): WebSearchApiKeyNotice | undefined {\n\tif (input.warnings.websearchApiKey === false) return undefined;\n\tif (!input.activeToolNames.includes(\"websearch\")) return undefined;\n\n\tconst credentials = resolveWebSearchCredentials(input.search);\n\tif (credentials.configured || credentials.explicitKeyless) return undefined;\n\n\treturn {\n\t\ttitle: WEBSEARCH_API_KEY_TITLE,\n\t\t// Two lines, like the Anthropic notice: what is wrong and the one setting\n\t\t// that fixes it. The other backends and the settings.json block are in\n\t\t// the docs — a startup notice is not the place to list them.\n\t\tbody: [\n\t\t\t\"Searching keyless DuckDuckGo, which is rate-limited. Set BRAVE_API_KEY or TAVILY_API_KEY.\",\n\t\t\t\"Turn off in /settings → Web search API key.\",\n\t\t],\n\t};\n}\n"]}
|
package/docs/settings.md
CHANGED
|
@@ -74,15 +74,20 @@ Set `HOOCODE_SKIP_VERSION_CHECK=1` to disable the HooCode version update check.
|
|
|
74
74
|
| Setting | Type | Default | Description |
|
|
75
75
|
|---------|------|---------|-------------|
|
|
76
76
|
| `warnings.anthropicExtraUsage` | boolean | `true` | Show a warning when Anthropic subscription auth may use paid extra usage |
|
|
77
|
+
| `warnings.websearchApiKey` | boolean | `true` | Show a warning when `websearch` is enabled with no search API key, so it falls back to keyless DuckDuckGo |
|
|
77
78
|
|
|
78
79
|
```json
|
|
79
80
|
{
|
|
80
81
|
"warnings": {
|
|
81
|
-
"anthropicExtraUsage": false
|
|
82
|
+
"anthropicExtraUsage": false,
|
|
83
|
+
"websearchApiKey": false
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
```
|
|
85
87
|
|
|
88
|
+
Both warnings are shown once per session in the TUI and can also be toggled from
|
|
89
|
+
`/settings` → Warnings.
|
|
90
|
+
|
|
86
91
|
### Compaction
|
|
87
92
|
|
|
88
93
|
| Setting | Type | Default | Description |
|
|
@@ -329,6 +334,7 @@ there.
|
|
|
329
334
|
|---------|------|---------|-------------|
|
|
330
335
|
| `enableWebTools` | boolean | `false` | Enable `webfetch` + `websearch` (network access). Needs the `webtools` binary. |
|
|
331
336
|
| `webtools.timeoutSecs` | number | `15` | Per-request timeout in seconds, clamped 1–120. Wins over `HOOCODE_WEBTOOLS_TIMEOUT`. |
|
|
337
|
+
| `webtools.search` | object | — | Search backend and credentials, read by the `webtools` binary itself. See [Web search providers](#web-search-providers). |
|
|
332
338
|
| `enableEmbsearchTools` | boolean | `true` | Build and fuse the semantic index into `search`. Set false to run `search` lexical-only. `grep`/`find` are unaffected. |
|
|
333
339
|
| `embsearchBinaryPath` | string | — | Explicit path to the `embsearch` binary. Default: resolve from `PATH`. |
|
|
334
340
|
| `embsearchThresholdBytes` | number | `0` | Minimum indexable source bytes before a repo is embedded. `0` indexes every repo. |
|
|
@@ -346,6 +352,49 @@ Environment variables:
|
|
|
346
352
|
On Android/Termux the published Linux builds do not run; install with
|
|
347
353
|
`pkg install <name>` instead.
|
|
348
354
|
|
|
355
|
+
#### Web search providers
|
|
356
|
+
|
|
357
|
+
`websearch` needs no configuration: it defaults to keyless DuckDuckGo Lite.
|
|
358
|
+
That backend is scraped HTML, though — rate-limited hard, and it can fail
|
|
359
|
+
outright — so when search reliability matters, point it at a backend with an
|
|
360
|
+
actual API contract. With none configured, the TUI says so once per session
|
|
361
|
+
(see [`warnings.websearchApiKey`](#warnings)).
|
|
362
|
+
|
|
363
|
+
The `webtools` binary reads its own `webtools` key out of the user-level
|
|
364
|
+
`~/.hoocode/settings.json`, using its own (snake_case) key names. HooCode never
|
|
365
|
+
writes this block; it only reads it to tell whether a keyed backend exists. Put
|
|
366
|
+
it in the user-level file — a project `.hoocode/settings.json` is not read by the
|
|
367
|
+
binary, and a file holding credentials is worth keeping out of a working tree.
|
|
368
|
+
|
|
369
|
+
```json
|
|
370
|
+
{
|
|
371
|
+
"webtools": {
|
|
372
|
+
"search": {
|
|
373
|
+
"provider": "brave",
|
|
374
|
+
"fallback": "duckduckgo",
|
|
375
|
+
"providers": {
|
|
376
|
+
"brave": { "api_key": "..." },
|
|
377
|
+
"tavily": { "api_key": "..." },
|
|
378
|
+
"searxng": { "base_url": "https://searx.internal" }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Environment variables win over the file, so a key can stay out of it entirely:
|
|
386
|
+
|
|
387
|
+
| Variable | Effect |
|
|
388
|
+
|---|---|
|
|
389
|
+
| `WEBTOOLS_SEARCH_PROVIDER` | Backend to use: `duckduckgo` (default), `brave`, `tavily`, `searxng`. Pin `duckduckgo` to accept the keyless backend and silence the warning. |
|
|
390
|
+
| `WEBTOOLS_SEARCH_FALLBACK` | Backend tried when the primary fails; `none` disables the fallback. |
|
|
391
|
+
| `WEBTOOLS_BRAVE_API_KEY`, `BRAVE_API_KEY` | Brave Search key. |
|
|
392
|
+
| `WEBTOOLS_TAVILY_API_KEY`, `TAVILY_API_KEY` | Tavily key. Returns cleaned page content, so a search often answers without a follow-up `webfetch`. |
|
|
393
|
+
| `WEBTOOLS_SEARXNG_URL`, `WEBTOOLS_SEARXNG_API_KEY` | Self-hosted SearXNG endpoint (and optional key), for networks where the public APIs are unreachable. |
|
|
394
|
+
|
|
395
|
+
Every result records the backend that answered it, so a fallback to the scraped
|
|
396
|
+
backend is never silent.
|
|
397
|
+
|
|
349
398
|
### Resources
|
|
350
399
|
|
|
351
400
|
These settings define where to load extensions, skills, prompts, and themes from.
|
|
@@ -408,7 +457,8 @@ See [packages.md](packages.md) for package management details.
|
|
|
408
457
|
},
|
|
409
458
|
"enabledModels": ["claude-*", "gpt-4o"],
|
|
410
459
|
"warnings": {
|
|
411
|
-
"anthropicExtraUsage": true
|
|
460
|
+
"anthropicExtraUsage": true,
|
|
461
|
+
"websearchApiKey": true
|
|
412
462
|
},
|
|
413
463
|
"packages": ["hoocode-skills"]
|
|
414
464
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolisachint/hoocode-agent",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.43",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"hoocodeConfig": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"prepublishOnly": "npm run clean && npm run build"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@kolisachint/hoocode-agent-core": "^0.5.
|
|
54
|
-
"@kolisachint/hoocode-ai": "^0.5.
|
|
55
|
-
"@kolisachint/hoocode-tui": "^0.5.
|
|
53
|
+
"@kolisachint/hoocode-agent-core": "^0.5.43",
|
|
54
|
+
"@kolisachint/hoocode-ai": "^0.5.43",
|
|
55
|
+
"@kolisachint/hoocode-tui": "^0.5.43",
|
|
56
56
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
57
57
|
"chalk": "^5.5.0",
|
|
58
58
|
"cli-highlight": "^2.1.11",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: |
|
|
4
|
+
Use this subagent when:
|
|
5
|
+
- Reviewing a diff, branch, or files for defects before merge
|
|
6
|
+
- The user asks for a review, a second pass, or what they missed
|
|
7
|
+
- Findings are wanted without spending parent context on every changed file
|
|
8
|
+
|
|
9
|
+
DO NOT use for:
|
|
10
|
+
- Applying the fixes (this agent reports; the parent decides)
|
|
11
|
+
- Hunting for security vulnerabilities specifically (use security-review)
|
|
12
|
+
|
|
13
|
+
Output: Ranked findings, each with path:line, the defect, and how it fails.
|
|
14
|
+
Cost: Medium (reads the full diff and surrounding code)
|
|
15
|
+
Isolation: Read-only; can run in parallel with other review tasks
|
|
16
|
+
tools: read, bash, grep, find, ls
|
|
17
|
+
model: capable
|
|
18
|
+
background: true
|
|
19
|
+
---
|
|
20
|
+
You are a code-review agent running inside hoocode. You find defects in changed
|
|
21
|
+
code and report them. You never edit files. You run in an isolated context and
|
|
22
|
+
cannot see the parent conversation, so state findings in full rather than
|
|
23
|
+
referring back to a discussion you cannot read.
|
|
24
|
+
|
|
25
|
+
Scope:
|
|
26
|
+
- Do not create, modify, or delete files.
|
|
27
|
+
- Use bash for read-only git commands (`git diff`, `git log`, `git show`) to
|
|
28
|
+
establish what changed. Do not commit, push, stash, or check out.
|
|
29
|
+
- Review what the caller named. If they named nothing, review the working tree
|
|
30
|
+
diff, then the branch against its base.
|
|
31
|
+
|
|
32
|
+
Method:
|
|
33
|
+
1. Establish the diff. Read every changed hunk before forming an opinion.
|
|
34
|
+
2. Read enough surrounding code to know whether a hunk is actually wrong. A
|
|
35
|
+
line that looks suspicious in isolation is usually fine in context.
|
|
36
|
+
3. For each candidate finding, construct the concrete failure: the input or
|
|
37
|
+
state that triggers it, and the wrong output or crash that results. If you
|
|
38
|
+
cannot construct one, it is not a finding.
|
|
39
|
+
4. Rank what survives by severity.
|
|
40
|
+
|
|
41
|
+
What counts as a finding:
|
|
42
|
+
- Correctness: logic that produces a wrong result, a crash, unhandled errors,
|
|
43
|
+
off-by-one, wrong operator, a case the code does not cover.
|
|
44
|
+
- Contract violations: a caller that does not match the callee's expectations,
|
|
45
|
+
a type narrowed incorrectly, an invariant the change breaks.
|
|
46
|
+
- Reuse: the change hand-rolls something the codebase already provides.
|
|
47
|
+
- Efficiency: work repeated in a loop that belongs outside it, an avoidable
|
|
48
|
+
quadratic, a re-read of something already in memory.
|
|
49
|
+
|
|
50
|
+
What does not:
|
|
51
|
+
- Style, formatting, or naming the linter does not flag.
|
|
52
|
+
- Restating what the code does without saying what is wrong with it.
|
|
53
|
+
- Speculative hardening for inputs the code cannot receive.
|
|
54
|
+
|
|
55
|
+
Guidance:
|
|
56
|
+
- Verify before reporting. A confident wrong finding costs the caller more than
|
|
57
|
+
a missed one, because they have to read the code to disprove it.
|
|
58
|
+
- Report as a ranked list: `path:line`, one sentence naming the defect, then the
|
|
59
|
+
failure case. No preamble, no summary of the change, no tool logs.
|
|
60
|
+
- If nothing survives verification, say so plainly. An empty review is a
|
|
61
|
+
legitimate result.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-review
|
|
3
|
+
description: |
|
|
4
|
+
Use this subagent when:
|
|
5
|
+
- Reviewing changes for vulnerabilities before merge or release
|
|
6
|
+
- The change touches auth, input parsing, paths, subprocesses, or secrets
|
|
7
|
+
- The user asks for a security review or audit of pending work
|
|
8
|
+
|
|
9
|
+
DO NOT use for:
|
|
10
|
+
- General correctness and quality review (use code-review)
|
|
11
|
+
- Applying the fixes (this agent reports; the parent decides)
|
|
12
|
+
|
|
13
|
+
Output: Findings ranked by severity, each with path:line, the attack, and the
|
|
14
|
+
fix.
|
|
15
|
+
Cost: Medium (reads the diff and traces untrusted input)
|
|
16
|
+
Isolation: Read-only; can run in parallel with other review tasks
|
|
17
|
+
tools: read, bash, grep, find, ls
|
|
18
|
+
model: capable
|
|
19
|
+
background: true
|
|
20
|
+
---
|
|
21
|
+
You are a security-review agent running inside hoocode. You find vulnerabilities
|
|
22
|
+
in changed code and report them. You never edit files. You run in an isolated
|
|
23
|
+
context and cannot see the parent conversation, so state findings in full rather
|
|
24
|
+
than referring back to a discussion you cannot read.
|
|
25
|
+
|
|
26
|
+
Scope:
|
|
27
|
+
- Do not create, modify, or delete files.
|
|
28
|
+
- Use bash for read-only git commands (`git diff`, `git log`, `git show`) to
|
|
29
|
+
establish what changed. Do not commit, push, stash, or check out.
|
|
30
|
+
- Review what the caller named. If they named nothing, review the working tree
|
|
31
|
+
diff, then the branch against its base.
|
|
32
|
+
|
|
33
|
+
Method:
|
|
34
|
+
1. Establish the diff, then identify every point where the change accepts input
|
|
35
|
+
it does not control: arguments, environment, files, network responses, tool
|
|
36
|
+
output, model output.
|
|
37
|
+
2. Trace each one to where it is used. A vulnerability is a path from untrusted
|
|
38
|
+
input to a dangerous operation, so follow the path rather than pattern
|
|
39
|
+
matching on the operation.
|
|
40
|
+
3. For each candidate, construct the attack: what an attacker supplies, what
|
|
41
|
+
they gain. If you cannot construct one, it is not a finding.
|
|
42
|
+
4. Rank what survives by severity, worst first.
|
|
43
|
+
|
|
44
|
+
Where to look hardest:
|
|
45
|
+
- Command execution: shell strings built from input, argument arrays that can be
|
|
46
|
+
injected, a path that reaches an interpreter.
|
|
47
|
+
- Path handling: traversal via `..` or absolute paths, symlinks, writes outside
|
|
48
|
+
an intended root.
|
|
49
|
+
- Secrets: credentials logged, embedded in errors, written to disk, sent to a
|
|
50
|
+
provider that does not need them.
|
|
51
|
+
- Deserialization and parsing: untrusted JSON, YAML, or archives fed to a parser
|
|
52
|
+
that can execute or allocate unboundedly.
|
|
53
|
+
- Authorization: a check that can be bypassed, a permission gate the change
|
|
54
|
+
routes around, a default that fails open.
|
|
55
|
+
- Injection into generated content: HTML, SQL, or prompt text assembled from
|
|
56
|
+
input without escaping.
|
|
57
|
+
|
|
58
|
+
What does not count:
|
|
59
|
+
- A theoretical issue in code the change did not touch and does not reach.
|
|
60
|
+
- Missing hardening with no reachable attack path.
|
|
61
|
+
- Dependency advisories the change neither introduces nor exercises.
|
|
62
|
+
|
|
63
|
+
Guidance:
|
|
64
|
+
- Verify reachability before reporting. An unreachable finding wastes the
|
|
65
|
+
caller's time and erodes trust in the review.
|
|
66
|
+
- Report as a severity-ranked list: `path:line`, the attack in one or two
|
|
67
|
+
sentences, then the fix. No preamble, no tool logs.
|
|
68
|
+
- Where a fix has a safer and a more convenient form, name the safer one.
|
|
69
|
+
- If nothing survives verification, say so plainly.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: artifact-design
|
|
3
|
+
description: Build a self-contained HTML visual written to disk — page, report, dashboard, mockup, diagram, or poster. Load before the first line of markup or CSS whenever something has to look right, or a redesign is asked for. Not for app code the project's own design system already governs.
|
|
4
|
+
allowed-tools: read, write, edit, grep, find, ls
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Designing a visual
|
|
8
|
+
|
|
9
|
+
hoocode writes visuals as one self-contained `.html` file on disk. There is no
|
|
10
|
+
host, no CSP, and no gallery: fonts and libraries may come from a CDN or from
|
|
11
|
+
local files, and the page is yours to lay out however the subject demands. The
|
|
12
|
+
constraint that remains is the only one that ever mattered — it has to be good.
|
|
13
|
+
|
|
14
|
+
Not for a canvas extension. A canvas serves a live page from its own loopback
|
|
15
|
+
server, under constraints that contradict half of this file, and `/new-canvas`
|
|
16
|
+
points at `../canvas-design/SKILL.md` for it.
|
|
17
|
+
|
|
18
|
+
## First, read the request
|
|
19
|
+
|
|
20
|
+
Decide the treatment, not whether to design. Every visual gets real typographic
|
|
21
|
+
hierarchy, considered spacing, and a deliberate palette. What varies is how far
|
|
22
|
+
past that you go.
|
|
23
|
+
|
|
24
|
+
- **Utilitarian** — a report, a plan, a status page, a diagram. Most requests.
|
|
25
|
+
Make it clean and well-composed. Skip the oversized hero. Keep flourishes few.
|
|
26
|
+
- **Editorial** — a landing page, a poster, something the user will show other
|
|
27
|
+
people. Take a real point of view and one deliberate risk.
|
|
28
|
+
|
|
29
|
+
When unsure, build the well-composed version. A restrained page is never wrong;
|
|
30
|
+
an over-designed one sometimes is.
|
|
31
|
+
|
|
32
|
+
## Honor what already exists
|
|
33
|
+
|
|
34
|
+
Look before you invent. If the repo has a design system — a tokens file, a
|
|
35
|
+
theme, CSS variables, an existing stylesheet, a brand section in a project
|
|
36
|
+
context file — use it. Precedence is always:
|
|
37
|
+
|
|
38
|
+
1. What the user asked for, in their words
|
|
39
|
+
2. The project's existing system
|
|
40
|
+
3. Your own choices
|
|
41
|
+
|
|
42
|
+
Your choices fill gaps. They never override the first two.
|
|
43
|
+
|
|
44
|
+
## Write the plan before the markup
|
|
45
|
+
|
|
46
|
+
This is the part that does the work. Before any code, write down:
|
|
47
|
+
|
|
48
|
+
- **Color** — 4 to 6 named hex values. Name them by role (`ink`, `ground`,
|
|
49
|
+
`accent`, `muted`), not by hue.
|
|
50
|
+
- **Type** — at least two faces with distinct jobs: one with character for
|
|
51
|
+
display, one comfortable for body text, and a third for data or captions if
|
|
52
|
+
the content needs it. Name the fallback stack for each.
|
|
53
|
+
- **Layout** — the structural idea in one or two sentences.
|
|
54
|
+
|
|
55
|
+
Then build from the plan and derive every color and type decision from it. A
|
|
56
|
+
page assembled without a plan reads as assembled without a plan.
|
|
57
|
+
|
|
58
|
+
## Fundamentals
|
|
59
|
+
|
|
60
|
+
**Ground it in the subject.** One concrete subject, one audience, one job for
|
|
61
|
+
the page. Distinctive choices come from the subject's own world — its materials,
|
|
62
|
+
its vocabulary, how people in it actually talk. Use real content throughout.
|
|
63
|
+
Never lorem, never placeholder rows.
|
|
64
|
+
|
|
65
|
+
**Type carries the page,** including when the page is not about type. Keep
|
|
66
|
+
running text near 65 characters wide. Set a scale and stay on it. Give headings
|
|
67
|
+
`text-wrap: balance`, give body text line-height room, give uppercase labels a
|
|
68
|
+
little letter-spacing. Always declare a real fallback stack — a silent fallback
|
|
69
|
+
is the most common way a good design ships looking wrong.
|
|
70
|
+
|
|
71
|
+
**Pick the neutrals.** A pure mid-grey reads as unconsidered. Bias the greys
|
|
72
|
+
slightly toward the accent hue and they read as chosen. Pure white and near-black
|
|
73
|
+
are fine grounds when the subject wants them; the point is that you decided.
|
|
74
|
+
|
|
75
|
+
**Let layout do the spacing.** Flex or grid with `gap`, not per-element margins
|
|
76
|
+
that collapse or double unpredictably. Wide content — tables, code blocks,
|
|
77
|
+
diagrams — gets its own `overflow-x: auto` container so the page body never
|
|
78
|
+
scrolls sideways. Use `font-variant-numeric: tabular-nums` wherever digits line
|
|
79
|
+
up in a column.
|
|
80
|
+
|
|
81
|
+
**Design both themes.** A local page sees only `prefers-color-scheme`, so this
|
|
82
|
+
is simpler here than on a hosted target: define the complete palette as custom
|
|
83
|
+
properties on `:root`, redefine only those properties inside
|
|
84
|
+
`@media (prefers-color-scheme: dark)`, and style every component through the
|
|
85
|
+
properties. Never give a color its only definition inside the media block — that
|
|
86
|
+
is how a page ends up rendering one theme's text on the other theme's ground.
|
|
87
|
+
Set an explicit `background` on `body`. Give the second theme the same attention
|
|
88
|
+
as the first rather than inverting it mechanically; check that the accent still
|
|
89
|
+
works on both grounds. A page that deliberately commits to one visual world may
|
|
90
|
+
stay single-theme, but then paint every color explicitly so it holds either way.
|
|
91
|
+
|
|
92
|
+
**Structure should encode something true.** Numbered markers, eyebrows,
|
|
93
|
+
dividers, and section labels are information, not decoration. Number things only
|
|
94
|
+
when the order actually matters to the reader.
|
|
95
|
+
|
|
96
|
+
**When it is a UI, not a document,** the craft shifts from typography to
|
|
97
|
+
information design. A dashboard is scanned, not read. Put the summary above the
|
|
98
|
+
detail. Encode state in form as well as in number — a pill, a chip, a severity
|
|
99
|
+
stripe — so what needs attention is visible at a glance. Semantic color (good,
|
|
100
|
+
warning, critical) is a separate system from your accent and does not count as
|
|
101
|
+
using it. Anything interactive should look interactive.
|
|
102
|
+
|
|
103
|
+
## Avoid the generated look
|
|
104
|
+
|
|
105
|
+
Machine-generated design keeps landing on the same handful of looks. When the
|
|
106
|
+
user has specified a direction, follow it exactly, including if it is one of
|
|
107
|
+
these. When nothing is specified, do not spend the freedom here:
|
|
108
|
+
|
|
109
|
+
- Warm cream ground, serif display face, terracotta accent
|
|
110
|
+
- Near-black ground with a single acid-green or vermilion pop
|
|
111
|
+
- Hairline rules and dense columns imitating a broadsheet
|
|
112
|
+
- Purple-to-blue gradient hero on white
|
|
113
|
+
- Inter or Space Grotesk chosen as the safe default
|
|
114
|
+
- Emoji as section markers
|
|
115
|
+
- Everything centered
|
|
116
|
+
- Uniform large corner radii on every surface
|
|
117
|
+
- A colored accent rail down the side of every card
|
|
118
|
+
|
|
119
|
+
## Words are design material
|
|
120
|
+
|
|
121
|
+
Write from the reader's side of the screen. Name things the way they would name
|
|
122
|
+
them, not the way the system is built. Active voice. A control says exactly what
|
|
123
|
+
it does, and the confirmation matches it. Errors say what went wrong and what to
|
|
124
|
+
do about it. Specific beats clever.
|
|
125
|
+
|
|
126
|
+
Give the page a real `<title>` — a short, specific noun phrase, not a category
|
|
127
|
+
label and not a name with an explainer bolted on after a dash.
|
|
128
|
+
|
|
129
|
+
## Libraries, and why the answer is usually none
|
|
130
|
+
|
|
131
|
+
A hosted page and a file on disk fail differently, and that decides this. A
|
|
132
|
+
hosted page is always viewed online, so a CDN dependency is free. A file gets
|
|
133
|
+
moved, attached to mail, and opened on a laptop in a tunnel — and there a
|
|
134
|
+
runtime dependency does not degrade, it collapses. Tailwind from a CDN with no
|
|
135
|
+
network is an unstyled document. React from a CDN with no network is a blank
|
|
136
|
+
one.
|
|
137
|
+
|
|
138
|
+
So the default is no runtime dependency at all. Write plain CSS; modern CSS has
|
|
139
|
+
custom properties, grid, `clamp()`, and container queries, and a page that needs
|
|
140
|
+
a utility framework to be laid out usually needs a clearer layout instead.
|
|
141
|
+
|
|
142
|
+
There is no build step here and nothing bundles this file, so a framework that
|
|
143
|
+
expects one — Vite, a JSX pipeline, anything importing bare module specifiers —
|
|
144
|
+
is not an option regardless.
|
|
145
|
+
|
|
146
|
+
When a library genuinely earns its place, and that is mostly charting or syntax
|
|
147
|
+
highlighting rather than layout:
|
|
148
|
+
|
|
149
|
+
- Inline it into the file if its licence permits, and the page stays whole.
|
|
150
|
+
- Otherwise pin an exact version, give the feature a readable fallback for when
|
|
151
|
+
the script does not load, and tell the user the page needs network.
|
|
152
|
+
|
|
153
|
+
Webfonts are the one dependency that degrades gracefully, because a real
|
|
154
|
+
fallback stack keeps the page readable when the link fails. Use one, and always
|
|
155
|
+
declare the stack.
|
|
156
|
+
|
|
157
|
+
## Build cleanly
|
|
158
|
+
|
|
159
|
+
- Watch selector specificity. Type-level and element-level selectors fighting
|
|
160
|
+
over the same padding is how spacing silently comes undone.
|
|
161
|
+
- Close every non-void element and double-quote every attribute.
|
|
162
|
+
- Give keyboard focus a visible state.
|
|
163
|
+
- Respect `prefers-reduced-motion`.
|
|
164
|
+
- For generative or decorative graphics, reach for Canvas or WebGL rather than
|
|
165
|
+
hand-authoring long SVG path data.
|
|
166
|
+
- Before finishing, scan the stylesheet for any color declared only inside a
|
|
167
|
+
media query.
|
|
168
|
+
|
|
169
|
+
## Delivering it
|
|
170
|
+
|
|
171
|
+
Write one self-contained `.html` file. Inline the page's own CSS and JS; embed
|
|
172
|
+
small assets as data URIs so the file survives being moved or sent to someone.
|
|
173
|
+
|
|
174
|
+
Put it where the user would expect it — alongside the data it visualizes, or in
|
|
175
|
+
the directory they named. Then give them the path as a markdown link with a
|
|
176
|
+
`file://` URL:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
[tokens.html](file:///abs/path/to/tokens.html)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
hoocode's markdown renderer turns that into an OSC 8 hyperlink wherever the
|
|
183
|
+
terminal supports one, so it is clickable in kitty, iTerm2, WezTerm and others,
|
|
184
|
+
and still readable as plain text everywhere else. Offer to open it rather than
|
|
185
|
+
opening it unasked.
|
|
186
|
+
|
|
187
|
+
Canvas is not a viewer for this. `/canvas` hosts canvas extensions — a directory
|
|
188
|
+
with an `extension.mjs` speaking the canvas JSON-RPC protocol — and `/canvas
|
|
189
|
+
open` takes an extension id, not a file path. A page you can hand someone is a
|
|
190
|
+
file; reach for `/new-canvas` only when the user wants a live surface the agent
|
|
191
|
+
can call typed actions on, which is a different and larger job than a visual.
|