Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
23 changes: 23 additions & 0 deletions .agents/skills/adobe-data-ai/.data-ai.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"package": "@adobe/data-ai",
"version": "0.9.83",
"skills": [
"archetype-to-type",
"archetypes",
"components",
"namespace",
"resources",
"services",
"structure",
"types",
"x-alpha",
"x-beta",
"x-double",
"x-execute",
"x-log",
"x-pow",
"x-subagent",
"x-trace",
"x-validate"
]
}
20 changes: 20 additions & 0 deletions .agents/skills/adobe-data-ai/archetype-to-type/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: archetype-to-type
description: Use when invoked or when editing types backed by archetypes.
paths:
- '**/*types*'
---

@see /archetypes /namespace /structure /types

A type is derived from an archetype using @adobe/data Schema.fromArchetype

The components arguments should come from `import * as components from` the barrel exporta index.ts of the corresponding components declarations.

fn execute() {
if not provided specific instructions then for each archetypes in the relevant archetypes folder {
create ../../types folder (it is a peer)
create archetypes peer types folder if missing
create a /namespace adhering type declaration in types folder
}
}
14 changes: 14 additions & 0 deletions .agents/skills/adobe-data-ai/archetypes/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: archetypes
description: Build or edit the archetypes/ part of a feature's data layer.
---

Build the requested archetypes under `data/archetypes/`.

The `structure/data/archetypes` rule holds the authoring guidance — UpperCase
naming, `satisfies Array<keyof typeof components>`, and extension by spreading.
Follow it. Worked examples:
@see ../structure/references/data/archetypes/*.ts

One declaration per file, plus the `index.ts` barrel re-exporting each.
Deriving types from archetypes is a separate phase — see `archetype-to-type`.
12 changes: 12 additions & 0 deletions .agents/skills/adobe-data-ai/components/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: components
description: Build or edit the components/ part of a feature's data layer.
---

Build the requested components under `data/components/`.

The `structure/data/components` rule holds the authoring guidance — schema
selection, struct vs object storage, and naming. Follow it. Worked examples:
@see ../structure/references/data/components/*.ts

One declaration per file, plus the `index.ts` barrel re-exporting each.
66 changes: 66 additions & 0 deletions .agents/skills/adobe-data-ai/namespace/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: aidd-namespace
description: Folder-internal namespace pattern for types/ and services/ entries. Use when creating, refactoring, or importing namespaced types and services.
---

@see /archetypes
@see ../structure/references/types/*.ts

This pattern creates a single importable name that combines both the type and a namespace of related declarations.

A consumer of the type will `import { TypeName } from "path/to/type-name/type-name.js"` and then can use the type with `TypeName` or use any public declarations related to the type with `TypeName.someDeclaration`.

This pattern provides strong typing, discoverability and tree shaking with modern bundlers. Any public declaration not consumed by an application will be omitted from the bundle.

Namespace file structure:

<name>/
<name>.ts
// export type <Name> = ?
// export * as <Name> from "./public.js";
public.ts
// export * from "./public-const1.js";
// export * from "./public-function1.js";
// export * from "./public-function2.js";
// declarations follow, each contains EXACTLY one public export per file
// each function declaration has a unit test file with ZERO public exports
// declaration files may have any number of private declarations
// we only move them out to their own files if needed by two or more other declaration files
<public-const1>.ts
<internal-const2>.ts
<public-function1>.ts
<public-function1>.test.ts
<public-function2>.ts
<public-function2>.test.ts
<internal-function1>.ts
<internal-function1>.test.ts

IMPORTANT NOTE: If a single declaration grows too large to manage conveniently within a single file then it can be converted into a single folder containing multiple files, but only the file of the same name as the folder is considered public and that will be exported from the public.ts For example `export * from "./my-large-declaration/my-large-declaration.js"; This is most common with large service factory functions. We do this for proper cohesion. It avoids cluttering up the shared file space with unrelated implementation details.

The namespace pattern *may* recurse by containing sub-folders of namespaced types that are re-exported by the containing type. This can make for convenient and discoverable related types.

For example, a service interface type may contain it's related types as child types for easy consumption.

import { MyService } from "./my-service/my-service.js";

const myInput: MyService.Input = { foo: 1 };
const myOutput: MyService.Output = await MyService.create().doSomething(myInput);

consumer constraints {
- never directly import `public.js`
- never use `import type` as that omits the namespaced declarations
- never directly import a namespace typed declaration file
}


## Execute

fn whenRefactoringASingleFileToNamespacePattern() {
Constraints {
Identify constants in the current file
Extract them into the standard pattern
Identify all consumers of public namespaces
Check each consumer for correct consumption pattern
Ensure unit tests exist per function file
}
}
13 changes: 13 additions & 0 deletions .agents/skills/adobe-data-ai/resources/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: resources
description: Build or edit the resources/ part of a feature's data layer.
---

Build the requested resources under `data/resources/`.

Resources are singleton components; the `structure/data/resources` rule covers
how they differ from components — no struct packing, and the `default` /
`nonPersistent` descriptor form. Follow it. Worked examples:
@see ../structure/references/data/resources/*.ts

One declaration per file, plus the `index.ts` barrel re-exporting each.
13 changes: 13 additions & 0 deletions .agents/skills/adobe-data-ai/services/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: services
description: Build or edit a feature's services/ layer — async capability contracts.
---

Build the requested services under `services/`.

The `structure/services` rule holds the authoring guidance — the
`-service` suffix, the `interface` contract and its `IsValid` assertion,
async-only members, pure `Data` I/O, and `create*` factories. Follow it,
along with `namespace.md`.

One namespace folder per service.
23 changes: 23 additions & 0 deletions .agents/skills/adobe-data-ai/structure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Feature structure

Each feature folder contains layered subfolders. Higher layers may import from lower layers; never the reverse.

```mermaid
flowchart TB
elements --> database
database --> types
database --> services
types <--> services
types --> data
services --> data
```

| Folder | Role |
|--------|------|
| `data/` | Archetypes, components, resources — ECS shape definitions |
| `types/` | Pure immutable data and synchronous helpers |
| `services/` | Async capability contracts and implementations |
| `database/` | ECS database wiring and persistence |
| `elements/` | UI |

`types/` and `services/` are peers at the same tier: both may import `data/`, and may import each other. `database/` may import either or both. `elements/` sits at the top and imports `database/`.
24 changes: 24 additions & 0 deletions .agents/skills/adobe-data-ai/structure/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: structure
description: Use when deciding file/folder layout, dependencies, package boundaries, or scaffolding a feature.
---

Lay out or scaffold feature code.

The `structure` rule tree holds the guidance — the feature-folder layers,
their dependency direction, and each layer's authoring rules. Follow it.
Worked examples of a full feature:
@see ./references/**/*.ts

Code is organised by feature; no source file lives outside a feature
folder (folders may nest to group sub-features). Build a feature bottom-up
so each layer can import the one below:

1. `data/` — components, archetypes, resources.
2. `types/` — pure data + helpers (derive archetype-backed types via
`archetype-to-type`).
3. `services/` — async ports.
4. `database/` — core → index → transaction → computed → service plugins.
5. `elements/` — UI.

Do only the layer(s) asked for; leave the rest to their own phases.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./kilo.js";
export * from "./lima.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as components from "../components/index.js";

export const Kilo = ["alpha", "beta"] as const satisfies Array<keyof typeof components>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as components from "../components/index.js";
import { Kilo } from "./kilo.js";

export const Lima = [...Kilo, "charlie"] as const satisfies Array<keyof typeof components>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Schema } from "@adobe/data/schema";

export const alpha = { type: "number", minimum: 0.0, maximum: 1.0 } as const satisfies Schema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Schema } from "@adobe/data/schema";
import { Vec3 } from "@adobe/data/math";

export const beta = Schema.fromStructProperties({
position: Vec3.F32.schema,
velocity: Vec3.F32.schema,
gridIndex: Vec3.U32.schema,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Vec3 } from "@adobe/data/math";

export const charlie = Vec3.F32.schema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Schema } from "@adobe/data/schema"

export const delta = Schema.fromObjectProperties({
name: { type: "string", minLength: 1, maxLength: 100 },
birthYear: { type: "number", minimum: 1900 },
birthMonth: { type: "number", minimum: 1, maximum: 12 },
birthDay: { type: "number", minimum: 1, maximum: 31 },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./beta.js";
export * from "./delta.js";
export * from "./alpha.js";
export * from "./charlie.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Vec3 } from "@adobe/data/math";

export const foxtrot = Vec3.F32.schema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

export type golf = {
readonly name: string,
readonly birthYear: number,
readonly birthMonth: number,
readonly birthDay: number,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./golf.js";
export * from "./foxtrot.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Assert } from "@adobe/data/types";
import { AsyncDataService, Service } from "@adobe/data/service";
import type { GenerateNamesInput } from "./generate-names-input.js";
import type { GeneratedName } from "./generated-name.js";

export interface CharacterNameService extends Service {
generateNames: (input: GenerateNamesInput) => Promise<readonly GeneratedName[]>;
generateNameStream: (input: GenerateNamesInput) => AsyncGenerator<GeneratedName>;
}

type _CheckCharacterNameService = Assert<AsyncDataService.IsValid<CharacterNameService>>;

export * as CharacterNameService from "./public.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import { CharacterNameService } from "./character-name-service.js";

describe("CharacterNameService.create", () => {
it("generates deterministic fake names", async () => {
const service = CharacterNameService.create();
const input = { count: 2, style: "fantasy" as const, seed: 42 };

expect(await service.generateNames(input)).toEqual([
{ value: "Dornthor", style: "fantasy" },
{ value: "Alddell", style: "fantasy" },
]);
});

it("streams generated names", async () => {
const service = CharacterNameService.create();
const names: CharacterNameService.GeneratedName[] = [];

for await (const name of service.generateNameStream({
count: 2,
style: "sci-fi",
seed: 7,
})) {
names.push(name);
}

expect(names).toEqual([
{ value: "Luma Flux", style: "sci-fi" },
{ value: "Vex IX", style: "sci-fi" },
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { fakeGenerateNameStream, fakeGenerateNames } from "./fake-generate-names.js";
import type { CharacterNameService } from "./character-name-service.js";

export const create = (): CharacterNameService => ({
generateNames: async (input) => fakeGenerateNames(input),
generateNameStream: (input) => fakeGenerateNameStream(input),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { describe, expect, it } from "vitest";
import { fakeGenerateNames } from "./fake-generate-names.js";

describe("fakeGenerateNames", () => {
it("caps batch size and uses style-specific parts", () => {
expect(fakeGenerateNames({ count: 99, style: "modern", seed: 3 })).toHaveLength(16);
expect(fakeGenerateNames({ count: 1, style: "modern", seed: 3 })[0]?.value).toBe("Morgan Shaw");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { GenerateNamesInput } from "./generate-names-input.js";
import type { GeneratedName } from "./generated-name.js";
import type { NameStyle } from "./name-style/name-style.js";

const prefixes: Record<NameStyle, readonly string[]> = {
fantasy: ["Ald", "Bran", "Cael", "Dorn", "Eira"],
"sci-fi": ["Nova", "Vex", "Zyn", "Korr", "Luma"],
modern: ["Alex", "Jordan", "Riley", "Morgan", "Casey"],
};

const suffixes: Record<NameStyle, readonly string[]> = {
fantasy: ["wyn", "thor", "mere", "dell", "ric"],
"sci-fi": ["-7", " Prime", " IX", " Unit", " Flux"],
modern: [" Lee", " Kim", " Cruz", " Park", " Shaw"],
};

const nextIndex = (seed: number, index: number): number =>
(seed * 1_103_515_245 + 12_345 + index * 97) % 2_147_483_647;

export const fakeGenerateNames = (input: GenerateNamesInput): readonly GeneratedName[] => {
const seed = input.seed ?? 1;
const count = Math.max(0, Math.min(input.count, 16));
const stylePrefixes = prefixes[input.style];
const styleSuffixes = suffixes[input.style];

return Array.from({ length: count }, (_, index) => {
const pick = nextIndex(seed, index);
const prefix = stylePrefixes[pick % stylePrefixes.length] ?? stylePrefixes[0];
const suffix = styleSuffixes[(pick >> 3) % styleSuffixes.length] ?? styleSuffixes[0];
return { value: `${prefix}${suffix}`, style: input.style };
});
};

export const fakeGenerateNameStream = async function* (
input: GenerateNamesInput,
): AsyncGenerator<GeneratedName> {
for (const name of fakeGenerateNames(input)) {
yield name;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NameStyle } from "./name-style/name-style.js";

export type GenerateNamesInput = {
readonly count: number;
readonly style: NameStyle;
readonly seed?: number;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { NameStyle } from "./name-style/name-style.js";

export type GeneratedName = {
readonly value: string;
readonly style: NameStyle;
};
Loading