@ankhorage/devtools 1.8.4 → 1.9.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/README.md +30 -3
- package/dist/cli/commands.d.ts +2 -2
- package/dist/cli/commands.js +4 -0
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +5 -5
- package/dist/cli/runRepositoryCommand.js +20 -0
- package/dist/internal/readmeDocs.js +5 -0
- package/dist/tools/agents/index.d.ts +6 -0
- package/dist/tools/agents/index.js +70 -0
- package/dist/tools/shared/managedFiles.d.ts +2 -2
- package/dist/tools/skills/assets/ankhorage-coding-rules/SKILL.md +81 -0
- package/dist/tools/skills/assets/ankhorage-coding-rules/agents/openai.yaml +7 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/SKILL.md +90 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/agents/openai.yaml +7 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/cli.md +117 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/expo-apps.md +43 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/hexagonal-architecture.md +120 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/migration.md +61 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/repository-profiles.md +91 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/skill-distribution.md +115 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/studio.md +134 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/ui-libraries.md +79 -0
- package/dist/tools/skills/assets/ankhorage-project-structure/references/utilities.md +62 -0
- package/dist/tools/skills/managed.d.ts +5 -0
- package/dist/tools/skills/managed.js +224 -0
- package/dist/tools/skills/manifest.d.ts +16 -0
- package/dist/tools/skills/manifest.js +95 -0
- package/dist/tools/workflows/files/renovate.yml +6 -3
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ src/
|
|
|
11
11
|
├── cli/
|
|
12
12
|
├── policy/
|
|
13
13
|
└── tools/
|
|
14
|
+
├── agents/
|
|
15
|
+
├── skills/
|
|
14
16
|
├── eslint/
|
|
15
17
|
├── prettier/
|
|
16
18
|
├── knip/
|
|
@@ -21,6 +23,8 @@ src/
|
|
|
21
23
|
|
|
22
24
|
- `policy`: shared repository runtime policy, including the canonical Bun version
|
|
23
25
|
- `changesets`: package-resolved Changesets execution and release command policy
|
|
26
|
+
- `agents`: canonical repository `AGENTS.md` rendered from stable package identity
|
|
27
|
+
- `skills`: immutable Ankhorage-owned repository skills under `.agents/skills/`
|
|
24
28
|
- `eslint`: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
|
|
25
29
|
- `prettier`: shared Prettier configuration and the bundled Prettier runner
|
|
26
30
|
- `knip`: shared Knip configuration helpers and the bundled Knip runner
|
|
@@ -61,6 +65,10 @@ The package is discovered under the `devtools` category and exposes these capabi
|
|
|
61
65
|
- `devtools.knip`
|
|
62
66
|
- `devtools.sync`
|
|
63
67
|
- `devtools.status`
|
|
68
|
+
- `devtools.agents.sync`
|
|
69
|
+
- `devtools.agents.status`
|
|
70
|
+
- `devtools.skills.sync`
|
|
71
|
+
- `devtools.skills.status`
|
|
64
72
|
- `devtools.eslint.sync`
|
|
65
73
|
- `devtools.eslint.status`
|
|
66
74
|
- `devtools.prettier.sync`
|
|
@@ -125,6 +133,8 @@ ankh devtools status .
|
|
|
125
133
|
Synchronize one concern:
|
|
126
134
|
|
|
127
135
|
```bash
|
|
136
|
+
ankh devtools agents sync .
|
|
137
|
+
ankh devtools skills sync .
|
|
128
138
|
ankh devtools eslint sync .
|
|
129
139
|
ankh devtools prettier sync .
|
|
130
140
|
ankh devtools knip sync .
|
|
@@ -136,6 +146,8 @@ ankh devtools vscode sync .
|
|
|
136
146
|
Report one concern:
|
|
137
147
|
|
|
138
148
|
```bash
|
|
149
|
+
ankh devtools agents status .
|
|
150
|
+
ankh devtools skills status .
|
|
139
151
|
ankh devtools eslint status .
|
|
140
152
|
ankh devtools prettier status .
|
|
141
153
|
ankh devtools knip status .
|
|
@@ -148,11 +160,13 @@ Preview synchronization without writing:
|
|
|
148
160
|
|
|
149
161
|
```bash
|
|
150
162
|
ankh devtools sync . --dry-run
|
|
163
|
+
ankh devtools agents sync . --dry-run
|
|
164
|
+
ankh devtools skills sync . --dry-run
|
|
151
165
|
ankh devtools eslint sync . --dry-run
|
|
152
166
|
ankh devtools package sync . --dry-run
|
|
153
167
|
```
|
|
154
168
|
|
|
155
|
-
A dry run reports `would create` and `would
|
|
169
|
+
A dry run reports `would create`, `would update`, and `would remove` actions without mutating files. `status` exits with code `1` when managed state has drifted and `0` when it is current.
|
|
156
170
|
|
|
157
171
|
## Synchronization guarantees
|
|
158
172
|
|
|
@@ -160,6 +174,7 @@ Synchronization is deterministic and idempotent:
|
|
|
160
174
|
|
|
161
175
|
- missing managed artifacts are created
|
|
162
176
|
- outdated centrally owned artifacts are updated
|
|
177
|
+
- stale files in Devtools-owned skill trees are removed
|
|
163
178
|
- the managed Bun runtime version is applied consistently to package metadata and workflows
|
|
164
179
|
- Changesets-enabled repositories use the Devtools-owned runner without a direct `@changesets/cli` declaration
|
|
165
180
|
- package changes are followed by `bun install` after all managed files have been written, keeping installed dependencies and `bun.lock` synchronized without invalidating the running sync
|
|
@@ -169,7 +184,19 @@ Synchronization is deterministic and idempotent:
|
|
|
169
184
|
- invalid target paths and write failures return a non-zero exit code
|
|
170
185
|
- create-only repository extension files are never overwritten after creation
|
|
171
186
|
|
|
172
|
-
The canonical workflow
|
|
187
|
+
The canonical workflow, VS Code, and skill files are packaged with `@ankhorage/devtools`; synchronization does not fetch mutable files from GitHub at runtime.
|
|
188
|
+
|
|
189
|
+
## Managed agent instructions
|
|
190
|
+
|
|
191
|
+
`ankh devtools agents sync` owns the repository-root `AGENTS.md`. The shared instructions are intentionally small and stable. The target repository's package name and description are rendered from `package.json`; the remaining content defines the unconditional current-architecture policy and directs structural work to the managed project-structure skill.
|
|
192
|
+
|
|
193
|
+
Only the current Ankhorage architecture is supported. Managed instructions reject deprecated APIs, compatibility aliases, shims, dual old/new paths, historical-state fallbacks, and migrations whose sole purpose is obsolete state. A canonical cross-package change requires affected repositories to update to the latest released public API.
|
|
194
|
+
|
|
195
|
+
## Managed repository skills
|
|
196
|
+
|
|
197
|
+
`ankh devtools skills sync` owns the complete `.agents/skills/ankhorage-coding-rules/` and `.agents/skills/ankhorage-project-structure/` trees from the immutable copies shipped in the Devtools release. It creates `.agents/` when missing, replaces stale files in those managed skills, and preserves every unrelated skill directory.
|
|
198
|
+
|
|
199
|
+
`.agents/.devtools-manifest.json` records the source Devtools version and SHA-256 hashes for every managed skill file. That ownership record allows status and dry-run to report drift and lets later releases remove stale owned files without deleting repository-owned skills.
|
|
173
200
|
|
|
174
201
|
## ESLint profiles
|
|
175
202
|
|
|
@@ -313,7 +340,7 @@ A repository participates in Changesets synchronization when `.changeset/config.
|
|
|
313
340
|
|
|
314
341
|
CI and Release render their `bun-version` from the same managed Bun runtime policy used for `package.json`. They also render Changesets status, version, and publish commands from the same policy that owns the synchronized package scripts. The CI workflow installs that Bun version with the frozen lockfile, builds before repository-provider validation, runs `bunx @ankhorage/ankh doctor validate .`, and conditionally runs lint, formatting, Knip, tests, typecheck, and Changesets checks. Ordinary pull requests run the strict `changeset:status --since=origin/main` guard. A same-repository `changeset-release/main` pull request instead runs `bun run changeset -- status --since=HEAD`, validating the Version Packages metadata from the detached release checkout without requiring a local `main` ref or another unreleased Changeset after the release Changeset was consumed. A fork using that branch name remains subject to the ordinary strict guard. Release publishing calls the Devtools-owned runner through the synchronized package script; it never resolves an ambient or mutable Changesets executable.
|
|
315
342
|
|
|
316
|
-
The Renovate workflow accepts only same-repository branches created by `renovate[bot]`. It calls the SHA-pinned `ankhorage/renovate` workflow to add a release Changeset for runtime Ankhorage dependency updates or an empty Changeset for dev-only updates
|
|
343
|
+
The Renovate workflow accepts only same-repository branches created by `renovate[bot]`. It calls the SHA-pinned `ankhorage/renovate` workflow to add a release Changeset for runtime Ankhorage dependency updates or an empty Changeset for dev-only updates. The trusted workflow scopes the Ankhorage Renovate Sync GitHub App token to the current repository and uses it only for the validated commit, allowing every normal pull-request CI workflow to start without manual approval. It never checks out or executes pull-request code in the privileged `pull_request_target` context.
|
|
317
344
|
|
|
318
345
|
## Managed VS Code configuration
|
|
319
346
|
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type DevtoolsToolName = 'changeset' | 'format' | 'knip' | 'lint';
|
|
2
|
-
type DevtoolsManagedScope = 'all' | 'eslint' | 'knip' | 'package' | 'prettier' | 'vscode' | 'workflows';
|
|
2
|
+
type DevtoolsManagedScope = 'agents' | 'all' | 'eslint' | 'knip' | 'package' | 'prettier' | 'skills' | 'vscode' | 'workflows';
|
|
3
3
|
type DevtoolsManagedOperation = 'status' | 'sync';
|
|
4
|
-
type DevtoolsCapability = 'devtools.changeset' | 'devtools.format' | 'devtools.knip' | 'devtools.lint' | 'devtools.status' | 'devtools.sync' | 'devtools.eslint.status' | 'devtools.eslint.sync' | 'devtools.knip.status' | 'devtools.knip.sync' | 'devtools.package.status' | 'devtools.package.sync' | 'devtools.prettier.status' | 'devtools.prettier.sync' | 'devtools.vscode.status' | 'devtools.vscode.sync' | 'devtools.workflows.status' | 'devtools.workflows.sync';
|
|
4
|
+
type DevtoolsCapability = 'devtools.changeset' | 'devtools.format' | 'devtools.knip' | 'devtools.lint' | 'devtools.status' | 'devtools.sync' | 'devtools.agents.status' | 'devtools.agents.sync' | 'devtools.eslint.status' | 'devtools.eslint.sync' | 'devtools.knip.status' | 'devtools.knip.sync' | 'devtools.package.status' | 'devtools.package.sync' | 'devtools.prettier.status' | 'devtools.prettier.sync' | 'devtools.skills.status' | 'devtools.skills.sync' | 'devtools.vscode.status' | 'devtools.vscode.sync' | 'devtools.workflows.status' | 'devtools.workflows.sync';
|
|
5
5
|
interface DevtoolsCommandBase {
|
|
6
6
|
readonly path: readonly [string, ...string[]];
|
|
7
7
|
readonly capability: DevtoolsCapability;
|
package/dist/cli/commands.js
CHANGED
|
@@ -5,6 +5,10 @@ const DEVTOOLS_COMMANDS = [
|
|
|
5
5
|
externalCommand('knip', 'devtools.knip', 'Run the shared Knip toolchain.', 'knip', 'knip'),
|
|
6
6
|
repositoryCommand(['sync'], 'devtools.sync', 'Synchronize all managed repository setup.', 'all', 'sync'),
|
|
7
7
|
repositoryCommand(['status'], 'devtools.status', 'Report drift for all managed repository setup.', 'all', 'status'),
|
|
8
|
+
repositoryCommand(['agents', 'sync'], 'devtools.agents.sync', 'Synchronize the canonical repository agent instructions.', 'agents', 'sync'),
|
|
9
|
+
repositoryCommand(['agents', 'status'], 'devtools.agents.status', 'Report drift for the canonical repository agent instructions.', 'agents', 'status'),
|
|
10
|
+
repositoryCommand(['skills', 'sync'], 'devtools.skills.sync', 'Synchronize canonical repository-local agent skills.', 'skills', 'sync'),
|
|
11
|
+
repositoryCommand(['skills', 'status'], 'devtools.skills.status', 'Report drift for canonical repository-local agent skills.', 'skills', 'status'),
|
|
8
12
|
repositoryCommand(['eslint', 'sync'], 'devtools.eslint.sync', 'Synchronize the shared ESLint setup.', 'eslint', 'sync'),
|
|
9
13
|
repositoryCommand(['eslint', 'status'], 'devtools.eslint.status', 'Report drift for the shared ESLint setup.', 'eslint', 'status'),
|
|
10
14
|
repositoryCommand(['prettier', 'sync'], 'devtools.prettier.sync', 'Synchronize the shared Prettier setup.', 'prettier', 'sync'),
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ declare const provider: {
|
|
|
2
2
|
id: string;
|
|
3
3
|
category: string;
|
|
4
4
|
version: string;
|
|
5
|
-
capabilities: ("devtools.changeset" | "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync")[];
|
|
5
|
+
capabilities: ("devtools.changeset" | "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.agents.status" | "devtools.agents.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.skills.status" | "devtools.skills.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync")[];
|
|
6
6
|
commands: {
|
|
7
7
|
path: string[];
|
|
8
|
-
capability: "devtools.changeset" | "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync";
|
|
8
|
+
capability: "devtools.changeset" | "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.agents.status" | "devtools.agents.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.skills.status" | "devtools.skills.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync";
|
|
9
9
|
summary: string;
|
|
10
10
|
}[];
|
|
11
11
|
handlers: {
|
package/dist/cli/index.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* `ankh devtools changeset`, `ankh devtools lint`, `ankh devtools format`, and
|
|
5
5
|
* `ankh devtools knip` execute the bundled Changesets, ESLint, Prettier, and Knip versions.
|
|
6
6
|
* Repository synchronization is available through
|
|
7
|
-
* `ankh devtools sync` and `ankh devtools status`, with focused `
|
|
8
|
-
* `package`, `workflows`, and `vscode` sync/status subcommands.
|
|
7
|
+
* `ankh devtools sync` and `ankh devtools status`, with focused `agents`, `skills`, `eslint`,
|
|
8
|
+
* `prettier`, `knip`, `package`, `workflows`, and `vscode` sync/status subcommands.
|
|
9
9
|
*
|
|
10
10
|
* Sync commands accept an optional target directory and `--dry-run`. Aggregate sync is
|
|
11
|
-
* deterministic and idempotent: canonical managed files are created or updated,
|
|
12
|
-
* local extension files remain repository-owned,
|
|
13
|
-
* replacing unrelated fields.
|
|
11
|
+
* deterministic and idempotent: canonical managed files and skill trees are created or updated,
|
|
12
|
+
* unrelated repository-local skills and create-only local extension files remain repository-owned,
|
|
13
|
+
* and package metadata is merge-updated without replacing unrelated fields.
|
|
14
14
|
*
|
|
15
15
|
* Fresh repositories can bootstrap the standard setup with `ankh devtools sync .` after adding
|
|
16
16
|
* `@ankhorage/devtools`. Existing ESLint configuration is preserved during first migration as a
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { agentsManagedFiles } from '../tools/agents/index.js';
|
|
1
2
|
import { eslintManagedFiles } from '../tools/eslint/managed.js';
|
|
2
3
|
import { knipManagedFiles } from '../tools/knip/managed.js';
|
|
3
4
|
import { inspectPackageManifest, readCurrentDevtoolsVersion, syncPackageManifest, } from '../tools/package/index.js';
|
|
4
5
|
import { planBunDependencySync, syncBunDependencies, } from '../tools/package/syncBunDependencies.js';
|
|
5
6
|
import { prettierManagedFiles } from '../tools/prettier/managed.js';
|
|
6
7
|
import { inspectManagedFiles, resolveManagedTargetDirectory, syncManagedFiles, } from '../tools/shared/managedFiles.js';
|
|
8
|
+
import { inspectManagedSkills, syncManagedSkills } from '../tools/skills/managed.js';
|
|
7
9
|
import { vscodeManagedFiles } from '../tools/vscode/index.js';
|
|
8
10
|
import { workflowManagedFiles } from '../tools/workflows/index.js';
|
|
9
11
|
export async function runRepositoryCommand(command, argv, context) {
|
|
@@ -47,6 +49,9 @@ async function runStatus(scope, targetDirectory, devtoolsVersion, context) {
|
|
|
47
49
|
statuses.push(await inspectPackageManifest(targetDirectory, devtoolsVersion));
|
|
48
50
|
}
|
|
49
51
|
statuses.push(...(await inspectManagedFiles(targetDirectory, getManagedFiles(scope))));
|
|
52
|
+
if (scope === 'all' || scope === 'skills') {
|
|
53
|
+
statuses.push(...(await inspectManagedSkills(targetDirectory, devtoolsVersion)));
|
|
54
|
+
}
|
|
50
55
|
writeStatusOutput(statuses, context);
|
|
51
56
|
return { exitCode: statuses.some((status) => status.state !== 'current') ? 1 : 0 };
|
|
52
57
|
}
|
|
@@ -59,6 +64,9 @@ async function runSync(scope, targetDirectory, devtoolsVersion, dryRun, context)
|
|
|
59
64
|
packageDependenciesChanged = packageResult.action !== 'unchanged';
|
|
60
65
|
}
|
|
61
66
|
results.push(...(await syncManagedFiles(targetDirectory, getManagedFiles(scope), { dryRun })));
|
|
67
|
+
if (scope === 'all' || scope === 'skills') {
|
|
68
|
+
results.push(...(await syncManagedSkills(targetDirectory, devtoolsVersion, { dryRun })));
|
|
69
|
+
}
|
|
62
70
|
if (packageDependenciesChanged) {
|
|
63
71
|
if (dryRun) {
|
|
64
72
|
results.push(planBunDependencySync(targetDirectory));
|
|
@@ -73,9 +81,11 @@ async function runSync(scope, targetDirectory, devtoolsVersion, dryRun, context)
|
|
|
73
81
|
}
|
|
74
82
|
function getManagedFiles(scope) {
|
|
75
83
|
const definitionsByScope = {
|
|
84
|
+
agents: agentsManagedFiles,
|
|
76
85
|
eslint: eslintManagedFiles,
|
|
77
86
|
knip: knipManagedFiles,
|
|
78
87
|
prettier: prettierManagedFiles,
|
|
88
|
+
skills: [],
|
|
79
89
|
vscode: vscodeManagedFiles,
|
|
80
90
|
workflows: workflowManagedFiles,
|
|
81
91
|
};
|
|
@@ -95,6 +105,9 @@ function writeStatusOutput(statuses, context) {
|
|
|
95
105
|
else if (status.state === 'missing') {
|
|
96
106
|
context.writeStdout(`+ ${status.relativePath} missing\n`);
|
|
97
107
|
}
|
|
108
|
+
else if (status.state === 'obsolete') {
|
|
109
|
+
context.writeStdout(`- ${status.relativePath} obsolete\n`);
|
|
110
|
+
}
|
|
98
111
|
else {
|
|
99
112
|
context.writeStdout(`✗ ${status.relativePath} outdated\n`);
|
|
100
113
|
}
|
|
@@ -113,6 +126,9 @@ function getActionPrefix(action) {
|
|
|
113
126
|
if (action === 'created' || action === 'would-create') {
|
|
114
127
|
return '+';
|
|
115
128
|
}
|
|
129
|
+
if (action === 'removed' || action === 'would-remove') {
|
|
130
|
+
return '-';
|
|
131
|
+
}
|
|
116
132
|
return '↻';
|
|
117
133
|
}
|
|
118
134
|
function formatAction(action) {
|
|
@@ -121,10 +137,14 @@ function formatAction(action) {
|
|
|
121
137
|
return 'created';
|
|
122
138
|
case 'updated':
|
|
123
139
|
return 'updated';
|
|
140
|
+
case 'removed':
|
|
141
|
+
return 'removed';
|
|
124
142
|
case 'unchanged':
|
|
125
143
|
return 'unchanged';
|
|
126
144
|
case 'would-create':
|
|
127
145
|
return 'would create';
|
|
146
|
+
case 'would-remove':
|
|
147
|
+
return 'would remove';
|
|
128
148
|
case 'would-update':
|
|
129
149
|
return 'would update';
|
|
130
150
|
}
|
|
@@ -6,6 +6,8 @@ const REQUIRED_README_SNIPPETS = [
|
|
|
6
6
|
'ankh devtools knip',
|
|
7
7
|
'ankh devtools sync',
|
|
8
8
|
'ankh devtools status',
|
|
9
|
+
'ankh devtools agents sync',
|
|
10
|
+
'ankh devtools skills sync',
|
|
9
11
|
'ankh devtools eslint sync',
|
|
10
12
|
'ankh devtools prettier sync',
|
|
11
13
|
'ankh devtools knip sync',
|
|
@@ -13,6 +15,9 @@ const REQUIRED_README_SNIPPETS = [
|
|
|
13
15
|
'ankh devtools workflows sync',
|
|
14
16
|
'ankh devtools vscode sync',
|
|
15
17
|
'devtools.eslint.sync',
|
|
18
|
+
'devtools.agents.sync',
|
|
19
|
+
'devtools.skills.sync',
|
|
20
|
+
'ankhorage-coding-rules',
|
|
16
21
|
'devtools.prettier.sync',
|
|
17
22
|
'devtools.knip.sync',
|
|
18
23
|
'devtools.package.sync',
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
export const agentsManagedFiles = [
|
|
4
|
+
{
|
|
5
|
+
relativePath: 'AGENTS.md',
|
|
6
|
+
render: renderAgentsFile,
|
|
7
|
+
},
|
|
8
|
+
];
|
|
9
|
+
async function renderAgentsFile(targetDirectory) {
|
|
10
|
+
const manifest = await readPackageManifest(targetDirectory);
|
|
11
|
+
const packageName = readNonEmptyString(manifest.name) ?? 'Package name not declared';
|
|
12
|
+
const description = readNonEmptyString(manifest.description) ?? 'No package description is declared.';
|
|
13
|
+
return `# AGENTS.md
|
|
14
|
+
|
|
15
|
+
<!-- This file is managed by @ankhorage/devtools. -->
|
|
16
|
+
|
|
17
|
+
## Repository
|
|
18
|
+
|
|
19
|
+
Package: \`${escapeInlineCode(packageName)}\`
|
|
20
|
+
|
|
21
|
+
${description}
|
|
22
|
+
|
|
23
|
+
## Current architecture only
|
|
24
|
+
|
|
25
|
+
Only the current Ankhorage architecture is valid. Do not add or retain deprecated APIs,
|
|
26
|
+
compatibility aliases, shims, dual old/new paths, historical-state fallbacks, or migrations whose
|
|
27
|
+
sole purpose is supporting obsolete states. Remove superseded implementations instead.
|
|
28
|
+
|
|
29
|
+
When a canonical change affects another repository, update that repository to the latest released
|
|
30
|
+
public API instead of preserving compatibility locally. Cross-package usage must go through
|
|
31
|
+
published public APIs and declared dependencies, never sibling source files.
|
|
32
|
+
|
|
33
|
+
Current-runtime error handling and canonical database or infrastructure migrations remain valid
|
|
34
|
+
when they support states that the current architecture can intentionally produce.
|
|
35
|
+
|
|
36
|
+
## Project structure
|
|
37
|
+
|
|
38
|
+
For directory ownership, package boundaries, architectural profiles, ports and adapters, public
|
|
39
|
+
entrypoints, or cross-repository structural work, load and follow
|
|
40
|
+
\`.agents/skills/ankhorage-project-structure/SKILL.md\`.
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
async function readPackageManifest(targetDirectory) {
|
|
44
|
+
try {
|
|
45
|
+
const contents = await readFile(resolve(targetDirectory, 'package.json'), 'utf8');
|
|
46
|
+
const parsed = JSON.parse(contents);
|
|
47
|
+
if (!isRecord(parsed)) {
|
|
48
|
+
throw new Error('package.json must contain a JSON object.');
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function escapeInlineCode(value) {
|
|
60
|
+
return value.replaceAll('`', '\\`');
|
|
61
|
+
}
|
|
62
|
+
function isNodeError(error) {
|
|
63
|
+
return error instanceof Error && 'code' in error;
|
|
64
|
+
}
|
|
65
|
+
function isRecord(value) {
|
|
66
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
67
|
+
}
|
|
68
|
+
function readNonEmptyString(value) {
|
|
69
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
|
|
70
|
+
}
|
|
@@ -7,8 +7,8 @@ export interface ManagedFileDefinition {
|
|
|
7
7
|
readonly render?: ManagedFileRenderer;
|
|
8
8
|
readonly mode?: ManagedFileMode;
|
|
9
9
|
}
|
|
10
|
-
type ManagedFileState = 'current' | 'missing' | 'outdated';
|
|
11
|
-
type ManagedFileSyncAction = 'unchanged' | 'created' | 'updated' | 'would-create' | 'would-update';
|
|
10
|
+
type ManagedFileState = 'current' | 'missing' | 'obsolete' | 'outdated';
|
|
11
|
+
export type ManagedFileSyncAction = 'unchanged' | 'created' | 'removed' | 'updated' | 'would-create' | 'would-remove' | 'would-update';
|
|
12
12
|
export interface ManagedFileStatus {
|
|
13
13
|
readonly relativePath: string;
|
|
14
14
|
readonly state: ManagedFileState;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ankhorage-coding-rules
|
|
3
|
+
description: >
|
|
4
|
+
Implement, refactor, test, review, or hand off code changes in Ankhorage repositories. Use for
|
|
5
|
+
task branches and pull requests, canonical lint compliance, behavior-focused testing, Paradox
|
|
6
|
+
function documentation, validation, and release-quality delivery. Use ankhorage-project-structure
|
|
7
|
+
as well when the task changes package ownership, public entrypoints, or source architecture.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ankhorage Coding Rules
|
|
11
|
+
|
|
12
|
+
Deliver focused changes that satisfy the repository's current canonical policy and are ready for
|
|
13
|
+
review. Read the repository `AGENTS.md`, package scripts, public exports, representative tests, and
|
|
14
|
+
documentation ownership before editing.
|
|
15
|
+
|
|
16
|
+
Repository instructions may add stricter requirements. They must not be bypassed with local
|
|
17
|
+
exceptions or replaced by generic preferences from this skill.
|
|
18
|
+
|
|
19
|
+
## Git and delivery
|
|
20
|
+
|
|
21
|
+
- For a new task, fetch the latest `origin/main` and create a fresh task branch from it. Never
|
|
22
|
+
implement directly on `main`.
|
|
23
|
+
- Continue existing pull-request work on its existing branch. Do not create a nested task branch.
|
|
24
|
+
- Preserve unrelated or dirty user work. Use an isolated worktree when changing branches would
|
|
25
|
+
disturb it.
|
|
26
|
+
- Keep the diff within the requested issue. Do not mix opportunistic cleanup into the change.
|
|
27
|
+
- Deliver coding work through a pull request. If remote writes are unavailable or unauthorized,
|
|
28
|
+
prepare the branch and report the pending push or PR explicitly.
|
|
29
|
+
- Describe the change, architectural effect, validation, public API and changeset impact,
|
|
30
|
+
documentation impact, and linked issue in the PR body.
|
|
31
|
+
|
|
32
|
+
## Canonical lint compliance
|
|
33
|
+
|
|
34
|
+
- New and materially changed code must satisfy the canonical ESLint configuration.
|
|
35
|
+
- Never add, widen, or depend on `eslint.local.config.mjs` exceptions. Existing exceptions are
|
|
36
|
+
removable migration debt, not policy for new work.
|
|
37
|
+
- Do not weaken rules or add suppression comments, unsafe casts, or similar bypasses merely to make
|
|
38
|
+
validation pass.
|
|
39
|
+
- Resolve size, complexity, and related findings around cohesive responsibilities and clear data
|
|
40
|
+
flow. Do not split a function or file mechanically just to cross a threshold.
|
|
41
|
+
- Prefer reusable pure transformations, immutable values, and explicit inputs and outputs. Keep
|
|
42
|
+
unavoidable mutation and external side effects at clear boundaries.
|
|
43
|
+
- Keep one canonical implementation. Do not add compatibility aliases, dual paths, historical-state
|
|
44
|
+
fallbacks, or sibling-source imports.
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
- Test observable behavior at the owning boundary. Keep tests deterministic and independent of
|
|
49
|
+
execution order, ambient state, and live network services.
|
|
50
|
+
- When testing dependency declarations, assert the required semver range shape or a shared policy
|
|
51
|
+
value instead of a Renovate-managed literal version. Assert an exact version only when the pin is
|
|
52
|
+
itself the contract under test.
|
|
53
|
+
- Follow the range-shape patterns in the
|
|
54
|
+
[Devtools repository sync test](https://github.com/ankhorage/devtools/blob/main/src/cli/runRepositoryCommand.test.ts)
|
|
55
|
+
and [ZORA PR #313](https://github.com/ankhorage/zora/pull/313).
|
|
56
|
+
- Run focused unit or integration tests before full E2E and smoke suites.
|
|
57
|
+
- Do not rerun an unchanged E2E or smoke failure without a new hypothesis. After two unsuccessful
|
|
58
|
+
full reruns without a substantive code or configuration change, stop cycling, inspect logs and
|
|
59
|
+
artifacts, and report the blocker.
|
|
60
|
+
|
|
61
|
+
## Paradox documentation
|
|
62
|
+
|
|
63
|
+
- Give every named production function under `src` a concise Paradox `/*** ... */` description,
|
|
64
|
+
including internal helpers. Inline callbacks are exempt.
|
|
65
|
+
- Add `@param`, `@returns`, `@example`, or `@readme` to public APIs only when the tag adds useful
|
|
66
|
+
information.
|
|
67
|
+
- Update documentation sources and regenerate tracked Paradox output. Never hand-edit generated
|
|
68
|
+
README or Paradox artifacts.
|
|
69
|
+
|
|
70
|
+
## Public API and release quality
|
|
71
|
+
|
|
72
|
+
- Export public symbols intentionally from canonical entrypoints and exercise those exports in
|
|
73
|
+
tests. Do not introduce consumer deep imports.
|
|
74
|
+
- Add the repository-required changeset for published behavior, package shape, dependency, or
|
|
75
|
+
public API changes.
|
|
76
|
+
- Justify new dependencies and keep generated artifacts, lockfiles, and package metadata aligned
|
|
77
|
+
with the repository's owning workflows.
|
|
78
|
+
- Review the final diff, then run the applicable build, lint, test, typecheck, Knip, format, docs,
|
|
79
|
+
changeset-status, and packaging checks.
|
|
80
|
+
- Report the exact commands run and any omissions or failures. Do not imply a fully green handoff
|
|
81
|
+
when a required check did not run or pass.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ankhorage-project-structure
|
|
3
|
+
description: >
|
|
4
|
+
Design, review, or migrate the source structure of Ankhorage repositories and generated
|
|
5
|
+
applications. Use for directory ownership, package boundaries, architectural profiles,
|
|
6
|
+
ports and adapters, package CLI layout, repository skill distribution, public entrypoints,
|
|
7
|
+
or cross-repository cleanup.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ankhorage Project Structure
|
|
11
|
+
|
|
12
|
+
Apply a repository profile instead of forcing one folder tree onto every package.
|
|
13
|
+
|
|
14
|
+
Before deciding structure:
|
|
15
|
+
|
|
16
|
+
1. Read the repository `AGENTS.md`, `package.json`, exports, source tree, and representative tests.
|
|
17
|
+
2. Load `ankhorage-coding-rules` and `ankhorage-package-structure` when available.
|
|
18
|
+
3. Identify the repository's owning capability and its profile below.
|
|
19
|
+
4. Identify current public subpaths and cross-package release boundaries.
|
|
20
|
+
|
|
21
|
+
Repository-specific rules may refine this skill, but must not silently reverse package ownership
|
|
22
|
+
or dependency direction.
|
|
23
|
+
|
|
24
|
+
## Primary boundary
|
|
25
|
+
|
|
26
|
+
An Ankhorage repository/package is the primary bounded capability and independently released
|
|
27
|
+
unit. Do not create a generic `src/features/` layer that imitates package boundaries.
|
|
28
|
+
|
|
29
|
+
- Independently bindable capabilities belong in standalone packages.
|
|
30
|
+
- Substantial responsibilities inside a package use cohesive domain directories directly under
|
|
31
|
+
`src/`.
|
|
32
|
+
- Cross-package access uses published APIs and declared dependencies, never sibling source.
|
|
33
|
+
- A package owns its application behavior; adapters belonging to another package are not copied
|
|
34
|
+
or proxied locally.
|
|
35
|
+
|
|
36
|
+
## Select a profile
|
|
37
|
+
|
|
38
|
+
Read [repository-profiles.md](references/repository-profiles.md), select one primary profile, and
|
|
39
|
+
apply only the references routed by that profile.
|
|
40
|
+
|
|
41
|
+
| Profile | Typical repositories | Required references |
|
|
42
|
+
| ---------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| Application, engine, or hybrid | Studio, Runtime, Orchestrator, Deploy, Infra | [hexagonal-architecture.md](references/hexagonal-architecture.md) |
|
|
44
|
+
| React Native / Expo application | generated apps, Studio app host | [hexagonal-architecture.md](references/hexagonal-architecture.md), [expo-apps.md](references/expo-apps.md) |
|
|
45
|
+
| Component or design-system library | ZORA, Surface | [ui-libraries.md](references/ui-libraries.md) |
|
|
46
|
+
| Contracts or value library | Contracts and other portable models | [repository-profiles.md](references/repository-profiles.md) |
|
|
47
|
+
| Utility library | Utility | [utilities.md](references/utilities.md) |
|
|
48
|
+
| Platform/provider adapter | Expo Runtime, Supabase packages, orchestrator modules | [repository-profiles.md](references/repository-profiles.md) |
|
|
49
|
+
| Tooling or CLI package | Ankh, Doctor, Devtools and any package exposing Ankh commands | [cli.md](references/cli.md) |
|
|
50
|
+
|
|
51
|
+
When a package declares an Ankh provider or changes `src/cli/`, always read
|
|
52
|
+
[cli.md](references/cli.md), regardless of its primary profile.
|
|
53
|
+
|
|
54
|
+
When the task is a structural cleanup or migration, also read
|
|
55
|
+
[migration.md](references/migration.md). For Studio specifically, read
|
|
56
|
+
[studio.md](references/studio.md).
|
|
57
|
+
|
|
58
|
+
When adding, distributing, or synchronizing repository-local agent skills, read
|
|
59
|
+
[skill-distribution.md](references/skill-distribution.md).
|
|
60
|
+
|
|
61
|
+
## Universal invariants
|
|
62
|
+
|
|
63
|
+
- Keep only intentional package entrypoints and required declaration shims directly under `src/`.
|
|
64
|
+
- Keep one abstraction level and responsibility among siblings.
|
|
65
|
+
- Prefer domain ownership over technical dumping grounds such as `common`, `core`, `helpers`,
|
|
66
|
+
`misc`, or `shared`.
|
|
67
|
+
- `app/`, `cli/`, `host/`, and `platform/` are package-level edges or composition areas, not
|
|
68
|
+
alternate owners of domain behavior.
|
|
69
|
+
- Ports are owned by the application/domain code that requires the capability.
|
|
70
|
+
- Adapters depend inward; domain/application code does not import concrete adapters.
|
|
71
|
+
- Add a port only for a real external or replaceable boundary. Do not wrap every function in
|
|
72
|
+
architecture ceremony.
|
|
73
|
+
- Colocate focused unit tests. Put cross-domain acceptance, E2E, smoke infrastructure, and large
|
|
74
|
+
fixtures outside production source.
|
|
75
|
+
- Public package subpaths may point to nested source. Do not keep files at `src/` merely because
|
|
76
|
+
they are exported.
|
|
77
|
+
- Preserve one canonical implementation. Do not add legacy paths, compatibility barrels, or
|
|
78
|
+
duplicate APIs to make a migration appear smaller.
|
|
79
|
+
|
|
80
|
+
## Ownership decision
|
|
81
|
+
|
|
82
|
+
For every file or new symbol, decide in order:
|
|
83
|
+
|
|
84
|
+
1. Which Ankhorage package owns the capability?
|
|
85
|
+
2. Which internal domain owns it?
|
|
86
|
+
3. Is it core policy, application orchestration, a required port, an edge adapter, composition,
|
|
87
|
+
or a public entrypoint?
|
|
88
|
+
4. Is it reusable enough to belong in `@ankhorage/utility` instead?
|
|
89
|
+
|
|
90
|
+
If any answer is unclear, resolve ownership before moving or creating code.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: 'Ankhorage Project Structure'
|
|
3
|
+
short_description: 'Structure and migrate Ankhorage repositories'
|
|
4
|
+
default_prompt: 'Use $ankhorage-project-structure to design or review this Ankhorage repository structure.'
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|