@nt-ai-lab/opencode-skillz 0.3.7 → 0.3.9
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/commands/component-design.md +202 -0
- package/commands/implement.md +9 -0
- package/commands/plan.md +82 -0
- package/commands/review-pr.md +42 -0
- package/commands/vitest-coverage.md +35 -0
- package/dist/tools/lint.js +5 -1
- package/package.json +1 -1
- package/scripts/living-architecture-eslint.config.mjs +0 -31
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Component Design Prompt
|
|
2
|
+
|
|
3
|
+
Work with the user to design the software components for a new feature. Help the user to understand how the new code will look to avoid nasty surprises when reviewing the PR that require a lot of wasted time fixing problems with the design.
|
|
4
|
+
|
|
5
|
+
Focus on presenting diverse options. The user will choose their preferred option. Don't hesitate to iterate and ask questions rather than making assumptions.
|
|
6
|
+
|
|
7
|
+
Search all relevant existing code. If some of the code is in other repos, look there as well. Don't be lazy, more too much research is better than not enough. If you're unsure ask the user. As a general rule, if the supporting documenting or existing code references another repository, you should almost certainly be looking there. You can't design new code without understanding how it fits into the bigger picture.
|
|
8
|
+
|
|
9
|
+
## Pre-flight checklist
|
|
10
|
+
|
|
11
|
+
1. If the requirements are missing or unclear and you need to make assumptions, stop and discuss the options with the user.
|
|
12
|
+
2. If is is not clear where the new code should be placed, or there are multiple viable options, stop and discuss the options with the user.
|
|
13
|
+
3. If you have even 1% doubt on anything required to produce the design, stop and discuss the options with the user.
|
|
14
|
+
|
|
15
|
+
## Task
|
|
16
|
+
|
|
17
|
+
Generate 3 or more component design options.
|
|
18
|
+
|
|
19
|
+
Options must be as unique as possible.
|
|
20
|
+
|
|
21
|
+
Example criteria for identifying unique options:
|
|
22
|
+
|
|
23
|
+
1. number of components => all of the code in 1 monolithic script vs breakign each fine-grained responsiblity into it's own component
|
|
24
|
+
2. size of components
|
|
25
|
+
3. touching existing code vs adding new code
|
|
26
|
+
4. introducing dependencies
|
|
27
|
+
5. coupling vs cohesion
|
|
28
|
+
6. DDD vs non-DDD
|
|
29
|
+
|
|
30
|
+
If two options have the same components with the same responsibilities, they are not unique.
|
|
31
|
+
|
|
32
|
+
## Component Naming Guidelines
|
|
33
|
+
|
|
34
|
+
All components must have names that comply with the following guidelines.
|
|
35
|
+
|
|
36
|
+
### Intention Revealing
|
|
37
|
+
|
|
38
|
+
A name must describe as clearly and precisely as possible what the the thing is and does.
|
|
39
|
+
|
|
40
|
+
Good examples:
|
|
41
|
+
- `aysnc-file-reader`
|
|
42
|
+
- `date-selector`
|
|
43
|
+
- `tax-calculator`
|
|
44
|
+
|
|
45
|
+
Bad examples:
|
|
46
|
+
- `data-manager`: what kind of data? how does it manage the data? The name tells us almost nothing here.
|
|
47
|
+
- `orders-service`: it does something related to orders but we don't know what? Easy to dump multiple unrelated things into this (like domain logic and external service calls)
|
|
48
|
+
|
|
49
|
+
### Domain-driven
|
|
50
|
+
|
|
51
|
+
A name should use established domain terminology wherever possible and should not invent new words and phrases that do not exist in the domain
|
|
52
|
+
|
|
53
|
+
### Compound noun phrases
|
|
54
|
+
|
|
55
|
+
Use this pattern as the default: `[Domain Object][Business Action][Role Noun]`.
|
|
56
|
+
|
|
57
|
+
Example: `InvoicePaymentCollector`
|
|
58
|
+
|
|
59
|
+
- `Invoice` = domain object
|
|
60
|
+
- `Payment` = business object/action target
|
|
61
|
+
- `Collector` = role noun / responsibility noun
|
|
62
|
+
|
|
63
|
+
### Forbidden terms
|
|
64
|
+
|
|
65
|
+
The following should be avoided at all costs unless truly necessary and reflective of the business domain:
|
|
66
|
+
|
|
67
|
+
- `util`
|
|
68
|
+
- `helper`
|
|
69
|
+
- `manager`
|
|
70
|
+
- `service`
|
|
71
|
+
|
|
72
|
+
If you are about to use one of these words, first look for a more precise alternative. "what kind of util?", "what kind of service?"...
|
|
73
|
+
|
|
74
|
+
## Component Design Guidelines
|
|
75
|
+
|
|
76
|
+
The following guidelines should be applied when designing components.
|
|
77
|
+
|
|
78
|
+
### Layering
|
|
79
|
+
|
|
80
|
+
Components should be put into the correct layer based on the type of logic they contain.
|
|
81
|
+
|
|
82
|
+
- `domain`: Business rules and domain logic. This should be kept pure and isolated from technical concerns like database transactions. A domain expert should be able to read it and understand it
|
|
83
|
+
|
|
84
|
+
- `use-case`: The use-case layer is like a menu, it describes the operations the application suppors like `place-order`, `cancel-order` and so on. Each use case is responsible for orchestrating domain logic and technical concerns. The most common pattern is start transaction => load domain object => invoke domain object => save domain object => return results
|
|
85
|
+
|
|
86
|
+
- `infra`: Technical capabilities live in here like database transactions, persistence, external service clients and so on. Use dedicated sub-folders to properly organize like `/persistence`, `/external-service`
|
|
87
|
+
|
|
88
|
+
- `/infra/{gateway}`: This sub-layer handles receiving inputs from the outside world and returning responses to the outside world, like gateways sitting at the edge of the application. It's common to see http controllers and event handlers in this layer. Examples of `{gateway}` include `http`, `event-handlers`
|
|
89
|
+
|
|
90
|
+
Each codebase has it's own layering conventions that should be respected, but when no layering convention exists use the above as the default.
|
|
91
|
+
|
|
92
|
+
### Component Archetypes
|
|
93
|
+
|
|
94
|
+
The following are common component archetypes (non-exhaustive, do not be constrained by this list). If a comoponent matches the description of multiple of these, splitting into multiple component each aligned with a single archetype is an alternative option. However, when components are small are handle 1 thing well, it's ok to ecompass multiple archetypes.
|
|
95
|
+
|
|
96
|
+
1. **entrypoint**: the part of the application that talks to the outside world, like a http controller. A thin layer that parses inputs and hands off to a `use-case` and formats the response
|
|
97
|
+
2. **coordinator**: Responsible for orchesstrating multiplpe components that starts a transaction, loads a database object and invokes an operation on it. A `use-case`: is a good example of a coordinator
|
|
98
|
+
3. **stateless calculator**: takes an input, makes a decision, and returns an output.
|
|
99
|
+
4. **mapper**: mapps from one format to another
|
|
100
|
+
5. **aggregate**: manages the lifecycle of a domain concept protecting invariants. Will only operations to be performed if they permitted in the current state.
|
|
101
|
+
6. **value-object**: an abstraction on top a certain piece of data like a `date`. Provides an API with domain terminology for operating on the underlying value. The underlying value cannot be accessed directly.
|
|
102
|
+
7. **external service client**: provides methods that represent operations that can be called on a 3rd party service over the network
|
|
103
|
+
8. **validator**: accepts as input some data and indicates a result indicating whether the data is valid according to specify rules, like an email validator
|
|
104
|
+
9. **factory**: responsible for the construction of an object.
|
|
105
|
+
10. **repository**: persists and loads objects, typically to and from a database. In domain-driven design a repository is responsible for saving and loading entire aggregates only and should never be used for partial or ad-hoc loading.
|
|
106
|
+
11. **query service**: used to fetch information about an application, typically by querying a database. In domain-driven design, query services are used for read operations and repositories are used for write operations (although repositories can be used for read operations as well)
|
|
107
|
+
|
|
108
|
+
### General guidelines
|
|
109
|
+
|
|
110
|
+
1. **Maximum file size is 400 lines**, enforced by lint rules. A component must be decomposed into multiple smaller components when it reaches this limit
|
|
111
|
+
|
|
112
|
+
## Output Format
|
|
113
|
+
|
|
114
|
+
# Design Options: [Feature Name]
|
|
115
|
+
|
|
116
|
+
## Option 1: [Name]
|
|
117
|
+
|
|
118
|
+
Describe this option by outlining the philosophy behind it and it's key characteristics.
|
|
119
|
+
|
|
120
|
+
### Diagram
|
|
121
|
+
|
|
122
|
+
Rules:
|
|
123
|
+
- Mark every node `[existing]`, `[new]`, or `[changed]`.
|
|
124
|
+
- Show actual dependencies and calls, not a fake straight-line sequence.
|
|
125
|
+
- A line means the source component directly calls or depends on the target component.
|
|
126
|
+
- Do not connect two components if they do not directly call each other.
|
|
127
|
+
- Use branches when one component calls multiple dependencies.
|
|
128
|
+
- Label every line with the request, method call, response, event, or query.
|
|
129
|
+
- Keep it small.
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
Client
|
|
133
|
+
|
|
|
134
|
+
| request / methodCall()
|
|
135
|
+
v
|
|
136
|
+
[changed] ExistingEntryPoint
|
|
137
|
+
|
|
|
138
|
+
| methodCall()
|
|
139
|
+
v
|
|
140
|
+
[new] NewComponent
|
|
141
|
+
|\
|
|
142
|
+
| \ callDependencyA()
|
|
143
|
+
| v
|
|
144
|
+
| [existing] ExistingDependencyA
|
|
145
|
+
|
|
|
146
|
+
| callDependencyB()
|
|
147
|
+
v
|
|
148
|
+
[new] NewDependencyB
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Components
|
|
152
|
+
|
|
153
|
+
| Component | Status | Role Archetypes | Responsibilities | Estimated Size |
|
|
154
|
+
|---|---|---|---|---|
|
|
155
|
+
| `ComponentName` | New / Existing / Changed | `entrypoint`, `coordinator`, `custom:bulk-copy-script` | <ul><li>Responsibility one</li><li>Responsibility two</li></ul> | Small / Medium / Large, or estimated lines |
|
|
156
|
+
|
|
157
|
+
**note:**comopnent names must adhere to the component naming guidelines defined in this document
|
|
158
|
+
|
|
159
|
+
**note:** role archetypes must use names from the Component Archetypes section when applicable. If a component does not match one of the listed archetypes, use a custom archetype prefixed with `custom:`. Example: `custom:bulk-copy-script`.
|
|
160
|
+
|
|
161
|
+
### New Dependencies
|
|
162
|
+
|
|
163
|
+
| Dependency | Status | Used By | Purpose |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| `DependencyName` | New / Existing / Changed | `ComponentName` | One sentence |
|
|
166
|
+
|
|
167
|
+
### Code Shape
|
|
168
|
+
|
|
169
|
+
List the main new or changed files only.
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
src/
|
|
173
|
+
api/
|
|
174
|
+
ExistingEntryPoint.ts [changed]
|
|
175
|
+
feature/
|
|
176
|
+
NewComponent.ts [new]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Why This Option Is Unique
|
|
180
|
+
|
|
181
|
+
Explain the uniqueness using only these criteria:
|
|
182
|
+
|
|
183
|
+
- number of components
|
|
184
|
+
- size of components
|
|
185
|
+
- touching existing code vs adding new code
|
|
186
|
+
- introducing dependencies
|
|
187
|
+
|
|
188
|
+
## Option 2: [Name]
|
|
189
|
+
|
|
190
|
+
Use the same format as Option 1.
|
|
191
|
+
|
|
192
|
+
## Option 3: [Name]
|
|
193
|
+
|
|
194
|
+
Use the same format as Option 1.
|
|
195
|
+
|
|
196
|
+
## Recommendation
|
|
197
|
+
|
|
198
|
+
Recommend one option in 1 short paragraph.
|
|
199
|
+
|
|
200
|
+
## Approval
|
|
201
|
+
|
|
202
|
+
Ask the user which option to approve, reject, or combine.
|
package/commands/implement.md
CHANGED
|
@@ -16,3 +16,12 @@ After each small TypeScript code change, call `nt_skillz_lint` with only the `fi
|
|
|
16
16
|
- all lint errors on new code must be addressed before continuing
|
|
17
17
|
- if the lint fails on existing code, ignore the error unless it is very close to the new code
|
|
18
18
|
- line-length limits do not count as existing code; if new code causes a file-length lint error, it must be fixed
|
|
19
|
+
|
|
20
|
+
## Test Coverage
|
|
21
|
+
|
|
22
|
+
Before each commit:
|
|
23
|
+
|
|
24
|
+
1. Run `git diff --name-only --cached --diff-filter=ACMR -- '*.ts' '*.tsx'`.
|
|
25
|
+
2. For each returned path, run `/nt-skillz:vitest-coverage <file>`.
|
|
26
|
+
3. Ignore only runs that print a `SKIP:` line.
|
|
27
|
+
4. Do not create the commit unless every remaining file has 100% Vitest coverage (or it's impossible to achieve 100% test coverage for the relevant component)
|
package/commands/plan.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create an implementation plan.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Plan:
|
|
6
|
+
$ARGUMENTS
|
|
7
|
+
|
|
8
|
+
Create a detailed implementation plan broken down into slices of functionality and not layer. Good Example: "Fuzzy searching on first name" is a slice of value. Bad Example: "Add data types" is just a layer of code that needs to be assembled later.
|
|
9
|
+
|
|
10
|
+
Search all relevant existing code. If some of the code is in other repos, look there as well. Don't be lazy, more too much research is better than not enough. If you're unsure ask the user. As a general rule, if the supporting documenting or existing code references another repository, you should almost certainly be looking there.
|
|
11
|
+
|
|
12
|
+
For each slide of value, challenge if it's needed. Find supporting evidence.
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
The plan starts with a context section explaining the problem that is being solved along with any relevant information like constraints. It also references any existing materials like notion pages. Context should be rich so that an engineer has all the information they need.
|
|
17
|
+
|
|
18
|
+
## Slices
|
|
19
|
+
|
|
20
|
+
List each slice of value that needs to be delivered and justify why it is necessary in a table with the column headings `slice`, `description`, `justification`.
|
|
21
|
+
|
|
22
|
+
## Task checklist
|
|
23
|
+
|
|
24
|
+
A plan is broken down into tasks. One task for each slice of value like "Fuzzy searching on first name" and each task is broken down into subtask. The task itself and each subtask are checklist items. This is crucial so that progress can be recorded by the engineer.
|
|
25
|
+
|
|
26
|
+
Tasks should be detailed so that an engineer has all the information they need to implement the task.
|
|
27
|
+
|
|
28
|
+
## Linting
|
|
29
|
+
|
|
30
|
+
Include a lint check before each commit:
|
|
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
|
|
34
|
+
|
|
35
|
+
## Test Coverage
|
|
36
|
+
|
|
37
|
+
Include a 100% Vitest coverage check before each commit:
|
|
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
|
|
42
|
+
|
|
43
|
+
## Software design & architecture
|
|
44
|
+
|
|
45
|
+
Leave a placeholder `<software design and architecture>`. This will be filled in by a following command.
|
|
46
|
+
|
|
47
|
+
### Template
|
|
48
|
+
|
|
49
|
+
```md
|
|
50
|
+
## Context
|
|
51
|
+
|
|
52
|
+
- Problem:
|
|
53
|
+
- Constraints:
|
|
54
|
+
- Related materials:
|
|
55
|
+
|
|
56
|
+
## Software design & architecture
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Tasks
|
|
61
|
+
|
|
62
|
+
- [ ] Task 1: <slice of functionality>
|
|
63
|
+
- [ ] Subtask 1.1
|
|
64
|
+
- [ ] Subtask 1.2
|
|
65
|
+
- [ ] Run `nt_skillz_lint` on changed `.ts` and `.tsx` files
|
|
66
|
+
- [ ] Verify 100% test coverage using `/nt-skillz:vitest-coverage <file>`
|
|
67
|
+
- [ ] Commit the changes
|
|
68
|
+
- [ ] Task 2: <slice of functionality>
|
|
69
|
+
- [ ] Subtask 2.1
|
|
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
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Important Notes
|
|
77
|
+
|
|
78
|
+
- Stop if you cannot implement the plan as described. If the proposed design or functionality will not work in practice, discuss with the user
|
|
79
|
+
|
|
80
|
+
- Ensure you mark of each subtask when complete
|
|
81
|
+
|
|
82
|
+
- If parts of the plan are incomplete, missing, or placeholders refuse to implement and tell the user. Do not implement a flawed plan
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run Vitest coverage for a pull request and update the PR description coverage block
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Review pull request coverage for:
|
|
6
|
+
$ARGUMENTS
|
|
7
|
+
|
|
8
|
+
## Software Design Compliance
|
|
9
|
+
|
|
10
|
+
Ensure all new and modified code complies with `/nt-skillz:software-design`
|
|
11
|
+
|
|
12
|
+
Leave in-line feedback on the PR for lines that do not comply. Prefix messages with [Software Design]
|
|
13
|
+
|
|
14
|
+
## Test Quality
|
|
15
|
+
|
|
16
|
+
Ensure all new and modified tests comply with `/nt-skillz-writing-tests`
|
|
17
|
+
|
|
18
|
+
Leave in-line feedback on the PR for lines that do not comply. Prefix messages with [Test Quality]
|
|
19
|
+
|
|
20
|
+
## Test Coverage Analysis
|
|
21
|
+
|
|
22
|
+
Add a test coverage analysis results section to the PR description:
|
|
23
|
+
|
|
24
|
+
1. Resolve the pull request from `$ARGUMENTS`.
|
|
25
|
+
2. If `$ARGUMENTS` does not identify a PR URL or PR number, stop and ask for one.
|
|
26
|
+
3. Run `gh pr diff <pr> --name-only`.
|
|
27
|
+
4. For each changed path from that output, run `/nt-skillz:vitest-coverage <file>`.
|
|
28
|
+
5. Ignore only runs that print a `SKIP:` line.
|
|
29
|
+
6. If every run prints a `SKIP:` line, use one fenced `text` block containing `No changed TypeScript source files.` as the coverage content.
|
|
30
|
+
7. Otherwise, build the PR coverage block in this exact shape:
|
|
31
|
+
- `<!-- nt-skillz-coverage:start -->`
|
|
32
|
+
- `## Coverage`
|
|
33
|
+
- one `### \`<file>\`` heading for each non-`SKIP:` file
|
|
34
|
+
- one fenced `text` block containing the exact raw `/nt-skillz:vitest-coverage <file>` output directly under that file heading
|
|
35
|
+
- `<!-- nt-skillz-coverage:end -->`
|
|
36
|
+
8. Run `gh pr view <pr> --json body --jq '.body'` and use the returned text as the current PR body.
|
|
37
|
+
9. If the current PR body already contains both marker lines, replace only the text from `<!-- nt-skillz-coverage:start -->` through `<!-- nt-skillz-coverage:end -->` with the new coverage block.
|
|
38
|
+
10. If the current PR body does not contain both marker lines, append the new coverage block to the end of the PR body separated by two newlines.
|
|
39
|
+
11. Write the updated PR body to a temporary file.
|
|
40
|
+
12. Run `gh pr edit <pr> --body-file <temporary-file>`.
|
|
41
|
+
13. Do not summarize, interpret, or paraphrase the coverage output.
|
|
42
|
+
14. Do not modify any other part of the PR body.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Reusable Vitest coverage workflow for TypeScript source files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run Vitest coverage for:
|
|
6
|
+
$ARGUMENTS
|
|
7
|
+
|
|
8
|
+
Treat `$ARGUMENTS` as exactly one repository-relative file path and run this exact workflow.
|
|
9
|
+
|
|
10
|
+
1. If the path does not end with `.ts` or `.tsx`, print exactly `SKIP: not a TypeScript source file` and stop.
|
|
11
|
+
2. If the path matches any of these patterns, print exactly `SKIP: excluded TypeScript file` and stop:
|
|
12
|
+
- `*.spec.ts`
|
|
13
|
+
- `*.spec.tsx`
|
|
14
|
+
- `*.test.ts`
|
|
15
|
+
- `*.test.tsx`
|
|
16
|
+
- `*.d.ts`
|
|
17
|
+
- `*.config.ts`
|
|
18
|
+
- `*.config.tsx`
|
|
19
|
+
- any path containing `/fixtures/`
|
|
20
|
+
- any path containing `/__fixtures__/`
|
|
21
|
+
3. Walk upward from the file directory and stop at the nearest directory containing `package.json`. Use that directory as the package root.
|
|
22
|
+
4. If the file has no ancestor `package.json`, stop and report a blocker.
|
|
23
|
+
5. Convert the file path to a path relative to the package root.
|
|
24
|
+
6. From the package root, run exactly this command:
|
|
25
|
+
`vitest related <file> --run --coverage.enabled --coverage.include=<file> --coverage.reporter=text`
|
|
26
|
+
7. Capture the raw Vitest `text` coverage output for that file.
|
|
27
|
+
8. This coverage check fails unless the raw output contains a coverage row for `<file>`.
|
|
28
|
+
9. This coverage check fails unless the coverage row for `<file>` shows all of the following:
|
|
29
|
+
- `100` in `% Stmts`
|
|
30
|
+
- `100` in `% Branch`
|
|
31
|
+
- `100` in `% Funcs`
|
|
32
|
+
- `100` in `% Lines`
|
|
33
|
+
10. This coverage check fails if the coverage row for `<file>` has any uncovered line numbers.
|
|
34
|
+
11. If the coverage check fails, print the raw failing Vitest `text` coverage output without summarizing it.
|
|
35
|
+
12. If the coverage check passes, print the raw Vitest `text` coverage output without summarizing it.
|
package/dist/tools/lint.js
CHANGED
|
@@ -26,6 +26,7 @@ class LintExecutionError extends Error {
|
|
|
26
26
|
super(message);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
const ansiEscapeSequencePattern = new RegExp(String.raw `\u001B\[[0-?]*[ -/]*[@-~]`, "g");
|
|
29
30
|
const toolDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
const toolRepositoryRoot = path.resolve(toolDirectory, "..", "..");
|
|
31
32
|
const eslintConfigPath = path.join(toolRepositoryRoot, "scripts", "living-architecture-eslint.config.mjs");
|
|
@@ -130,6 +131,9 @@ function createLintTitle(filePaths, baseReference) {
|
|
|
130
131
|
}
|
|
131
132
|
return "Lint current TypeScript files";
|
|
132
133
|
}
|
|
134
|
+
function removeAnsiEscapeSequences(value) {
|
|
135
|
+
return value.replaceAll(ansiEscapeSequencePattern, "");
|
|
136
|
+
}
|
|
133
137
|
async function runEslint(repositoryRoot, lintTargets) {
|
|
134
138
|
const previousLintRepositoryRoot = process.env.NT_SKILLZ_LINT_REPO_ROOT;
|
|
135
139
|
process.env.NT_SKILLZ_LINT_REPO_ROOT = repositoryRoot;
|
|
@@ -142,7 +146,7 @@ async function runEslint(repositoryRoot, lintTargets) {
|
|
|
142
146
|
try {
|
|
143
147
|
const lintResults = await eslint.lintFiles(lintTargets);
|
|
144
148
|
const formatter = await eslint.loadFormatter("stylish");
|
|
145
|
-
const formattedOutput = (await formatter.format(lintResults)).trim();
|
|
149
|
+
const formattedOutput = removeAnsiEscapeSequences((await formatter.format(lintResults)).trim());
|
|
146
150
|
const errorCount = lintResults.reduce((count, lintResult) => count + lintResult.errorCount + lintResult.fatalErrorCount, 0);
|
|
147
151
|
return {
|
|
148
152
|
exitCode: errorCount > 0 ? 1 : 0,
|
package/package.json
CHANGED
|
@@ -22,8 +22,6 @@ if (!lintRepositoryRoot) {
|
|
|
22
22
|
|
|
23
23
|
const typescriptFiles = ['**/*.ts', '**/*.tsx']
|
|
24
24
|
const testFiles = ['**/*.spec.ts', '**/*.spec.tsx', '**/*.test.ts', '**/*.test.tsx']
|
|
25
|
-
const thinLayerFiles = ['**/entrypoint/**/*.ts', '**/commands/**/*.ts', '**/queries/**/*.ts']
|
|
26
|
-
const thinLayerIgnoredFiles = ['**/*.spec.ts', '**/*.test.ts']
|
|
27
25
|
const ignoredPaths = [
|
|
28
26
|
'**/dist',
|
|
29
27
|
'**/out-tsc',
|
|
@@ -58,21 +56,6 @@ const noEmptyStringFallbackRule = {
|
|
|
58
56
|
|
|
59
57
|
const restrictedSyntaxRules = ['error', noLetRule, noGenericErrorRule, noEmptyStringFallbackRule]
|
|
60
58
|
|
|
61
|
-
const entrypointRestrictedSyntaxRules = [
|
|
62
|
-
'error',
|
|
63
|
-
noLetRule,
|
|
64
|
-
noGenericErrorRule,
|
|
65
|
-
{
|
|
66
|
-
selector: 'FunctionDeclaration:not([parent.type="ExportNamedDeclaration"])',
|
|
67
|
-
message: 'Entrypoints must not define private functions. Move logic to commands/, queries/, or infra/.',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
selector: 'VariableDeclarator > ArrowFunctionExpression',
|
|
71
|
-
message: 'Entrypoints must not define private arrow functions. Move logic to commands/, queries/, or infra/.',
|
|
72
|
-
},
|
|
73
|
-
noEmptyStringFallbackRule,
|
|
74
|
-
]
|
|
75
|
-
|
|
76
59
|
const restrictedImportPatterns = [
|
|
77
60
|
{
|
|
78
61
|
group: ['*/utils/*', '*/utils', '*/utilities'],
|
|
@@ -229,20 +212,6 @@ export default tseslint.config(
|
|
|
229
212
|
'@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: false }],
|
|
230
213
|
},
|
|
231
214
|
},
|
|
232
|
-
{
|
|
233
|
-
files: thinLayerFiles,
|
|
234
|
-
ignores: thinLayerIgnoredFiles,
|
|
235
|
-
rules: {
|
|
236
|
-
'max-lines': ['error', { max: 150, skipBlankLines: true, skipComments: true }],
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
files: ['**/entrypoint/**/*.ts'],
|
|
241
|
-
ignores: thinLayerIgnoredFiles,
|
|
242
|
-
rules: {
|
|
243
|
-
'no-restricted-syntax': entrypointRestrictedSyntaxRules,
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
215
|
{
|
|
247
216
|
files: typescriptFiles,
|
|
248
217
|
plugins: {
|