@robot-inventor/agent-skills 0.0.0
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/LICENSE +21 -0
- package/README.md +31 -0
- package/package.json +33 -0
- package/plugin.json +12 -0
- package/scripts/opencode.ts +26 -0
- package/skills/cleanup-changes/SKILL.md +33 -0
- package/skills/identify-search-maintenance/SKILL.md +78 -0
- package/skills/identify-search-maintenance/references/selection-rubric.md +45 -0
- package/skills/identify-search-maintenance/scripts/select_rank_led_candidates.py +212 -0
- package/skills/prove-it-diagnostics/SKILL.md +60 -0
- package/skills/review-loop/SKILL.md +45 -0
- package/skills/simple-engineering/SKILL.md +27 -0
- package/skills/web-master/SKILL.md +109 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 roboin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Roboin Agent Skills
|
|
2
|
+
|
|
3
|
+
My personal collection of Agent Skills.
|
|
4
|
+
|
|
5
|
+
- [cleanup-changes](skills/cleanup-changes/SKILL.md): The skill to revert unnecessary changes and leave only essential changes.
|
|
6
|
+
- [identify-search-maintenance](skills/identify-search-maintenance): The skill to identify web pages that are performing poorly in search results and require maintenance.
|
|
7
|
+
- [prove-it-diagnostics](skills/prove-it-diagnostics/SKILL.md): The skill to fix bugs and improve performance by proving the root cause through minimal reproduction code or benchmarks, rather than relying on speculation based solely on reading the code.
|
|
8
|
+
- [review-loop](skills/review-loop/SKILL.md): Thoroughly improve code quality by requesting a review from a sub-agent.
|
|
9
|
+
- [simple-engineering](skills/simple-engineering/SKILL.md): A skill outlining the fundamental principles to keep in mind when writing or editing code.
|
|
10
|
+
- [web-master](skills/web-master/SKILL.md): A skill outlining the fundamental principles to keep in mind when writing or editing web-related code.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Install all skills
|
|
16
|
+
npx skills add Robot-Inventor/agent-skills
|
|
17
|
+
|
|
18
|
+
# Install a specific skill
|
|
19
|
+
npx skills add Robot-Inventor/agent-skills --skill <skill-name>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Plugins
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Codex
|
|
26
|
+
codex plugin marketplace add Robot-Inventor/agent-skills
|
|
27
|
+
codex plugin add agent-skills@robot-inventor
|
|
28
|
+
|
|
29
|
+
# OpenCode
|
|
30
|
+
opencode plugin @robot-inventor/agent-skills --global
|
|
31
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robot-inventor/agent-skills",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Roboin's personal collection of Agent Skills.",
|
|
5
|
+
"homepage": "https://github.com/Robot-Inventor/agent-skills#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/Robot-Inventor/agent-skills/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Robot-Inventor/agent-skills.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Robot-Inventor",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"files": ["./plugin.json", "./skills/", "./scripts/opencode.ts"],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"provenance": false
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"ci:version": "changeset version && bun ./scripts/syncVersion.ts && git add .",
|
|
23
|
+
"ci:publish": "changeset publish"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@changesets/changelog-github": "^1.0.0",
|
|
27
|
+
"@changesets/cli": "^3.0.0",
|
|
28
|
+
"@opencode-ai/plugin": "^1.18.18",
|
|
29
|
+
"@robot-inventor/tsconfig-base": "^7.1.0",
|
|
30
|
+
"@types/bun": "^1.3.14",
|
|
31
|
+
"@types/node": "^26.2.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "roboin-agent-skills",
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"description": "My personal collection of Agent Skills.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Robot-Inventor",
|
|
8
|
+
"url": "https://roboin.io/"
|
|
9
|
+
},
|
|
10
|
+
"repository": "https://github.com/Robot-Inventor/agent-skills",
|
|
11
|
+
"license": "MIT"
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
import { SKILLS } from "./skills";
|
|
3
|
+
import { exists } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const plugin: Plugin = async ({ project, directory }) => {
|
|
8
|
+
const isWebProject = await exists(join(directory, "package.json"));
|
|
9
|
+
const skills = isWebProject ? SKILLS.web : SKILLS.nonWeb;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
config: async (config) => {
|
|
13
|
+
config.instructions ??= [];
|
|
14
|
+
|
|
15
|
+
for (const skill of skills) {
|
|
16
|
+
const path = fileURLToPath(new URL(`../skills/${skill}/SKILL.md`, import.meta.url));
|
|
17
|
+
|
|
18
|
+
if (!config.instructions.includes(path)) {
|
|
19
|
+
config.instructions.push(path);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { plugin };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cleanup-changes
|
|
3
|
+
description: The skill to revert unnecessary changes and leave only essential changes when a user requests a cleanup of changes, or before committing and creating a pull request.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Robot-Inventor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Cleanup Changes Skill
|
|
10
|
+
|
|
11
|
+
Both humans and agents typically go through a trial-and-error process when writing code, making numerous changes (including non-essential and unnecessary ones) to achieve their final goals. However, non-essential and unnecessary changes resulting from this trial-and-error process should not be included in the final code. Changes should be reviewed as needed, and unnecessary changes should be reversed to focus only on essential ones.
|
|
12
|
+
|
|
13
|
+
## When to apply
|
|
14
|
+
|
|
15
|
+
You should perform this skill when a user requests a cleanup of changes, or before creating a commit or pull request.
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
The following is the core of this skill; please follow the specified steps. Do not change the Git staging state unless explicitly instructed to do so by the user. Users may have staged their changes as a snapshot of the code in its last expected state.
|
|
20
|
+
|
|
21
|
+
### 1. Looking back on the trial-and-error process
|
|
22
|
+
|
|
23
|
+
Reflect on the trial-and-error process of the task and distinguish between changes that were essentially necessary and changes that, despite being tried, ultimately proved ineffective and unnecessary.
|
|
24
|
+
|
|
25
|
+
You shouldn't make assumptions about whether a change is essentially necessary. For example, when fixing a bug, review the changes made when the bug was finally fixed, understand the essential and root cause of the bug, and then, based on that, determine whether each change is essentially necessary.
|
|
26
|
+
|
|
27
|
+
When determining whether each change is necessary, pay particular attention to bugs caused by multiple factors. Bugs can be caused by a single, simple cause, or by multiple factors. If it's caused by a single, simple cause, the last change made when the bug is fixed may be sufficient, and previous changes may be unnecessary. If it's a bug caused by multiple factors, the last change alone may not be enough, and some of the changes made during the trial-and-error process leading up to it may also be necessary. Carefully consider the underlying cause of the bug, including whether it's due to a single cause or multiple factors, and then determine which changes are necessary and which are not.
|
|
28
|
+
|
|
29
|
+
### 2. Undo unnecessary changes
|
|
30
|
+
|
|
31
|
+
Revert any changes you determined were unnecessary in the previous step, leaving only the essential changes.
|
|
32
|
+
|
|
33
|
+
However, even if a change isn't strictly necessary, minor corrections to typos around the current change or small refactorings are acceptable. Generally, in coding, correcting typos found around a change or making small refactorings related to the current change are generally tolerated, even if they aren't strictly necessary.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: identify-search-maintenance
|
|
3
|
+
description: Identify and diagnose organic-search articles that need maintenance. Use when asked to find pages whose organic traffic declined mainly from ranking loss rather than search-demand decline, analyze Search Console or GA4 data across recent and year-over-year periods, prioritize a maintenance backlog, or compare target pages with current Google results. Do not use this skill to edit article content.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Identify Search Maintenance
|
|
7
|
+
|
|
8
|
+
Find pages that merit search-maintenance work, with evidence that separates rank-driven losses from changes in demand. The deliverable is a ranked investigation backlog and diagnosis; it is never an article rewrite or publishing task.
|
|
9
|
+
|
|
10
|
+
## Scope and guardrails
|
|
11
|
+
|
|
12
|
+
- Confirm the property, traffic channel, dates, locale/device, and whether Google must be inspected through the user's signed-in Chrome session.
|
|
13
|
+
- Use both recent comparison (normally the latest 3 months vs. the preceding 3 months) and a year-over-year comparison (normally the same recent 3 months one year earlier). End periods on the latest Search Console date that is complete enough to compare, use equal day counts and comparable weekday mix, and state any unavailable baseline as `N/A`, never as zero.
|
|
14
|
+
- Fix the Search Console search type, country, device, property scope, canonical-URL handling, and any brand-query exclusion across each comparison. Record GA4 timezone, Organic Search channel definition, and landing-page URL normalization.
|
|
15
|
+
- Treat a page as a candidate only after checking page-level **and** query-level Search Console data. GA4 is corroborating traffic evidence, not a substitute for query and rank data.
|
|
16
|
+
- Do not attribute a loss to rankings from clicks alone. URL-filtered and property-wide Search Console impressions are affected by the site's own visibility and are not standalone demand measures. Check an independent demand proxy (for example Google Trends for the query/topic, Keyword Planner where available, or a documented market-data source), then use property-wide query data only as corroboration. Also check URL/query rank changes, cannibalization, and live SERP composition.
|
|
17
|
+
- Do not alter CMS content, metadata, internal links, or publish settings. If the user asks for changes, finish this investigation first and obtain a separate article-update scope.
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
### 1. Collect comparable evidence
|
|
22
|
+
|
|
23
|
+
1. Export Search Console performance by **page** for the two period pairs. Prefer an export/CSV over manually paging a truncated table.
|
|
24
|
+
2. Retain clicks, impressions, CTR, and average position for each period. Record filters, property, and export dates.
|
|
25
|
+
3. Obtain GA4 organic-search landing-page sessions or views for the same dates when available. Note that GA4 and Search Console count different things.
|
|
26
|
+
4. Normalize both the recent-pair and year-over-year page exports to the columns in [selection-rubric.md](references/selection-rubric.md). Run the bundled filter on each, take the union of the two candidate lists, and label the comparison(s) that surfaced each URL. A page that only passes one comparison still needs the full evidence checks in both.
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
python "<skill-directory>/scripts/select_rank_led_candidates.py" normalized-pages.csv --format markdown
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Replace `<skill-directory>` with the directory containing this `SKILL.md`. Use the script to screen candidates, not to make the final decision. Tune its thresholds only if the report explains why.
|
|
33
|
+
|
|
34
|
+
### 2. Separate causes before selecting pages
|
|
35
|
+
|
|
36
|
+
Classify each materially declining page into one of these mutually exclusive buckets:
|
|
37
|
+
|
|
38
|
+
| Bucket | Evidence | Action |
|
|
39
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
40
|
+
| Rank-led maintenance candidate | Clicks materially down; impressions broadly stable; average position/query ranks worsen | Investigate and prioritize |
|
|
41
|
+
| Demand/obsolescence decline | Impressions and clicks fall together, or the task/query has ended | Exclude from the rank-led list |
|
|
42
|
+
| CTR/SERP change | Position broadly stable but CTR falls; SERP features or title/snippet changes explain it | Keep separate; do not call it rank-led |
|
|
43
|
+
| Technical/indexing issue | Indexing, canonical, rendering, or availability evidence | Escalate separately |
|
|
44
|
+
| Cannibalization/internal competition | Another site URL gained the same query or intent | Exclude from this content-maintenance list; route for consolidation/targeting decision |
|
|
45
|
+
| Insufficient evidence | Low volume or no comparable baseline | Do not force into the top 10 |
|
|
46
|
+
|
|
47
|
+
For every selected URL, verify at least three previously valuable declined queries. For each query, collect three views with identical search type/country/device/date filters: (a) the target URL's query metrics and position, (b) the **whole property** query metrics without a page filter, and (c) an independent demand proxy. Treat (c) as the demand evidence; use (b) only to understand the site's overall visibility. Use year-over-year data to catch longer-running changes. Check whether another URL in the property gained the same query; classify confirmed cannibalization in its own exclusion bucket. If the query mix changed, say so.
|
|
48
|
+
|
|
49
|
+
Before retaining a content-maintenance candidate, perform a minimum health check: URL Inspection/index status, user-selected canonical, HTTP availability, and rendered-page accessibility. Classify a failing page as a technical/indexing issue instead of diagnosing an editorial gap.
|
|
50
|
+
|
|
51
|
+
### 3. Diagnose each selected page in Google
|
|
52
|
+
|
|
53
|
+
When the user specifies Chrome or Google, use the Chrome browser, not a general web-search backend. For each selected URL:
|
|
54
|
+
|
|
55
|
+
1. Search at least three declined, historically valuable queries in Google, using the required locale/device context where possible.
|
|
56
|
+
2. Capture the target URL's visible rank or absence, the top competing URLs, and material SERP features (AI Overview, official result, video carousel, PAA, news, forums, etc.). Use fresh tabs and do not change account or site settings.
|
|
57
|
+
3. Compare the target and the leading pages on search intent, freshness, direct answer quality, task completion, factual completeness, first-party evidence, visual/media support, structure, and trust signals. Base observations on actual page content; distinguish observed facts from inference.
|
|
58
|
+
4. Identify the gap to investigate, not the edit itself. Examples: outdated UI steps, missing current constraints, poor intent match, weak original evidence, or a SERP now dominated by official sources.
|
|
59
|
+
|
|
60
|
+
If parallel agents are available, assign no more than two pages to each agent. Require each agent to return the period metrics, three or more queries, Google competitors, SERP features, observed content gaps, exclusions, and source URLs. Consolidate only after all agents return evidence.
|
|
61
|
+
|
|
62
|
+
### 4. Produce the maintenance report
|
|
63
|
+
|
|
64
|
+
Create or append to `report.md`; preserve earlier findings unless the user asks to replace them. Use this structure:
|
|
65
|
+
|
|
66
|
+
1. Scope, dates, data sources, filters, and limitations.
|
|
67
|
+
2. Selection method and explicit rank-led thresholds.
|
|
68
|
+
3. A prioritized table of up to the requested number of rank-led pages (default 10): URL, recent metrics, year-over-year metrics, property-wide query-demand evidence, ranking evidence, priority, and confidence. If fewer pages meet the evidence threshold, report fewer and explain the shortfall.
|
|
69
|
+
4. Per-page diagnosis: three or more declined queries, current Google competitors/SERP observations, and content gaps to investigate.
|
|
70
|
+
5. Excluded near-misses with the evidence for demand decline, obsolete intent, CTR-only loss, technical issue, cannibalization, or insufficient data.
|
|
71
|
+
6. A separate follow-up queue for work that needs a technical or editorial decision.
|
|
72
|
+
|
|
73
|
+
Before delivery, verify that every page in the final rank-led list has stable-enough independent demand evidence, a documented query-level rank deterioration, and a passing health check. Never fill a quota with demand-led losses.
|
|
74
|
+
|
|
75
|
+
## Resources
|
|
76
|
+
|
|
77
|
+
- [selection-rubric.md](references/selection-rubric.md): normalization schema, default thresholds, and report checklist.
|
|
78
|
+
- `scripts/select_rank_led_candidates.py`: deterministic first-pass filter for normalized Search Console page exports.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Rank-led candidate selection rubric
|
|
2
|
+
|
|
3
|
+
## Normalize the export
|
|
4
|
+
|
|
5
|
+
Create a CSV with this exact header before using the script:
|
|
6
|
+
|
|
7
|
+
```csv
|
|
8
|
+
url,current_clicks,previous_clicks,current_impressions,previous_impressions,current_position,previous_position
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`current_*` is the recent period and `previous_*` is its comparison period. Preserve the original Search Console exports as evidence. Do not combine GA4 and Search Console counts in the same columns. The normalized page CSV is only a ranking-loss screen; it cannot establish search demand.
|
|
12
|
+
|
|
13
|
+
For annual comparison, make a second normalized CSV where `previous_*` is the equivalent period one year earlier. Run the screen on both CSVs, union their candidates, and show which comparison surfaced each URL. If that page did not exist then or the data is unavailable, report `N/A`.
|
|
14
|
+
|
|
15
|
+
## Default first-pass thresholds
|
|
16
|
+
|
|
17
|
+
The bundled script selects a page only if all conditions hold:
|
|
18
|
+
|
|
19
|
+
| Signal | Default | Reason |
|
|
20
|
+
| --------------------------------------- | ----------------------------: | ---------------------------------------- |
|
|
21
|
+
| Previous-period clicks | at least 100 | Avoid low-volume noise |
|
|
22
|
+
| Click change | -20% or worse | Requires a material loss |
|
|
23
|
+
| Impression change | within ±25% | Excludes most demand-led losses |
|
|
24
|
+
| Position change | +0.5 or worse | Requires a measurable rank deterioration |
|
|
25
|
+
| Click decline beyond impression decline | at least 10 percentage points | Requires loss beyond demand movement |
|
|
26
|
+
|
|
27
|
+
These are screening defaults, not proof. A page can be excluded despite passing them when an independent demand proxy shows demand loss, a one-off event occurred, a competing URL gained the query, or the task is obsolete. A page can be included with explained threshold adjustments only when the report documents why.
|
|
28
|
+
|
|
29
|
+
## Interpret the metrics
|
|
30
|
+
|
|
31
|
+
- Position delta is `current_position - previous_position`; a positive number is worse.
|
|
32
|
+
- Page-level impressions are not demand: a lower rank can itself lower the page's impressions. Property-wide query impressions can also fall when the whole site loses visibility. Use Google Trends, Keyword Planner, or another documented independent source to assess demand. Compare property-wide (unfiltered-by-page) query impressions only as a corroborating visibility signal, then compare the target URL's rank/clicks separately.
|
|
33
|
+
- A stable page-level impression total can hide query-mix movement. Verify individual declined queries before calling the loss rank-led, and inspect pages that gained the query for cannibalization.
|
|
34
|
+
- A stable position with a falling CTR is a CTR/SERP case, not a rank-led case.
|
|
35
|
+
- A fall in impressions paired with a similar fall in clicks is normally demand-led or obsolescence-led until evidence proves otherwise.
|
|
36
|
+
- Search Console average position is an aggregate. Use live Google searches only as a point-in-time confirmation and label locale, device, signed-in state, and date.
|
|
37
|
+
|
|
38
|
+
## Completion checklist
|
|
39
|
+
|
|
40
|
+
- [ ] Recent and year-over-year comparisons were both attempted.
|
|
41
|
+
- [ ] Every final candidate has page-level metrics, at least three declined historic queries, and property-wide visibility checks for those queries.
|
|
42
|
+
- [ ] Every final candidate has independent demand evidence, a URL Inspection/index/canonical/availability health check, and no confirmed cannibalization.
|
|
43
|
+
- [ ] Google SERP checks list query, target visibility, competitors, and material SERP features.
|
|
44
|
+
- [ ] The report explicitly separates rank-led candidates from demand-led, CTR-only, technical, and insufficient-evidence cases.
|
|
45
|
+
- [ ] The report contains diagnoses and maintenance priorities, but no article edits.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Screen normalized Search Console page data for rank-led traffic-loss candidates."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import csv
|
|
8
|
+
import math
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Iterable, TextIO
|
|
12
|
+
|
|
13
|
+
REQUIRED_COLUMNS = (
|
|
14
|
+
"url",
|
|
15
|
+
"current_clicks",
|
|
16
|
+
"previous_clicks",
|
|
17
|
+
"current_impressions",
|
|
18
|
+
"previous_impressions",
|
|
19
|
+
"current_position",
|
|
20
|
+
"previous_position",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parse_number(value: str, column: str, row_number: int) -> float:
|
|
25
|
+
try:
|
|
26
|
+
number = float(value.replace(",", "").strip())
|
|
27
|
+
except (AttributeError, ValueError) as error:
|
|
28
|
+
raise ValueError(
|
|
29
|
+
f"Row {row_number}: {column} must be numeric, got {value!r}."
|
|
30
|
+
) from error
|
|
31
|
+
if not math.isfinite(number):
|
|
32
|
+
raise ValueError(f"Row {row_number}: {column} must be finite, got {value!r}.")
|
|
33
|
+
if (column.endswith("clicks") or column.endswith("impressions")) and number < 0:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Row {row_number}: {column} cannot be negative, got {value!r}."
|
|
36
|
+
)
|
|
37
|
+
if column.endswith("position") and number <= 0:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"Row {row_number}: {column} must be greater than zero, got {value!r}."
|
|
40
|
+
)
|
|
41
|
+
return number
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def percentage_change(current: float, previous: float) -> float:
|
|
45
|
+
return (current - previous) / previous
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def read_rows(stream: TextIO) -> Iterable[dict[str, str]]:
|
|
49
|
+
reader = csv.DictReader(stream)
|
|
50
|
+
if reader.fieldnames is None:
|
|
51
|
+
raise ValueError("Input CSV has no header row.")
|
|
52
|
+
|
|
53
|
+
reader.fieldnames[0] = reader.fieldnames[0].lstrip("\ufeff")
|
|
54
|
+
missing = [column for column in REQUIRED_COLUMNS if column not in reader.fieldnames]
|
|
55
|
+
if missing:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
f"Input CSV is missing required columns: {', '.join(missing)}."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return reader
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def select_candidates(
|
|
64
|
+
rows: Iterable[dict[str, str]],
|
|
65
|
+
min_previous_clicks: float,
|
|
66
|
+
max_click_change: float,
|
|
67
|
+
max_absolute_impression_change: float,
|
|
68
|
+
min_position_worsening: float,
|
|
69
|
+
min_click_gap_vs_impressions: float,
|
|
70
|
+
) -> list[dict[str, float | str]]:
|
|
71
|
+
candidates: list[dict[str, float | str]] = []
|
|
72
|
+
seen_urls: set[str] = set()
|
|
73
|
+
for row_number, row in enumerate(rows, start=2):
|
|
74
|
+
url = row["url"].strip()
|
|
75
|
+
if not url:
|
|
76
|
+
raise ValueError(f"Row {row_number}: url cannot be empty.")
|
|
77
|
+
if url in seen_urls:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"Row {row_number}: duplicate url {url!r}; aggregate the export first."
|
|
80
|
+
)
|
|
81
|
+
seen_urls.add(url)
|
|
82
|
+
values = {
|
|
83
|
+
column: parse_number(row[column], column, row_number)
|
|
84
|
+
for column in REQUIRED_COLUMNS[1:]
|
|
85
|
+
}
|
|
86
|
+
previous_clicks = values["previous_clicks"]
|
|
87
|
+
previous_impressions = values["previous_impressions"]
|
|
88
|
+
if previous_clicks <= 0 or previous_impressions <= 0:
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
click_change = percentage_change(values["current_clicks"], previous_clicks)
|
|
92
|
+
impression_change = percentage_change(
|
|
93
|
+
values["current_impressions"], previous_impressions
|
|
94
|
+
)
|
|
95
|
+
position_worsening = values["current_position"] - values["previous_position"]
|
|
96
|
+
click_gap_vs_impressions = impression_change - click_change
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
previous_clicks >= min_previous_clicks
|
|
100
|
+
and click_change <= max_click_change
|
|
101
|
+
and abs(impression_change) <= max_absolute_impression_change
|
|
102
|
+
and position_worsening >= min_position_worsening
|
|
103
|
+
and click_gap_vs_impressions >= min_click_gap_vs_impressions
|
|
104
|
+
):
|
|
105
|
+
candidates.append(
|
|
106
|
+
{
|
|
107
|
+
"url": url,
|
|
108
|
+
"current_clicks": values["current_clicks"],
|
|
109
|
+
"previous_clicks": previous_clicks,
|
|
110
|
+
"click_change": click_change,
|
|
111
|
+
"current_impressions": values["current_impressions"],
|
|
112
|
+
"previous_impressions": previous_impressions,
|
|
113
|
+
"impression_change": impression_change,
|
|
114
|
+
"current_position": values["current_position"],
|
|
115
|
+
"previous_position": values["previous_position"],
|
|
116
|
+
"position_worsening": position_worsening,
|
|
117
|
+
"click_gap_vs_impressions": click_gap_vs_impressions,
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return sorted(
|
|
122
|
+
candidates,
|
|
123
|
+
key=lambda candidate: (
|
|
124
|
+
float(candidate["previous_clicks"]) - float(candidate["current_clicks"])
|
|
125
|
+
),
|
|
126
|
+
reverse=True,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def percentage(value: float) -> str:
|
|
131
|
+
return f"{value * 100:.1f}%"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def markdown(candidates: Iterable[dict[str, float | str]]) -> str:
|
|
135
|
+
lines = [
|
|
136
|
+
"| URL | Clicks (current / previous) | Click change | Impressions change | Position worsening |",
|
|
137
|
+
"| --- | ---: | ---: | ---: | ---: |",
|
|
138
|
+
]
|
|
139
|
+
for candidate in candidates:
|
|
140
|
+
lines.append(
|
|
141
|
+
"| {url} | {current_clicks:.0f} / {previous_clicks:.0f} | {click_change} | {impression_change} | {position_worsening:+.1f} |".format(
|
|
142
|
+
url=candidate["url"],
|
|
143
|
+
current_clicks=float(candidate["current_clicks"]),
|
|
144
|
+
previous_clicks=float(candidate["previous_clicks"]),
|
|
145
|
+
click_change=percentage(float(candidate["click_change"])),
|
|
146
|
+
impression_change=percentage(float(candidate["impression_change"])),
|
|
147
|
+
position_worsening=float(candidate["position_worsening"]),
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
return "\n".join(lines)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main() -> int:
|
|
154
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
155
|
+
parser.add_argument(
|
|
156
|
+
"input", help="Normalized CSV path, or - to read CSV from standard input."
|
|
157
|
+
)
|
|
158
|
+
parser.add_argument("--min-previous-clicks", type=float, default=100)
|
|
159
|
+
parser.add_argument("--max-click-change", type=float, default=-0.20)
|
|
160
|
+
parser.add_argument("--max-absolute-impression-change", type=float, default=0.25)
|
|
161
|
+
parser.add_argument("--min-position-worsening", type=float, default=0.5)
|
|
162
|
+
parser.add_argument("--min-click-gap-vs-impressions", type=float, default=0.10)
|
|
163
|
+
parser.add_argument("--format", choices=("markdown", "csv"), default="markdown")
|
|
164
|
+
arguments = parser.parse_args()
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
if arguments.input == "-":
|
|
168
|
+
candidates = select_candidates(
|
|
169
|
+
read_rows(sys.stdin),
|
|
170
|
+
arguments.min_previous_clicks,
|
|
171
|
+
arguments.max_click_change,
|
|
172
|
+
arguments.max_absolute_impression_change,
|
|
173
|
+
arguments.min_position_worsening,
|
|
174
|
+
arguments.min_click_gap_vs_impressions,
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
with Path(arguments.input).open(
|
|
178
|
+
encoding="utf-8-sig", newline=""
|
|
179
|
+
) as input_file:
|
|
180
|
+
candidates = select_candidates(
|
|
181
|
+
read_rows(input_file),
|
|
182
|
+
arguments.min_previous_clicks,
|
|
183
|
+
arguments.max_click_change,
|
|
184
|
+
arguments.max_absolute_impression_change,
|
|
185
|
+
arguments.min_position_worsening,
|
|
186
|
+
arguments.min_click_gap_vs_impressions,
|
|
187
|
+
)
|
|
188
|
+
except (OSError, ValueError, csv.Error) as error:
|
|
189
|
+
parser.error(str(error))
|
|
190
|
+
|
|
191
|
+
if arguments.format == "markdown":
|
|
192
|
+
print(markdown(candidates))
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
writer = csv.DictWriter(
|
|
196
|
+
sys.stdout,
|
|
197
|
+
fieldnames=(
|
|
198
|
+
"url",
|
|
199
|
+
*REQUIRED_COLUMNS[1:],
|
|
200
|
+
"click_change",
|
|
201
|
+
"impression_change",
|
|
202
|
+
"position_worsening",
|
|
203
|
+
"click_gap_vs_impressions",
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
writer.writeheader()
|
|
207
|
+
writer.writerows(candidates)
|
|
208
|
+
return 0
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prove-it-diagnostics
|
|
3
|
+
description: Prove it for debugging, performance investigation, bottleneck analysis, profiling, and optimization work. When reading code does not prove the cause, run a minimal reproduction, add temporary traces, measure bottlenecks, and verify the fix or improvement.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Robot-Inventor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Prove It Diagnostics
|
|
10
|
+
|
|
11
|
+
Use this skill to pinpoint the causes of bugs and performance bottlenecks and fix them. Reading code alone may not reveal the source of a bug or bottleneck. Do not claim that a speculative code change fixed a bug or improved performance. Run the code, gather evidence, identify the cause, and verify the fix.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
|
|
15
|
+
The following is the core of this skill; please follow the specified steps.
|
|
16
|
+
|
|
17
|
+
## 1. Establish a performance baseline for optimization tasks
|
|
18
|
+
|
|
19
|
+
For performance optimization tasks, create and run a benchmark before making changes. Run the benchmark as many times as practical. Use averages, percentiles, and other relevant statistics to establish a stable baseline instead of relying on a single run.
|
|
20
|
+
|
|
21
|
+
## 2. Read the code
|
|
22
|
+
|
|
23
|
+
Read the code related to the bug or performance issue. For debugging tasks, you may fix the issue and skip the remaining steps when you find a clear cause, such as an incorrect operator or an obvious contradiction in the logic.
|
|
24
|
+
|
|
25
|
+
Continue with the remaining steps when you cannot identify the cause with confidence or when you find suspicious code but lack enough evidence to blame it.
|
|
26
|
+
|
|
27
|
+
### 3. Verify the cause
|
|
28
|
+
|
|
29
|
+
Do not rely on code inspection alone. Run experiments to identify the source of the bug or bottleneck.
|
|
30
|
+
|
|
31
|
+
For debugging tasks:
|
|
32
|
+
|
|
33
|
+
1. Create a reproduction in a temporary file or REPL and run it to confirm the bug. If the reproduction does not fail, add relevant behavior from the target code until it reproduces the bug.
|
|
34
|
+
2. Remove or revert each part of the reproduction to find the smallest program that still triggers the bug. A minimal reproduction narrows the list of possible causes.
|
|
35
|
+
3. Investigate the minimal reproduction, identify the cause, and fix it. Address the root cause instead of applying a superficial workaround, as described in the Rules section.
|
|
36
|
+
4. Confirm that the fix prevents the bug in the minimal reproduction.
|
|
37
|
+
5. Apply the fix to the production code and confirm that it resolves the original bug. If the bug remains, return to the reproduction step and repeat the process.
|
|
38
|
+
|
|
39
|
+
For performance optimization tasks:
|
|
40
|
+
|
|
41
|
+
1. Use benchmarks, profilers, timing logs, or similar tools to identify the slow operation.
|
|
42
|
+
2. Fix the bottleneck, then use the same measurements to confirm that performance improved.
|
|
43
|
+
3. Apply the fix to the production code and confirm the improvement there. If performance does not improve, return to bottleneck analysis and repeat the process.
|
|
44
|
+
|
|
45
|
+
### 4. Clean up
|
|
46
|
+
|
|
47
|
+
Remove temporary logs, reproduction code, generated files, and log data created during verification.
|
|
48
|
+
|
|
49
|
+
## Rules
|
|
50
|
+
|
|
51
|
+
If a user reports that your fix did not resolve the bug or improve performance, treat your previous diagnosis as unverified and restart the investigation.
|
|
52
|
+
|
|
53
|
+
Surface-level and stop-gap fixes are shameful; identify and fix the root cause. For example, when an error triggers a bug, find and fix the source of the error instead of adding a fallback for the failure. Keep asking yourself, “Is this the root cause?”
|
|
54
|
+
|
|
55
|
+
When evidence points to a bug in a dependency, do not patch the dependency. Tell the user that the dependency appears to contain a bug, describe the evidence, and propose changes they can make in their project without modifying the dependency.
|
|
56
|
+
|
|
57
|
+
When you get stuck, try these approaches:
|
|
58
|
+
|
|
59
|
+
- Add temporary logs across the affected path. Use them to find the boundary between correct and incorrect behavior, or between fast and slow execution.
|
|
60
|
+
- When behavior contradicts the code’s logic or you cannot make progress, search the relevant library’s GitHub issues and pull requests, Stack Overflow, and community articles for reports of the same problem and possible solutions.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-loop
|
|
3
|
+
description: Thoroughly improve code quality by requesting a review from a sub-agent after completing tasks requiring code editing, before returning the conversation turn to the user. Use for any task that edits code or configuration. Read this skill before starting edits, and after completing the requested changes run an isolated sub-agent review loop before returning the final response.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Robot-Inventor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Review Loop Skill
|
|
10
|
+
|
|
11
|
+
This skill involves thoroughly improving code quality by repeatedly requesting a review from a sub-agent after editing and improving the code based on the results. Humans typically improve code quality by creating a pull request after editing code and having it reviewed by a reviewer. Such reviews are important because they can identify problems that might be missed through self-review alone.
|
|
12
|
+
|
|
13
|
+
## When to apply
|
|
14
|
+
|
|
15
|
+
Apply this skill in the following situations:
|
|
16
|
+
|
|
17
|
+
- When you have performed a task that requires code editing, and
|
|
18
|
+
- After completing the task instructed by the user, and
|
|
19
|
+
- Before returning the conversation to the user
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
|
|
23
|
+
The following is the core of this skill; please follow the specified steps.
|
|
24
|
+
|
|
25
|
+
### 1. Request a code review
|
|
26
|
+
|
|
27
|
+
Request a code review from the subagent.
|
|
28
|
+
|
|
29
|
+
If you have the option to choose whether to fork the context or launch the subagent in an isolated context when starting it, **always** launch it in an isolated context. Forking the context would be equivalent to a self-review, so you need to launch a subagent in an isolated context to review it from a different perspective.
|
|
30
|
+
|
|
31
|
+
When requesting a review from a sub-agent, be sure to provide them with an overview of the task requested by the user, and instruct them to thoroughly review the changes made to the current workspace.
|
|
32
|
+
|
|
33
|
+
### 2. Receive reviews and improve your code
|
|
34
|
+
|
|
35
|
+
Once you receive the review results from the sub-agent, consider whether each point is valid. You may ignore clearly unreasonable points, but you must address valid points and correct the code accordingly.
|
|
36
|
+
|
|
37
|
+
In particular, if you receive feedback on a part that doesn't strictly follow the user's instructions as a result of compromises, do not ignore it. Instead, correct it according to the review to align with the user's instructions as much as possible. If you absolutely cannot comply with the user's request, you may ignore the sub-agent's point about that part, but be sure to inform the user of the compromise you made when you return the conversation to them.
|
|
38
|
+
|
|
39
|
+
### 3. Review loop
|
|
40
|
+
|
|
41
|
+
After addressing valid feedback from the subagent, repeat steps 1 and 2 ("1. Request a code review" and "2. Receive reviews and improve your code") until there is no more valid feedback from the subagent, thoroughly improving your code quality. In this review loop, always have a new subagent review your code each time.
|
|
42
|
+
|
|
43
|
+
### 4. Report the results to the user
|
|
44
|
+
|
|
45
|
+
Once the review loop is complete, return the conversation to the user. Be sure to honestly report to the user any points where you had to compromise and did not strictly follow their instructions.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: simple-engineering
|
|
3
|
+
description: A skill outlining the fundamental principles to keep in mind when writing or editing code. Read this skill before developing an implementation plan or writing or reviewing code, and apply it to your work.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Robot-Inventor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Simple Engineering
|
|
10
|
+
|
|
11
|
+
## Keep it simple and elegant
|
|
12
|
+
|
|
13
|
+
Overengineering is a shameful and foolish act, and it is important to always pursue simple and elegant solutions.
|
|
14
|
+
|
|
15
|
+
Before implementing a feature or fixing a bug, consider all options. Instead of just listing similar choices or being bound by preconceptions and previous thinking, consider all options, including completely different approaches. Among the available options, choose the one that is the clearest, simplest, requires the fewest lines of code, and meets the user's requirements. There is no need to use iron plates and nails to repair torn clothes. Choose a right-sized, simple approach rather than an overly large and complex one.
|
|
16
|
+
|
|
17
|
+
Also avoid bringing unnecessary complexity into your code. Carefully examine related processing to ensure there are no impossible conditional branches or unnecessary try-catch blocks. Check the type definitions and actual processing, and remove any impossible conditional branches. There is no need to wrap code in a new try-catch block if an exception is unlikely to occur normally or if a try-catch already exists inside the called function.
|
|
18
|
+
|
|
19
|
+
## Avoid reinventing the wheel
|
|
20
|
+
|
|
21
|
+
Reinventing the wheel should be avoided. Before adding code, check whether existing code in the project or the project's dependencies already provide functionality that covers part or all of that processing. When implementing complex processing, investigate whether a popular and well-maintained library that achieves equivalent functionality exists, and if so, propose using it to the user.
|
|
22
|
+
|
|
23
|
+
## Keep only necessary changes
|
|
24
|
+
|
|
25
|
+
Keep the YAGNI (You aren't gonna need it) principle in mind. You should not introduce complexity just because you might need it in the future. Also, there is no need to maintain backward compatibility for unreleased features.
|
|
26
|
+
|
|
27
|
+
The code ultimately delivered to the user should be such that every line of change is necessary and no unnecessary code remains. Carefully review the diff line by line, rather than file by file, to ensure that all changes are truly necessary.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-master
|
|
3
|
+
description: A skill outlining the fundamental principles to keep in mind when writing or editing web-related code. Read this skill before developing an implementation plan or writing or reviewing code for web-related tasks, and apply it to your work.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Robot-Inventor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Web Master
|
|
10
|
+
|
|
11
|
+
Follow these instructions when writing web-related code.
|
|
12
|
+
|
|
13
|
+
## TypeScript
|
|
14
|
+
|
|
15
|
+
### Do not use type assertions
|
|
16
|
+
|
|
17
|
+
Do not reach for type assertions unless you need one. Never add a type assertion up front because you think you might need it later. You may use a type assertion only when all of these conditions apply:
|
|
18
|
+
|
|
19
|
+
- You first write the code without a type assertion and encounter a type error.
|
|
20
|
+
- You cannot resolve that type error by improving type definitions elsewhere.
|
|
21
|
+
- Adding the type assertion does not compromise type safety.
|
|
22
|
+
|
|
23
|
+
### Do not use `ReturnType`
|
|
24
|
+
|
|
25
|
+
Do not use `ReturnType` unless you need it. Use the following approaches instead.
|
|
26
|
+
|
|
27
|
+
If you need to use the return type of a specific function defined in the project somewhere other than that function:
|
|
28
|
+
|
|
29
|
+
- For a simple primitive type such as `boolean` or `string`, write the type directly.
|
|
30
|
+
- For a complex type, define a named type, specify it as the function's return type (`(): T => {}`), and reuse `T`.
|
|
31
|
+
|
|
32
|
+
If you need the return type of a library function:
|
|
33
|
+
|
|
34
|
+
- Check the function's type definitions first.
|
|
35
|
+
- If the library exports that type, use the exported type.
|
|
36
|
+
- If you need to pass the return type of another library function as a type argument and the library does not export that function's return type, you may use `ReturnType` inside the type argument.
|
|
37
|
+
- If the library does not export the type:
|
|
38
|
+
- Write the type directly if it is a simple primitive type.
|
|
39
|
+
- Otherwise, you may use `ReturnType`.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// Incorrect
|
|
43
|
+
import foo from "foo";
|
|
44
|
+
|
|
45
|
+
const myFunc = (): string => {...};
|
|
46
|
+
|
|
47
|
+
const myFunc2 = (arg: ReturnType<typeof foo>, arg2: ReturnType<typeof myFunc>): number => {...};
|
|
48
|
+
|
|
49
|
+
// Correct
|
|
50
|
+
import type { FooResult } from "foo";
|
|
51
|
+
|
|
52
|
+
const myFunc = (): string => {...};
|
|
53
|
+
|
|
54
|
+
const myFunc2 = (arg: FooResult, arg2: string): number => {...};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Prefer `as const satisfies` over type annotations
|
|
58
|
+
|
|
59
|
+
Do not add type annotations unless you need them. Start by writing the code without a type annotation. If that causes a type error, first try to resolve it by improving type definitions elsewhere. Use a type annotation only as a last resort when those changes cannot solve the problem.
|
|
60
|
+
|
|
61
|
+
When you write an object and need to guarantee that it conforms to a specific type, use `satisfies`. If the object will not change, also use `as const`.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// Incorrect
|
|
65
|
+
const foo: Record<string, string> = {
|
|
66
|
+
bar: "bar",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Correct
|
|
70
|
+
const foo = {
|
|
71
|
+
bar: "bar",
|
|
72
|
+
} as const satisfies T;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Do not disable ESLint rules
|
|
76
|
+
|
|
77
|
+
Do not disable ESLint rules as an easy workaround. ESLint errors usually identify low-quality code rather than create pointless obstacles. Disabling a rule to silence an error leaves the underlying problem in place.
|
|
78
|
+
|
|
79
|
+
Disable an ESLint rule only as a last resort when no better option exists. Handle ESLint errors as follows:
|
|
80
|
+
|
|
81
|
+
1. Understand what ESLint is reporting. Identify the affected code and the purpose behind the rule.
|
|
82
|
+
2. Improve the code in a way that addresses that purpose.
|
|
83
|
+
3. Disable the rule only when complying with it would make the code **excessively** complex, the warning is a clear false positive, you have another legitimate reason to disable it, or a possible fix would conflict with the rule's underlying intent.
|
|
84
|
+
|
|
85
|
+
#### Examples
|
|
86
|
+
|
|
87
|
+
The ESLint `sort-imports` rule defines a consistent import order. In properly modularized code, behavior should not depend on import order, so you should follow the rule.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// Incorrect
|
|
91
|
+
/* eslint-disable sort-imports */
|
|
92
|
+
import foo from "foo";
|
|
93
|
+
import bar from "bar";
|
|
94
|
+
|
|
95
|
+
// Correct
|
|
96
|
+
import bar from "bar";
|
|
97
|
+
import foo from "foo";
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The `no-magic-numbers` rule exists to give numeric values meaningful names when their purpose would otherwise be unclear. You do not need to force an obvious value, such as `60` when it represents the number of seconds in a minute, into a constant just to satisfy the rule. In that case, disabling the rule is acceptable. Replacing `1` with a constant named `ONE` also defeats the purpose of `no-magic-numbers`. Give the value a meaningful name instead, or disable the rule if its meaning is already obvious. Also consider whether the number needs to be hard-coded in the first place.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// Incorrect
|
|
104
|
+
// eslint-disable-next-line no-magic-numbers
|
|
105
|
+
if (myArray.length !== 0) {...}
|
|
106
|
+
|
|
107
|
+
// Correct
|
|
108
|
+
if (myArray.length) {...}
|
|
109
|
+
```
|