@ankhorage/devtools 1.8.5 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -2
- 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 +1 -1
- 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
|
|
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
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Ankhorage Package CLI
|
|
2
|
+
|
|
3
|
+
An Ankhorage package may expose one package-level Ankh command provider under `src/cli/`. The
|
|
4
|
+
package is the CLI ownership boundary.
|
|
5
|
+
|
|
6
|
+
Do not create `cli/` directories inside internal domains. An independently bound package owns its
|
|
7
|
+
own package-level `src/cli/` and release lifecycle.
|
|
8
|
+
|
|
9
|
+
## Command tree
|
|
10
|
+
|
|
11
|
+
The filesystem below `src/cli/commands/` mirrors the public command path after the package prefix.
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
ankh <package> <segment> ... <command>
|
|
15
|
+
-> src/cli/commands/<segment>/.../<command>.ts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
ankh studio dev
|
|
22
|
+
-> src/cli/commands/dev.ts
|
|
23
|
+
|
|
24
|
+
ankh studio projects list
|
|
25
|
+
-> src/cli/commands/projects/list.ts
|
|
26
|
+
|
|
27
|
+
ankh studio projects create --name Shop
|
|
28
|
+
-> src/cli/commands/projects/create.ts
|
|
29
|
+
|
|
30
|
+
ankh deploy release inspect
|
|
31
|
+
-> src/cli/commands/release/inspect.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The package prefix is represented by the provider and is not repeated under `commands/`. Flags and
|
|
35
|
+
positional arguments do not affect the directory tree.
|
|
36
|
+
|
|
37
|
+
Bad:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
src/cli/commands/studio/projects/list.ts
|
|
41
|
+
src/cli/commands/listProjects.ts
|
|
42
|
+
src/projects/cli/list.ts
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Good:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
src/cli/commands/projects/list.ts
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Layout
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
src/
|
|
55
|
+
cli/
|
|
56
|
+
index.ts
|
|
57
|
+
commands/
|
|
58
|
+
<command>.ts
|
|
59
|
+
<group>/
|
|
60
|
+
<command>.ts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`src/cli/index.ts` owns provider identity, category, capabilities, command definitions, handler
|
|
64
|
+
registration, and package-level composition. It contains no substantial command implementation.
|
|
65
|
+
|
|
66
|
+
Each leaf command module owns one command handler and its command-specific argument/output mapping.
|
|
67
|
+
Tests are colocated:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
src/cli/commands/projects/list.ts
|
|
71
|
+
src/cli/commands/projects/list.test.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Do not add command-group barrels unless the group intentionally exposes an API or requires genuine
|
|
75
|
+
composition.
|
|
76
|
+
|
|
77
|
+
## Naming exception
|
|
78
|
+
|
|
79
|
+
Command filenames follow public CLI segments rather than exported handler names:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
command: ankh studio projects list
|
|
83
|
+
file: src/cli/commands/projects/list.ts
|
|
84
|
+
handler: listProjects
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This is an intentional exception to primary-symbol filenames. It applies only to CLI command leaf
|
|
88
|
+
modules.
|
|
89
|
+
|
|
90
|
+
## Adapter boundary
|
|
91
|
+
|
|
92
|
+
A handler may parse `request.argv`, use the provided command context, call package-owned
|
|
93
|
+
application operations, translate failures into command results, and create the package
|
|
94
|
+
composition needed for the invocation.
|
|
95
|
+
|
|
96
|
+
A handler must not:
|
|
97
|
+
|
|
98
|
+
- implement domain rules;
|
|
99
|
+
- duplicate application behavior;
|
|
100
|
+
- implement substantial filesystem, network, process, database, credential, or provider logic;
|
|
101
|
+
- proxy behavior owned by another package to place it under the local prefix;
|
|
102
|
+
- preserve removed commands through aliases or hidden handlers.
|
|
103
|
+
|
|
104
|
+
## Capabilities and public metadata
|
|
105
|
+
|
|
106
|
+
Keep provider metadata, command definitions, handler paths, public exports, documentation, and
|
|
107
|
+
tests synchronized. Remove unused capabilities when a command disappears. Published command or
|
|
108
|
+
metadata changes require the repository's normal changeset treatment.
|
|
109
|
+
|
|
110
|
+
## Standalone generated applications
|
|
111
|
+
|
|
112
|
+
Generated applications own their installation, validation, build, and runtime commands. Execute
|
|
113
|
+
the generated application's canonical command with that application as `cwd`.
|
|
114
|
+
|
|
115
|
+
Studio must not own a parallel workspace-install command or assume generated applications are
|
|
116
|
+
Studio workspace members. Do not preserve obsolete workspace commands, capabilities, handlers,
|
|
117
|
+
tests, or documentation after the standalone architecture replaces them.
|