Agent skill
file-organization
Esposter file and folder organisation — one export per file, no export{} syntax, models vs services vs constants, command pattern field ordering, constant maps with as-const-satisfies, generic Vue components, MIME types, and LF line endings.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/file-organization-esposter-esposter
SKILL.md
File & Folder Organisation
Imports
- Always use
@/alias imports — never use relative imports (./,../), even for files in the same folder. Example:import { ... } from "@/composables/tableEditor/file/useEditedItemDataSourceOperations/testUtils"not"./testUtils".
Files and Exports
- One export per file — each exported function, class, or interface lives in its own file. Exception: Zod schemas may be co-located with their interface/type since they are tightly coupled.
- One class per file — classes belong in a
models/folder (e.g.,app/models/,shared/models/). - Never use
export { }syntax — always useexport const,export class,export interface,export type, orexport functionat the declaration site. The only valid exceptions are emptyexport {}in.d.tsfiles (to mark them as a module) andctix-generated barrel files (pinned package). If you seeexport { ... }in a hand-written.tsfile, it is wrong — inline theexportkeyword at each declaration instead. - Constants go in
constants.ts— all module-level constants in aconstants.tsfile underservices/alongside the files that use them. Never putconstants.tsinsidecomposables/. - Functions go in
services/— factory functions, command creators, and other exported functions belong inservices/, notmodels/.models/is strictly for classes and interfaces/types. - Generic browser utilities go in
app/utils/(e.g.,readFileAsText.ts). - Feature folders: related models/services/components are grouped under a feature subfolder (e.g.,
tableEditor/file/). - No magic strings — always use enums instead of string literals for discriminants, command types, and other categorical values.
Constant Maps
- Constant maps and arrays use PascalCase matching the filename with
as const satisfies— e.g.export const DataSourceConfigurationMap = { ... } as const satisfies Record<...>andexport const ColumnStatDefinitions = [ ... ] as const satisfies readonly ColumnStatDefinition[].- Exception: for generic definition arrays where the
format(or similar) callback is contravariant over the union of entry types,satisfieswill fail with a type error. In that case, useas constalone and cast at the call site withas never. See.claude/skills/typescript/SKILL.md— "Generic Definition Arrays".
- Exception: for generic definition arrays where the
- Destructure in
v-forunless passing the base item as component props —v-for="{ key, format } of ColumnStatDefinitions"is preferred overv-for="def of ColumnStatDefinitions"when you only need specific fields. Exception: if the item itself must be passed as a prop to a child component (e.g.<SomeCard :item="def" />), keep the variable name undestructured. - One constant map per file, named after the constant —
ColumnTypeFormSchemaMap.tsexports onlyColumnTypeFormSchemaMap. Never co-locate multiple maps in one file. When a map is a transformation of another (e.g. omitting a key), derive it directly rather than repeating the source values:[ColumnType.Boolean]: ColumnTypeFormSchemaMap[ColumnType.Boolean].omit({ name: true }). - Generic type maps for polymorphic dispatch — when a constant map needs to associate a discriminant key (e.g.
DataSourceType) with a type-parameterised generic (e.g.DataSourceConfiguration<TItem>), define an explicit type map first, then use a mapped type insatisfiesto get per-entry type safety without anyascasts:typescript// 1. Explicit type map (one file, in models/) type DataSourceItemTypeMap = { [DataSourceType.Csv]: CsvDataSourceItem }; // 2. Satisfies mapped type — each entry is checked against its specific type param export const DataSourceConfigurationMap: Record< DataSourceType, DataSourceConfiguration<DataSourceItemTypeMap[keyof DataSourceItemTypeMap]> > = { ... }; - Generic map lookup composables — when a component needs to look up a typed configuration from a generic map using a discriminant key on a generic item, extract the lookup into a composable. Use
MaybeRefOrGetter<TItem>withtoValue()so callers can pass refs or plain values. Hide the single internalascast and expose a fully typed API:typescriptexport const useDataSourceConfiguration = < TDataSourceItem extends DataSourceItemTypeMap[keyof DataSourceItemTypeMap], >( item: MaybeRefOrGetter<TDataSourceItem>, ): ComputedRef<DataSourceConfiguration<TDataSourceItem>> => computed(() => DataSourceConfigurationMap[toValue(item).type] as DataSourceConfiguration<TDataSourceItem>); // Caller (no cast needed): const dataSourceConfiguration = useDataSourceConfiguration(modelValue);
Generic Vue Components
Use <script setup lang="ts" generic="T extends SomeBase"> to make components type-safe over a specific subtype. Pass the typed value AND its associated generic config/interface as props so the parent resolves the concrete types and the child stays fully typed without lookups or casts:
<!-- Parent (knows concrete type): -->
<FilePicker :item="modelValue" :configuration="DataSourceConfigurationMap[DataSourceType.Csv]" />
<!-- Child: -->
<script setup lang="ts" generic="TDataSourceItem extends DataSourceItemTypeMap[keyof DataSourceItemTypeMap]">
interface FilePickerProps {
configuration: DataSourceConfiguration<TDataSourceItem>;
item: TDataSourceItem;
}
</script>
Command Pattern
Commands are classes extending ADataSourceCommand<T extends CommandType>. Each command declares readonly type = CommandType.X (no name — the base class provides get name() { return this.type; }). CommandType enum lives in models/tableEditor/file/commands/CommandType.ts. Class field ordering within a command: readonly type → blank line → get description() → blank line → all private readonly fields grouped together (no blank lines between same-level fields) → blank line → constructor → blank line between each method.
MIME Types
Store MIME type strings in the relevant configuration map (e.g. DataSourceConfigurationMap) rather than calling mime-types lookup at runtime — mime-types uses Node.js path.extname which is not available in the browser. Access mimeType through the configuration map at the call site.
Line Endings
- All files must use LF line endings (
\n), not CRLF. - The
Writetool on Windows always produces CRLF. Immediately after everyWritecall, convert with:bashFor multiple files at once:sed -i 's/\r//' "path/to/file"bashfind "path/to/dir" -name "*.md" | xargs -I{} sed -i 's/\r//' "{}"
File Length
- Target 50–100 lines per
.tsfile — a file over 100 lines is a yellow flag that a helper, sub-service, or model extraction is overdue. - Each file should have a single clear responsibility. If a file grows because it handles multiple concerns, split it.
- Exceptions: generated files, large constant maps with many entries, and files where co-location of tightly coupled logic (e.g. a Zod schema next to its interface) is intentional.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?