@bonesofspring/ai-rules 0.2.1 → 0.2.2
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/README.md +10 -20
- package/bin/cli.js +73 -260
- package/package.json +4 -13
- package/presets/claude/ios-swift/CLAUDE.md +27 -0
- package/presets/claude/ios-swift/README.md +29 -0
- package/presets/claude/ios-swift/commands/README.md +3 -0
- package/presets/claude/ios-swift/rules/README.md +44 -0
- package/presets/claude/ios-swift/rules/api-and-data/README.md +9 -0
- package/presets/claude/ios-swift/rules/api-and-data/networking.md +49 -0
- package/presets/claude/ios-swift/rules/architecture/README.md +10 -0
- package/presets/claude/ios-swift/rules/architecture/boundaries.md +33 -0
- package/presets/claude/ios-swift/rules/architecture/feature-delivery.md +60 -0
- package/presets/claude/ios-swift/rules/stack/README.md +10 -0
- package/presets/claude/ios-swift/rules/stack/ios-app-core.md +42 -0
- package/presets/claude/ios-swift/rules/stack/swift-conventions.md +51 -0
- package/presets/claude/ios-swift/rules/testing/README.md +10 -0
- package/presets/claude/ios-swift/rules/testing/ui.md +41 -0
- package/presets/claude/ios-swift/rules/testing/unit.md +41 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/README.md +11 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/code-quality.md +38 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/code-review.md +41 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/post-change-build.md +31 -0
- package/presets/claude/ios-swift/rules/ui-and-accessibility/README.md +10 -0
- package/presets/claude/ios-swift/rules/ui-and-accessibility/swiftui.md +61 -0
- package/presets/claude/ios-swift/rules/ui-and-accessibility/viewmodels.md +43 -0
- package/presets/claude/next/CLAUDE.md +5 -33
- package/presets/claude/next/README.md +0 -10
- package/presets/claude/next/agents/README.md +0 -126
- package/presets/claude/next/agents/playwright-test-generator.md +65 -16
- package/presets/claude/next/agents/playwright-test-healer.md +51 -16
- package/presets/claude/next/agents/playwright-test-planner.md +55 -15
- package/presets/claude/next/commands/README.md +2 -10
- package/presets/claude/next/hooks/README.md +2 -5
- package/presets/claude/next/rules/README.md +11 -29
- package/presets/claude/next/rules/api-and-data/README.md +1 -7
- package/presets/claude/next/rules/architecture/README.md +2 -10
- package/presets/claude/next/rules/stack/README.md +1 -10
- package/presets/claude/next/rules/testing/README.md +1 -9
- package/presets/claude/next/rules/tooling-and-review/README.md +1 -14
- package/presets/claude/next/rules/ui-and-accessibility/README.md +1 -7
- package/presets/claude/next/skills/README.md +1 -11
- package/presets/cursor/ios-swift/README.md +17 -0
- package/presets/cursor/ios-swift/commands/README.md +3 -0
- package/presets/cursor/ios-swift/rules/README.md +33 -0
- package/presets/cursor/ios-swift/rules/architecture-boundaries.mdc +34 -0
- package/presets/cursor/ios-swift/rules/code-quality-and-refactoring.mdc +39 -0
- package/presets/cursor/ios-swift/rules/code-review-mr.mdc +42 -0
- package/presets/cursor/ios-swift/rules/feature-delivery-workflow.mdc +61 -0
- package/presets/cursor/ios-swift/rules/ios-app-core.mdc +43 -0
- package/presets/cursor/ios-swift/rules/networking-services.mdc +49 -0
- package/presets/cursor/ios-swift/rules/post-change-build.mdc +32 -0
- package/presets/cursor/ios-swift/rules/state-and-viewmodels.mdc +43 -0
- package/presets/cursor/ios-swift/rules/swift-conventions.mdc +51 -0
- package/presets/cursor/ios-swift/rules/swiftui-ui.mdc +61 -0
- package/presets/cursor/ios-swift/rules/tests-ui.mdc +41 -0
- package/presets/cursor/ios-swift/rules/tests-unit.mdc +41 -0
- package/presets/cursor/next/commands/README.md +1 -49
- package/presets/cursor/next/rules/README.md +0 -67
- package/presets/cursor/next/rules/api-services.mdc +10 -12
- package/presets/cursor/next/rules/architecture-boundaries.mdc +12 -31
- package/presets/cursor/next/rules/code-quality-and-refactoring.mdc +4 -5
- package/presets/cursor/next/rules/code-review-mr.mdc +40 -21
- package/presets/cursor/next/rules/next-app-core.mdc +61 -18
- package/presets/cursor/next/rules/no-props-spread.mdc +4 -27
- package/presets/cursor/next/rules/playwright-agents.mdc +1 -2
- package/presets/cursor/next/rules/react-ui.mdc +3 -33
- package/presets/cursor/next/rules/store-rtk.mdc +6 -13
- package/presets/cursor/next/rules/tests-unit.mdc +10 -30
- package/CHANGELOG.md +0 -22
- package/CONTRIBUTING.md +0 -102
- package/fragments/BUGBOT.md +0 -23
- package/presets/claude/next/BUGBOT.md +0 -16
- package/presets/claude/next/agents/accessibility-reviewer.md +0 -65
- package/presets/claude/next/agents/api-contract-reviewer.md +0 -69
- package/presets/claude/next/agents/build-verifier.md +0 -64
- package/presets/claude/next/agents/ci-investigator.md +0 -62
- package/presets/claude/next/agents/code-reviewer.md +0 -62
- package/presets/claude/next/agents/debugger.md +0 -63
- package/presets/claude/next/agents/feature-developer.md +0 -27
- package/presets/claude/next/agents/migration-specialist.md +0 -69
- package/presets/claude/next/agents/performance-auditor.md +0 -68
- package/presets/claude/next/agents/qa-tester.md +0 -56
- package/presets/claude/next/agents/security-reviewer.md +0 -64
- package/presets/claude/next/agents/solution-architect.md +0 -70
- package/presets/claude/next/agents/task-analyst.md +0 -109
- package/presets/claude/next/agents/task-router.md +0 -94
- package/presets/claude/next/agents/tech-writer.md +0 -60
- package/presets/claude/next/agents/unit-test-generator.md +0 -37
- package/presets/claude/next/agents/unit-test-healer.md +0 -38
- package/presets/claude/next/agents/unit-test-planner.md +0 -62
- package/presets/claude/next/commands/feature-continue.md +0 -51
- package/presets/claude/next/commands/feature-start.md +0 -30
- package/presets/claude/next/commands/task-continue.md +0 -49
- package/presets/claude/next/commands/task.md +0 -50
- package/presets/claude/next/commands/technical-retro.md +0 -58
- package/presets/claude/next/hooks/chain-team-phases.sh +0 -342
- package/presets/claude/next/hooks/guard-shell-command.sh +0 -77
- package/presets/claude/next/hooks.json +0 -17
- package/presets/claude/next/rules/api-and-data/api-services.md +0 -57
- package/presets/claude/next/rules/api-and-data/http-client.md +0 -40
- package/presets/claude/next/rules/api-and-data/store-rtk.md +0 -65
- package/presets/claude/next/rules/architecture/api-public-imports.md +0 -8
- package/presets/claude/next/rules/architecture/architecture-boundaries-ui.md +0 -15
- package/presets/claude/next/rules/architecture/architecture-boundaries.md +0 -75
- package/presets/claude/next/rules/architecture/feature-delivery-workflow.md +0 -79
- package/presets/claude/next/rules/architecture/layer-barrel-exports.md +0 -58
- package/presets/claude/next/rules/architecture/public-imports.md +0 -46
- package/presets/claude/next/rules/architecture/reference-features.md +0 -37
- package/presets/claude/next/rules/architecture/types-public-imports.md +0 -8
- package/presets/claude/next/rules/stack/arrow-functions.md +0 -45
- package/presets/claude/next/rules/stack/navigation-router-ui.md +0 -15
- package/presets/claude/next/rules/stack/navigation-router.md +0 -62
- package/presets/claude/next/rules/stack/next-app-core.md +0 -33
- package/presets/claude/next/rules/stack/next-app-router.md +0 -36
- package/presets/claude/next/rules/stack/no-type-assertion.md +0 -59
- package/presets/claude/next/rules/stack/types-jsdoc.md +0 -37
- package/presets/claude/next/rules/testing/playwright-agents.md +0 -74
- package/presets/claude/next/rules/testing/tests-e2e-structure.md +0 -52
- package/presets/claude/next/rules/testing/tests-unit.md +0 -66
- package/presets/claude/next/rules/tooling-and-review/agent-team-intake.md +0 -15
- package/presets/claude/next/rules/tooling-and-review/agent-team-orchestrator.md +0 -140
- package/presets/claude/next/rules/tooling-and-review/code-quality.md +0 -42
- package/presets/claude/next/rules/tooling-and-review/code-review-mr.md +0 -30
- package/presets/claude/next/rules/tooling-and-review/package-manager.md +0 -11
- package/presets/claude/next/rules/tooling-and-review/post-change-lint.md +0 -33
- package/presets/claude/next/rules/ui-and-accessibility/css-property-order.md +0 -14
- package/presets/claude/next/rules/ui-and-accessibility/no-props-spread.md +0 -57
- package/presets/claude/next/rules/ui-and-accessibility/react-a11y-coding.md +0 -37
- package/presets/claude/next/rules/ui-and-accessibility/react-ui.md +0 -91
- package/presets/claude/next/skills/ci-investigation/SKILL.md +0 -36
- package/presets/claude/next/skills/code-review/SKILL.md +0 -26
- package/presets/claude/next/skills/debug-investigation/SKILL.md +0 -28
- package/presets/claude/next/skills/feature-delivery/SKILL.md +0 -15
- package/presets/claude/next/skills/playwright-e2e/SKILL.md +0 -31
- package/presets/claude/next/skills/technical-retro/SKILL.md +0 -40
- package/presets/claude/next/skills/unit-testing/SKILL.md +0 -32
- package/presets/claude/next/team/README.md +0 -47
- package/presets/claude/next/team/fixtures/bugfix-standard.json +0 -15
- package/presets/claude/next/team/fixtures/feature-full.json +0 -16
- package/presets/claude/next/team/fixtures/feature-light.json +0 -17
- package/presets/claude/next/team/tasks/.gitkeep +0 -1
- package/presets/cursor/next/AGENTS.md +0 -36
- package/presets/cursor/next/BUGBOT.md +0 -16
- package/presets/cursor/next/agents/README.md +0 -159
- package/presets/cursor/next/agents/accessibility-reviewer.md +0 -67
- package/presets/cursor/next/agents/api-contract-reviewer.md +0 -71
- package/presets/cursor/next/agents/build-verifier.md +0 -66
- package/presets/cursor/next/agents/ci-investigator.md +0 -64
- package/presets/cursor/next/agents/code-reviewer.md +0 -64
- package/presets/cursor/next/agents/debugger.md +0 -64
- package/presets/cursor/next/agents/feature-developer.md +0 -27
- package/presets/cursor/next/agents/migration-specialist.md +0 -71
- package/presets/cursor/next/agents/performance-auditor.md +0 -70
- package/presets/cursor/next/agents/playwright-test-generator.md +0 -26
- package/presets/cursor/next/agents/playwright-test-healer.md +0 -26
- package/presets/cursor/next/agents/playwright-test-planner.md +0 -28
- package/presets/cursor/next/agents/qa-tester.md +0 -57
- package/presets/cursor/next/agents/security-reviewer.md +0 -66
- package/presets/cursor/next/agents/solution-architect.md +0 -71
- package/presets/cursor/next/agents/task-analyst.md +0 -112
- package/presets/cursor/next/agents/task-router.md +0 -95
- package/presets/cursor/next/agents/tech-writer.md +0 -62
- package/presets/cursor/next/agents/unit-test-generator.md +0 -39
- package/presets/cursor/next/agents/unit-test-healer.md +0 -40
- package/presets/cursor/next/agents/unit-test-planner.md +0 -64
- package/presets/cursor/next/commands/feature-continue.md +0 -19
- package/presets/cursor/next/commands/feature-start.md +0 -33
- package/presets/cursor/next/commands/task-continue.md +0 -49
- package/presets/cursor/next/commands/task.md +0 -50
- package/presets/cursor/next/commands/technical-retro.md +0 -81
- package/presets/cursor/next/hooks/README.md +0 -8
- package/presets/cursor/next/hooks/chain-team-phases.sh +0 -380
- package/presets/cursor/next/hooks/guard-shell-command.sh +0 -77
- package/presets/cursor/next/hooks.json +0 -17
- package/presets/cursor/next/rules/agent-team-intake.mdc +0 -14
- package/presets/cursor/next/rules/agent-team-orchestrator.mdc +0 -139
- package/presets/cursor/next/rules/api-public-imports.mdc +0 -8
- package/presets/cursor/next/rules/architecture-boundaries-ui.mdc +0 -15
- package/presets/cursor/next/rules/arrow-functions.mdc +0 -46
- package/presets/cursor/next/rules/css-property-order-stylelint.mdc +0 -16
- package/presets/cursor/next/rules/feature-delivery-workflow.mdc +0 -76
- package/presets/cursor/next/rules/http-client.mdc +0 -42
- package/presets/cursor/next/rules/layer-barrel-exports.mdc +0 -59
- package/presets/cursor/next/rules/navigation-router-stack.mdc +0 -62
- package/presets/cursor/next/rules/navigation-router-ui.mdc +0 -16
- package/presets/cursor/next/rules/next-app-router.mdc +0 -36
- package/presets/cursor/next/rules/no-cross-component-styles-import.mdc +0 -8
- package/presets/cursor/next/rules/no-type-assertion-as-import-export.mdc +0 -60
- package/presets/cursor/next/rules/package-manager.mdc +0 -16
- package/presets/cursor/next/rules/post-change-lint.mdc +0 -38
- package/presets/cursor/next/rules/public-imports.mdc +0 -48
- package/presets/cursor/next/rules/react-a11y-coding.mdc +0 -37
- package/presets/cursor/next/rules/reference-features.mdc +0 -39
- package/presets/cursor/next/rules/technical-retro.mdc +0 -12
- package/presets/cursor/next/rules/types-jsdoc.mdc +0 -42
- package/presets/cursor/next/rules/types-public-imports.mdc +0 -8
- package/presets/cursor/next/skills/README.md +0 -15
- package/presets/cursor/next/skills/ci-investigation/SKILL.md +0 -36
- package/presets/cursor/next/skills/code-review/SKILL.md +0 -26
- package/presets/cursor/next/skills/debug-investigation/SKILL.md +0 -28
- package/presets/cursor/next/skills/feature-delivery/SKILL.md +0 -15
- package/presets/cursor/next/skills/playwright-e2e/SKILL.md +0 -31
- package/presets/cursor/next/skills/technical-retro/SKILL.md +0 -40
- package/presets/cursor/next/skills/unit-testing/SKILL.md +0 -32
- package/presets/cursor/next/team/README.md +0 -105
- package/presets/cursor/next/team/fixtures/bugfix-standard.json +0 -15
- package/presets/cursor/next/team/fixtures/feature-full.json +0 -16
- package/presets/cursor/next/team/fixtures/feature-light.json +0 -17
- package/presets/cursor/next/team/tasks/.gitkeep +0 -0
- package/scripts/README.md +0 -84
- package/scripts/golden-prompts.json +0 -276
- package/scripts/preset-manifest.json +0 -72
- package/scripts/regression-results/.gitkeep +0 -0
- package/scripts/validate-preset.sh +0 -484
package/README.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# @bonesofspring/ai-rules
|
|
2
2
|
|
|
3
|
-
Presets for Cursor and Claude Code: rules, commands, plus
|
|
3
|
+
Presets for Cursor and Claude Code: rules, commands, plus for Claude `CLAUDE.md` and `skills` / `agents` / `hooks` when the preset includes them. Install from npm into a project so you don’t have to copy files out of the repo by hand.
|
|
4
4
|
|
|
5
5
|
Node 18+.
|
|
6
6
|
|
|
7
|
-
Contributing and Cursor↔Claude sync checklist: **[CONTRIBUTING.md](./CONTRIBUTING.md)**.
|
|
8
|
-
|
|
9
7
|
```bash
|
|
10
8
|
# from the project root
|
|
11
9
|
npx @bonesofspring/ai-rules init cursor --preset next
|
|
@@ -15,18 +13,9 @@ npx @bonesofspring/ai-rules clean cursor
|
|
|
15
13
|
|
|
16
14
|
Global: `npm i -g @bonesofspring/ai-rules`, then `ai-rules ...`.
|
|
17
15
|
|
|
18
|
-
`init` merges into the
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npx @bonesofspring/ai-rules init cursor --preset next
|
|
22
|
-
npx @bonesofspring/ai-rules init cursor next
|
|
23
|
-
npx @bonesofspring/ai-rules init cursor # preset next by default
|
|
24
|
-
npx @bonesofspring/ai-rules cursor init --preset next
|
|
25
|
-
```
|
|
16
|
+
`init` merges into the current directory: dirs are created, matching files are **overwritten**. `--preset` and the subcommand can be in either order, e.g. `--preset=next init cursor`.
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`clean cursor` / `clean claude` removes what this CLI installs: `rules`, `commands`, optional `agents`, `hooks`, `skills`, `team`, Cursor `hooks.json` / `BUGBOT.md`, and Claude `CLAUDE.md` when present. Other files under `.cursor/` or `.claude/` are left intact.
|
|
18
|
+
`clean cursor` / `clean claude` removes what this CLI installs: for Cursor, the entire `.cursor` directory; for Claude, `.claude/rules`, `.claude/commands`, `skills`, `agents`, `hooks`, and `CLAUDE.md` when present. Anything else under `.claude` is left alone.
|
|
30
19
|
|
|
31
20
|
Where things go on `init`:
|
|
32
21
|
|
|
@@ -34,13 +23,14 @@ Where things go on `init`:
|
|
|
34
23
|
|---|--------|--------|
|
|
35
24
|
| rules | `.cursor/rules` | `.claude/rules` |
|
|
36
25
|
| commands | `.cursor/commands` | `.claude/commands` |
|
|
37
|
-
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| team artifacts template | `.cursor/team` | `.claude/team` |
|
|
41
|
-
| other | root `BUGBOT.md` → `.cursor/BUGBOT.md` | root `CLAUDE.md` → `.claude/CLAUDE.md` |
|
|
26
|
+
| other | — | root `CLAUDE.md` → `.claude/CLAUDE.md`; folders `skills`, `agents`, `hooks` → under `.claude/` with the same names |
|
|
27
|
+
|
|
28
|
+
The package ships presets including **`next`** (Next.js stack) and **`ios-swift`** (iOS apps on Swift / SwiftUI). Other preset names: see `presets/` in the [repo](https://github.com/bonesofspring/ai-rules). Name is a single path segment, no `/` or `..`.
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
```bash
|
|
31
|
+
npx @bonesofspring/ai-rules init cursor --preset ios-swift
|
|
32
|
+
npx @bonesofspring/ai-rules init claude --preset ios-swift
|
|
33
|
+
```
|
|
44
34
|
|
|
45
35
|
Help: `ai-rules --help`.
|
|
46
36
|
|
package/bin/cli.js
CHANGED
|
@@ -5,88 +5,51 @@
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
|
+
const packageRoot = path.join(__dirname, '..');
|
|
9
|
+
|
|
8
10
|
/** Single path segment: letters, digits, dot, underscore, hyphen; no separators or "..". */
|
|
9
11
|
const PRESET_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
10
|
-
const DEFAULT_PRESET = 'next';
|
|
11
|
-
const TOOLS = new Set(['cursor', 'claude']);
|
|
12
12
|
|
|
13
13
|
const PRESETS = {
|
|
14
14
|
cursor: {
|
|
15
15
|
rulesDir: '.cursor/rules',
|
|
16
16
|
commandsDir: '.cursor/commands',
|
|
17
17
|
relBase: ['presets', 'cursor'],
|
|
18
|
-
optionalCursorDirs: ['agents', 'hooks', 'skills', 'team'],
|
|
19
|
-
hooksJsonSrc: 'hooks.json',
|
|
20
|
-
toolRootFiles: ['BUGBOT.md', 'AGENTS.md'],
|
|
21
18
|
},
|
|
22
19
|
claude: {
|
|
23
20
|
rulesDir: '.claude/rules',
|
|
24
21
|
commandsDir: '.claude/commands',
|
|
25
22
|
relBase: ['presets', 'claude'],
|
|
26
|
-
|
|
23
|
+
/** Optional dirs under preset root → under `.claude/` (recursive copy). */
|
|
24
|
+
optionalClaudeDirs: ['skills', 'agents', 'hooks'],
|
|
25
|
+
/** Copied from preset root to `.claude/CLAUDE.md` when present. */
|
|
27
26
|
claudeMdSrc: 'CLAUDE.md',
|
|
28
|
-
hooksJsonSrc: 'hooks.json',
|
|
29
|
-
toolRootFiles: ['BUGBOT.md'],
|
|
30
27
|
},
|
|
31
28
|
};
|
|
32
29
|
|
|
33
|
-
function getPackageRoot() {
|
|
34
|
-
try {
|
|
35
|
-
return path.dirname(require.resolve('@bonesofspring/ai-rules/package.json'));
|
|
36
|
-
} catch {
|
|
37
|
-
return path.join(__dirname, '..');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
30
|
function printHelp() {
|
|
42
31
|
console.log(`@bonesofspring/ai-rules
|
|
43
32
|
|
|
44
33
|
Usage:
|
|
45
|
-
ai-rules init <cursor|claude>
|
|
46
|
-
ai-rules
|
|
47
|
-
ai-rules clean <cursor|claude> [--cwd <dir>]
|
|
48
|
-
|
|
49
|
-
Options:
|
|
50
|
-
--preset <name> Preset name (default: ${DEFAULT_PRESET})
|
|
51
|
-
--cwd <dir> Target project directory (default: current working directory)
|
|
34
|
+
ai-rules init <cursor|claude> --preset <name> Copy preset rules and commands into the current directory
|
|
35
|
+
ai-rules clean <cursor|claude> Remove preset target rules and commands directories
|
|
52
36
|
|
|
53
37
|
Notes:
|
|
54
|
-
init merges into
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Command forms: "init cursor --preset next", "init cursor next", "--preset next init cursor", "cursor init --preset next".
|
|
38
|
+
init merges into existing target folders; same-named files are overwritten.
|
|
39
|
+
clean cursor removes the entire .cursor directory (if present).
|
|
40
|
+
Flags may appear before or after the subcommand (e.g. --preset next init cursor).
|
|
58
41
|
|
|
59
42
|
Examples:
|
|
60
43
|
npx @bonesofspring/ai-rules init cursor --preset next
|
|
61
|
-
npx @bonesofspring/ai-rules init
|
|
62
|
-
npx @bonesofspring/ai-rules
|
|
63
|
-
npx @bonesofspring/ai-rules clean cursor
|
|
44
|
+
npx @bonesofspring/ai-rules init claude --preset ios-swift
|
|
45
|
+
npx @bonesofspring/ai-rules clean claude
|
|
64
46
|
`);
|
|
65
47
|
}
|
|
66
48
|
|
|
67
49
|
function parseArgs(argv) {
|
|
68
|
-
const args = { _: []
|
|
50
|
+
const args = { _: [] };
|
|
69
51
|
for (let i = 0; i < argv.length; i++) {
|
|
70
52
|
const a = argv[i];
|
|
71
|
-
if (a === '--cwd') {
|
|
72
|
-
const value = argv[i + 1];
|
|
73
|
-
if (value === undefined || value.startsWith('-')) {
|
|
74
|
-
args.cwdError = 'Missing value for --cwd';
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
args.cwd = value;
|
|
78
|
-
i++;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (a.startsWith('--cwd=')) {
|
|
82
|
-
const value = a.slice('--cwd='.length);
|
|
83
|
-
if (value === '') {
|
|
84
|
-
args.cwdError = 'Missing value after --cwd=';
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
args.cwd = value;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
53
|
if (a === '--preset') {
|
|
91
54
|
const value = argv[i + 1];
|
|
92
55
|
if (value === undefined || value.startsWith('-')) {
|
|
@@ -107,53 +70,17 @@ function parseArgs(argv) {
|
|
|
107
70
|
args.preset = value;
|
|
108
71
|
continue;
|
|
109
72
|
}
|
|
110
|
-
if (a === '-h' || a === '--help') {
|
|
111
|
-
args.help = true;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
73
|
args._.push(a);
|
|
115
74
|
}
|
|
116
75
|
return args;
|
|
117
76
|
}
|
|
118
77
|
|
|
119
|
-
/**
|
|
120
|
-
* Supports: init cursor, cursor init, init cursor next (positional preset).
|
|
121
|
-
*/
|
|
122
|
-
function normalizeInitClean(parsed) {
|
|
123
|
-
const positional = [...parsed._];
|
|
124
|
-
let command;
|
|
125
|
-
let tool;
|
|
126
|
-
let presetFromPositional;
|
|
127
|
-
|
|
128
|
-
if (positional.length >= 2 && TOOLS.has(positional[0]) && positional[1] === 'init') {
|
|
129
|
-
command = 'init';
|
|
130
|
-
tool = positional[0];
|
|
131
|
-
presetFromPositional = positional[2];
|
|
132
|
-
} else if (positional.length >= 2 && positional[0] === 'init' && TOOLS.has(positional[1])) {
|
|
133
|
-
command = 'init';
|
|
134
|
-
tool = positional[1];
|
|
135
|
-
presetFromPositional = positional[2];
|
|
136
|
-
} else if (positional.length >= 2 && positional[0] === 'clean' && TOOLS.has(positional[1])) {
|
|
137
|
-
command = 'clean';
|
|
138
|
-
tool = positional[1];
|
|
139
|
-
} else if (positional.length >= 1) {
|
|
140
|
-
command = positional[0];
|
|
141
|
-
tool = positional[1];
|
|
142
|
-
if (command === 'init' && positional[2] && PRESET_NAME_RE.test(positional[2])) {
|
|
143
|
-
presetFromPositional = positional[2];
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const preset = parsed.preset || presetFromPositional;
|
|
148
|
-
return { command, tool, preset };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
78
|
/**
|
|
152
79
|
* @returns {string | null} Error message or null if valid.
|
|
153
80
|
*/
|
|
154
81
|
function validatePresetName(preset) {
|
|
155
82
|
if (preset === undefined || preset === '') {
|
|
156
|
-
return 'Missing
|
|
83
|
+
return 'Missing --preset <name>';
|
|
157
84
|
}
|
|
158
85
|
if (!PRESET_NAME_RE.test(preset) || preset.includes('..')) {
|
|
159
86
|
return (
|
|
@@ -164,6 +91,9 @@ function validatePresetName(preset) {
|
|
|
164
91
|
return null;
|
|
165
92
|
}
|
|
166
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Ensures resolved preset dir stays under the tool presets root (defense in depth).
|
|
96
|
+
*/
|
|
167
97
|
function assertPresetDirInsideRoot(presetsRoot, presetBase) {
|
|
168
98
|
const root = path.resolve(presetsRoot);
|
|
169
99
|
const resolved = path.resolve(presetBase);
|
|
@@ -187,17 +117,10 @@ function copyDir(src, dest) {
|
|
|
187
117
|
copyDir(from, to);
|
|
188
118
|
} else {
|
|
189
119
|
fs.copyFileSync(from, to);
|
|
190
|
-
if (e.name.endsWith('.sh')) {
|
|
191
|
-
fs.chmodSync(to, 0o755);
|
|
192
|
-
}
|
|
193
120
|
}
|
|
194
121
|
}
|
|
195
122
|
}
|
|
196
123
|
|
|
197
|
-
function getOptionalDirs(cfg) {
|
|
198
|
-
return cfg.optionalCursorDirs || cfg.optionalClaudeDirs || [];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
124
|
function removeDirIfExists(dir) {
|
|
202
125
|
if (fs.existsSync(dir)) {
|
|
203
126
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
@@ -210,157 +133,76 @@ function removeFileIfExists(filePath) {
|
|
|
210
133
|
}
|
|
211
134
|
}
|
|
212
135
|
|
|
213
|
-
function listPresetNames(presetsRoot) {
|
|
214
|
-
if (!fs.existsSync(presetsRoot)) {
|
|
215
|
-
return [];
|
|
216
|
-
}
|
|
217
|
-
return fs
|
|
218
|
-
.readdirSync(presetsRoot, { withFileTypes: true })
|
|
219
|
-
.filter((e) => e.isDirectory())
|
|
220
|
-
.map((e) => e.name)
|
|
221
|
-
.filter((name) => PRESET_NAME_RE.test(name));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
136
|
function presetHasAnySource(base, cfg) {
|
|
225
137
|
const rulesSrc = path.join(base, 'rules');
|
|
226
138
|
const commandsSrc = path.join(base, 'commands');
|
|
227
139
|
if (fs.existsSync(rulesSrc) || fs.existsSync(commandsSrc)) {
|
|
228
140
|
return true;
|
|
229
141
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
142
|
+
if (cfg.optionalClaudeDirs) {
|
|
143
|
+
for (const name of cfg.optionalClaudeDirs) {
|
|
144
|
+
if (fs.existsSync(path.join(base, name))) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
233
147
|
}
|
|
234
148
|
}
|
|
235
149
|
if (cfg.claudeMdSrc && fs.existsSync(path.join(base, cfg.claudeMdSrc))) {
|
|
236
150
|
return true;
|
|
237
151
|
}
|
|
238
|
-
if (cfg.hooksJsonSrc && fs.existsSync(path.join(base, cfg.hooksJsonSrc))) {
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
for (const name of cfg.toolRootFiles || []) {
|
|
242
|
-
if (fs.existsSync(path.join(base, name))) {
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
152
|
return false;
|
|
247
153
|
}
|
|
248
154
|
|
|
249
|
-
function
|
|
250
|
-
const
|
|
251
|
-
if (!
|
|
252
|
-
console.error(`
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
if (!fs.statSync(cwd).isDirectory()) {
|
|
256
|
-
console.error(`Target path is not a directory: ${cwd}`);
|
|
155
|
+
function cmdInit(tool, preset) {
|
|
156
|
+
const cfg = PRESETS[tool];
|
|
157
|
+
if (!cfg) {
|
|
158
|
+
console.error(`Unknown tool: ${tool}. Use cursor or claude.`);
|
|
257
159
|
process.exit(1);
|
|
258
160
|
}
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function resolvePresetName(tool, preset, packageRoot) {
|
|
263
|
-
const cfg = PRESETS[tool];
|
|
264
|
-
const presetsRoot = path.join(packageRoot, ...cfg.relBase);
|
|
265
|
-
let name = preset || DEFAULT_PRESET;
|
|
266
|
-
|
|
267
|
-
const nameErr = validatePresetName(name);
|
|
161
|
+
const nameErr = validatePresetName(preset);
|
|
268
162
|
if (nameErr) {
|
|
269
163
|
console.error(nameErr);
|
|
270
164
|
process.exit(1);
|
|
271
165
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!presetHasAnySource(base, cfg)) {
|
|
275
|
-
const available = listPresetNames(presetsRoot);
|
|
276
|
-
if (!preset && available.includes(DEFAULT_PRESET)) {
|
|
277
|
-
name = DEFAULT_PRESET;
|
|
278
|
-
base = path.join(presetsRoot, name);
|
|
279
|
-
} else {
|
|
280
|
-
console.error(`Preset "${name}" for ${tool} not found.`);
|
|
281
|
-
console.error(`Looked in: ${base}`);
|
|
282
|
-
console.error(`Package root: ${packageRoot}`);
|
|
283
|
-
if (available.length > 0) {
|
|
284
|
-
console.error(`Available presets: ${available.join(', ')}`);
|
|
285
|
-
} else {
|
|
286
|
-
console.error(`No presets directory at: ${presetsRoot}`);
|
|
287
|
-
console.error(
|
|
288
|
-
'The installed package may be incomplete. Reinstall @bonesofspring/ai-rules or run from the package source.',
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
process.exit(1);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
166
|
+
const presetsRoot = path.join(packageRoot, ...cfg.relBase);
|
|
167
|
+
const base = path.join(presetsRoot, preset);
|
|
295
168
|
assertPresetDirInsideRoot(presetsRoot, base);
|
|
296
|
-
return { name, base, presetsRoot };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function cmdInit(tool, preset, cwdOption) {
|
|
300
|
-
const cfg = PRESETS[tool];
|
|
301
|
-
if (!cfg) {
|
|
302
|
-
console.error(`Unknown tool: ${tool}. Use cursor or claude.`);
|
|
303
|
-
process.exit(1);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const packageRoot = getPackageRoot();
|
|
307
|
-
const cwd = resolveTargetCwd(cwdOption);
|
|
308
|
-
const { name: presetName, base } = resolvePresetName(tool, preset, packageRoot);
|
|
309
169
|
|
|
310
170
|
const rulesSrc = path.join(base, 'rules');
|
|
311
171
|
const commandsSrc = path.join(base, 'commands');
|
|
312
|
-
const
|
|
313
|
-
const toolRootAbs = path.join(cwd, toolRoot);
|
|
314
|
-
|
|
315
|
-
fs.mkdirSync(toolRootAbs, { recursive: true });
|
|
172
|
+
const cwd = process.cwd();
|
|
316
173
|
|
|
317
|
-
|
|
174
|
+
if (!presetHasAnySource(base, cfg)) {
|
|
175
|
+
console.error(`Preset "${preset}" for ${tool} not found under ${base}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
318
178
|
|
|
319
179
|
try {
|
|
320
180
|
if (tool === 'claude' && cfg.claudeMdSrc) {
|
|
321
181
|
const mdFrom = path.join(base, cfg.claudeMdSrc);
|
|
322
182
|
if (fs.existsSync(mdFrom)) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
183
|
+
const claudeRoot = path.join(cwd, '.claude');
|
|
184
|
+
fs.mkdirSync(claudeRoot, { recursive: true });
|
|
185
|
+
fs.copyFileSync(mdFrom, path.join(claudeRoot, 'CLAUDE.md'));
|
|
186
|
+
console.log('Copied CLAUDE.md → .claude/CLAUDE.md');
|
|
326
187
|
}
|
|
327
188
|
}
|
|
328
189
|
|
|
329
190
|
if (fs.existsSync(rulesSrc)) {
|
|
330
191
|
copyDir(rulesSrc, path.join(cwd, cfg.rulesDir));
|
|
331
192
|
console.log(`Copied rules → ${cfg.rulesDir}`);
|
|
332
|
-
copiedSteps++;
|
|
333
193
|
}
|
|
334
194
|
if (fs.existsSync(commandsSrc)) {
|
|
335
195
|
copyDir(commandsSrc, path.join(cwd, cfg.commandsDir));
|
|
336
196
|
console.log(`Copied commands → ${cfg.commandsDir}`);
|
|
337
|
-
copiedSteps++;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
for (const name of getOptionalDirs(cfg)) {
|
|
341
|
-
const src = path.join(base, name);
|
|
342
|
-
if (fs.existsSync(src)) {
|
|
343
|
-
copyDir(src, path.join(toolRootAbs, name));
|
|
344
|
-
console.log(`Copied ${name}/ → ${toolRoot}/${name}/`);
|
|
345
|
-
copiedSteps++;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (cfg.hooksJsonSrc) {
|
|
350
|
-
const hooksFrom = path.join(base, cfg.hooksJsonSrc);
|
|
351
|
-
if (fs.existsSync(hooksFrom)) {
|
|
352
|
-
fs.copyFileSync(hooksFrom, path.join(toolRootAbs, 'hooks.json'));
|
|
353
|
-
console.log(`Copied hooks.json → ${toolRoot}/hooks.json`);
|
|
354
|
-
copiedSteps++;
|
|
355
|
-
}
|
|
356
197
|
}
|
|
357
198
|
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
fs.
|
|
362
|
-
|
|
363
|
-
|
|
199
|
+
if (tool === 'claude' && cfg.optionalClaudeDirs) {
|
|
200
|
+
for (const name of cfg.optionalClaudeDirs) {
|
|
201
|
+
const src = path.join(base, name);
|
|
202
|
+
if (fs.existsSync(src)) {
|
|
203
|
+
copyDir(src, path.join(cwd, '.claude', name));
|
|
204
|
+
console.log(`Copied ${name}/ → .claude/${name}/`);
|
|
205
|
+
}
|
|
364
206
|
}
|
|
365
207
|
}
|
|
366
208
|
} catch (err) {
|
|
@@ -368,95 +210,66 @@ function cmdInit(tool, preset, cwdOption) {
|
|
|
368
210
|
console.error(`init failed: ${msg}`);
|
|
369
211
|
process.exit(1);
|
|
370
212
|
}
|
|
371
|
-
|
|
372
|
-
if (copiedSteps === 0) {
|
|
373
|
-
console.error(`init failed: preset "${presetName}" had no files to copy from ${base}`);
|
|
374
|
-
process.exit(1);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (!fs.existsSync(toolRootAbs)) {
|
|
378
|
-
console.error(`init failed: ${toolRootAbs} was not created`);
|
|
379
|
-
process.exit(1);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
console.log(`Done. ${toolRoot} installed at ${toolRootAbs} (preset: ${presetName})`);
|
|
383
213
|
}
|
|
384
214
|
|
|
385
|
-
function cmdClean(tool
|
|
215
|
+
function cmdClean(tool) {
|
|
386
216
|
const cfg = PRESETS[tool];
|
|
387
217
|
if (!cfg) {
|
|
388
218
|
console.error(`Unknown tool: ${tool}. Use cursor or claude.`);
|
|
389
219
|
process.exit(1);
|
|
390
220
|
}
|
|
391
|
-
const cwd =
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
221
|
+
const cwd = process.cwd();
|
|
222
|
+
if (tool === 'cursor') {
|
|
223
|
+
removeDirIfExists(path.join(cwd, '.cursor'));
|
|
224
|
+
console.log('Removed .cursor (if present)');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
395
227
|
removeDirIfExists(path.join(cwd, cfg.rulesDir));
|
|
396
228
|
removeDirIfExists(path.join(cwd, cfg.commandsDir));
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
removeFileIfExists(path.join(cwd, toolRoot, 'hooks.json'));
|
|
411
|
-
removedParts.push(`${toolRoot}/hooks.json`);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
for (const name of cfg.toolRootFiles || []) {
|
|
415
|
-
removeFileIfExists(path.join(cwd, toolRoot, name));
|
|
416
|
-
removedParts.push(`${toolRoot}/${name}`);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
console.log(`Removed ${removedParts.join(', ')} (if present)`);
|
|
229
|
+
if (tool === 'claude' && cfg.optionalClaudeDirs) {
|
|
230
|
+
for (const name of cfg.optionalClaudeDirs) {
|
|
231
|
+
removeDirIfExists(path.join(cwd, '.claude', name));
|
|
232
|
+
}
|
|
233
|
+
removeFileIfExists(path.join(cwd, '.claude', 'CLAUDE.md'));
|
|
234
|
+
}
|
|
235
|
+
console.log(
|
|
236
|
+
`Removed ${cfg.rulesDir} and ${cfg.commandsDir}` +
|
|
237
|
+
(tool === 'claude' && cfg.optionalClaudeDirs
|
|
238
|
+
? `, optional .claude/{${cfg.optionalClaudeDirs.join(',')}}, and .claude/CLAUDE.md`
|
|
239
|
+
: '') +
|
|
240
|
+
' (if present)',
|
|
241
|
+
);
|
|
420
242
|
}
|
|
421
243
|
|
|
422
244
|
function main() {
|
|
423
245
|
const argv = process.argv.slice(2);
|
|
424
|
-
if (argv.length === 0) {
|
|
246
|
+
if (argv.length === 0 || argv.includes('-h') || argv.includes('--help')) {
|
|
425
247
|
printHelp();
|
|
426
248
|
process.exit(0);
|
|
427
249
|
}
|
|
428
250
|
|
|
429
251
|
const parsed = parseArgs(argv);
|
|
430
|
-
|
|
431
|
-
printHelp();
|
|
432
|
-
process.exit(0);
|
|
433
|
-
}
|
|
252
|
+
const [command, tool] = parsed._;
|
|
434
253
|
|
|
435
254
|
if (parsed.presetError) {
|
|
436
255
|
console.error(parsed.presetError);
|
|
437
256
|
process.exit(1);
|
|
438
257
|
}
|
|
439
|
-
if (parsed.cwdError) {
|
|
440
|
-
console.error(parsed.cwdError);
|
|
441
|
-
process.exit(1);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const { command, tool, preset } = normalizeInitClean(parsed);
|
|
445
258
|
|
|
446
259
|
if (command === 'init') {
|
|
447
|
-
if (!tool
|
|
448
|
-
console.error('Missing
|
|
260
|
+
if (!tool) {
|
|
261
|
+
console.error('Missing <cursor|claude> after init.');
|
|
449
262
|
process.exit(1);
|
|
450
263
|
}
|
|
451
|
-
cmdInit(tool,
|
|
264
|
+
cmdInit(tool, parsed.preset);
|
|
452
265
|
return;
|
|
453
266
|
}
|
|
454
267
|
if (command === 'clean') {
|
|
455
|
-
if (!tool
|
|
456
|
-
console.error('Missing
|
|
268
|
+
if (!tool) {
|
|
269
|
+
console.error('Missing <cursor|claude> after clean.');
|
|
457
270
|
process.exit(1);
|
|
458
271
|
}
|
|
459
|
-
cmdClean(tool
|
|
272
|
+
cmdClean(tool);
|
|
460
273
|
return;
|
|
461
274
|
}
|
|
462
275
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bonesofspring/ai-rules",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Presets of Cursor and Claude rules/commands for Revy Ross personal use",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Revy Ross",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "
|
|
9
|
+
"url": "https://github.com/bonesofspring/ai-rules.git"
|
|
10
10
|
},
|
|
11
11
|
"keywords": [
|
|
12
12
|
"cursor",
|
|
@@ -16,21 +16,12 @@
|
|
|
16
16
|
"bonesofspring"
|
|
17
17
|
],
|
|
18
18
|
"bin": {
|
|
19
|
-
"ai-rules": "bin/cli.js"
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"validate": "bash scripts/validate-preset.sh",
|
|
23
|
-
"validate:init": "bash scripts/validate-preset.sh --init-smoke",
|
|
24
|
-
"validate:golden": "bash scripts/validate-preset.sh --check-golden"
|
|
19
|
+
"ai-rules": "./bin/cli.js"
|
|
25
20
|
},
|
|
26
21
|
"files": [
|
|
27
22
|
"README.md",
|
|
28
|
-
"CHANGELOG.md",
|
|
29
|
-
"CONTRIBUTING.md",
|
|
30
23
|
"bin",
|
|
31
|
-
"
|
|
32
|
-
"presets",
|
|
33
|
-
"scripts"
|
|
24
|
+
"presets"
|
|
34
25
|
],
|
|
35
26
|
"engines": {
|
|
36
27
|
"node": ">=18"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# iOS Swift stack — project instructions
|
|
2
|
+
|
|
3
|
+
Краткая точка входа для Claude Code. Подробные правила — модульные topic `.md` под `rules/` (derived из Cursor SoT).
|
|
4
|
+
|
|
5
|
+
See @rules/README.md for the full Cursor → Claude mapping and sync checklist.
|
|
6
|
+
|
|
7
|
+
## Where to look
|
|
8
|
+
|
|
9
|
+
| Area | Folder |
|
|
10
|
+
|------|--------|
|
|
11
|
+
| Stack / conventions | @rules/stack/ |
|
|
12
|
+
| Architecture / feature delivery | @rules/architecture/ |
|
|
13
|
+
| Networking / data | @rules/api-and-data/ |
|
|
14
|
+
| SwiftUI / ViewModels | @rules/ui-and-accessibility/ |
|
|
15
|
+
| Tests | @rules/testing/ |
|
|
16
|
+
| Quality / review / build | @rules/tooling-and-review/ |
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- Одна тема на файл; path scope — frontmatter `paths:` (из Cursor `globs:`).
|
|
21
|
+
- SoT: `presets/cursor/ios-swift/rules/*.mdc`. Change Cursor → update Claude twin same PR.
|
|
22
|
+
- Slash-команды — в `commands/`; skills — когда появятся в пресете.
|
|
23
|
+
|
|
24
|
+
## Architecture (postcard)
|
|
25
|
+
|
|
26
|
+
- **Presentation** → **Domain** ← **Data**; DI в `App/`.
|
|
27
|
+
- Views тонкие; ViewModels на `@MainActor`; DTO не покидают Data.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Preset `ios-swift` (Claude Code)
|
|
2
|
+
|
|
3
|
+
Правила для Swift, SwiftUI, слоистой архитектуры и тестов (XCTest, XCUITest).
|
|
4
|
+
|
|
5
|
+
Структура отличается от пресета Cursor: здесь повторяется типичный **layout проекта Claude Code** под `.claude/`, а не плоский список `.mdc` в `rules/`.
|
|
6
|
+
|
|
7
|
+
| В пресете | Куда копирует `ai-rules init claude --preset ios-swift` |
|
|
8
|
+
|-----------|---------------------------------------------------------|
|
|
9
|
+
| `CLAUDE.md` | `.claude/CLAUDE.md` (точка входа, грузится в начале сессии) |
|
|
10
|
+
| `rules/**` | `.claude/rules/**` (рекурсивно: подпапки по темам, см. [документацию](https://code.claude.com/docs/en/memory#organize-rules-with-clauderules)) |
|
|
11
|
+
| `commands/**` | `.claude/commands/**` |
|
|
12
|
+
| `skills/**` | `.claude/skills/**` (если папка есть) |
|
|
13
|
+
| `agents/**` | `.claude/agents/**` (если папка есть) |
|
|
14
|
+
| `hooks/**` | `.claude/hooks/**` (если папка есть) |
|
|
15
|
+
|
|
16
|
+
Файлы правил — обычный Markdown с опциональным YAML frontmatter (`paths:` для скоупа по путям), не формат `.mdc` Cursor.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @bonesofspring/ai-rules init claude --preset ios-swift
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Parity with Cursor
|
|
23
|
+
|
|
24
|
+
- **SoT:** `presets/cursor/ios-swift/rules/*.mdc`
|
|
25
|
+
- **Claude twins:** topic `.md` under `rules/**` (bodies derived from Cursor; frontmatter uses `paths:` instead of `globs:` / `alwaysApply`)
|
|
26
|
+
- **Mapping + sync checklist:** [`rules/README.md`](./rules/README.md)
|
|
27
|
+
- **Entry index:** [`CLAUDE.md`](./CLAUDE.md) (thin — no second long-form SoT)
|
|
28
|
+
|
|
29
|
+
When editing rules: change Cursor first, then update the Claude twin in the same PR.
|