@ankhorage/devtools 1.9.5 → 1.10.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/dist/tools/skills/assets/ankhorage-project-structure/references/skill-distribution.md +5 -0
- package/dist/tools/skills/assets/zora-designer/SKILL.md +89 -0
- package/dist/tools/skills/assets/zora-designer/agents/openai.yaml +6 -0
- package/dist/tools/skills/assets/zora-designer/assets/audit-rubric.json +312 -0
- package/dist/tools/skills/assets/zora-designer/references/artifact.md +93 -0
- package/dist/tools/skills/assets/zora-designer/references/audit.md +100 -0
- package/dist/tools/skills/assets/zora-designer/references/workflow.md +137 -0
- package/dist/tools/skills/assets/zora-designer/scripts/audit.mjs +573 -0
- package/dist/tools/skills/assets/zora-designer/scripts/owner-api.mjs +440 -0
- package/dist/tools/skills/assets/zora-designer/scripts/scaffold-template.mjs +222 -0
- package/dist/tools/skills/managed.js +10 -9
- package/dist/tools/skills/selection.d.ts +7 -0
- package/dist/tools/skills/selection.js +82 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { lstat, mkdir, readdir, readFile, rmdir, unlink, writeFile } from 'node:
|
|
|
2
2
|
import { dirname, join, sep } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { assertManagedSkillPath, createManifestContents, isNodeError, MANIFEST_PATH, readManagedSkillsManifest, resolveManagedPath, SKILLS_ROOT, } from './manifest.js';
|
|
5
|
-
|
|
5
|
+
import { selectManagedSkillNames } from './selection.js';
|
|
6
6
|
export async function inspectManagedSkills(targetDirectory, devtoolsVersion) {
|
|
7
7
|
return (await createManagedSkillPlan(targetDirectory, devtoolsVersion)).statuses;
|
|
8
8
|
}
|
|
@@ -56,9 +56,9 @@ async function assertNoSymlinkSegments(targetDirectory, relativePath) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
async function collectCanonicalSkillFiles() {
|
|
59
|
+
async function collectCanonicalSkillFiles(skillNames) {
|
|
60
60
|
const contentsByPath = new Map();
|
|
61
|
-
for (const skillName of
|
|
61
|
+
for (const skillName of skillNames) {
|
|
62
62
|
const sourceDirectory = fileURLToPath(new URL(`./assets/${skillName}/`, import.meta.url));
|
|
63
63
|
const files = await readDirectoryFiles(sourceDirectory);
|
|
64
64
|
assertSkillName(skillName, files);
|
|
@@ -68,9 +68,9 @@ async function collectCanonicalSkillFiles() {
|
|
|
68
68
|
}
|
|
69
69
|
return contentsByPath;
|
|
70
70
|
}
|
|
71
|
-
async function collectObsoletePaths(targetDirectory, desiredPaths, previousManifest) {
|
|
71
|
+
async function collectObsoletePaths(targetDirectory, desiredPaths, previousManifest, skillNames) {
|
|
72
72
|
const obsoletePaths = new Set();
|
|
73
|
-
for (const skillName of
|
|
73
|
+
for (const skillName of skillNames) {
|
|
74
74
|
const skillRoot = `${SKILLS_ROOT}/${skillName}`;
|
|
75
75
|
for (const existingPath of await readTargetDirectoryFiles(targetDirectory, skillRoot)) {
|
|
76
76
|
if (!desiredPaths.has(existingPath)) {
|
|
@@ -80,7 +80,7 @@ async function collectObsoletePaths(targetDirectory, desiredPaths, previousManif
|
|
|
80
80
|
}
|
|
81
81
|
if (previousManifest !== null) {
|
|
82
82
|
for (const [skillName, entry] of Object.entries(previousManifest.skills)) {
|
|
83
|
-
if (
|
|
83
|
+
if (skillNames.includes(skillName)) {
|
|
84
84
|
continue;
|
|
85
85
|
}
|
|
86
86
|
for (const managedPath of Object.keys(entry.files)) {
|
|
@@ -94,14 +94,15 @@ async function collectObsoletePaths(targetDirectory, desiredPaths, previousManif
|
|
|
94
94
|
}
|
|
95
95
|
async function createManagedSkillPlan(targetDirectory, devtoolsVersion) {
|
|
96
96
|
const previousManifest = await readManagedSkillsManifest(targetDirectory);
|
|
97
|
-
const
|
|
97
|
+
const skillNames = await selectManagedSkillNames(targetDirectory);
|
|
98
|
+
const contentsByPath = await collectCanonicalSkillFiles(skillNames);
|
|
98
99
|
const desiredPaths = new Set(contentsByPath.keys());
|
|
99
|
-
const manifestContents = createManifestContents(contentsByPath, devtoolsVersion,
|
|
100
|
+
const manifestContents = createManifestContents(contentsByPath, devtoolsVersion, skillNames);
|
|
100
101
|
const statuses = [];
|
|
101
102
|
for (const [relativePath, contents] of [...contentsByPath.entries()].sort()) {
|
|
102
103
|
statuses.push(await inspectDesiredFile(targetDirectory, relativePath, contents));
|
|
103
104
|
}
|
|
104
|
-
for (const relativePath of await collectObsoletePaths(targetDirectory, desiredPaths, previousManifest)) {
|
|
105
|
+
for (const relativePath of await collectObsoletePaths(targetDirectory, desiredPaths, previousManifest, skillNames)) {
|
|
105
106
|
statuses.push({ relativePath, state: 'obsolete' });
|
|
106
107
|
}
|
|
107
108
|
statuses.push(await inspectDesiredFile(targetDirectory, MANIFEST_PATH, Buffer.from(manifestContents)));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const BASELINE_SKILL_NAMES: readonly ["ankhorage-coding-rules", "ankhorage-project-structure"];
|
|
2
|
+
export declare const PROFILE_SKILL_NAMES: readonly ["zora-designer"];
|
|
3
|
+
export type ManagedSkillName = (typeof BASELINE_SKILL_NAMES)[number] | (typeof PROFILE_SKILL_NAMES)[number];
|
|
4
|
+
/*** Select baseline and profile-specific managed skills from current repository traits. */
|
|
5
|
+
export declare function selectManagedSkillNames(targetDirectory: string): Promise<readonly ManagedSkillName[]>;
|
|
6
|
+
/*** Identify ZORA owners and generated-app authoring repositories without explicit skill lists. */
|
|
7
|
+
export declare function isZoraDesignerContext(packageManifest: Readonly<Record<string, unknown>>): boolean;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
export const BASELINE_SKILL_NAMES = [
|
|
4
|
+
'ankhorage-coding-rules',
|
|
5
|
+
'ankhorage-project-structure',
|
|
6
|
+
];
|
|
7
|
+
export const PROFILE_SKILL_NAMES = ['zora-designer'];
|
|
8
|
+
const ZORA_DESIGNER_OWNER_PACKAGES = new Set([
|
|
9
|
+
'@ankhorage/studio',
|
|
10
|
+
'@ankhorage/templates',
|
|
11
|
+
'@ankhorage/zora',
|
|
12
|
+
]);
|
|
13
|
+
const ZORA_DESIGNER_APP_DEPENDENCIES = new Set([
|
|
14
|
+
'@ankhorage/expo-runtime',
|
|
15
|
+
'@ankhorage/runtime',
|
|
16
|
+
'@ankhorage/studio',
|
|
17
|
+
'@ankhorage/templates',
|
|
18
|
+
]);
|
|
19
|
+
/*** Select baseline and profile-specific managed skills from current repository traits. */
|
|
20
|
+
export async function selectManagedSkillNames(targetDirectory) {
|
|
21
|
+
const packageManifest = await readPackageManifest(targetDirectory);
|
|
22
|
+
return isZoraDesignerContext(packageManifest)
|
|
23
|
+
? [...BASELINE_SKILL_NAMES, ...PROFILE_SKILL_NAMES]
|
|
24
|
+
: BASELINE_SKILL_NAMES;
|
|
25
|
+
}
|
|
26
|
+
/*** Identify ZORA owners and generated-app authoring repositories without explicit skill lists. */
|
|
27
|
+
export function isZoraDesignerContext(packageManifest) {
|
|
28
|
+
const packageName = readNonEmptyString(packageManifest.name);
|
|
29
|
+
if (packageName !== null && ZORA_DESIGNER_OWNER_PACKAGES.has(packageName)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
const dependencies = collectDependencyNames(packageManifest);
|
|
33
|
+
return (dependencies.has('@ankhorage/zora') &&
|
|
34
|
+
[...ZORA_DESIGNER_APP_DEPENDENCIES].some((dependency) => dependencies.has(dependency)));
|
|
35
|
+
}
|
|
36
|
+
/*** Read package metadata used for profile selection, treating a missing manifest as an empty one. */
|
|
37
|
+
async function readPackageManifest(targetDirectory) {
|
|
38
|
+
try {
|
|
39
|
+
const contents = await readFile(resolve(targetDirectory, 'package.json'), 'utf8');
|
|
40
|
+
const parsed = JSON.parse(contents);
|
|
41
|
+
if (!isRecord(parsed)) {
|
|
42
|
+
throw new Error('package.json must contain a JSON object for managed-skill selection.');
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/*** Combine all dependency sections that describe the repository's current package context. */
|
|
54
|
+
function collectDependencyNames(packageManifest) {
|
|
55
|
+
const names = new Set();
|
|
56
|
+
const dependencySections = [
|
|
57
|
+
packageManifest.dependencies,
|
|
58
|
+
packageManifest.devDependencies,
|
|
59
|
+
packageManifest.optionalDependencies,
|
|
60
|
+
packageManifest.peerDependencies,
|
|
61
|
+
];
|
|
62
|
+
for (const dependencies of dependencySections) {
|
|
63
|
+
if (isRecord(dependencies)) {
|
|
64
|
+
for (const dependency of Object.keys(dependencies)) {
|
|
65
|
+
names.add(dependency);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return names;
|
|
70
|
+
}
|
|
71
|
+
/*** Narrow filesystem errors that expose a Node error code. */
|
|
72
|
+
function isNodeError(error) {
|
|
73
|
+
return error instanceof Error && 'code' in error;
|
|
74
|
+
}
|
|
75
|
+
/*** Narrow unknown package metadata to a record. */
|
|
76
|
+
function isRecord(value) {
|
|
77
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
78
|
+
}
|
|
79
|
+
/*** Return a trimmed package name when one is declared. */
|
|
80
|
+
function readNonEmptyString(value) {
|
|
81
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
|
|
82
|
+
}
|
package/package.json
CHANGED