@hotwired-sh/playbooks 1.0.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/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @hotwired-sh/playbooks
2
+
3
+ [![Hotwired](hotwired-sh.png)](https://hotwired.sh)
4
+
5
+ Playbook definitions for [Hotwired](https://hotwired.sh) multi-agent workflows.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @hotwired-sh/playbooks
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### TypeScript/JavaScript
16
+
17
+ ```typescript
18
+ import { loadAllPlaybooks, loadPlaybook, type Playbook } from '@hotwired-sh/playbooks';
19
+
20
+ // Load all active playbooks
21
+ const playbooks = loadAllPlaybooks();
22
+
23
+ // Load a specific playbook
24
+ const playbook = loadPlaybook('plan-build');
25
+ ```
26
+
27
+ ### Direct file access
28
+
29
+ The playbook files are included in the package and can be accessed directly:
30
+
31
+ ```typescript
32
+ import { createRequire } from 'module';
33
+ const require = createRequire(import.meta.url);
34
+ const packagePath = require.resolve('@hotwired-sh/playbooks/package.json');
35
+ // Playbooks are in ./playbooks/ relative to package root
36
+ ```
37
+
38
+ ## Playbook Structure
39
+
40
+ Each playbook is a directory containing:
41
+
42
+ - `playbook.json` - Metadata (name, roles, flow, etc.)
43
+ - `protocol.md` - Shared protocol/rules for all agents
44
+ - `*.md` - Role-specific prompts
45
+
46
+ ## Available Playbooks
47
+
48
+ - **plan-build** - Planning and building features (Strategist + Builder)
49
+ - **doc-editor** - Documentation writing and editing (Writer + Critiquer)
50
+
51
+ ## Creating Custom Playbooks
52
+
53
+ Fork this repository and add your own playbook directories following the same structure in `./playbooks/`.
54
+
55
+ ## License
56
+
57
+ MIT
package/dist/data.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Playbook data loader
3
+ */
4
+ import type { Playbook } from './types.js';
5
+ export declare const PLAYBOOK_IDS: readonly ["doc-editor", "plan-build"];
6
+ export type PlaybookId = typeof PLAYBOOK_IDS[number];
7
+ /**
8
+ * List all available playbook IDs (active only by default)
9
+ */
10
+ export declare function listPlaybookIds(activeOnly?: boolean): string[];
11
+ /**
12
+ * Load a specific playbook by ID
13
+ */
14
+ export declare function loadPlaybook(playbookId: string): Playbook | null;
15
+ /**
16
+ * Load all active playbooks
17
+ */
18
+ export declare function loadAllPlaybooks(): Playbook[];
19
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAO7D,eAAO,MAAM,YAAY,uCAGf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,UAAO,GAAG,MAAM,EAAE,CAe3D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAwChE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAY7C"}
package/dist/data.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Playbook data loader
3
+ */
4
+ import { readFileSync, existsSync } from 'node:fs';
5
+ import { join, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ // Go up from dist/ to package root, then into playbooks/
9
+ const PLAYBOOKS_DIR = join(__dirname, '..', 'playbooks');
10
+ export const PLAYBOOK_IDS = [
11
+ 'doc-editor',
12
+ 'plan-build',
13
+ ];
14
+ /**
15
+ * List all available playbook IDs (active only by default)
16
+ */
17
+ export function listPlaybookIds(activeOnly = true) {
18
+ return PLAYBOOK_IDS.filter((id) => {
19
+ if (!activeOnly)
20
+ return true;
21
+ const metadataPath = join(PLAYBOOKS_DIR, id, 'playbook.json');
22
+ if (!existsSync(metadataPath))
23
+ return false;
24
+ try {
25
+ const content = readFileSync(metadataPath, 'utf-8');
26
+ const metadata = JSON.parse(content);
27
+ return metadata.active !== false;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ });
33
+ }
34
+ /**
35
+ * Load a specific playbook by ID
36
+ */
37
+ export function loadPlaybook(playbookId) {
38
+ const playbookDir = join(PLAYBOOKS_DIR, playbookId);
39
+ if (!existsSync(playbookDir)) {
40
+ console.error(`[playbooks] Playbook directory not found: ${playbookDir}`);
41
+ return null;
42
+ }
43
+ try {
44
+ // Load metadata
45
+ const metadataPath = join(playbookDir, 'playbook.json');
46
+ const metadataContent = readFileSync(metadataPath, 'utf-8');
47
+ const metadata = JSON.parse(metadataContent);
48
+ // Load protocol
49
+ const protocolPath = join(playbookDir, 'protocol.md');
50
+ const protocol = existsSync(protocolPath)
51
+ ? readFileSync(protocolPath, 'utf-8')
52
+ : '';
53
+ // Load role prompts
54
+ const rolePrompts = {};
55
+ for (const role of metadata.roles) {
56
+ if (role.promptFile) {
57
+ const promptPath = join(playbookDir, role.promptFile);
58
+ if (existsSync(promptPath)) {
59
+ rolePrompts[role.id] = readFileSync(promptPath, 'utf-8');
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ metadata,
65
+ protocol,
66
+ role_prompts: rolePrompts,
67
+ };
68
+ }
69
+ catch (error) {
70
+ console.error(`[playbooks] Error loading playbook: ${playbookId}`, error);
71
+ return null;
72
+ }
73
+ }
74
+ /**
75
+ * Load all active playbooks
76
+ */
77
+ export function loadAllPlaybooks() {
78
+ const ids = listPlaybookIds();
79
+ const playbooks = [];
80
+ for (const id of ids) {
81
+ const playbook = loadPlaybook(id);
82
+ if (playbook) {
83
+ playbooks.push(playbook);
84
+ }
85
+ }
86
+ return playbooks;
87
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @hotwired-sh/playbooks
3
+ *
4
+ * Playbook definitions for Hotwired multi-agent workflows.
5
+ *
6
+ * @example Loading playbooks
7
+ * ```typescript
8
+ * import { loadAllPlaybooks, loadPlaybook } from '@hotwired-sh/playbooks';
9
+ *
10
+ * const playbooks = loadAllPlaybooks();
11
+ * const playbook = loadPlaybook('plan-build');
12
+ * ```
13
+ *
14
+ * @example Creating custom playbooks
15
+ * ```typescript
16
+ * import type { PlaybookMetadata, PlaybookRole } from '@hotwired-sh/playbooks';
17
+ * import { validatePlaybook } from '@hotwired-sh/playbooks';
18
+ *
19
+ * // Validate your playbook directory
20
+ * const result = validatePlaybook('./my-playbook');
21
+ * if (!result.valid) {
22
+ * console.error(result.errors);
23
+ * }
24
+ * ```
25
+ */
26
+ export type { Playbook, PlaybookMetadata, PlaybookRole, PlaybooksManifest, ValidationResult, } from './types.js';
27
+ export { loadAllPlaybooks, loadPlaybook, listPlaybookIds, PLAYBOOK_IDS, type PlaybookId, } from './data.js';
28
+ export { validatePlaybook, validateAllPlaybooks, } from './validate.js';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @hotwired-sh/playbooks
3
+ *
4
+ * Playbook definitions for Hotwired multi-agent workflows.
5
+ *
6
+ * @example Loading playbooks
7
+ * ```typescript
8
+ * import { loadAllPlaybooks, loadPlaybook } from '@hotwired-sh/playbooks';
9
+ *
10
+ * const playbooks = loadAllPlaybooks();
11
+ * const playbook = loadPlaybook('plan-build');
12
+ * ```
13
+ *
14
+ * @example Creating custom playbooks
15
+ * ```typescript
16
+ * import type { PlaybookMetadata, PlaybookRole } from '@hotwired-sh/playbooks';
17
+ * import { validatePlaybook } from '@hotwired-sh/playbooks';
18
+ *
19
+ * // Validate your playbook directory
20
+ * const result = validatePlaybook('./my-playbook');
21
+ * if (!result.valid) {
22
+ * console.error(result.errors);
23
+ * }
24
+ * ```
25
+ */
26
+ // Data loading
27
+ export { loadAllPlaybooks, loadPlaybook, listPlaybookIds, PLAYBOOK_IDS, } from './data.js';
28
+ // Validation
29
+ export { validatePlaybook, validateAllPlaybooks, } from './validate.js';
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Playbook type definitions
3
+ *
4
+ * These types define the structure of playbook configurations
5
+ * used by the Hotwired multi-agent system.
6
+ */
7
+ /**
8
+ * Defines an agent role within a playbook
9
+ */
10
+ export interface PlaybookRole {
11
+ /** Unique identifier for the role (kebab-case, e.g., 'test-writer') */
12
+ id: string;
13
+ /** Display name for the role (e.g., 'Test Writer') */
14
+ name: string;
15
+ /** Role type identifier (e.g., 'orchestrator', 'implementer') */
16
+ role: string;
17
+ /** Description of what this role does */
18
+ description: string;
19
+ /** Filename of the prompt markdown file (e.g., 'writer.md') */
20
+ promptFile: string;
21
+ /** Role capabilities */
22
+ capabilities?: {
23
+ canResolveImpediments?: boolean;
24
+ };
25
+ }
26
+ /**
27
+ * Metadata for a playbook, stored in playbook.json
28
+ */
29
+ export interface PlaybookMetadata {
30
+ /** Unique identifier for the playbook (kebab-case, e.g., 'plan-build') */
31
+ id: string;
32
+ /** Display name for the playbook */
33
+ name: string;
34
+ /** Short tagline describing the playbook */
35
+ tagline: string;
36
+ /** Detailed description of what the playbook does */
37
+ description: string;
38
+ /** Version string */
39
+ version?: string;
40
+ /** Icon identifier (Lucide icon name) */
41
+ icon: string;
42
+ /** Whether the playbook is active and available for use (default: true) */
43
+ active?: boolean;
44
+ /** Agent roles in this playbook */
45
+ roles: PlaybookRole[];
46
+ /** Workflow phases */
47
+ phases?: string[];
48
+ /** Flow description (array of step descriptions) */
49
+ flow: string[];
50
+ /** Description of human's role in the workflow */
51
+ humanRole: string;
52
+ /** List of use cases this playbook is best suited for */
53
+ bestFor: string[];
54
+ }
55
+ /**
56
+ * A fully loaded playbook with all content
57
+ */
58
+ export interface Playbook {
59
+ /** Playbook metadata from playbook.json */
60
+ metadata: PlaybookMetadata;
61
+ /** Protocol markdown content (shared rules for all agents) */
62
+ protocol: string;
63
+ /** Map of role ID to prompt markdown content */
64
+ role_prompts: Record<string, string>;
65
+ }
66
+ /**
67
+ * Response format for the playbooks API
68
+ */
69
+ export interface PlaybooksManifest {
70
+ /** List of all playbooks */
71
+ playbooks: Playbook[];
72
+ /** Version timestamp for cache invalidation */
73
+ version: number;
74
+ }
75
+ /**
76
+ * Result of validating a playbook
77
+ */
78
+ export interface ValidationResult {
79
+ /** Whether the playbook is valid */
80
+ valid: boolean;
81
+ /** List of validation errors (empty if valid) */
82
+ errors: string[];
83
+ /** List of validation warnings */
84
+ warnings: string[];
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,YAAY,CAAC,EAAE;QACb,qBAAqB,CAAC,EAAE,OAAO,CAAC;KACjC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mCAAmC;IACnC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Playbook type definitions
3
+ *
4
+ * These types define the structure of playbook configurations
5
+ * used by the Hotwired multi-agent system.
6
+ */
7
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Playbook validation utilities
3
+ */
4
+ import type { ValidationResult } from './types.js';
5
+ /**
6
+ * Validate a playbook directory
7
+ */
8
+ export declare function validatePlaybook(playbookDir: string): ValidationResult;
9
+ /**
10
+ * Validate all playbooks in a directory
11
+ */
12
+ export declare function validateAllPlaybooks(playbooksDir: string): Map<string, ValidationResult>;
13
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CA4EtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAYxF"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Playbook validation utilities
3
+ */
4
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ /**
7
+ * Validate a playbook directory
8
+ */
9
+ export function validatePlaybook(playbookDir) {
10
+ const errors = [];
11
+ const warnings = [];
12
+ // Check playbook.json exists
13
+ const metadataPath = join(playbookDir, 'playbook.json');
14
+ if (!existsSync(metadataPath)) {
15
+ errors.push('Missing playbook.json');
16
+ return { valid: false, errors, warnings };
17
+ }
18
+ // Parse and validate playbook.json
19
+ let metadata;
20
+ try {
21
+ const content = readFileSync(metadataPath, 'utf-8');
22
+ metadata = JSON.parse(content);
23
+ }
24
+ catch (e) {
25
+ errors.push(`Invalid playbook.json: ${e instanceof Error ? e.message : String(e)}`);
26
+ return { valid: false, errors, warnings };
27
+ }
28
+ // Required fields
29
+ const requiredFields = ['id', 'name', 'tagline', 'description', 'icon', 'roles', 'flow', 'humanRole', 'bestFor'];
30
+ for (const field of requiredFields) {
31
+ if (!(field in metadata)) {
32
+ errors.push(`Missing required field: ${field}`);
33
+ }
34
+ }
35
+ // Validate id format
36
+ if (metadata.id && !/^[a-z][a-z0-9-]*$/.test(metadata.id)) {
37
+ errors.push(`Invalid id format: "${metadata.id}" (must be kebab-case)`);
38
+ }
39
+ // Validate roles
40
+ if (Array.isArray(metadata.roles)) {
41
+ if (metadata.roles.length === 0) {
42
+ errors.push('Playbook must have at least one role');
43
+ }
44
+ const roleIds = new Set();
45
+ for (const role of metadata.roles) {
46
+ // Check for duplicate role IDs
47
+ if (roleIds.has(role.id)) {
48
+ errors.push(`Duplicate role id: "${role.id}"`);
49
+ }
50
+ roleIds.add(role.id);
51
+ // Check role id format
52
+ if (!/^[a-z][a-z0-9-]*$/.test(role.id)) {
53
+ errors.push(`Invalid role id format: "${role.id}" (must be kebab-case)`);
54
+ }
55
+ // Check prompt file exists
56
+ if (role.promptFile) {
57
+ const promptPath = join(playbookDir, role.promptFile);
58
+ if (!existsSync(promptPath)) {
59
+ errors.push(`Missing prompt file for role "${role.id}": ${role.promptFile}`);
60
+ }
61
+ }
62
+ else {
63
+ errors.push(`Missing promptFile for role "${role.id}"`);
64
+ }
65
+ }
66
+ }
67
+ // Check for protocol.md (warning if missing)
68
+ const protocolPath = join(playbookDir, 'protocol.md');
69
+ if (!existsSync(protocolPath)) {
70
+ warnings.push('Missing protocol.md (recommended for shared agent instructions)');
71
+ }
72
+ return {
73
+ valid: errors.length === 0,
74
+ errors,
75
+ warnings,
76
+ };
77
+ }
78
+ /**
79
+ * Validate all playbooks in a directory
80
+ */
81
+ export function validateAllPlaybooks(playbooksDir) {
82
+ const results = new Map();
83
+ const entries = readdirSync(playbooksDir, { withFileTypes: true });
84
+ for (const entry of entries) {
85
+ if (entry.isDirectory()) {
86
+ const playbookPath = join(playbooksDir, entry.name);
87
+ results.set(entry.name, validatePlaybook(playbookPath));
88
+ }
89
+ }
90
+ return results;
91
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../src/validate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { validatePlaybook, validateAllPlaybooks } from './validate.js';
5
+ import { loadPlaybook, loadAllPlaybooks, listPlaybookIds, PLAYBOOK_IDS } from './data.js';
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const PLAYBOOKS_DIR = join(__dirname, '..', 'playbooks');
8
+ describe('validatePlaybook', () => {
9
+ it('validates plan-build playbook', () => {
10
+ const result = validatePlaybook(join(PLAYBOOKS_DIR, 'plan-build'));
11
+ expect(result.valid).toBe(true);
12
+ expect(result.errors).toHaveLength(0);
13
+ });
14
+ it('validates doc-editor playbook', () => {
15
+ const result = validatePlaybook(join(PLAYBOOKS_DIR, 'doc-editor'));
16
+ expect(result.valid).toBe(true);
17
+ expect(result.errors).toHaveLength(0);
18
+ });
19
+ it('returns error for non-existent directory', () => {
20
+ const result = validatePlaybook(join(PLAYBOOKS_DIR, 'non-existent'));
21
+ expect(result.valid).toBe(false);
22
+ expect(result.errors).toContain('Missing playbook.json');
23
+ });
24
+ });
25
+ describe('validateAllPlaybooks', () => {
26
+ it('validates all playbooks in directory', () => {
27
+ const results = validateAllPlaybooks(PLAYBOOKS_DIR);
28
+ expect(results.size).toBeGreaterThan(0);
29
+ for (const [id, result] of results) {
30
+ expect(result.valid, `Playbook "${id}" should be valid`).toBe(true);
31
+ }
32
+ });
33
+ });
34
+ describe('loadPlaybook', () => {
35
+ it('loads plan-build playbook', () => {
36
+ const playbook = loadPlaybook('plan-build');
37
+ expect(playbook).not.toBeNull();
38
+ expect(playbook?.metadata.id).toBe('plan-build');
39
+ expect(playbook?.metadata.roles.length).toBeGreaterThan(0);
40
+ expect(playbook?.protocol).toBeTruthy();
41
+ });
42
+ it('loads doc-editor playbook', () => {
43
+ const playbook = loadPlaybook('doc-editor');
44
+ expect(playbook).not.toBeNull();
45
+ expect(playbook?.metadata.id).toBe('doc-editor');
46
+ });
47
+ it('returns null for non-existent playbook', () => {
48
+ const playbook = loadPlaybook('non-existent');
49
+ expect(playbook).toBeNull();
50
+ });
51
+ });
52
+ describe('loadAllPlaybooks', () => {
53
+ it('loads all active playbooks', () => {
54
+ const playbooks = loadAllPlaybooks();
55
+ expect(playbooks.length).toBeGreaterThan(0);
56
+ for (const playbook of playbooks) {
57
+ expect(playbook.metadata.id).toBeTruthy();
58
+ expect(playbook.metadata.active).not.toBe(false);
59
+ }
60
+ });
61
+ });
62
+ describe('listPlaybookIds', () => {
63
+ it('returns all playbook IDs', () => {
64
+ const ids = listPlaybookIds();
65
+ expect(ids.length).toBeGreaterThan(0);
66
+ expect(ids).toContain('plan-build');
67
+ expect(ids).toContain('doc-editor');
68
+ });
69
+ });
70
+ describe('PLAYBOOK_IDS', () => {
71
+ it('contains expected playbooks', () => {
72
+ expect(PLAYBOOK_IDS).toContain('plan-build');
73
+ expect(PLAYBOOK_IDS).toContain('doc-editor');
74
+ });
75
+ });
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@hotwired-sh/playbooks",
3
+ "version": "1.0.0",
4
+ "description": "Playbook definitions for Hotwired multi-agent workflows",
5
+ "author": "Hotwired <hello@hotwired.sh>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ },
15
+ "./schemas/playbook.schema.json": "./schemas/playbook.schema.json",
16
+ "./playbooks": "./playbooks",
17
+ "./playbooks/*": "./playbooks/*"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "playbooks",
22
+ "schemas"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/hotwired-sh/playbooks.git"
27
+ },
28
+ "homepage": "https://hotwired.sh",
29
+ "bugs": {
30
+ "url": "https://github.com/hotwired-sh/playbooks/issues"
31
+ },
32
+ "keywords": [
33
+ "hotwired",
34
+ "playbooks",
35
+ "ai-agents",
36
+ "multi-agent",
37
+ "workflow"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "lint": "eslint src --ext .ts",
44
+ "lint:fix": "eslint src --ext .ts --fix",
45
+ "typecheck": "tsc --noEmit",
46
+ "validate": "node --loader ts-node/esm scripts/validate-playbooks.ts",
47
+ "prepublishOnly": "npm run build",
48
+ "prepare": "husky"
49
+ },
50
+ "devDependencies": {
51
+ "@commitlint/cli": "^19.0.0",
52
+ "@commitlint/config-conventional": "^19.0.0",
53
+ "@semantic-release/changelog": "^6.0.3",
54
+ "@semantic-release/exec": "^6.0.3",
55
+ "@semantic-release/git": "^10.0.1",
56
+ "@types/node": "^22.0.0",
57
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
58
+ "@typescript-eslint/parser": "^7.0.0",
59
+ "conventional-changelog-conventionalcommits": "^7.0.2",
60
+ "eslint": "^8.57.0",
61
+ "husky": "^9.0.0",
62
+ "semantic-release": "^24.0.0",
63
+ "typescript": "^5.6.3",
64
+ "vitest": "^2.0.0"
65
+ },
66
+ "engines": {
67
+ "node": ">=18"
68
+ }
69
+ }