{"_id":"@emanuelsan/mosaic-js","_rev":"2-8b3a3f6dfc518af3b93513d2cf679ae6","name":"@emanuelsan/mosaic-js","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@emanuelsan/mosaic-js","version":"1.0.0","keywords":["template","markdown","ai","instruction","composition","modular","hierarchical","mustache","effect"],"author":{"name":"Emanuel Sandu"},"license":"MIT","_id":"@emanuelsan/mosaic-js@1.0.0","maintainers":[{"name":"emanuelsan","email":"e.sandu@gmail.com"}],"homepage":"https://github.com/emanuelsan/mosaic-js#readme","bugs":{"url":"https://github.com/emanuelsan/mosaic-js/issues"},"dist":{"shasum":"a59c6aa7668b667849316e9989bc86629b3c5801","tarball":"https://registry.npmjs.org/@emanuelsan/mosaic-js/-/mosaic-js-1.0.0.tgz","fileCount":56,"integrity":"sha512-bTSgcgI3hdfMLG7EKAprmccXtY8Kl2VLARmV6MJYlL095d3zZZbIYQscBntvTuLEVl9XO8mr0PCmKCA+RScHCw==","signatures":[{"sig":"MEQCID7F50Zv7DPLIkPZGQ5Rs6EbocN9FjdWgKzfW/L3l+2NAiB23y9ZiPTi9VzeYXVsLnRhYs0xATEtfue1I7ivwsEE6g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":103159},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"8886f0eb436269e6f2579cdf4051a83fb514c45f","scripts":{"test":"echo \"No tests yet\" && exit 0","build":"tsc"},"_npmUser":{"name":"emanuelsan","email":"e.sandu@gmail.com"},"repository":{"url":"git+https://github.com/emanuelsan/mosaic-js.git","type":"git"},"_npmVersion":"10.8.3","description":"Composable Markdown-based AI instruction engine for Node.js","directories":{},"_nodeVersion":"22.9.0","dependencies":{"effect":"^3.16.16","mustache":"^4.2.0","fast-glob":"^3.3.3","picocolors":"^1.1.1","gray-matter":"^4.0.3"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.3","@types/node":"^24.0.12","@types/mustache":"^4.2.6"},"_npmOperationalInternal":{"tmp":"tmp/mosaic-js_1.0.0_1754034526184_0.23204756239252666","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@emanuelsan/mosaic-js","version":"1.0.2","description":"Composable Markdown-based AI instruction engine for Node.js","type":"module","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"vitest run && tsc","test":"vitest run","test:watch":"vitest"},"author":{"name":"Emanuel Sandu"},"license":"MIT","keywords":["template","markdown","ai","instruction","composition","modular","hierarchical","mustache","effect"],"repository":{"type":"git","url":"git+https://github.com/emanuelsan/mosaic-js.git"},"homepage":"https://github.com/emanuelsan/mosaic-js#readme","bugs":{"url":"https://github.com/emanuelsan/mosaic-js/issues"},"dependencies":{"effect":"^3.16.16","fast-glob":"^3.3.3","gray-matter":"^4.0.3","mustache":"^4.2.0"},"devDependencies":{"@types/node":"^24.0.12","typescript":"^5.8.3","@types/mustache":"^4.2.6","vitest":"^2.0.4"},"_id":"@emanuelsan/mosaic-js@1.0.2","gitHead":"f73216f7e8f49ace43b6359b2bd65302cc5814ef","_nodeVersion":"22.9.0","_npmVersion":"10.8.3","dist":{"integrity":"sha512-h1ZaHhTfDJmOTydw1C84pTwsf33w7SI0sy1RGEytWrV88rLOXYxMU7DGM+7nsZQQH44ejI7Gn1HjbNdqVlVDAg==","shasum":"0a6cb842ed26f476ce68b1c6f48f3a3d4a36600c","tarball":"https://registry.npmjs.org/@emanuelsan/mosaic-js/-/mosaic-js-1.0.2.tgz","fileCount":55,"unpackedSize":93036,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEKbbqmLsChB5HIt/MsoDTsthuy6OGX49sdm4LbHABVEAiBXY5td4VEJjM1QYxIiHLekw9nbC51k3g0FLpe3TtmoQQ=="}]},"_npmUser":{"name":"emanuelsan","email":"e.sandu@gmail.com"},"directories":{},"maintainers":[{"name":"emanuelsan","email":"e.sandu@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mosaic-js_1.0.2_1754217414080_0.21782090556698397"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-01T07:48:46.071Z","modified":"2025-08-03T10:36:54.479Z","1.0.0":"2025-08-01T07:48:46.371Z","1.0.2":"2025-08-03T10:36:54.303Z"},"bugs":{"url":"https://github.com/emanuelsan/mosaic-js/issues"},"author":{"name":"Emanuel Sandu"},"license":"MIT","homepage":"https://github.com/emanuelsan/mosaic-js#readme","keywords":["template","markdown","ai","instruction","composition","modular","hierarchical","mustache","effect"],"repository":{"type":"git","url":"git+https://github.com/emanuelsan/mosaic-js.git"},"description":"Composable Markdown-based AI instruction engine for Node.js","maintainers":[{"name":"emanuelsan","email":"e.sandu@gmail.com"}],"readme":"<div align=\"center\">\n\n# Mosaic.js\n\n<img src=\"assets/mosaic-logo.png\" alt=\"Mosaic.js Logo\" width=\"150\" />\n\n**Composable Markdown-based AI instruction engine for Node.js**\n\n[![NPM Version](https://img.shields.io/npm/v/@emanuelsan/mosaic-js.svg)](https://www.npmjs.com/package/@emanuelsan/mosaic-js)\n\n</div>\n\nMosaic.js is a powerful template composition library that allows you to build complex, hierarchical instruction sets from modular Markdown files. Perfect for AI agents, documentation systems, and any scenario where you need to compose dynamic content from reusable templates.\n\n## Principles of Mosaic\n\nMosaic was designed with specific principles in mind to make template composition accessible, reliable, and flexible:\n\n### 🛡️ Silent Safety\nMosaic is built to be **silently safe** - it never breaks or throws errors that stop execution. Instead, it gracefully handles edge cases and continues processing:\n- **Duplicate references**: Automatically detected and eliminated, processing continues\n- **Duplicate IDs**: First occurrence is used, warning displayed, processing continues\n- **Circular references**: Detected and removed with warnings, processing continues\n- **Self-references**: Identified and filtered out, processing continues\n- **Missing variables**: Replaced with empty strings, processing continues\n\nThe only requirement is providing a directory with Markdown files - everything else is optional and handled gracefully.\n\n### 👥 Business-User Friendly\nMosaic empowers **non-technical users** to create and compose complex templates without any programming knowledge:\n- **No JavaScript required**: Business users can compose templates using simple Markdown syntax\n- **Intuitive reference system**: Natural `{{ template-name }}` syntax for linking templates\n- **Plain English**: Templates are written in Markdown, readable by anyone\n- **Visual structure**: Directory organization mirrors logical template hierarchy\n\n### 🔧 Developer Flexibility\nMosaic provides **optional developer control** while respecting business user autonomy:\n- **Variable injection**: Developers can pass dynamic values into business-created templates\n- **Path-specific overrides**: Fine-grained control over variable values per template\n- **Chainable API**: Flexible variable management with intuitive method chaining\n- **Graceful degradation**: Templates work with or without developer-provided variables\n\n### 🎯 Always Tries to Render\nMosaic follows the principle of **best effort composition** - it always attempts to produce meaningful output:\n- Missing variables become empty strings rather than errors\n- Broken references are removed and logged, not fatal\n- Partial templates are better than no templates\n- Users get feedback through warnings, not crashes\n\n### 🌐 Framework & Platform Agnostic\nMosaic is designed to be **universally applicable** across different contexts and environments:\n- **Framework independent**: Works with any Node.js-compatible framework or standalone applications\n- **Domain agnostic**: While built to solve AI orchestration challenges, it's useful for any hierarchical text composition\n- **Platform neutral**: Runs in any Node.js environment without external dependencies\n- **Context flexible**: Adapts to business domains from documentation to instructions to content generation\n- **Pure text assembly**: At its core, it's simply a tool for composing text from modular pieces\n\nThese principles make Mosaic ideal for **collaborative workflows** where business teams create content and development teams provide dynamic data, without either side blocking the other.\n\n## Features\n\n- 🧩 **Modular Templates**: Compose complex instructions from simple Markdown files\n- 🔗 **Reference System**: Link templates together using intuitive selector syntax\n- 🎯 **Variable Injection**: Dynamic content with mustache templating and path-specific overrides\n- 🔄 **Recursive Expansion**: Automatically resolves nested references and dependencies\n- 🛡️ **Loop Detection**: Built-in protection against circular references\n- ⚡ **Effect-based**: Built on the Effect library for robust error handling and composability\n\n## Installation\n\n```bash\nnpm install mosaic-js\n```\n\n## Quick Start\n\n```typescript\nimport { Mosaic } from 'mosaic-js';\n\n// 1. Create a Mosaic instance from a directory of templates\nconst instructions = Mosaic.fromDirectory('src/templates');\n\n// 2. Provide global variables\ninstructions.provideVariables({\n  agentName: 'Assistant',\n  maxAttempts: 3,\n});\n\n// 3. Provide path-specific overrides\ninstructions.provideOverrides({\n  '#special-agent': {\n    maxAttempts: 10,  // Override for specific template\n  },\n});\n\n// 4. Compose the final result\nconst result = instructions.compose('agents/main-agent');\nconsole.log(result);\n```\n\n## Directory Structure\n\nOrganize your templates in a hierarchical directory structure:\n\n```\ntemplates/\n├── agents/\n│   ├── main-agent.md\n│   └── specialist.md\n├── rules/\n│   ├── general-rules.md\n│   └── special-rules.md\n├── company/\n│   └── description.md\n└── shared/\n    └── footer.md\n```\n\n## Template Files\n\nTemplates are Markdown files with optional frontmatter and reference slots:\n\n**`agents/main-agent.md`**\n```markdown\n---\nid: main-agent\n---\n\n# AI Agent Instructions\n\nYou are {{ $agentName }}, an AI assistant.\n\n## Company Information\n{{ company/description }}\n\n## Rules\n{{ rules/general-rules }}\n{{ #special-rules }}\n\n## Attempts\nYou have {{ $maxAttempts }} attempts to complete tasks.\n```\n\n**`rules/special-rules.md`**\n```markdown\n---\nid: special-rules\n---\n\nThese are special operational rules:\n\n- Always be helpful and accurate\n- You have {{ $maxAttempts }} attempts maximum\n- Follow all safety guidelines\n```\n\n**`company/description.md`**\n```markdown\nWe are ACME Corp, a leading technology company specializing in AI solutions.\nOur mission is to {{ $companyMission }}.\n```\n\n## Reference Syntax\n\nMosaic supports three types of template selectors, each with different resolution behavior:\n\n### 1. Relative Path Selectors\n```markdown\n{{ agents/specialist }}\n{{ rules/general-rules }}\n```\n\n**How it works**: Relative path selectors resolve paths relative to the **current template's location**. If you're in `company/policies/main.md` and reference `{{ shared/footer }}`, Mosaic will look for `company/policies/shared/footer.md`. This allows for contextual, hierarchical organization where templates can reference nearby files naturally.\n\n**Example**:\n- Current file: `agents/main-agent.md`\n- Reference: `{{ specialist }}`\n- Resolves to: `agents/specialist.md`\n\n### 2. ID Selectors (using frontmatter id)\n```markdown\n{{ #special-rules }}\n{{ #main-agent }}\n```\n\n**How it works**: ID selectors use the `id` field from a template's frontmatter to locate files anywhere in the directory tree. Mosaic searches the entire template directory for any `.md` file with a matching `id` in its frontmatter, regardless of its location. This provides location-independent referencing.\n\n**Duplicate ID Handling**: If multiple files have the same `id` in their frontmatter (which is a configuration mistake), Mosaic will use the first file it encounters during the search and display a warning in the console about the duplicate IDs. It's recommended to keep IDs unique across your template directory.\n\n**Example**:\n- Reference: `{{ #special-rules }}`\n- Searches for any file with `id: special-rules` in frontmatter\n- Could resolve to: `rules/advanced/special-rules.md` or `policies/special-rules.md`\n- Location doesn't matter, only the ID match\n\n### 3. Root Selectors\n```markdown\n{{ @shared/footer }}\n{{ @company/description }}\n```\n\n**How it works**: Root selectors always resolve paths relative to the **root directory** where Mosaic was instantiated, regardless of the current template's location. The `@` prefix indicates \"start from the root directory\". This provides absolute path referencing within your template hierarchy.\n\n**Example**:\n- Mosaic created with: `Mosaic.fromDirectory('templates')`\n- Reference: `{{ @shared/footer }}` (from any template)\n- Always resolves to: `templates/shared/footer.md`\n- Current template location is irrelevant\n\n## Variable System\n\n### Global Variables\n\nProvide variables that are available throughout all templates:\n\n```typescript\ninstructions.provideVariables({\n  agentName: 'Claude',\n  companyMission: 'democratize AI technology',\n  maxAttempts: 5,\n});\n```\n\n### Chainable Variables\n\nLater calls override earlier ones for the same variable:\n\n```typescript\ninstructions\n  .provideVariables({\n    agentName: 'Assistant',\n    maxAttempts: 3,\n  })\n  .provideVariables({\n    agentName: 'Claude',  // Overrides 'Assistant'\n    specialMode: true,\n  });\n```\n\n### Path-Specific Overrides\n\nOverride global variables for specific templates:\n\n```typescript\ninstructions.provideOverrides({\n  '#special-rules': {\n    maxAttempts: 10,      // Only for special-rules template\n  },\n  'agents/specialist': {\n    agentName: 'Expert',  // Only for specialist template\n  },\n  '@company/description': {\n    companyMission: 'lead innovation',  // Only for description template\n  },\n});\n```\n\n## Template Variables\n\nUse mustache syntax with `$` prefix in your templates:\n\n```markdown\nHello {{ $userName }}!\n\nYou have {{ $attemptsRemaining }} attempts left.\n\n{{ $customInstructions }}\n```\n\n> **Note on Variable Syntax:**\n> \n> While template variables are defined with a `$` prefix (e.g., `{{ $variableName }}`), you should provide them in your configuration **without** the `$` prefix. Mosaic automatically strips the `$` from the template before matching it with the provided variable keys.\n> \n> For example, to provide a value for `{{ $userName }}`, you would use the key `userName` in your `provideVariables` or `provideOverrides` call:\n> \n> ```typescript\n> instructions.provideVariables({\n>   userName: 'Alice', // Correct: no dollar sign\n> });\n> ```\n\n## How It Works\n\n1. **Template Discovery**: Mosaic scans your directory and indexes all `.md` files\n2. **Reference Parsing**: Extracts `{{ selector }}` references from template content\n3. **Dependency Resolution**: Builds a dependency tree of template relationships\n4. **Variable Expansion**: Expands variables using path-specific overrides and global fallbacks\n5. **Recursive Composition**: Recursively resolves all references until no more remain\n6. **Loop Detection**: Prevents infinite loops from circular references\n7. **Final Assembly**: Returns the fully composed content as a string\n\n## API Reference\n\n### `Mosaic.fromDirectory(path: string)`\n\nCreates a new Mosaic instance from a directory of templates.\n\n### `.provideVariables(variables: TemplateVariables)`\n\nProvides global variables available to all templates. Chainable.\n\n### `.provideOverrides(overrides: TemplateOverrides)`\n\nProvides path-specific variable overrides. Chainable.\n\n### `.compose(selector: string)`\n\nComposes the final template from the given root selector.\n\n## TypeScript Support\n\nMosaic is written in TypeScript and provides full type definitions:\n\n```typescript\nimport { Mosaic, TemplateVariables, TemplateOverrides } from 'mosaic-js';\n\nconst variables: TemplateVariables = {\n  name: 'Claude',\n  attempts: 5,\n};\n\nconst overrides: TemplateOverrides = {\n  '#special': {\n    attempts: 10,\n  },\n};\n```\n\n## Examples\n\n### AI Agent Instructions\n\n```typescript\nconst agent = Mosaic.fromDirectory('agent-templates');\n\nagent\n  .provideVariables({\n    agentName: 'Claude',\n    personality: 'helpful and accurate',\n    maxTokens: 4000,\n  })\n  .provideOverrides({\n    '#creative-mode': {\n      personality: 'creative and imaginative',\n      maxTokens: 8000,\n    },\n  });\n\nconst instructions = agent.compose('agents/main');\n```\n\n### Documentation Generation\n\n```typescript\nconst docs = Mosaic.fromDirectory('docs-templates');\n\ndocs.provideVariables({\n  projectName: 'My Project',\n  version: '1.0.0',\n  author: 'John Doe',\n});\n\nconst readme = docs.compose('documentation/readme');\nconst apiDocs = docs.compose('documentation/api');\n```\n\n## Why Mosaic?\n\nMosaic was created to solve real-world problems encountered when building AI agent orchestrations and managing complex instruction sets:\n\n### 🔧 IDE Indentation Issues\nWhen writing agent instructions directly in code, IDE auto-indentation would add unwanted whitespace that got passed to AI APIs. Instructions embedded within agent object definitions inherited the surrounding code's indentation, creating formatting issues in the final prompts sent to AI models.\n\n### 📋 Instruction Duplication\nMultiple agents in orchestrations often required the same instruction blocks, leading to:\n- **Copy-paste proliferation**: Same instructions duplicated across multiple agent definitions\n- **Maintenance nightmare**: Updates required manually finding and modifying every duplicate\n- **Version drift**: Easy to miss instances during updates, leading to inconsistent instructions\n\n### 🔍 Scattered Instructions\nAgent instructions were scattered throughout the codebase wherever agents were defined, making it difficult to:\n- **Locate instructions**: No central place to find and review all agent prompts\n- **Maintain consistency**: Hard to ensure similar agents used consistent instruction patterns\n- **Get overview**: Impossible to see the full instruction landscape at a glance\n\n### 👥 Business-Developer Handoff Friction\nWhen business stakeholders wanted to improve agent instructions, the process was cumbersome:\n- **Technical barriers**: Business users couldn't directly edit instructions in code\n- **Complex navigation**: Explaining where to find agent definitions and which properties to modify\n- **Developer bottleneck**: Every instruction change required developer intervention to implement\n- **Slow iteration**: Business improvements were blocked by development cycles\n\n### 🚀 Post-Launch Enhancement Reality\nOnce AI orchestrations were working, improvements primarily involved instruction refinement rather than code changes:\n- **Instructions are the key**: Most performance gains came from better prompts, not code\n- **Business expertise needed**: Domain experts, not developers, knew how to improve instructions\n- **Developer dependency**: Despite instructions being the main lever, developers were still required for every change\n\n### The Mosaic Solution\n\nMosaic addresses these problems by:\n- **Centralizing instructions** in a dedicated directory structure\n- **Enabling direct business user editing** through simple Markdown files\n- **Eliminating duplication** through a reference and composition system\n- **Removing developer bottlenecks** for instruction updates\n- **Providing clean formatting** free from code indentation issues\n- **Supporting rapid iteration** on the most impactful part of AI systems: the instructions\n\nThis allows AI orchestrations to evolve and improve continuously, with business stakeholders directly contributing their domain expertise without technical barriers.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT License - see LICENSE file for details.\n\n## Related\n\n- [Effect](https://effect.website/) - The foundational library for functional programming in TypeScript\n- [Mustache.js](https://github.com/janl/mustache.js/) - Logic-less templates for JavaScript\n- [gray-matter](https://github.com/jonschlinkert/gray-matter) - Front-matter parser\n- [fast-glob](https://github.com/mrmlnc/fast-glob) - For traversing the file system and returning pathnames that matched a defined set of a specified pattern\n\n","readmeFilename":"README.md"}