From harness-claude
Implements GOF Composite pattern in TypeScript for tree structures like file systems, org charts, ASTs, enabling uniform treatment of leaves and composites with recursive operations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Compose objects into tree structures and treat individual and composite objects uniformly.
Implements Composite pattern in JavaScript to compose objects into tree structures like file systems, UI components, or org charts, treating leaves and composites uniformly.
Generates Composite pattern for PHP 8.4 with interface, leaf, composite classes, and unit tests. For tree structures like menus, file systems, org charts.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Compose objects into tree structures and treat individual and composite objects uniformly.
Core structure — Component interface, Leaf, Composite:
// Component — uniform interface for both leaves and composites
interface FileSystemItem {
name: string;
size(): number;
print(indent?: string): void;
}
// Leaf — no children
class File implements FileSystemItem {
constructor(
public readonly name: string,
private readonly bytes: number
) {}
size(): number {
return this.bytes;
}
print(indent = ''): void {
console.log(`${indent}📄 ${this.name} (${this.bytes}B)`);
}
}
// Composite — has children, delegates to them
class Directory implements FileSystemItem {
private children: FileSystemItem[] = [];
constructor(public readonly name: string) {}
add(item: FileSystemItem): this {
this.children.push(item);
return this;
}
remove(item: FileSystemItem): void {
this.children = this.children.filter((c) => c !== item);
}
size(): number {
// Recursion handled uniformly — leaves and composites both have size()
return this.children.reduce((total, child) => total + child.size(), 0);
}
print(indent = ''): void {
console.log(`${indent}📁 ${this.name}`);
for (const child of this.children) {
child.print(indent + ' ');
}
}
}
// Client code treats everything as FileSystemItem
function printSummary(item: FileSystemItem): void {
item.print();
console.log(`Total size: ${item.size()}B`);
}
// Build the tree
const root = new Directory('project')
.add(new File('package.json', 512))
.add(new File('tsconfig.json', 256))
.add(
new Directory('src')
.add(new File('index.ts', 1024))
.add(new Directory('utils').add(new File('logger.ts', 768)))
);
printSummary(root);
Permission tree (practical RBAC example):
interface Permission {
name: string;
check(userId: string, action: string): boolean;
}
class AtomicPermission implements Permission {
constructor(
public readonly name: string,
private readonly resource: string
) {}
check(userId: string, action: string): boolean {
// Real implementation would check DB
return action === this.resource;
}
}
class PermissionGroup implements Permission {
private permissions: Permission[] = [];
constructor(public readonly name: string) {}
add(permission: Permission): this {
this.permissions.push(permission);
return this;
}
// OR semantics: granted if any child grants it
check(userId: string, action: string): boolean {
return this.permissions.some((p) => p.check(userId, action));
}
}
const adminRole = new PermissionGroup('admin')
.add(new AtomicPermission('read-users', 'users'))
.add(new AtomicPermission('write-users', 'users'))
.add(
new PermissionGroup('billing')
.add(new AtomicPermission('read-invoices', 'invoices'))
.add(new AtomicPermission('write-invoices', 'invoices'))
);
TypeScript discriminated union alternative: For simpler cases, a discriminated union often beats a class hierarchy:
type TreeNode =
| { kind: 'leaf'; name: string; value: number }
| { kind: 'branch'; name: string; children: TreeNode[] };
function sum(node: TreeNode): number {
if (node.kind === 'leaf') return node.value;
return node.children.reduce((acc, child) => acc + sum(child), 0);
}
Anti-patterns:
add/remove on the Component interface — leaves can't implement these; use optional methods or a type guardadd()Traversal strategies:
function* depthFirst(item: FileSystemItem): Generator<FileSystemItem> {
yield item;
if (item instanceof Directory) {
for (const child of item.getChildren()) {
yield* depthFirst(child);
}
}
}
// Count all files
const fileCount = [...depthFirst(root)].filter((item) => item instanceof File).length;
refactoring.guru/design-patterns/composite