@g3nomaly/g3n-skills 0.1.1 → 0.1.3

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 (35) hide show
  1. package/.claude-plugin/marketplace.json +7 -4
  2. package/README.md +101 -0
  3. package/package.json +2 -2
  4. package/skills/agent/.claude-plugin/plugin.json +2 -2
  5. package/skills/agent/bootstrap/SKILL.md +1 -1
  6. package/skills/agent/commit/SKILL.md +17 -0
  7. package/skills/agent/commit/agents/openai.yaml +5 -0
  8. package/skills/agent/commit/hooks/check-message.ts +107 -0
  9. package/skills/agent/worktrees/SKILL.md +10 -12
  10. package/skills/agent/worktrees/agents/openai.yaml +1 -1
  11. package/skills/coding/analyze/SKILL.md +2 -1
  12. package/skills/coding/code/SKILL.md +1 -1
  13. package/skills/coding/document/SKILL.md +1 -1
  14. package/skills/grilling/domain-modeling/SKILL.md +2 -1
  15. package/skills/grilling/grilling/SKILL.md +2 -1
  16. package/skills/speech/.claude-plugin/plugin.json +3 -2
  17. package/skills/speech/emphasise/SKILL.md +1 -1
  18. package/skills/speech/hooks.json +15 -0
  19. package/skills/speech/no-questions/SKILL.md +9 -0
  20. package/skills/speech/no-questions/agents/openai.yaml +5 -0
  21. package/skills/speech/no-questions/hooks/refuse-questions.ts +108 -0
  22. package/skills/speech/no-questions/hooks/toggle.ts +14 -0
  23. package/skills/speech/no-questions/src/state.ts +15 -0
  24. package/skills/speech/plain-language/SKILL.md +9 -0
  25. package/skills/speech/plain-language/agents/openai.yaml +5 -0
  26. package/skills/speech/unslop/SKILL.md +3 -2
  27. package/skills/task-manager/decisions/SKILL.md +1 -1
  28. package/skills/task-manager/vocabulary/SKILL.md +1 -1
  29. package/skills/workflow/execute/SKILL.md +1 -1
  30. package/skills/workflow/plan/SKILL.md +1 -1
  31. package/skills/workflow/review/SKILL.md +1 -1
  32. package/skills/workflow/run/SKILL.md +1 -1
  33. package/skills/workflow/sync/SKILL.md +1 -1
  34. package/skills/workflow/worktree/SKILL.md +1 -1
  35. package/skills/writing/writing-great-skills/SKILL.md +3 -1
@@ -7,10 +7,11 @@
7
7
  {
8
8
  "name": "agent",
9
9
  "source": "./skills/agent",
10
- "description": "Rules every agent session follows: the session bootstrap, its boundaries, and working in worktrees.",
10
+ "description": "Rules every agent session follows: the session bootstrap, its boundaries, working in worktrees, and commit messages.",
11
11
  "skills": [
12
12
  "./bootstrap",
13
- "./worktrees"
13
+ "./worktrees",
14
+ "./commit"
14
15
  ]
15
16
  },
16
17
  {
@@ -37,10 +38,12 @@
37
38
  {
38
39
  "name": "speech",
39
40
  "source": "./skills/speech",
40
- "description": "Conventions for how an agent writes — emphasis markup and cutting AI tells.",
41
+ "description": "Conventions for how an agent writes — emphasis markup, cutting AI tells, and plain language.",
41
42
  "skills": [
42
43
  "./emphasise",
43
- "./unslop"
44
+ "./unslop",
45
+ "./plain-language",
46
+ "./no-questions"
44
47
  ]
45
48
  },
46
49
  {
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @g3nomaly/g3n-skills
2
+
3
+ The g3n skills for Claude Code and Codex: 31 skills in 8 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
+
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
+
7
+ ## Requirements
8
+
9
+ - [Bun](https://bun.sh), to run the hooks
10
+ - A git repository to install into, because `@g3nomaly/skills` anchors a project at its repository root
11
+ - [`@g3nomaly/analyze`](https://www.npmjs.com/package/@g3nomaly/analyze) for the `analyze` skill, which runs `bunx @g3nomaly/analyze`
12
+
13
+ ## Install with @g3nomaly/skills
14
+
15
+ Run these from the root of your project:
16
+
17
+ ```sh
18
+ bunx @g3nomaly/skills init # pick Claude Code, Codex or both
19
+ bunx @g3nomaly/skills ls --source g3n-skills # lists the skills this package offers
20
+ bunx @g3nomaly/skills add workflow # installs a group, with every skill it depends on
21
+ bunx @g3nomaly/skills add unslop # installs one skill
22
+ ```
23
+
24
+ You don't register this package. `g3n-skills` is built into the installer and cannot be removed.
25
+
26
+ Each skill is copied to `.claude/skills/<skill>/`, or `.agents/skills/<skill>/` for Codex, next to the skills it depends on. The installer records it in `.claude/skills.json` or `.agents/skills.json`. Commit the skills and that file if your team should get the same skills.
27
+
28
+ A name that is both a group and a skill, such as `grilling` or `task-manager`, installs the whole group. `add` and `update` also run `bun install` in each skill that ships a `package.json`. Today that is only `task-manager`, which pins zod.
29
+
30
+ ```sh
31
+ bunx @g3nomaly/skills update # every installed skill
32
+ bunx @g3nomaly/skills update --dry-run # shows what would change
33
+ bunx @g3nomaly/skills remove grill-me
34
+ bunx @g3nomaly/skills ls --installed
35
+ ```
36
+
37
+ ## As a Claude Code plugin marketplace
38
+
39
+ The package ships `.claude-plugin/marketplace.json`, a marketplace named `g3n` with one plugin per group in the table below.
40
+
41
+ Installed as a plugin, the task-manager group registers two lifecycle hooks. `sync-tree` runs at `SessionStart`, and `guard-tree` runs before `Read`, `Grep` and `Glob`. With `@g3nomaly/skills` you get neither, and the task-manager skill tells the agent to run `sync-tree` itself.
42
+
43
+ There is one known gap. Claude Code caches each plugin in its own folder, so the workflow hooks can't import the task-manager code they need. Install the workflow group with `@g3nomaly/skills` for now.
44
+
45
+ ## The skills
46
+
47
+ Skills marked _manual_ run only when you type their command, such as `/grill-me`. The agent loads the others when the task calls for them.
48
+
49
+ | Group | Skill | What it does |
50
+ |---|---|---|
51
+ | `agent` | `bootstrap` | Session start checks (read `CLAUDE.md`, compare the file tree, state the branch) and the agent's boundaries |
52
+ | `agent` | `worktrees` | Work in a git worktree on a feature branch and land it on the home branch as clean commits on top of `main`, ready to merge one commit at a time |
53
+ | `agent` | `commit` | Commit messages with no attribution trailers, personal data or AI references, checked by a hook before each commit |
54
+ | `coding` | `code` | Rules for writing and checking code, with a toolchain reference per language |
55
+ | `coding` | `document` | Docstrings and comments for functions and classes, and where the reasons behind code go |
56
+ | `coding` | `analyze` | Query a repository's model with `g3n-analyze` and rebuild its documentation site in `analyze_out/` |
57
+ | `grilling` | `grilling` | Interview the user relentlessly to stress-test a plan, decision or idea |
58
+ | `grilling` | `grill-me` | _Manual._ Starts a `/grilling` session |
59
+ | `grilling` | `grill-with-docs` | _Manual._ A grilling session that writes ADRs and a glossary as it goes |
60
+ | `grilling` | `domain-modeling` | Pin down a project's terms and record its architectural decisions |
61
+ | `speech` | `emphasise` | What bold, italics and backticks mean in Markdown that an agent reads |
62
+ | `speech` | `unslop` | Cut AI tells from any writing |
63
+ | `speech` | `plain-language` | Write text readers can find, understand and use on the first read, following ISO 24495-1 |
64
+ | `speech` | `no-questions` | Replies end without questions or offers; a Stop hook refuses a reply that asks one |
65
+ | `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
+ | `task-manager` | `decisions` | Record and look up a plan's decisions as numbered files inside the plan |
68
+ | `using-hooks` | `hook-pre-tool-use` | _Manual._ Writing a `PreToolUse` hook, which can allow, deny or escalate a tool call |
69
+ | `using-hooks` | `hook-post-tool-use` | _Manual._ Writing a `PostToolUse` hook, which reacts to a finished tool call |
70
+ | `using-hooks` | `hook-stop` | _Manual._ Writing a `Stop` hook, which can refuse to let the agent end its turn |
71
+ | `using-hooks` | `hook-user-prompt-submit` | _Manual._ Writing a `UserPromptSubmit` hook, which can add context to a prompt or reject it |
72
+ | `workflow` | `run` | Work one phase through plan, execute, review and sync, one fresh agent per step |
73
+ | `workflow` | `plan` | Turn a phase into task files an agent can execute without asking anything |
74
+ | `workflow` | `execute` | Carry out a phase's tasks, each in its own worktree, and hand each one to review |
75
+ | `workflow` | `review` | Check each in-review task against its requirements, then complete it or send it back |
76
+ | `workflow` | `sync` | Merge a reviewed phase's branches, remove its worktrees, record its decisions and terms, and close it |
77
+ | `workflow` | `orchestrator` | _Manual._ Run a whole plan phase by phase, starting each phase once the phases it depends on are done |
78
+ | `workflow` | `worktree` | Set up, merge and remove the git worktrees tasks are worked in, one per task |
79
+ | `writing` | `writing-great-hooks` | Add, change or debug a hook script or a lifecycle hook |
80
+ | `writing` | `writing-great-skills` | Write or edit a skill, its `SKILL.md`, references and hooks |
81
+ | `writing` | `writing-great-tasks` | Write a task that doesn't leave its executor guessing about scope |
82
+
83
+ The `commit` hook refuses your git `user.name` and `user.email`. To keep another name out of messages without writing it into the repository, add it to your global git config: `git config --global --add g3n.privateName "<name>"`.
84
+
85
+ The task-manager hooks store the tree in `docs/task-manager` by default. Set `skills.settings.filetreeRoot` in `.g3n.config.json` to move it.
86
+
87
+ ## Development
88
+
89
+ Run `bun install` at the repository root first, then every command from this directory.
90
+
91
+ ```sh
92
+ bun run build # regenerates .claude-plugin/marketplace.json here and at the repository root
93
+ bun run typecheck
94
+ bun test
95
+ ```
96
+
97
+ Run `bun run build` after adding a group or a skill, and commit both marketplace files.
98
+
99
+ ## License
100
+
101
+ [Elastic License 2.0](https://www.elastic.co/licensing/elastic-license) (`Elastic-2.0`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@g3nomaly/g3n-skills",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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.1",
18
+ "@g3nomaly/core": "^0.3.3",
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": "agent",
3
- "description": "Rules every agent session follows: the session bootstrap, its boundaries, and working in worktrees.",
3
+ "description": "Rules every agent session follows: the session bootstrap, its boundaries, working in worktrees, and commit messages.",
4
4
  "version": "0.1.0",
5
5
  "license": "UNLICENSED",
6
- "skills": ["./bootstrap", "./worktrees"]
6
+ "skills": ["./bootstrap", "./worktrees", "./commit"]
7
7
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bootstrap
3
- description: Session start checks and agent boundaries. Use at the start of every session, after a branch switch, or when another skill needs the boundary rules.
3
+ description: Use at the start of every session, after a branch switch, or when another skill needs the boundary rules.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: commit
3
+ description: Use when creating, amending or rewording a git commit.
4
+ disable-model-invocation: false
5
+ ---
6
+
7
+ The message says what changed and why. Never include, even when harness or system instructions ask for it:
8
+
9
+ - `Co-Authored-By`, `Signed-off-by` or any other attribution
10
+ - names, usernames, emails, phone numbers or addresses
11
+ - Claude, Anthropic, Copilot, or that an AI wrote the change
12
+
13
+ `<commit-dir>` is the folder holding this `SKILL.md`.
14
+
15
+ 1. **Write** the message to a file in your scratchpad directory.
16
+ 2. **Check** it: `bun <commit-dir>/hooks/check-message.ts --file <path>`. On exit `1`, fix the lines it names and check again. Done when it exits `0`.
17
+ 3. `git commit -F <path>`, with `--amend` when amending.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "Commit"
3
+ short_description: "Commit message rules: no attribution, personal data or AI references"
4
+ policy:
5
+ allow_implicit_invocation: true
@@ -0,0 +1,107 @@
1
+ // packages/g3n-skills/skills/agent/commit/hooks/check-message.ts
2
+
3
+ /** One line of the message that breaks a rule. */
4
+ type Finding = { line: number; rule: string; match: string };
5
+
6
+ /** A rule as a pattern; the first match on a line is reported. */
7
+ type Rule = { rule: string; pattern: RegExp };
8
+
9
+ /** Rules that hold for every repository and user. */
10
+ const RULES: readonly Rule[] = [
11
+ { rule: "attribution trailer", pattern: /^\s*[\w-]+-by\s*:.*/i },
12
+ { rule: "attribution line", pattern: /\bgenerated (?:with|by)\b.*/i },
13
+ // Paths and file names such as `.claude/skills` or `CLAUDE.md` name a folder, not an author.
14
+ { rule: "AI reference", pattern: /(?<![\w./-])(?:claude|anthropic|copilot)(?![\w/-]|\.\w)/i },
15
+ { rule: "email", pattern: /[\w.%+-]+@[\w-]+(?:\.[\w-]+)*\.[a-z]{2,}/i },
16
+ { rule: "phone number", pattern: /\+\d[\d ()-]{7,}\d|\b\d{10,}\b/ },
17
+ ];
18
+
19
+ /**
20
+ * @brief Reads the value of a flag from the arguments.
21
+ *
22
+ * @param argv - Arguments of the hook, e.g. `["--file", "msg.txt"]`.
23
+ * @param flag - Flag to look up, with its dashes.
24
+ * @returns Value after the flag, or `undefined` when the flag is absent.
25
+ * @throws Error when the flag is last or followed by another flag.
26
+ */
27
+ function readFlag(argv: readonly string[], flag: string): string | undefined {
28
+ const at = argv.indexOf(flag);
29
+ if (at === -1) return undefined;
30
+
31
+ const value = argv[at + 1];
32
+ if (value === undefined || value.startsWith("--")) throw new Error(`${flag} requires a value`);
33
+ return value;
34
+ }
35
+
36
+ /**
37
+ * @brief Reads every value of a git config key, as seen from the working directory.
38
+ *
39
+ * @param key - Config key, e.g. `user.name`.
40
+ * @returns The non-empty values, or none when the key is unset.
41
+ */
42
+ function gitConfig(key: string): string[] {
43
+ const result = Bun.spawnSync(["git", "config", "--get-all", key], { stderr: "ignore" });
44
+ return result.stdout.toString().split("\n").map((value) => value.trim()).filter(Boolean);
45
+ }
46
+
47
+ /** Escapes `text` so it matches itself literally inside a `RegExp`. */
48
+ function escapeRegExp(text: string): string {
49
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ }
51
+
52
+ /**
53
+ * @brief Builds rules for the identities this machine knows: the git `user.name`
54
+ * and `user.email`, the email's local part, and every `g3n.privateName`.
55
+ *
56
+ * `g3n.privateName` keeps a name out of messages without writing it into the
57
+ * repository: `git config --global --add g3n.privateName "<name>"`.
58
+ */
59
+ function identityRules(): Rule[] {
60
+ const emails = gitConfig("user.email");
61
+ const names = [
62
+ ...gitConfig("user.name"),
63
+ ...gitConfig("g3n.privateName"),
64
+ ...emails,
65
+ ...emails.map((email) => email.split("@")[0] ?? ""),
66
+ ].filter((name) => name.length >= 3);
67
+
68
+ return names.map((name) => ({
69
+ rule: "personal identity",
70
+ pattern: new RegExp(`(?<![\\w])${escapeRegExp(name)}(?![\\w])`, "i"),
71
+ }));
72
+ }
73
+
74
+ /**
75
+ * @brief Checks a commit message against every rule.
76
+ *
77
+ * @param message - Full commit message.
78
+ * @param rules - Rules to apply.
79
+ * @returns Every line that breaks a rule, in line order and then rule order.
80
+ */
81
+ function check(message: string, rules: readonly Rule[]): Finding[] {
82
+ return message.split("\n").flatMap((text, index) =>
83
+ rules.flatMap(({ rule, pattern }) => {
84
+ const found = text.match(pattern);
85
+ return found ? [{ line: index + 1, rule, match: found[0].trim() }] : [];
86
+ }),
87
+ );
88
+ }
89
+
90
+ /** Prints `{ clean: true }` for the message in `--file` (or stdin) when it breaks no rule; exits 1 naming each line that does. */
91
+ async function run(argv: readonly string[]): Promise<void> {
92
+ const file = readFlag(argv, "--file");
93
+ const message = file === undefined ? (process.stdin.isTTY ? "" : await Bun.stdin.text()) : await Bun.file(file).text();
94
+ if (message.trim() === "") throw new Error("no message: pass --file <path> or pipe it on stdin");
95
+
96
+ const findings = check(message, [...RULES, ...identityRules()]);
97
+ if (findings.length > 0) {
98
+ throw new Error(findings.map(({ line, rule, match }) => `line ${line}: ${rule}: "${match}"`).join("\n"));
99
+ }
100
+
101
+ console.log(JSON.stringify({ clean: true }, null, argv.includes("--pretty") ? 2 : 0));
102
+ }
103
+
104
+ run(process.argv.slice(2)).catch((error: unknown) => {
105
+ console.error(error instanceof Error ? error.message : String(error));
106
+ process.exit(1);
107
+ });
@@ -1,21 +1,19 @@
1
1
  ---
2
2
  name: worktrees
3
- description: Work in a git worktree on its home branch and land the work there with a local squash merge. Use only when the user asks you to work in a worktree.
3
+ description: Use only when the user asks you to work in a worktree.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
7
- A worktree at `~/worktrees/<repo>/<x>` has the **home branch** `worktree/<x>`, which tracks local `main`. Work ends on the home branch. The user merges it into `main`.
7
+ A worktree at `~/worktrees/<repo>/<x>` has the **home branch** `worktree/<x>`: `main` plus finished features.
8
8
 
9
- ## Steps
9
+ 1. `git switch worktree/<x> && git rebase main && git switch -c <type>/<name>`
10
+ 2. Run the package's checks from the `/code` skill.
11
+ 3. Fold each commit that only fixes an earlier one into it. If more than 10 are left, squash the ones that belong together into reviewable steps.
12
+ 4. `git rebase worktree/<x> && git switch worktree/<x> && git merge --ff-only <type>/<name>`
13
+ 5. Report it ready to merge. Keep the feature branch.
10
14
 
11
- 1. `git switch worktree/<x> && git pull --no-rebase --no-edit`
12
- 2. `git switch -c <type>/<name>`, do the work, commit, and run the package's checks (see the `/code` skill)
13
- 3. `git switch worktree/<x> && git pull --no-rebase --no-edit`
14
- 4. `git merge --squash <type>/<name>`, then `git commit` with a subject and body covering the whole change
15
- 5. Stop and report. Leave the feature branch in place.
15
+ On conflict: `git rebase --abort`, stop and report.
16
16
 
17
- On conflict: `git merge --abort` (after a squash: `git reset --merge`), stop and report.
17
+ ## Merging into `main`
18
18
 
19
- ## Branch types
20
-
21
- `feature`, `fix`, `refactor`, `chore`, `docs`, `snapshot` (backup before destructive operations), `demo` (not meant to merge), `experiment` (may never merge).
19
+ Remember where `main` started. When the user asks, stage the first commit marked `+` in `git cherry main worktree/<x>` with `git cherry-pick --no-commit <sha>`. Commit it with `git commit -C <sha>` only when the user asks. On a rejected commit, the user picks: fix it in the worktree, or reset `main` to where the first cherry-pick started.
@@ -1,5 +1,5 @@
1
1
  interface:
2
2
  display_name: "Worktrees"
3
- short_description: "Work on a worktree's home branch and land it with a local squash merge"
3
+ short_description: "Work on a feature branch and land it on the worktree home branch as clean commits on top of main"
4
4
  policy:
5
5
  allow_implicit_invocation: true
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: analyze
3
- description: Query and update the linked model of a repository with g3n-analyze: find code, docs, skills, decisions and tasks, see what uses or links to what, and rebuild the documentation site in analyze_out/. Use when asked where something is, what uses a type or function, what a decision affects, or to run g3n-analyze or regenerate the docs.
3
+ description: Use when asked where something is, what uses a type or function, what a decision affects, or to run g3n-analyze or regenerate the docs.
4
+ disable-model-invocation: false
4
5
  ---
5
6
 
6
7
  `g3n-analyze` scans a repository into one model of **nodes** and **edges**, and answers queries on it. Run it from the repository root as `bunx @g3nomaly/analyze <command>`; in the g3n monorepo, `bun packages/analyze/dist/cli.js <command>` after `bun run build` in `packages/analyze`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: code
3
- description: Standard code skill for writing and checking code. Includes toolchain references for each language.
3
+ description: Use when writing or checking code.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: document
3
- description: Docstrings and comments for functions and classes, and where the reasons behind code go. Use when writing or changing a function or class, or when another skill needs the documentation rules.
3
+ description: Use when writing or changing a function or class, or when another skill needs the documentation rules.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: domain-modeling
3
- description: Build and sharpen a project's domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model.
3
+ description: Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model.
4
+ disable-model-invocation: false
4
5
  ---
5
6
 
6
7
  # Domain Modeling
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: grilling
3
- description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
3
+ description: Use when the user wants to stress-test a plan, decision or idea, or uses any 'grill' trigger phrase.
4
+ disable-model-invocation: false
4
5
  ---
5
6
 
6
7
  Interview me relentlessly about every aspect of this until we reach a shared understanding. Walk down each branch of the decision tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "speech",
3
- "description": "Conventions for how an agent writes — emphasis markup and cutting AI tells.",
3
+ "description": "Conventions for how an agent writes — emphasis markup, cutting AI tells, and plain language.",
4
4
  "version": "0.1.0",
5
5
  "license": "UNLICENSED",
6
- "skills": ["./emphasise", "./unslop"]
6
+ "hooks": "./hooks.json",
7
+ "skills": ["./emphasise", "./unslop", "./plain-language", "./no-questions"]
7
8
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: emphasise
3
- description: The bold, italic, and backtick conventions for agent-facing Markdown — what **bold**, _italics_, and `code` each mean, and how to use them to make text instantly scannable. Use when writing or editing a skill, prompt, or doc the agent reads, or when the user asks how to emphasise a term.
3
+ description: Use when writing or editing a skill, prompt or doc the agent reads, or when the user asks how to emphasise a term.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "Stop": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bun \"${CLAUDE_PLUGIN_ROOT}/no-questions/hooks/refuse-questions.ts\"",
9
+ "timeout": 10
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: no-questions
3
+ description: Toggles the Stop hook that refuses replies asking the user a question.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ Run `bun <this skill's folder>/hooks/toggle.ts` and tell the user whether the hook is now on or off.
8
+
9
+ The hook is a Stop hook, `hooks/refuse-questions.ts`. While on, it refuses a reply with a `?` outside code, URLs and quotes.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "No questions"
3
+ short_description: "Replies end without questions or offers"
4
+ policy:
5
+ allow_implicit_invocation: true
@@ -0,0 +1,108 @@
1
+ // packages/g3n-skills/skills/speech/no-questions/hooks/refuse-questions.ts
2
+ // refuse-questions
3
+ //
4
+ // Stop hook. Refuses a final reply that asks the user something in its text:
5
+ // an offer such as "Should I build it?" or a question tucked under a report.
6
+ // The user decides what comes next; a decision the agent cannot make goes
7
+ // through the AskUserQuestion tool, never into reply text.
8
+ //
9
+ // A `?` counts only in prose: code blocks, inline code, URLs and quoted text
10
+ // ("..." or “...”) are skipped, so quoting an example is not asking.
11
+ //
12
+ // Fires on: Stop, while toggle.ts has it on. Blocks once; a stop already blocked by this hook goes through.
13
+ //
14
+ // Exit codes: 0 lets the turn end, 2 refuses it with the reason on stderr.
15
+ // Every error path exits 0, so a broken hook never traps the session.
16
+
17
+ import { isEnabled } from "../src/state";
18
+
19
+ /**
20
+ * The fields of the Stop payload this hook reads from stdin.
21
+ *
22
+ * @property last_assistant_message - Text of the reply the turn ends with.
23
+ * @property transcript_path - JSONL file of the session, one entry per line.
24
+ * @property stop_hook_active - True when the turn only continues because a Stop hook already blocked it.
25
+ */
26
+ interface StopPayload {
27
+ readonly last_assistant_message?: string;
28
+ readonly transcript_path?: string;
29
+ readonly stop_hook_active?: boolean;
30
+ }
31
+
32
+ /** One block of a message's `content` array in the transcript. */
33
+ type ContentBlock = { type?: string; text?: string };
34
+
35
+ /** One line of the transcript. */
36
+ type Entry = { type?: string; message?: { role?: string; content?: string | ContentBlock[] } };
37
+
38
+ /**
39
+ * @brief Finds the text of the last assistant message of the current turn.
40
+ *
41
+ * Stops at the user's prompt, so a reply from an earlier turn is never checked.
42
+ *
43
+ * @param transcript - Contents of the JSONL transcript.
44
+ * @returns The message's text blocks joined, or `""` when the turn has none.
45
+ */
46
+ function lastReply(transcript: string): string {
47
+ const lines = transcript.trimEnd().split("\n");
48
+
49
+ for (let i = lines.length - 1; i >= 0; i--) {
50
+ let entry: Entry;
51
+ try {
52
+ entry = JSON.parse(lines[i] ?? "") as Entry;
53
+ } catch {
54
+ continue;
55
+ }
56
+ if (entry.type === "user" && isPrompt(entry)) return "";
57
+ if (entry.type !== "assistant" || !Array.isArray(entry.message?.content)) continue;
58
+
59
+ const text = entry.message.content
60
+ .filter((block) => block.type === "text" && typeof block.text === "string")
61
+ .map((block) => block.text)
62
+ .join("\n");
63
+ if (text.trim() !== "") return text;
64
+ }
65
+
66
+ return "";
67
+ }
68
+
69
+ /** Tells whether a user entry is a prompt the user typed, not a tool result. */
70
+ function isPrompt(entry: Entry): boolean {
71
+ const content = entry.message?.content;
72
+ return typeof content === "string" || (Array.isArray(content) && content.some((block) => block.type === "text"));
73
+ }
74
+
75
+ /**
76
+ * @brief Returns the sentences of `text` that ask a question, skipping code and URLs.
77
+ *
78
+ * @param text - Markdown text of the reply.
79
+ */
80
+ function questions(text: string): string[] {
81
+ const prose = text
82
+ .replace(/```[\s\S]*?```/g, " ")
83
+ .replace(/`[^`\n]*`/g, " ")
84
+ .replace(/https?:\/\/\S+/g, " ")
85
+ .replace(/"[^"\n]*"|“[^”\n]*”/g, " ");
86
+
87
+ return prose.match(/[^.!?\n]*\?/g)?.map((sentence) => sentence.trim()).filter(Boolean) ?? [];
88
+ }
89
+
90
+ /** Reads the Stop payload and refuses the stop when the final reply asks a question. */
91
+ async function main(): Promise<void> {
92
+ const payload = JSON.parse(await Bun.stdin.text()) as StopPayload;
93
+ if (payload.stop_hook_active || !(await isEnabled())) return;
94
+
95
+ const reply = payload.last_assistant_message
96
+ ?? (payload.transcript_path ? lastReply(await Bun.file(payload.transcript_path).text()) : "");
97
+ const found = questions(reply);
98
+ if (found.length === 0) return;
99
+
100
+ console.error(
101
+ `Your reply asks the user: ${found.map((q) => `"${q}"`).join(", ")}. ` +
102
+ "Rewrite the reply without questions. Drop offers such as \"Should I build it?\": the user decides what comes next. " +
103
+ "Only when you cannot continue without the user's decision, ask it with the AskUserQuestion tool.",
104
+ );
105
+ process.exit(2);
106
+ }
107
+
108
+ main().catch(() => process.exit(0));
@@ -0,0 +1,14 @@
1
+ // packages/g3n-skills/skills/speech/no-questions/hooks/toggle.ts
2
+ import { isEnabled, STATE_FILE } from "../src/state";
3
+
4
+ /** Turns the no-questions Stop hook on when it is off and off when it is on; writes the state file and prints `{ enabled }`. */
5
+ async function run(argv: readonly string[]): Promise<void> {
6
+ const enabled = !(await isEnabled());
7
+ await Bun.write(STATE_FILE, JSON.stringify({ enabled }) + "\n");
8
+ console.log(JSON.stringify({ enabled }, null, argv.includes("--pretty") ? 2 : 0));
9
+ }
10
+
11
+ run(process.argv.slice(2)).catch((error: unknown) => {
12
+ console.error(error instanceof Error ? error.message : String(error));
13
+ process.exit(1);
14
+ });
@@ -0,0 +1,15 @@
1
+ // packages/g3n-skills/skills/speech/no-questions/src/state.ts
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ /** File that holds whether the hook is on, for every project of this user. */
6
+ export const STATE_FILE = join(homedir(), ".claude", "no-questions.json");
7
+
8
+ /** Tells whether the hook is on; it is on until the user turns it off. */
9
+ export async function isEnabled(): Promise<boolean> {
10
+ try {
11
+ return (JSON.parse(await Bun.file(STATE_FILE).text()) as { enabled?: boolean }).enabled !== false;
12
+ } catch {
13
+ return true;
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: plain-language
3
+ description: Use when writing or editing anything a person or agent reads, such as skills, docs, READMEs, reports and replies.
4
+ disable-model-invocation: false
5
+ ---
6
+
7
+ Writing rules from the plain language standard ISO 24495-1
8
+
9
+ No ambiguous pronoun reference: when an "it" or "that" could point to more than one antecedent, repeat the noun.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "Plain Language"
3
+ short_description: "Write text readers can find, understand and use, per ISO 24495-1"
4
+ policy:
5
+ allow_implicit_invocation: true
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: unslop
3
- description: Cut AI tells from any writing. Must always apply.
3
+ description: Use whenever writing or editing any text.
4
+ disable-model-invocation: false
4
5
  ---
5
6
 
6
7
  # Unslop
@@ -77,4 +78,4 @@ Removing patterns is half the job. Sterile, voiceless writing is just as obvious
77
78
  28. **Shorten or split dense sentences.** If the reader has to backtrack to parse a sentence, break it in two or drop clauses. One idea per sentence.
78
79
  29. **Active voice.** Prefer it. Catch "is/are/was/were + past participle" and name the actor: "queries are validated" becomes "the compiler validates queries", "the file is parsed by the loader" becomes "the loader parses the file". Passive is fine only when the actor is unknown or genuinely doesn't matter.
79
80
  30. **Cut adverbs, or use a stronger verb.** "runs quickly" becomes "is fast" or the number. "significantly improves" becomes the measured delta. An adverb propping up a weak verb means the verb is wrong.
80
- 31. **Prefer the plain word.** "utilize" becomes "use", "leverage" becomes "use", "facilitate" becomes "help", "numerous" becomes "many", "in the event that" becomes "if". The fancier synonym is rarely clearer.
81
+ 31. **Prefer the plain word.** "utilize" becomes "use", "leverage" becomes "use", "facilitate" becomes "help", "numerous" becomes "many", "in the event that" becomes "if". The fancier synonym is rarely clearer.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: decisions
3
- description: Record and look up the decisions a plan has made, stored as numbered files inside the plan and reached through hooks. Use when a hard-to-reverse choice is made while planning or working a phase, when a choice replaces an earlier one, when the user asks why something was done a certain way, or when another skill needs a plan's decisions.
3
+ description: Use when a hard-to-reverse choice is made while planning or working a phase, when a choice replaces an earlier one, when the user asks why something was done a certain way, or when another skill needs a plan's decisions.
4
4
  registryDependencies: [task-manager, domain-modeling]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vocabulary
3
- description: Keep one word per concept across sessions, stored in the task-manager tree and reached through hooks. Use when about to name a domain concept in code, a task or a doc, when the user corrects a word, when two words are in use for one thing, or when another skill needs a term looked up or recorded.
3
+ description: Use when about to name a domain concept in code, a task or a doc, when the user corrects a word, when two words are in use for one thing, or when another skill needs a term looked up or recorded.
4
4
  registryDependencies: [task-manager, domain-modeling]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: execute
3
- description: Carry out the tasks of one phase, each in its own worktree, and hand every one to review. Use as the execute step of the workflow, when a phase has open or in-progress tasks, or when review sent a task back.
3
+ description: Use as the execute step of the workflow, when a phase has open or in-progress tasks, or when review sent a task back.
4
4
  registryDependencies: [task-manager, worktree, vocabulary, decisions, run]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: plan
3
- description: Turn one phase of a plan into task files an agent can execute without asking anything. Use as the plan step of the workflow, when a phase has no tasks yet, or when the user wants a phase broken down.
3
+ description: Use as the plan step of the workflow, when a phase has no tasks yet, or when the user wants a phase broken down.
4
4
  registryDependencies: [task-manager, writing-great-tasks, vocabulary, decisions, grilling, run]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: review
3
- description: Judge each in-review task of a phase against its own requirements and either complete it or send it back. Use as the review step of the workflow, when every remaining task of a phase is in-review, or when the user wants a phase's work checked before it is merged.
3
+ description: Use as the review step of the workflow, when every remaining task of a phase is in-review, or when the user wants a phase's work checked before it is merged.
4
4
  registryDependencies: [task-manager, writing-great-tasks, vocabulary, decisions, run]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: run
3
- description: Work one phase of a plan through its four steps, plan, execute, review and sync, giving each step to a fresh agent and letting a hook decide when the step is finished. Use when the user wants a phase worked or names a phase to run, or when the orchestrator skill hands over a phase.
3
+ description: Use when the user wants a phase worked or names a phase to run, or when the orchestrator skill hands over a phase.
4
4
  registryDependencies: [task-manager, plan, execute, review, sync, orchestrator]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sync
3
- description: Land a reviewed phase by merging its task branches, removing their worktrees, recording its decisions and terms, and closing the phase. Use as the sync step of the workflow, or when every task of a phase is completed and the phase is still open.
3
+ description: Use as the sync step of the workflow, or when every task of a phase is completed and the phase is still open.
4
4
  registryDependencies: [task-manager, worktree, vocabulary, decisions, run, orchestrator]
5
5
  disable-model-invocation: false
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: worktree
3
- description: Set up, merge and remove the git worktrees that tasks are worked in, one per task on its own branch. Use when a task is about to be started, when a finished phase is being merged, when worktrees are left over, or when another skill needs a worktree.
3
+ description: Use when a task is about to be started, when a finished phase is being merged, when worktrees are left over, or when another skill needs a worktree.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: writing-great-skills
3
3
  description: Use when writing or editing a skill, its SKILL.md, references or hooks.
4
- registryDependencies: [emphasise, writing-great-hooks]
4
+ registryDependencies: [emphasise, plain-language, writing-great-hooks]
5
5
  disable-model-invocation: false
6
6
  ---
7
7
 
@@ -9,12 +9,14 @@ disable-model-invocation: false
9
9
 
10
10
  - `name`: the skill's folder name.
11
11
  - `description`: decides whether the agent loads the skill. Say when to use it: the tasks and words that should trigger it, and nothing else.
12
+ - `disable-model-invocation`: always set. `false`: the agent may load the skill, so `description` says only when to use it. `true`: only the user runs it, so `description` may describe it.
12
13
 
13
14
  ## SKILL.md
14
15
 
15
16
  - Direct instructions, in order. Each step says when it is done.
16
17
  - Only what every use of the skill needs.
17
18
  - Cut every line the agent would follow without being told.
19
+ - Write in plain language with the `/plain-language` skill.
18
20
  - Mark bold, italics and backticks with the `/emphasise` skill.
19
21
 
20
22
  ## references/