@nt-ai-lab/opencode-skillz 0.3.15 → 0.4.1
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/AGENTS.md +31 -0
- package/agents/default.md +4 -0
- package/agents/tdd.md +1 -0
- package/commands/plan.md +135 -45
- package/commands/resolve-pr-feedback.md +138 -0
- package/dist/commands/dont-stop/hooks.js +4 -10
- package/dist/git-workflow-gates.d.ts +21 -0
- package/dist/git-workflow-gates.js +103 -0
- package/dist/plugin-registry/agents.js +0 -4
- package/dist/plugin-registry/commands.js +0 -2
- package/dist/plugin-registry/index.js +29 -2
- package/dist/tools/create-pr-tool.d.ts +4 -0
- package/dist/tools/create-pr-tool.js +22 -0
- package/dist/tools/infra/lint/guidance.d.ts +8 -0
- package/dist/tools/infra/lint/guidance.js +98 -0
- package/dist/tools/{lint-review.d.ts → infra/lint/review.d.ts} +1 -1
- package/dist/tools/{lint-review.js → infra/lint/review.js} +1 -1
- package/dist/tools/infra/pull-request/create-draft-pull-request.d.ts +11 -0
- package/dist/tools/infra/pull-request/create-draft-pull-request.js +115 -0
- package/dist/tools/infra/pull-request/feedback.d.ts +14 -0
- package/dist/tools/infra/pull-request/feedback.js +280 -0
- package/dist/tools/infra/vitest-coverage/command.d.ts +16 -0
- package/dist/tools/infra/vitest-coverage/command.js +85 -0
- package/dist/tools/{vitest-coverage.d.ts → infra/vitest-coverage/review.d.ts} +1 -18
- package/dist/tools/{vitest-coverage.js → infra/vitest-coverage/review.js} +15 -52
- package/dist/tools/infra/vitest-coverage/test-support.d.ts +15 -0
- package/dist/tools/infra/vitest-coverage/test-support.js +156 -0
- package/dist/tools/lint.d.ts +3 -17
- package/dist/tools/lint.js +29 -18
- package/dist/tools/pull-request-feedback-tool.d.ts +5 -0
- package/dist/tools/pull-request-feedback-tool.js +23 -0
- package/dist/tools/vitest-coverage-tool.d.ts +4 -0
- package/dist/tools/vitest-coverage-tool.js +31 -0
- package/dist/types.d.ts +8 -0
- package/package.json +4 -3
- package/scripts/check-tools-folder-boundary.mjs +78 -0
- package/scripts/install-git-hooks.mjs +42 -4
- package/scripts/lint-ts.mjs +32 -7
- package/scripts/living-architecture-eslint.config.mjs +2 -2
- package/scripts/no-generic-names-eslint-rule.mjs +2 -24
- package/scripts/no-generic-names-eslint-rule.mjs.d.ts +28 -0
- /package/dist/tools/{pull-request-files.d.ts → infra/source-control/changed-files.d.ts} +0 -0
- /package/dist/tools/{pull-request-files.js → infra/source-control/changed-files.js} +0 -0
package/AGENTS.md
CHANGED
|
@@ -7,6 +7,10 @@ Purpose: package OpenCode workflow assets as a plugin.
|
|
|
7
7
|
- `index.js`: plugin entrypoint; auto-registers bundled commands and agents.
|
|
8
8
|
- `commands/*.md`: command definitions (frontmatter + template body).
|
|
9
9
|
- `agents/*.md`: custom agent definitions (frontmatter + full prompt body).
|
|
10
|
+
- `src/tools/lint.ts`: OpenCode lint tool entrypoint.
|
|
11
|
+
- `src/tools/*-tool.ts`: other OpenCode tool entrypoints.
|
|
12
|
+
- `src/tools/infra/<concept>/`: support code used by tools but not itself an OpenCode tool.
|
|
13
|
+
- `src/tools/infra/lint/guidance.ts`: lint failure remediation guidance injected by the lint tool.
|
|
10
14
|
|
|
11
15
|
## Conventions
|
|
12
16
|
|
|
@@ -19,6 +23,33 @@ Purpose: package OpenCode workflow assets as a plugin.
|
|
|
19
23
|
- Prefer minimal additions; only add new commands when needed.
|
|
20
24
|
- Do not add `agent:` in command frontmatter unless the command must force a specific agent.
|
|
21
25
|
|
|
26
|
+
## Lint guidance mechanism
|
|
27
|
+
|
|
28
|
+
- `src/tools/lint.ts` prepends lint failures with remediation guidance.
|
|
29
|
+
- `src/tools/infra/lint/guidance.ts` owns the generic message and rule-specific guidance.
|
|
30
|
+
- Update `src/tools/infra/lint/guidance.ts` when adding portable lint rules that represent design quality, test quality, type-safety, or security constraints.
|
|
31
|
+
- Guidance must direct agents to fix the underlying problem, not suppress rules, delete coverage, or weaken assertions.
|
|
32
|
+
|
|
33
|
+
## Lint boundaries: portable tool rules vs repo-only checks
|
|
34
|
+
|
|
35
|
+
- `scripts/living-architecture-eslint.config.mjs` is bundled into the lint tool and applies to every codebase that uses `nt_skillz_lint`.
|
|
36
|
+
- Do not add opencode-skillz repository organization rules to `scripts/living-architecture-eslint.config.mjs`.
|
|
37
|
+
- Only add rules to `scripts/living-architecture-eslint.config.mjs` when the rule is intentionally portable across all target repositories.
|
|
38
|
+
- Repository-only checks belong in this repo's local validation flow, such as a dedicated script wired into this repo's `package.json` scripts.
|
|
39
|
+
- `scripts/check-tools-folder-boundary.mjs` is a repo-only check. It enforces that top-level `src/tools/*.ts` files are real OpenCode tool entrypoints and support code lives under `src/tools/infra/<concept>/`.
|
|
40
|
+
- Before adding any lint rule, decide whether it is portable product behavior or local repository hygiene. Mixing those two scopes is a release-impacting mistake.
|
|
41
|
+
|
|
42
|
+
## Command writing rules
|
|
43
|
+
|
|
44
|
+
### Be eplicit, avoid vagueness
|
|
45
|
+
|
|
46
|
+
Commands are run by agents that may choose different valid-looking tool calls unless the command removes that choice. A command must include the exact operation, command text, query text, arguments, and expected fields when those details are known. This prevents each run from rediscovering APIs, using different command variants, or failing because the agent guessed a tool shape.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
- Bad: Fetch unresolved GitHub review threads.
|
|
50
|
+
- Good: Run `gh pr view "$PR_NUMBER" --json reviewThreads` and read unresolved threads from the returned `reviewThreads` field.
|
|
51
|
+
- Good: If GraphQL is required, include the full `gh api graphql ...` command, the full query, variables, and response path.
|
|
52
|
+
|
|
22
53
|
## Versioning strategy
|
|
23
54
|
|
|
24
55
|
- Use SemVer in `package.json`.
|
package/agents/default.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Minimal default robot agent
|
|
3
3
|
mode: primary
|
|
4
|
+
temperature: 2
|
|
4
5
|
preload_commands: software-design, writing-tests
|
|
5
6
|
---
|
|
6
7
|
|
|
@@ -15,6 +16,7 @@ You are a tool that takes user commands and produces responses.
|
|
|
15
16
|
- Never flatter, or mirror user emotion.
|
|
16
17
|
- Use neutral phrasing such as "That is correct" or "That may not be correct" when confirming or challenging.
|
|
17
18
|
- Keep responses concise, factual, and operational but use simple and clear terminology rather than advanced vocabulary and complex jargon.
|
|
19
|
+
- Use plain human language. Avoid stacked phrases such as "current PR branch lint config" or "invalid PR description lint output". Name the exact thing instead, such as the file, command, tool result, or PR description section.
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
# Behavior rules (non-negotiable, 100% adherence mandatory)
|
|
@@ -27,4 +29,6 @@ You are a tool that takes user commands and produces responses.
|
|
|
27
29
|
|
|
28
30
|
- NEVER bypass rules or do workarounds to make a problem go away. If there are lint violations in code, fix the violations don't disable linting. If test coverage thresholds are not met, add tests, don't ignore code from coverage.
|
|
29
31
|
|
|
32
|
+
- Do not sacrifice code quality or test coverage to satisfy lint rules. These rules are not objectives; they are signs that the code needs to be split, simplified, or clarified.
|
|
33
|
+
|
|
30
34
|
- Tight feedback loops: validate your work regularly. Don't modify 50 files and then run lint and find 100 errors. Run lint after each file edit. Make small regular commits.
|
package/agents/tdd.md
CHANGED
package/commands/plan.md
CHANGED
|
@@ -2,81 +2,171 @@
|
|
|
2
2
|
description: Create an implementation plan.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
Plan:
|
|
5
|
+
Plan request:
|
|
6
6
|
$ARGUMENTS
|
|
7
7
|
|
|
8
|
-
Create
|
|
8
|
+
Create an implementation plan for the request. The plan must sequence value slices, not implementation layers.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Clarification gate
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Before creating the final plan, resolve every question that affects the plan.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
First try to answer questions through available evidence:
|
|
15
|
+
- existing code
|
|
16
|
+
- source files and tests
|
|
17
|
+
- repository documentation
|
|
18
|
+
- issues and pull requests
|
|
19
|
+
- Notion pages
|
|
20
|
+
- linked design documents
|
|
21
|
+
- external API documentation
|
|
22
|
+
- related repositories mentioned by code or documentation
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
Do not ask the user questions already answered by evidence. Use the evidence and cite it in the plan.
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
Ask the user only when evidence cannot answer a plan-affecting question, or when the answer is a product or business decision.
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
Check for missing information that would change:
|
|
29
|
+
- the problem being solved
|
|
30
|
+
- user value
|
|
31
|
+
- scope
|
|
32
|
+
- explicit out-of-scope behavior
|
|
33
|
+
- constraints
|
|
34
|
+
- related code, docs, issues, Notion pages, or repositories
|
|
35
|
+
- dependencies or external systems
|
|
36
|
+
- acceptance and validation outcomes
|
|
37
|
+
- risks or edge cases
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
If any plan-affecting question remains after research, do not create the final plan. Respond only with this format:
|
|
23
40
|
|
|
24
|
-
|
|
41
|
+
```md
|
|
42
|
+
## Clarification needed
|
|
25
43
|
|
|
26
|
-
|
|
44
|
+
<brief explanation of what could not be resolved through research>
|
|
27
45
|
|
|
28
|
-
|
|
46
|
+
1. <question>
|
|
47
|
+
Reason: <why this affects the plan>
|
|
48
|
+
Why evidence could not answer it: <what was checked and why it was insufficient>
|
|
49
|
+
Recommended: <optional recommendation with evidence, if available>
|
|
50
|
+
```
|
|
29
51
|
|
|
30
|
-
|
|
31
|
-
- get the staged changed `.ts` and `.tsx` files
|
|
32
|
-
- run `nt_skillz_lint` on the changed files
|
|
33
|
-
- do not create the commit unless the lint check passes
|
|
52
|
+
Ask at most 5 clarification questions in one response. Ask only questions that materially affect the plan. If more than 5 plan-affecting questions exist, ask the 5 highest-impact questions first.
|
|
34
53
|
|
|
35
|
-
|
|
54
|
+
A recommended answer is not an assumption. Do not proceed with the final plan using a recommended answer until the user confirms it or provides a different answer.
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
- get the staged changed `.ts` and `.tsx` files
|
|
39
|
-
- ignore tests, declaration files, config files, and fixtures
|
|
40
|
-
- run `/nt-skillz:vitest-coverage <file>` for each remaining file
|
|
41
|
-
- do not create the commit unless every remaining file passes with 100% Vitest coverage
|
|
56
|
+
The final plan must not contain open questions.
|
|
42
57
|
|
|
43
|
-
##
|
|
58
|
+
## Research expectations
|
|
59
|
+
|
|
60
|
+
Search all relevant existing code and documentation before finalizing the plan. If code or documentation references another repository that may affect the plan, inspect that repository before finalizing the plan.
|
|
61
|
+
|
|
62
|
+
If a required repository, document, issue, Notion page, or other evidence source is inaccessible and that evidence affects the plan, stop and report the blocker using the clarification-needed format. Do not guess and do not create the final plan.
|
|
63
|
+
|
|
64
|
+
Do not search unrelated repositories.
|
|
65
|
+
|
|
66
|
+
Every factual claim about existing behavior, constraints, dependencies, or related systems must be backed by evidence from code, docs, issues, Notion pages, linked materials, or referenced repositories. If no evidence was found, do not state the claim as fact.
|
|
67
|
+
|
|
68
|
+
## Planning level
|
|
69
|
+
|
|
70
|
+
The proposed solution may discuss software and technical direction at a planning level. Include only solution design details that are strictly required to explain the plan or are directly supported by existing code and documentation.
|
|
71
|
+
|
|
72
|
+
Do not add new design elements just because they seem useful. Leave non-essential component structure, file layout, classes, functions, internal boundaries, and implementation mechanics to the software design and implementation steps.
|
|
73
|
+
|
|
74
|
+
Validation must describe observable outcomes and required quality gates. Do not prescribe exact test file names, test helper structure, mocks, fixtures, or implementation-level test mechanics unless they already exist and are directly relevant evidence.
|
|
75
|
+
|
|
76
|
+
## Value slices
|
|
77
|
+
|
|
78
|
+
A value slice is a user-observable capability or behavior.
|
|
79
|
+
|
|
80
|
+
Good value slice: "Fuzzy searching on first name".
|
|
81
|
+
Bad value slice: "Add data types".
|
|
82
|
+
|
|
83
|
+
For each slice, challenge whether it is necessary. The `justification` column must explain the evidence that the slice is necessary. If the evidence is weak or unclear, do not include the slice in the final plan; ask for clarification instead.
|
|
84
|
+
|
|
85
|
+
Tasks must be organized by value slice so each slice can be implemented and verified independently.
|
|
86
|
+
|
|
87
|
+
Map technical work to the value slice it supports. For example, models, services, interfaces, schemas, and tests belong inside the value-slice task they enable.
|
|
44
88
|
|
|
45
|
-
|
|
89
|
+
Do not create standalone top-level tasks for technical layers unless that task independently delivers user value.
|
|
46
90
|
|
|
47
|
-
|
|
91
|
+
Each value slice must include an independent verification outcome. The outcome must prove that slice works without relying on later slices. If a slice cannot be independently verified, merge it with the slice that makes it valuable or explain why it must remain separate.
|
|
92
|
+
|
|
93
|
+
## Output format
|
|
94
|
+
|
|
95
|
+
If the final output does not follow the required template exactly, the plan is invalid.
|
|
96
|
+
|
|
97
|
+
Do not add extra top-level sections. Do not rename headings. Do not omit required sections.
|
|
98
|
+
|
|
99
|
+
The final plan may use only these top-level headings:
|
|
100
|
+
- `## Context`
|
|
101
|
+
- `## Slices`
|
|
102
|
+
- `## Software design & architecture`
|
|
103
|
+
- `## Tasks`
|
|
104
|
+
|
|
105
|
+
Return exactly this Markdown structure:
|
|
48
106
|
|
|
49
107
|
```md
|
|
50
108
|
## Context
|
|
51
109
|
|
|
52
|
-
|
|
53
|
-
- Constraints:
|
|
54
|
-
- Related materials:
|
|
110
|
+
### Problem
|
|
55
111
|
|
|
56
|
-
|
|
112
|
+
<prose only; describe the problem being solved, not the solution>
|
|
113
|
+
|
|
114
|
+
### Evidence reviewed
|
|
115
|
+
|
|
116
|
+
<prose only; summarize the code, documentation, linked materials, and repositories that informed the plan. Include specific paths, document names, links, or repository names.>
|
|
117
|
+
|
|
118
|
+
### Constraints
|
|
57
119
|
|
|
120
|
+
<prose only; describe constraints, dependencies, existing materials, affected systems, and known limits>
|
|
58
121
|
|
|
122
|
+
### Proposed solution
|
|
123
|
+
|
|
124
|
+
<prose only; describe the intended technical direction at a planning level. Include only solution design details that are strictly required to explain the plan or are directly supported by existing code and documentation.>
|
|
125
|
+
|
|
126
|
+
## Slices
|
|
127
|
+
|
|
128
|
+
| slice | description | justification |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| <value slice> | <user-observable capability or behavior> | <evidence that this slice is necessary> |
|
|
131
|
+
|
|
132
|
+
## Software design & architecture
|
|
133
|
+
|
|
134
|
+
<software design and architecture>
|
|
59
135
|
|
|
60
136
|
## Tasks
|
|
61
137
|
|
|
62
|
-
- [ ] Task
|
|
63
|
-
- [ ]
|
|
64
|
-
- [ ]
|
|
65
|
-
- [ ]
|
|
66
|
-
- [ ] Verify
|
|
67
|
-
- [ ]
|
|
68
|
-
- [ ]
|
|
69
|
-
- [ ]
|
|
70
|
-
- [ ] Subtask 2.2
|
|
71
|
-
- [ ] Run `nt_skillz_lint` on changed `.ts` and `.tsx` files
|
|
72
|
-
- [ ] Verify 100% test coverage using `/nt-skillz:vitest-coverage <file>`
|
|
73
|
-
- [ ] Commit the changes
|
|
138
|
+
- [ ] Task: <value slice>
|
|
139
|
+
- [ ] Read the software design and architecture section before implementation.
|
|
140
|
+
- [ ] Implement the value slice according to the software design and architecture.
|
|
141
|
+
- [ ] Verify outcome: <observable behavior that proves the slice works independently>.
|
|
142
|
+
- [ ] Verify the implementation aligns with the software design and architecture.
|
|
143
|
+
- [ ] Run `nt_skillz_lint` on changed `.ts` and `.tsx` files.
|
|
144
|
+
- [ ] Verify 100% test coverage using `/nt-skillz:vitest-coverage <file>`.
|
|
145
|
+
- [ ] Commit the changes.
|
|
74
146
|
```
|
|
75
147
|
|
|
76
|
-
|
|
148
|
+
Tasks must be planning-level only. They must describe the value slice, intended outcome, and required validation. They must not prescribe internal design, files, components, classes, functions, or modules unless those details already exist and are directly relevant evidence.
|
|
149
|
+
|
|
150
|
+
## Final self-check
|
|
151
|
+
|
|
152
|
+
Before returning the final plan, verify:
|
|
153
|
+
- The output uses exactly the required top-level headings.
|
|
154
|
+
- The output does not add extra top-level sections.
|
|
155
|
+
- The Problem section contains no solution language.
|
|
156
|
+
- The Context subsections are prose, not bullet lists.
|
|
157
|
+
- The Evidence reviewed section names specific sources.
|
|
158
|
+
- The Slices table exists and every slice includes evidence-backed justification.
|
|
159
|
+
- Every task maps to exactly one value slice.
|
|
160
|
+
- Every task stays at planning level and avoids premature design details.
|
|
161
|
+
- Every task includes an observable verification outcome.
|
|
162
|
+
- The software design and architecture section contains only `<software design and architecture>`.
|
|
163
|
+
- No assumptions are presented as facts.
|
|
164
|
+
- No open questions remain.
|
|
77
165
|
|
|
78
|
-
|
|
166
|
+
After producing the final plan, stop. Do not implement. Do not run the software design and architecture step. Wait for the user to invoke the next command.
|
|
79
167
|
|
|
80
|
-
|
|
168
|
+
## Important notes
|
|
81
169
|
|
|
82
|
-
-
|
|
170
|
+
- Stop if the plan cannot be completed without unanswered plan-affecting questions.
|
|
171
|
+
- If parts of a plan are incomplete, missing, or placeholders other than `<software design and architecture>` remain, refuse to implement and tell the user.
|
|
172
|
+
- During implementation, ensure each subtask is marked off when complete.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Resolve approved GitHub pull request feedback with local evidence, validation, replies, and thread resolution
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Resolve GitHub pull request feedback for:
|
|
6
|
+
$ARGUMENTS
|
|
7
|
+
|
|
8
|
+
## Required PR input
|
|
9
|
+
|
|
10
|
+
1. Extract both values from `$ARGUMENTS`:
|
|
11
|
+
- pull request number
|
|
12
|
+
- full pull request URL
|
|
13
|
+
2. If either value is missing, stop and ask for the missing value.
|
|
14
|
+
3. Do not fetch repository metadata with `gh repo view`; use the local worktree and the provided pull request values.
|
|
15
|
+
|
|
16
|
+
## Feedback discovery
|
|
17
|
+
|
|
18
|
+
1. Call the `nt_skillz_pr_feedback` tool with exactly these arguments:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"pullRequestNumber": "<pull request number>",
|
|
23
|
+
"pullRequestUrl": "<full pull request URL>"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Use the tool output as the feedback source of truth.
|
|
28
|
+
3. Do not replace the tool with ad hoc `gh pr view`, `gh pr diff`, `gh api`, or local-only review discovery commands.
|
|
29
|
+
|
|
30
|
+
## Approval plan format
|
|
31
|
+
|
|
32
|
+
Before editing files, present every unresolved thread from the tool output using exactly this format:
|
|
33
|
+
|
|
34
|
+
```md
|
|
35
|
+
## Thread <thread_id>
|
|
36
|
+
|
|
37
|
+
### Reviewer feedback
|
|
38
|
+
<copy the full Reviewer feedback section from the tool output>
|
|
39
|
+
|
|
40
|
+
### Review context
|
|
41
|
+
<copy the full Review context section from the tool output>
|
|
42
|
+
|
|
43
|
+
### Current local code
|
|
44
|
+
<copy the full Current local code section from the tool output>
|
|
45
|
+
|
|
46
|
+
### Problem analysis
|
|
47
|
+
- Problem: <specific defect, missing test, design issue, or documentation mismatch visible in the feedback, diff hunk, and local code>
|
|
48
|
+
- Evidence: <specific evidence from the reviewer comment, diff hunk, and current local code>
|
|
49
|
+
- Scope: <exact files and behavior affected>
|
|
50
|
+
|
|
51
|
+
### Proposed change
|
|
52
|
+
- Edit `<path>`: <exact code, test, or documentation behavior change>
|
|
53
|
+
- Leave unchanged: <exact files or behavior that will not be touched>
|
|
54
|
+
|
|
55
|
+
### Validation
|
|
56
|
+
- During edits: call `nt_skillz_lint` after each small TypeScript change with the changed `.ts` or `.tsx` file paths only.
|
|
57
|
+
- Before commit: run `git diff --name-only --diff-filter=ACMR HEAD -- '*.ts' '*.tsx'`, then call `nt_skillz_lint` with every returned file path.
|
|
58
|
+
- Before commit coverage: run `git diff --name-only --diff-filter=ACMR HEAD -- '*.ts' '*.tsx'`, then run `/nt-skillz:vitest-coverage <file>` for each returned path and require 100% coverage unless the run prints `SKIP:`.
|
|
59
|
+
- Additional command: `<exact project command required to verify this feedback, or "none">`
|
|
60
|
+
|
|
61
|
+
### GitHub reply to post after validation passes
|
|
62
|
+
`[Resolve] <exact response>`
|
|
63
|
+
|
|
64
|
+
### Resolution action
|
|
65
|
+
Resolve thread after successful reply: yes
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Approval stop
|
|
69
|
+
|
|
70
|
+
1. Stop after presenting the approval plan.
|
|
71
|
+
2. Ask which thread fixes are approved.
|
|
72
|
+
3. Do not edit files until the user approves specific thread IDs.
|
|
73
|
+
4. If the user approves only some threads, edit only the approved thread fixes.
|
|
74
|
+
5. If a thread cannot be mapped to a concrete file and behavior change, ask for clarification instead of editing.
|
|
75
|
+
|
|
76
|
+
## Implementation standards
|
|
77
|
+
|
|
78
|
+
1. Implement approved fixes only.
|
|
79
|
+
2. Do not make unrelated refactors, formatting changes, dependency changes, or drive-by cleanups.
|
|
80
|
+
3. After each small TypeScript code change, call `nt_skillz_lint` with only the `files` argument for the changed `.ts` or `.tsx` file or files from that step.
|
|
81
|
+
4. Do not pass `base` or `head` to `nt_skillz_lint` during normal implementation work.
|
|
82
|
+
5. Fix all lint errors on new code before continuing.
|
|
83
|
+
6. If lint fails on existing code, ignore only errors that are unrelated and not near the changed code.
|
|
84
|
+
7. Line-length limits do not count as existing code; fix line-length errors caused by new code.
|
|
85
|
+
|
|
86
|
+
## Required validation before any commit
|
|
87
|
+
|
|
88
|
+
Before committing feedback fixes:
|
|
89
|
+
|
|
90
|
+
1. Run `git diff --name-only --diff-filter=ACMR HEAD -- '*.ts' '*.tsx'`.
|
|
91
|
+
2. Call `nt_skillz_lint` with `files` set to every path returned by step 1.
|
|
92
|
+
3. Run `/nt-skillz:vitest-coverage <file>` for each path returned by step 1.
|
|
93
|
+
4. Ignore only coverage runs that print a `SKIP:` line.
|
|
94
|
+
5. Do not commit unless every remaining file has 100% Vitest coverage or the user explicitly confirms that 100% coverage is impossible for the relevant component.
|
|
95
|
+
6. Run each approved thread's `Additional command` when the value is not `none`.
|
|
96
|
+
7. Do not commit if any required validation fails.
|
|
97
|
+
|
|
98
|
+
## Commit rule
|
|
99
|
+
|
|
100
|
+
1. Do not commit unless the user explicitly requests a commit.
|
|
101
|
+
2. If the user requests a commit, run the required validation before the commit.
|
|
102
|
+
3. Commit only files changed for approved thread fixes.
|
|
103
|
+
|
|
104
|
+
## GitHub reply and thread resolution
|
|
105
|
+
|
|
106
|
+
After validation passes for an approved thread:
|
|
107
|
+
|
|
108
|
+
1. Reply to the thread with this exact GraphQL mutation:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
gh api graphql \
|
|
112
|
+
-f threadId='<thread_id>' \
|
|
113
|
+
-f body='[Resolve] <approved response body>' \
|
|
114
|
+
-f query='mutation($threadId: ID!, $body: String!) { addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) { comment { url } } }'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
2. Resolve the thread with this exact GraphQL mutation:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
gh api graphql \
|
|
121
|
+
-f threadId='<thread_id>' \
|
|
122
|
+
-f query='mutation($threadId: ID!) { resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } } }'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
3. Do not resolve a thread before the reply mutation succeeds.
|
|
126
|
+
4. Do not resolve a thread when validation failed.
|
|
127
|
+
5. Do not resolve a thread that was not approved by the user.
|
|
128
|
+
|
|
129
|
+
## Final report
|
|
130
|
+
|
|
131
|
+
Report only:
|
|
132
|
+
|
|
133
|
+
- approved thread IDs fixed
|
|
134
|
+
- unapproved thread IDs left untouched
|
|
135
|
+
- validation commands run and pass/fail status
|
|
136
|
+
- GitHub reply URLs returned by the reply mutation
|
|
137
|
+
- resolved thread IDs returned by the resolution mutation
|
|
138
|
+
- blockers that require user action
|
|
@@ -81,12 +81,9 @@ function parseAssistantState(value) {
|
|
|
81
81
|
return "continue";
|
|
82
82
|
}
|
|
83
83
|
async function showToast(client, body) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
84
|
+
const toastWasShown = await client.tui.showToast({ body }).then(() => true, () => false);
|
|
85
|
+
if (!toastWasShown)
|
|
88
86
|
return;
|
|
89
|
-
}
|
|
90
87
|
}
|
|
91
88
|
function isMessagePart(value) {
|
|
92
89
|
return isRecord(value);
|
|
@@ -134,10 +131,7 @@ function buildContinuationPrompt(criteria, status) {
|
|
|
134
131
|
function buildReviewRequestKey(status) {
|
|
135
132
|
return `${status.state}:${status.reason}`;
|
|
136
133
|
}
|
|
137
|
-
async function notifyReviewRequested(client,
|
|
138
|
-
const session = state.get(sessionID);
|
|
139
|
-
if (!session)
|
|
140
|
-
return;
|
|
134
|
+
async function notifyReviewRequested(client, session, status) {
|
|
141
135
|
const reviewKey = buildReviewRequestKey(status);
|
|
142
136
|
if (session.pendingReviewKey === reviewKey)
|
|
143
137
|
return;
|
|
@@ -221,7 +215,7 @@ async function handleEvent(client, state, event) {
|
|
|
221
215
|
try {
|
|
222
216
|
const latestStatus = await getLatestAssistantStatus(client, sessionID);
|
|
223
217
|
if (latestStatus?.state === "completion-requested" || latestStatus?.state === "blocked-requested") {
|
|
224
|
-
await notifyReviewRequested(client,
|
|
218
|
+
await notifyReviewRequested(client, session, latestStatus);
|
|
225
219
|
return;
|
|
226
220
|
}
|
|
227
221
|
session.pendingReviewKey = undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface CommandRunResult {
|
|
2
|
+
status: number | null;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
errorMessage?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CommandRunner {
|
|
8
|
+
run(executable: string, commandArguments: string[], workingDirectory: string): CommandRunResult;
|
|
9
|
+
}
|
|
10
|
+
export interface ToolExecutionInput {
|
|
11
|
+
tool: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ToolExecutionOutput {
|
|
14
|
+
args: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface GitWorkflowGate {
|
|
17
|
+
beforeToolExecution(input: ToolExecutionInput, output: ToolExecutionOutput): void;
|
|
18
|
+
recordLintedFiles(filePaths: string[]): void;
|
|
19
|
+
}
|
|
20
|
+
export declare const directPullRequestCreationBlockedMessage: string;
|
|
21
|
+
export declare function createGitWorkflowGate(repositoryRoot: string, commandRunner: CommandRunner): GitWorkflowGate;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export const directPullRequestCreationBlockedMessage = [
|
|
2
|
+
"Direct gh pr create is banned for this workspace.",
|
|
3
|
+
"Use nt_skillz_create_pr instead.",
|
|
4
|
+
].join("\n");
|
|
5
|
+
class GitWorkflowGateError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function readBashCommandText(value) {
|
|
11
|
+
if (typeof value !== "string") {
|
|
12
|
+
throw new GitWorkflowGateError(`Expected bash command text. Got ${String(value)}.`);
|
|
13
|
+
}
|
|
14
|
+
return value.trim();
|
|
15
|
+
}
|
|
16
|
+
function readCommandFailureMessage(commandResult) {
|
|
17
|
+
const stderr = commandResult.stderr.trim();
|
|
18
|
+
if (stderr) {
|
|
19
|
+
return stderr;
|
|
20
|
+
}
|
|
21
|
+
const stdout = commandResult.stdout.trim();
|
|
22
|
+
if (stdout) {
|
|
23
|
+
return stdout;
|
|
24
|
+
}
|
|
25
|
+
return `exit status ${commandResult.status}`;
|
|
26
|
+
}
|
|
27
|
+
function isDirectPullRequestCreation(commandText) {
|
|
28
|
+
return /(?:^|[;&|()]|\s)gh\s+pr\s+create(?:\s|$|[;&|()])/.test(commandText);
|
|
29
|
+
}
|
|
30
|
+
function isGitCommit(commandText) {
|
|
31
|
+
return /(?:^|[;&|()]|\s)git\s+commit(?:\s|$|[;&|()])/.test(commandText);
|
|
32
|
+
}
|
|
33
|
+
function normalizeCommandOutput(commandResult, description) {
|
|
34
|
+
if (commandResult.errorMessage) {
|
|
35
|
+
throw new GitWorkflowGateError(`Expected ${description} to run. Got ${commandResult.errorMessage}.`);
|
|
36
|
+
}
|
|
37
|
+
if (commandResult.status !== 0) {
|
|
38
|
+
throw new GitWorkflowGateError(`Expected ${description} to succeed. Got ${readCommandFailureMessage(commandResult)}.`);
|
|
39
|
+
}
|
|
40
|
+
return commandResult.stdout.trim();
|
|
41
|
+
}
|
|
42
|
+
function isTypeScriptFilePath(filePath) {
|
|
43
|
+
return filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
44
|
+
}
|
|
45
|
+
function createUnlintedCommitMessage(filePaths) {
|
|
46
|
+
return [
|
|
47
|
+
"Commit blocked: TypeScript files changed without nt_skillz_lint validation.",
|
|
48
|
+
`Run nt_skillz_lint with files: ${JSON.stringify(filePaths)}`,
|
|
49
|
+
"Then retry the commit.",
|
|
50
|
+
].join("\n");
|
|
51
|
+
}
|
|
52
|
+
export function createGitWorkflowGate(repositoryRoot, commandRunner) {
|
|
53
|
+
const lintedFingerprints = new Map();
|
|
54
|
+
function runGit(commandArguments, description) {
|
|
55
|
+
return normalizeCommandOutput(commandRunner.run("git", commandArguments, repositoryRoot), description);
|
|
56
|
+
}
|
|
57
|
+
function readWorkingTreeFingerprint(filePath) {
|
|
58
|
+
return runGit(["hash-object", "--", filePath], `working tree hash for ${filePath}`);
|
|
59
|
+
}
|
|
60
|
+
function readStagedFingerprint(filePath) {
|
|
61
|
+
return runGit(["rev-parse", `:${filePath}`], `staged hash for ${filePath}`);
|
|
62
|
+
}
|
|
63
|
+
function readStagedTypeScriptFilePaths() {
|
|
64
|
+
const output = runGit([
|
|
65
|
+
"diff",
|
|
66
|
+
"--name-only",
|
|
67
|
+
"--cached",
|
|
68
|
+
"--diff-filter=ACMR",
|
|
69
|
+
"--",
|
|
70
|
+
"*.ts",
|
|
71
|
+
"*.tsx",
|
|
72
|
+
], "staged TypeScript file discovery");
|
|
73
|
+
return output.split("\n").map((filePath) => filePath.trim()).filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
function ensureStagedTypeScriptFilesAreLinted() {
|
|
76
|
+
const stagedFilePaths = readStagedTypeScriptFilePaths();
|
|
77
|
+
const unlintedFilePaths = stagedFilePaths.filter((filePath) => lintedFingerprints.get(filePath) !== readStagedFingerprint(filePath));
|
|
78
|
+
if (unlintedFilePaths.length === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
throw new GitWorkflowGateError(createUnlintedCommitMessage(unlintedFilePaths));
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
beforeToolExecution(input, output) {
|
|
85
|
+
if (input.tool !== "bash") {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const commandText = readBashCommandText(output.args.command);
|
|
89
|
+
if (isDirectPullRequestCreation(commandText)) {
|
|
90
|
+
throw new GitWorkflowGateError(directPullRequestCreationBlockedMessage);
|
|
91
|
+
}
|
|
92
|
+
if (isGitCommit(commandText)) {
|
|
93
|
+
ensureStagedTypeScriptFilesAreLinted();
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
recordLintedFiles(filePaths) {
|
|
97
|
+
const typeScriptFilePaths = filePaths.filter(isTypeScriptFilePath);
|
|
98
|
+
for (const filePath of typeScriptFilePaths) {
|
|
99
|
+
lintedFingerprints.set(filePath, readWorkingTreeFingerprint(filePath));
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -15,8 +15,6 @@ function getParentAgentName(rawAgent) {
|
|
|
15
15
|
}
|
|
16
16
|
function appendAgentPromptParts(promptParts, rawAgents, name) {
|
|
17
17
|
const rawAgent = rawAgents[name];
|
|
18
|
-
if (!rawAgent)
|
|
19
|
-
return;
|
|
20
18
|
const parentAgentName = getParentAgentName(rawAgent);
|
|
21
19
|
if (parentAgentName && rawAgents[parentAgentName]?.body) {
|
|
22
20
|
promptParts.push(rawAgents[parentAgentName].body);
|
|
@@ -47,8 +45,6 @@ export function registerAgents(agentConfig, pluginRoot, commands) {
|
|
|
47
45
|
const rawAgents = readMarkdownEntries(pluginRoot, "agents");
|
|
48
46
|
function collectPreloadedCommands(agentName, stack = new Set()) {
|
|
49
47
|
const rawAgent = rawAgents[agentName];
|
|
50
|
-
if (!rawAgent)
|
|
51
|
-
return [];
|
|
52
48
|
if (stack.has(agentName))
|
|
53
49
|
return parseCsvList(rawAgent.meta.preload_commands);
|
|
54
50
|
stack.add(agentName);
|
|
@@ -19,8 +19,6 @@ function loadMarkdownCommands(pluginRoot) {
|
|
|
19
19
|
const commands = {};
|
|
20
20
|
function buildComposedTemplate(name, stack = new Set()) {
|
|
21
21
|
const rawCommand = rawCommands[name];
|
|
22
|
-
if (!rawCommand)
|
|
23
|
-
return "";
|
|
24
22
|
if (stack.has(name))
|
|
25
23
|
return rawCommand.body;
|
|
26
24
|
stack.add(name);
|