@g3nomaly/g3n-skills 0.1.3 → 0.1.5
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/.claude-plugin/marketplace.json +18 -4
- package/README.md +9 -2
- package/package.json +2 -2
- package/skills/coding/.claude-plugin/plugin.json +2 -2
- package/skills/coding/code/SKILL.md +4 -1
- package/skills/coding/code/references/cpp.md +43 -0
- package/skills/coding/code/references/python.md +28 -0
- package/skills/coding/code/references/rust.md +26 -0
- package/skills/coding/document/SKILL.md +10 -0
- package/skills/coding/document/hooks/install-highlighting.ts +136 -0
- package/skills/coding/document/references/cpp.md +105 -0
- package/skills/coding/document/references/python.md +95 -0
- package/skills/coding/document/references/rust.md +122 -0
- package/skills/coding/document/vscode/package.json +28 -0
- package/skills/coding/document/vscode/syntaxes/python.json +56 -0
- package/skills/coding/document/vscode/syntaxes/rust.json +25 -0
- package/skills/coding/vocabulary/SKILL.md +47 -0
- package/skills/coding/vocabulary/agents/openai.yaml +5 -0
- package/skills/task-manager/.claude-plugin/plugin.json +1 -2
- package/skills/task-manager/task-manager/hooks/get-entry.ts +4 -4
- package/skills/task-manager/task-manager/hooks/guard-tree.ts +2 -2
- package/skills/task-manager/task-manager/hooks/next-entry.ts +3 -3
- package/skills/task-manager/task-manager/references/hooks.md +1 -1
- package/skills/task-manager/task-manager/references/tree.md +0 -1
- package/skills/task-manager/task-manager/src/hooks/cli.ts +11 -1
- package/skills/task-manager/task-manager/src/hooks/index.ts +1 -1
- package/skills/task-manager/task-manager/src/schemas/index.ts +2 -2
- package/skills/task-manager/task-manager/src/schemas/term.ts +9 -22
- package/skills/task-manager/task-manager/src/vocabulary/file.ts +40 -84
- package/skills/task-manager/task-manager/src/vocabulary/index.ts +3 -3
- package/skills/task-manager/task-manager/src/vocabulary/match.ts +3 -34
- package/skills/teach/.claude-plugin/plugin.json +7 -0
- package/skills/teach/architecture/SKILL.md +19 -0
- package/skills/teach/architecture/agents/openai.yaml +5 -0
- package/skills/teach/architecture/references/examples.md +16 -0
- package/skills/teach/deep-dive/SKILL.md +19 -0
- package/skills/teach/deep-dive/agents/openai.yaml +5 -0
- package/skills/teach/deep-dive/references/examples.md +15 -0
- package/skills/teach/explain/SKILL.md +19 -0
- package/skills/teach/explain/agents/openai.yaml +5 -0
- package/skills/teach/explain/references/examples.md +9 -0
- package/skills/teach/guided-debugging/SKILL.md +25 -0
- package/skills/teach/guided-debugging/agents/openai.yaml +5 -0
- package/skills/teach/guided-debugging/references/debugging-loop.md +18 -0
- package/skills/teach/guided-debugging/references/examples.md +24 -0
- package/skills/teach/guided-review/SKILL.md +20 -0
- package/skills/teach/guided-review/agents/openai.yaml +5 -0
- package/skills/teach/guided-review/references/examples.md +18 -0
- package/skills/teach/learn-by-building/SKILL.md +19 -0
- package/skills/teach/learn-by-building/agents/openai.yaml +5 -0
- package/skills/teach/learn-by-building/references/examples.md +15 -0
- package/skills/teach/teaching/SKILL.md +22 -0
- package/skills/teach/teaching/agents/openai.yaml +5 -0
- package/skills/teach/teaching/references/learning-model.md +24 -0
- package/skills/task-manager/vocabulary/SKILL.md +0 -42
- package/skills/task-manager/vocabulary/agents/openai.yaml +0 -5
- package/skills/task-manager/vocabulary/hooks/get-term.ts +0 -18
- package/skills/task-manager/vocabulary/hooks/list-terms.ts +0 -12
- package/skills/task-manager/vocabulary/hooks/remove-term.ts +0 -22
- package/skills/task-manager/vocabulary/hooks/set-term.ts +0 -37
- package/skills/task-manager/vocabulary/references/usage.md +0 -26
- package/skills/task-manager/vocabulary/tsconfig.json +0 -7
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
{
|
|
18
18
|
"name": "coding",
|
|
19
19
|
"source": "./skills/coding",
|
|
20
|
-
"description": "How to write, check and document code, per language,
|
|
20
|
+
"description": "How to write, check and document code, per language, query a repository with analyze, and keep its glossary.",
|
|
21
21
|
"skills": [
|
|
22
22
|
"./code",
|
|
23
23
|
"./document",
|
|
24
|
-
"./analyze"
|
|
24
|
+
"./analyze",
|
|
25
|
+
"./vocabulary"
|
|
25
26
|
]
|
|
26
27
|
},
|
|
27
28
|
{
|
|
@@ -49,13 +50,26 @@
|
|
|
49
50
|
{
|
|
50
51
|
"name": "task-manager",
|
|
51
52
|
"source": "./skills/task-manager",
|
|
52
|
-
"description": "Query and drive a tree of plans, phases and tasks through hooks, with the plan's decisions
|
|
53
|
+
"description": "Query and drive a tree of plans, phases and tasks through hooks, with the plan's decisions kept beside them.",
|
|
53
54
|
"skills": [
|
|
54
55
|
"./task-manager",
|
|
55
|
-
"./vocabulary",
|
|
56
56
|
"./decisions"
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
"name": "teach",
|
|
61
|
+
"source": "./skills/teach",
|
|
62
|
+
"description": "Teach through real work: build, explain, dive deep, decide architecture, debug and review while the user learns the reasoning.",
|
|
63
|
+
"skills": [
|
|
64
|
+
"./teaching",
|
|
65
|
+
"./learn-by-building",
|
|
66
|
+
"./explain",
|
|
67
|
+
"./deep-dive",
|
|
68
|
+
"./architecture",
|
|
69
|
+
"./guided-debugging",
|
|
70
|
+
"./guided-review"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
59
73
|
{
|
|
60
74
|
"name": "using-hooks",
|
|
61
75
|
"source": "./skills/using-hooks",
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @g3nomaly/g3n-skills
|
|
2
2
|
|
|
3
|
-
The g3n skills for Claude Code and Codex:
|
|
3
|
+
The g3n skills for Claude Code and Codex: 38 skills in 9 groups. This package holds the skills and has no CLI. Install them with [`@g3nomaly/skills`](https://www.npmjs.com/package/@g3nomaly/skills), which reads this package as its built-in source, or read it as a Claude Code plugin marketplace.
|
|
4
4
|
|
|
5
5
|
Some skills carry their own hooks. A hook is a TypeScript file in the skill's `hooks/` folder, and the agent runs it with `bun`. The task-manager hooks keep plans, phases and tasks as Markdown files that the agent reaches only through those hooks. The workflow hooks decide when a step of a phase is finished.
|
|
6
6
|
|
|
@@ -54,6 +54,7 @@ Skills marked _manual_ run only when you type their command, such as `/grill-me`
|
|
|
54
54
|
| `coding` | `code` | Rules for writing and checking code, with a toolchain reference per language |
|
|
55
55
|
| `coding` | `document` | Docstrings and comments for functions and classes, and where the reasons behind code go |
|
|
56
56
|
| `coding` | `analyze` | Query a repository's model with `g3n-analyze` and rebuild its documentation site in `analyze_out/` |
|
|
57
|
+
| `coding` | `vocabulary` | One word per concept, recorded in `docs/glossary.md`, so names don't drift between sessions |
|
|
57
58
|
| `grilling` | `grilling` | Interview the user relentlessly to stress-test a plan, decision or idea |
|
|
58
59
|
| `grilling` | `grill-me` | _Manual._ Starts a `/grilling` session |
|
|
59
60
|
| `grilling` | `grill-with-docs` | _Manual._ A grilling session that writes ADRs and a glossary as it goes |
|
|
@@ -63,8 +64,14 @@ Skills marked _manual_ run only when you type their command, such as `/grill-me`
|
|
|
63
64
|
| `speech` | `plain-language` | Write text readers can find, understand and use on the first read, following ISO 24495-1 |
|
|
64
65
|
| `speech` | `no-questions` | Replies end without questions or offers; a Stop hook refuses a reply that asks one |
|
|
65
66
|
| `task-manager` | `task-manager` | Query plans, phases and tasks and change their status through hooks |
|
|
66
|
-
| `task-manager` | `vocabulary` | One word per concept, stored in the task-manager tree, so names don't drift between sessions |
|
|
67
67
|
| `task-manager` | `decisions` | Record and look up a plan's decisions as numbered files inside the plan |
|
|
68
|
+
| `teach` | `teaching` | The teaching principles every teach skill follows: real work, causality, mental models and guidance per concept |
|
|
69
|
+
| `teach` | `learn-by-building` | Learn a subject while completing a real task on the user's own system |
|
|
70
|
+
| `teach` | `explain` | Explain existing material through its purpose, mental model, flow and failure modes |
|
|
71
|
+
| `teach` | `deep-dive` | Trace the layers beneath an abstraction, only as deep as the question needs |
|
|
72
|
+
| `teach` | `architecture` | Reason from forces and options to an architecture decision and its consequences |
|
|
73
|
+
| `teach` | `guided-debugging` | Find a failure's root cause through hypotheses and small experiments, teaching the method |
|
|
74
|
+
| `teach` | `guided-review` | Review work by consequence and explain the principle behind each finding |
|
|
68
75
|
| `using-hooks` | `hook-pre-tool-use` | _Manual._ Writing a `PreToolUse` hook, which can allow, deny or escalate a tool call |
|
|
69
76
|
| `using-hooks` | `hook-post-tool-use` | _Manual._ Writing a `PostToolUse` hook, which reacts to a finished tool call |
|
|
70
77
|
| `using-hooks` | `hook-stop` | _Manual._ Writing a `Stop` hook, which can refuse to let the agent end its turn |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@g3nomaly/g3n-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "The g3n skills, each with its own hooks: a skill source for @g3nomaly/skills and a Claude Code plugin marketplace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test:clean": "CLEAN_TMP=1 bun test"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@g3nomaly/core": "^0.3.
|
|
18
|
+
"@g3nomaly/core": "^0.3.5",
|
|
19
19
|
"@types/bun": "^1.4.0",
|
|
20
20
|
"typescript": "~7.0.2",
|
|
21
21
|
"zod": "^4.4.3"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coding",
|
|
3
|
-
"description": "How to write, check and document code, per language,
|
|
3
|
+
"description": "How to write, check and document code, per language, query a repository with analyze, and keep its glossary.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
|
-
"skills": ["./code", "./document", "./analyze"]
|
|
6
|
+
"skills": ["./code", "./document", "./analyze", "./vocabulary"]
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ disable-model-invocation: false
|
|
|
9
9
|
Apply SOLID principles, DRY, and KISS. Use descriptive names for variables, functions, and classes.
|
|
10
10
|
|
|
11
11
|
- **One domain per file**: schemas, selectors and business logic live in separate files. Split god objects.
|
|
12
|
-
- **Strict typing**: per the language reference.
|
|
12
|
+
- **Strict typing**: per the language reference.
|
|
13
13
|
- **Production grade**: handle errors explicitly, validate external input at the boundary, leave no dead code.
|
|
14
14
|
- **No shims**: on a rename or refactor, update every call site. Leave no aliases.
|
|
15
15
|
- **Config from the environment**: read configuration from environment variables through a schema.
|
|
@@ -26,3 +26,6 @@ Each language has a reference that names its toolchain, its commands and its rul
|
|
|
26
26
|
| Language | Reference | Files |
|
|
27
27
|
|---|---|---|
|
|
28
28
|
| TypeScript | [`references/typescript.md`](references/typescript.md) | `.ts` `.tsx` |
|
|
29
|
+
| Python | [`references/python.md`](references/python.md) | `.py` |
|
|
30
|
+
| C++ | [`references/cpp.md`](references/cpp.md) | `.cpp` `.cc` `.hpp` `.h` |
|
|
31
|
+
| Rust | [`references/rust.md`](references/rust.md) | `.rs` |
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# C++
|
|
2
|
+
|
|
3
|
+
CMake configures and builds with clang. vcpkg supplies dependencies in manifest mode, from `vcpkg.json`. Run commands from inside the project directory, where `CMakeLists.txt` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Configure | `cmake --preset <preset>` when `CMakePresets.json` exists, otherwise `cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` |
|
|
8
|
+
| Add a dependency | `vcpkg add port <pkg>`, then `find_package` and `target_link_libraries` in `CMakeLists.txt` |
|
|
9
|
+
| Build | `cmake --build build` |
|
|
10
|
+
| Run | the target's binary under `build/` |
|
|
11
|
+
|
|
12
|
+
## Checks
|
|
13
|
+
|
|
14
|
+
| Check | Command | When |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Build | `cmake --build build` | always |
|
|
17
|
+
| Format | `clang-format --dry-run --Werror <files>`, or `clang-format -i <files>` to fix | always |
|
|
18
|
+
| Lint | see [clang-tidy](#clang-tidy) | always |
|
|
19
|
+
| Test | `ctest --test-dir build --output-on-failure` | when the project has tests |
|
|
20
|
+
|
|
21
|
+
## clang-tidy
|
|
22
|
+
|
|
23
|
+
Its output is long enough to be cut off in a tool result, and it exits `0` on warnings. Never judge it from the terminal output.
|
|
24
|
+
|
|
25
|
+
1. Run it in the foreground, all output to a log:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
clang-tidy -p build --quiet --warnings-as-errors='*' <files> > build/clang-tidy.log 2>&1; echo "exit $?"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. Count the findings: `grep -cE ':[0-9]+:[0-9]+: (warning|error):' build/clang-tidy.log`.
|
|
32
|
+
3. List them: `grep -nE ':[0-9]+:[0-9]+: (warning|error):' build/clang-tidy.log`. When the list itself is long, page through it with `sed -n '1,100p'`, then the next 100, until the line count matches step 2.
|
|
33
|
+
|
|
34
|
+
The check passes only when the exit code is `0` **and** the count is `0`. Never pipe clang-tidy into `head` or `tail`, and never run it in the background.
|
|
35
|
+
|
|
36
|
+
## Rules
|
|
37
|
+
|
|
38
|
+
- Never install libraries with `apt` or vendor them by hand. vcpkg owns dependencies.
|
|
39
|
+
- `.clang-format` and `.clang-tidy` at the project root set the style. Fix the code, not the config, to pass a check.
|
|
40
|
+
- The C++ standard is set once in `CMakeLists.txt`. Do not raise it without asking.
|
|
41
|
+
- Targets compile with `-Wall -Wextra -Wpedantic -Werror`.
|
|
42
|
+
- RAII for every resource. Ownership goes through `std::unique_ptr` or `std::shared_ptr`, never a raw `new` or `delete`.
|
|
43
|
+
- External input is parsed into typed objects at the boundary.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
|
|
3
|
+
uv handles install, run and build. Run commands from inside the project directory, where `pyproject.toml` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Install | `uv sync` |
|
|
8
|
+
| Add a dependency | `uv add <pkg>`, or `uv add --dev <pkg>` for dev |
|
|
9
|
+
| Run a file | `uv run <file>` |
|
|
10
|
+
| Run a tool once | `uvx <tool>` |
|
|
11
|
+
| Build | `uv build` |
|
|
12
|
+
|
|
13
|
+
## Checks
|
|
14
|
+
|
|
15
|
+
| Check | Command | When |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Typecheck | `uv run ty check` | always |
|
|
18
|
+
| Lint | `uv run ruff check --fix`, then fix what remains by hand | always |
|
|
19
|
+
| Format | `uv run ruff format --check`, or `uv run ruff format` to fix | always |
|
|
20
|
+
| Test | `uv run pytest` | when the project has tests |
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
- Never `pip`, `python -m venv` or `poetry`. uv owns the environment.
|
|
25
|
+
- Ruff and ty read their settings from `pyproject.toml`. Fix the code, not the config, to pass a check.
|
|
26
|
+
- Every function signature has type hints, including the return type.
|
|
27
|
+
- `object` and narrowing in place of `Any`.
|
|
28
|
+
- External input is parsed into typed objects at the boundary.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
|
|
3
|
+
Cargo handles install, run, test and build. Run commands from inside the crate or workspace directory, where `Cargo.toml` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Install | `cargo fetch`, or let `cargo build` fetch |
|
|
8
|
+
| Add a dependency | `cargo add <crate>`, or `cargo add --dev <crate>` for dev |
|
|
9
|
+
| Run | `cargo run` |
|
|
10
|
+
| Build | `cargo build`, or `cargo build --release` |
|
|
11
|
+
|
|
12
|
+
## Checks
|
|
13
|
+
|
|
14
|
+
| Check | Command | When |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Typecheck | `cargo check` | always |
|
|
17
|
+
| Lint | `cargo clippy --all-targets -- -D warnings` | always |
|
|
18
|
+
| Format | `cargo fmt --check`, or `cargo fmt` to fix | always |
|
|
19
|
+
| Test | `cargo test` | when the crate has tests |
|
|
20
|
+
|
|
21
|
+
## Rules
|
|
22
|
+
|
|
23
|
+
- Fix the code, not the lint config, to pass a check. No `#[allow(...)]` to silence clippy.
|
|
24
|
+
- Errors return as `Result` and travel with `?`. No `unwrap` or `expect` outside tests.
|
|
25
|
+
- No `unsafe` without asking.
|
|
26
|
+
- External input is parsed into typed structs at the boundary, e.g. with `serde`.
|
|
@@ -54,3 +54,13 @@ A decision is a choice **the user** made, with its _why_, never the _how_. A cho
|
|
|
54
54
|
The syntax for each language:
|
|
55
55
|
|
|
56
56
|
- TypeScript: [`references/typescript.md`](references/typescript.md)
|
|
57
|
+
- Python: [`references/python.md`](references/python.md)
|
|
58
|
+
- C++: [`references/cpp.md`](references/cpp.md)
|
|
59
|
+
- Rust: [`references/rust.md`](references/rust.md)
|
|
60
|
+
|
|
61
|
+
## Editor highlighting
|
|
62
|
+
|
|
63
|
+
Only when the user asks for docstring highlighting in VS Code. `<document-dir>` is the folder holding this `SKILL.md`.
|
|
64
|
+
|
|
65
|
+
1. **Install**: `bun <document-dir>/hooks/install-highlighting.ts`. It installs the extension in `vscode/`, which colours Rust doc sections and Python docstring sections like JSDoc tags, and turns off rust-analyzer's comment tokens that would cover them. On exit `1`, report its message.
|
|
66
|
+
2. **Reload**: tell the user to run `Developer: Reload Window`. Done when they see the colours.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// packages/g3n-skills/skills/coding/document/hooks/install-highlighting.ts
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
/** The editor setting that lets rust-analyzer paint over every grammar inside comments. */
|
|
7
|
+
const RUST_COMMENT_TOKENS = "rust-analyzer.semanticHighlighting.comments.enable";
|
|
8
|
+
|
|
9
|
+
/** Folder of the grammar-only extension this hook installs. */
|
|
10
|
+
const EXTENSION_DIR = path.join(import.meta.dir, "..", "vscode");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @brief Identity of the extension, read from its `package.json`.
|
|
14
|
+
*
|
|
15
|
+
* @property id - `publisher.name`, e.g. `g3nomaly.g3n-doc-tags`.
|
|
16
|
+
* @property version - Version from `package.json`, e.g. `0.1.0`.
|
|
17
|
+
*/
|
|
18
|
+
type Extension = { id: string; version: string };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @brief Reads the id and version of the extension in `EXTENSION_DIR`.
|
|
22
|
+
*
|
|
23
|
+
* @returns The extension's identity.
|
|
24
|
+
* @throws Error when `package.json` lacks `publisher`, `name` or `version`.
|
|
25
|
+
*/
|
|
26
|
+
function readExtension(): Extension {
|
|
27
|
+
const manifest: unknown = JSON.parse(fs.readFileSync(path.join(EXTENSION_DIR, "package.json"), "utf8"));
|
|
28
|
+
if (typeof manifest !== "object" || manifest === null) throw new Error("vscode/package.json is not an object");
|
|
29
|
+
const { publisher, name, version } = manifest as Record<string, unknown>;
|
|
30
|
+
if (typeof publisher !== "string" || typeof name !== "string" || typeof version !== "string") {
|
|
31
|
+
throw new Error("vscode/package.json needs publisher, name and version");
|
|
32
|
+
}
|
|
33
|
+
return { id: `${publisher}.${name}`, version };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @brief Runs a command and fails with its output when it exits non-zero.
|
|
38
|
+
*
|
|
39
|
+
* @param command - Program and arguments, e.g. `["code", "--install-extension", "x.vsix"]`.
|
|
40
|
+
* @param cwd - Working directory of the command.
|
|
41
|
+
* @throws Error with the command's stderr when it exits non-zero.
|
|
42
|
+
*/
|
|
43
|
+
function run(command: string[], cwd: string): void {
|
|
44
|
+
const result = Bun.spawnSync(command, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
45
|
+
if (result.exitCode !== 0) {
|
|
46
|
+
throw new Error(`${command.join(" ")} failed:\n${result.stderr.toString() || result.stdout.toString()}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @brief Packages the extension into a `.vsix` in a fresh temporary folder, so the skill folder stays untouched.
|
|
52
|
+
*
|
|
53
|
+
* @param extension - Identity of the extension, which names the file.
|
|
54
|
+
* @returns Absolute path of the `.vsix`.
|
|
55
|
+
*/
|
|
56
|
+
function packageExtension(extension: Extension): string {
|
|
57
|
+
const work = fs.mkdtempSync(path.join(os.tmpdir(), "g3n-doc-tags-"));
|
|
58
|
+
const source = path.join(work, "extension");
|
|
59
|
+
fs.cpSync(EXTENSION_DIR, source, { recursive: true });
|
|
60
|
+
|
|
61
|
+
const vsix = path.join(work, `${extension.id}-${extension.version}.vsix`);
|
|
62
|
+
run(["bunx", "@vscode/vsce@3", "package", "--no-dependencies", "--skip-license", "--allow-missing-repository", "--out", vsix], source);
|
|
63
|
+
return vsix;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @brief Finds the settings file VS Code reads on this machine: the WSL machine settings when a VS Code server
|
|
68
|
+
* runs here, otherwise the user settings.
|
|
69
|
+
*
|
|
70
|
+
* @returns Absolute path of the settings file, which may not exist yet.
|
|
71
|
+
*/
|
|
72
|
+
function settingsPath(): string {
|
|
73
|
+
const home = os.homedir();
|
|
74
|
+
if (fs.existsSync(path.join(home, ".vscode-server"))) return path.join(home, ".vscode-server", "data", "Machine", "settings.json");
|
|
75
|
+
if (process.platform === "darwin") return path.join(home, "Library", "Application Support", "Code", "User", "settings.json");
|
|
76
|
+
return path.join(home, ".config", "Code", "User", "settings.json");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @brief Turns off rust-analyzer's comment tokens in `file`, so the extension's colours show. Writes the file
|
|
81
|
+
* only when the setting is not already `false`.
|
|
82
|
+
*
|
|
83
|
+
* @param file - Absolute path of a VS Code settings file.
|
|
84
|
+
* @returns True when the file was written.
|
|
85
|
+
* @throws Error when the file is not plain JSON, such as a file with comments, naming the setting to add by hand.
|
|
86
|
+
*/
|
|
87
|
+
function disableRustCommentTokens(file: string): boolean {
|
|
88
|
+
const text = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "{}";
|
|
89
|
+
let settings: unknown;
|
|
90
|
+
try {
|
|
91
|
+
settings = JSON.parse(text.trim() === "" ? "{}" : text);
|
|
92
|
+
} catch {
|
|
93
|
+
settings = null;
|
|
94
|
+
}
|
|
95
|
+
if (typeof settings !== "object" || settings === null || Array.isArray(settings)) {
|
|
96
|
+
throw new Error(`${file} is not plain JSON. Add by hand: "${RUST_COMMENT_TOKENS}": false`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const record = settings as Record<string, unknown>;
|
|
100
|
+
if (record[RUST_COMMENT_TOKENS] === false) return false;
|
|
101
|
+
record[RUST_COMMENT_TOKENS] = false;
|
|
102
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
103
|
+
fs.writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @brief Installs the doc tag highlighting: packages the extension, installs it with VS Code's `code` command,
|
|
109
|
+
* and turns off rust-analyzer's comment tokens in the VS Code settings. Writes the settings file.
|
|
110
|
+
*
|
|
111
|
+
* @param argv - Arguments of the hook. `--pretty` indents the JSON.
|
|
112
|
+
* @throws Error when `code` is not on the PATH, packaging or installing fails, or the settings are not plain JSON.
|
|
113
|
+
*/
|
|
114
|
+
function main(argv: readonly string[]): void {
|
|
115
|
+
const code = Bun.which("code");
|
|
116
|
+
if (code === null) throw new Error("VS Code's `code` command is not on the PATH. Run this from a VS Code terminal.");
|
|
117
|
+
|
|
118
|
+
const extension = readExtension();
|
|
119
|
+
const vsix = packageExtension(extension);
|
|
120
|
+
run([code, "--install-extension", vsix, "--force"], path.dirname(vsix));
|
|
121
|
+
fs.rmSync(path.dirname(vsix), { recursive: true, force: true });
|
|
122
|
+
|
|
123
|
+
const settings = settingsPath();
|
|
124
|
+
const settingsChanged = disableRustCommentTokens(settings);
|
|
125
|
+
|
|
126
|
+
const result = { extension: `${extension.id}@${extension.version}`, settings, settingsChanged };
|
|
127
|
+
console.log(JSON.stringify(result, null, argv.includes("--pretty") ? 2 : undefined));
|
|
128
|
+
console.error("Run `Developer: Reload Window` in VS Code to load the highlighting.");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
main(process.argv.slice(2));
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# C++
|
|
2
|
+
|
|
3
|
+
Doxygen: a `/** */` block directly above the declaration, in the header.
|
|
4
|
+
|
|
5
|
+
- **Line 1**: `// src/users/user_store.hpp`, the relative path. No `@file` block.
|
|
6
|
+
- **Declarations with a docstring**: functions, classes, structs, enums, type aliases and concepts. The header declaration carries it; the definition in the `.cpp` does not repeat it.
|
|
7
|
+
- **Summary**: `/** Adds two numbers. */` on one line. A longer block opens with `@brief` and the summary.
|
|
8
|
+
- **Tags**: `@param name text`, `@return text`, `@throws Type When ...`, and for the members of a class, struct or enum `@property name text`. Any other tag that fits is fine: `@tparam T text`, `@example`, `@see`, `@deprecated`, `@since`.
|
|
9
|
+
- **Throws**: a function that can throw has one `@throws` tag per exception type, with its full name, e.g. `std::filesystem::filesystem_error`.
|
|
10
|
+
- **Example**: `@example` with the code on the lines below it. Not `@code` ... `@endcode`, which analyze does not read.
|
|
11
|
+
|
|
12
|
+
```cpp
|
|
13
|
+
/** Adds two numbers. */
|
|
14
|
+
[[nodiscard]] int add(int a, int b);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @brief Retrieves a user by id.
|
|
18
|
+
*
|
|
19
|
+
* @param user_id Surrogate key from the `users` table, not the auth provider's id.
|
|
20
|
+
* @return Custom User class used by the authentication service, or `std::nullopt` when no row matches.
|
|
21
|
+
*/
|
|
22
|
+
[[nodiscard]] std::optional<User> get_user(std::int64_t user_id);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @brief Reads a file and checks it against its schema.
|
|
26
|
+
*
|
|
27
|
+
* @param path Absolute path of the file, e.g. `/home/me/app/docs/auth.md`.
|
|
28
|
+
* @return Custom Tree struct with one node per section of the file.
|
|
29
|
+
* @throws FileValidationError When a field does not match the schema.
|
|
30
|
+
* @throws std::filesystem::filesystem_error When the file cannot be read.
|
|
31
|
+
*/
|
|
32
|
+
[[nodiscard]] Tree parse_file(const std::filesystem::path& path);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @brief Error for a file on disk that does not match its schema.
|
|
36
|
+
*
|
|
37
|
+
* Holds the path of the file and one issue per rejected field, and `what()`
|
|
38
|
+
* lists them all. Thrown by `parse_file` and by the readers in `filetree/`,
|
|
39
|
+
* `decisions/` and `vocabulary/` that check a file's layout by hand. Callers
|
|
40
|
+
* tell it apart from other errors with `catch (const FileValidationError&)`.
|
|
41
|
+
*
|
|
42
|
+
* @property file Absolute path of the file that failed, e.g. `/home/me/app/docs/auth.md`.
|
|
43
|
+
* @property issues Rejected fields, e.g. `{ "phases.0.status", "is required" }`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* throw FileValidationError("/home/me/app/docs/auth.md", {{"status", "is \"done\""}});
|
|
47
|
+
*/
|
|
48
|
+
class FileValidationError : public std::runtime_error {
|
|
49
|
+
public:
|
|
50
|
+
/** Builds the message from the file path and every issue. */
|
|
51
|
+
FileValidationError(std::filesystem::path file, std::vector<FileValidationIssue> issues);
|
|
52
|
+
|
|
53
|
+
const std::filesystem::path file;
|
|
54
|
+
const std::vector<FileValidationIssue> issues;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @brief Entries a set of requests resolved to, and the requests that did not resolve.
|
|
59
|
+
*
|
|
60
|
+
* @property entries Resolved entries by key, e.g. `emphasise`.
|
|
61
|
+
* @property unknown Requests no source offers, e.g. `ghost@mine`.
|
|
62
|
+
* @property notes Why an entry nobody named is in the set, e.g. `{ "vocabulary", "dep of plan" }`.
|
|
63
|
+
*/
|
|
64
|
+
struct Resolution {
|
|
65
|
+
std::unordered_map<std::string, SourceOffer> entries;
|
|
66
|
+
std::vector<std::string> unknown;
|
|
67
|
+
std::unordered_map<std::string, std::string> notes;
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Bad
|
|
72
|
+
|
|
73
|
+
Each one next to what is wrong with it. The examples above show the fix.
|
|
74
|
+
|
|
75
|
+
```cpp
|
|
76
|
+
struct Resolution {
|
|
77
|
+
std::vector<std::string> unknown; ///< Requests no source offers.
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
A trailing `///<` docstring on a member. Members go in the struct's docstring as `@property` tags.
|
|
81
|
+
|
|
82
|
+
```cpp
|
|
83
|
+
// Retrieves a user by id.
|
|
84
|
+
std::optional<User> get_user(std::int64_t user_id);
|
|
85
|
+
```
|
|
86
|
+
A plain `//` comment in place of a docstring. Doxygen and clangd only read `/** */`.
|
|
87
|
+
|
|
88
|
+
```cpp
|
|
89
|
+
/**
|
|
90
|
+
* @brief Reads a file and checks it against its schema.
|
|
91
|
+
*
|
|
92
|
+
* @return The parsed tree. Throws when the file is invalid.
|
|
93
|
+
*/
|
|
94
|
+
[[nodiscard]] Tree parse_file(const std::filesystem::path& path);
|
|
95
|
+
```
|
|
96
|
+
The exception is prose inside `@return`, with no type. Each exception gets its own `@throws` tag, as in `parse_file` above.
|
|
97
|
+
|
|
98
|
+
```cpp
|
|
99
|
+
/**
|
|
100
|
+
* Resolves the dependencies of one entry against every offer.
|
|
101
|
+
*
|
|
102
|
+
* @param preferred Source to take a key from when several offer it.
|
|
103
|
+
*/
|
|
104
|
+
```
|
|
105
|
+
More than one line without `@brief`. It opens with `@brief Resolves the dependencies...`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
|
|
3
|
+
Google-style docstrings: a `"""` block as the first statement inside the declaration.
|
|
4
|
+
|
|
5
|
+
- **Line 1**: `# src/app/users.py`, the relative path. No module docstring, so ruff's `D100` and `D104` stay off.
|
|
6
|
+
- **Declarations with a docstring**: functions, methods, classes, dataclasses, `TypedDict`s, `Protocol`s and enums.
|
|
7
|
+
- **Summary**: `"""Adds two numbers."""` on one line. The first line is the `@brief`: a longer block opens with the summary, then a blank line.
|
|
8
|
+
- **Tags**: sections `Args:`, `Returns:`, `Raises:` and `Example:`. The fields of a class, dataclass, `TypedDict` or enum go under `Attributes:` in its docstring.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
def add(a: int, b: int) -> int:
|
|
12
|
+
"""Adds two numbers."""
|
|
13
|
+
return a + b
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_user(user_id: int) -> User | None:
|
|
17
|
+
"""Retrieves a user by id.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
user_id: Surrogate key from the `users` table, not the auth provider's id.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Custom User class used by the authentication service, or `None` when no row matches.
|
|
24
|
+
"""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FileValidationError(Exception):
|
|
29
|
+
"""Error for a file on disk that does not match its schema.
|
|
30
|
+
|
|
31
|
+
Holds the path of the file and one issue per rejected field, and its message
|
|
32
|
+
lists them all. Raised by `parse_file` and by the readers in `filetree/`,
|
|
33
|
+
`decisions/` and `vocabulary/` that check a file's layout by hand. Callers
|
|
34
|
+
tell it apart from other errors with `except FileValidationError`.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
file: Absolute path of the file that failed, e.g. `/home/me/app/docs/auth.md`.
|
|
38
|
+
issues: Rejected fields, e.g. `FileValidationIssue(field="phases.0.status", message="is required")`.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
raise FileValidationError("/home/me/app/docs/auth.md", [
|
|
42
|
+
FileValidationIssue(field="status", message='is "done"'),
|
|
43
|
+
])
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, file: str, issues: list[FileValidationIssue]) -> None:
|
|
47
|
+
"""Builds the message from the file path and every issue."""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class Resolution:
|
|
53
|
+
"""Entries a set of requests resolved to, and the requests that did not resolve.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
entries: Resolved entries by key, e.g. `emphasise`.
|
|
57
|
+
unknown: Requests no source offers, e.g. `ghost@mine`.
|
|
58
|
+
notes: Why an entry nobody named is in the set, e.g. `{"vocabulary": "dep of plan"}`.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
entries: dict[str, SourceOffer]
|
|
62
|
+
unknown: list[str]
|
|
63
|
+
notes: dict[str, str]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`__init__` gets a summary only when the class docstring already tags its arguments as attributes.
|
|
67
|
+
|
|
68
|
+
## Bad
|
|
69
|
+
|
|
70
|
+
Each one next to what is wrong with it. The examples above show the fix.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
@dataclass
|
|
74
|
+
class Resolution:
|
|
75
|
+
"""Entries a set of requests resolved to."""
|
|
76
|
+
|
|
77
|
+
entries: dict[str, SourceOffer]
|
|
78
|
+
"""Resolved entries by key."""
|
|
79
|
+
```
|
|
80
|
+
A docstring on a field. Fields go in the class docstring under `Attributes:`.
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
def get_user(user_id: int) -> User | None:
|
|
84
|
+
# Looks the user up by surrogate key.
|
|
85
|
+
"""Retrieves a user by id."""
|
|
86
|
+
```
|
|
87
|
+
A comment that says what a tag can say. It goes under `Args:`.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
def matches(request: EntryRequest, offers: Sequence[SourceOffer]) -> Match:
|
|
91
|
+
"""
|
|
92
|
+
Finds the entries one request names.
|
|
93
|
+
"""
|
|
94
|
+
```
|
|
95
|
+
The summary starts on line 2, and the custom types `EntryRequest`, `SourceOffer` and `Match` have no `Args:` or `Returns:` entry.
|