Merge pull request 'feat: complete bootstrap → monorepo migration (archive-ready)' (#345) from feat/framework-migration-complete into main
This commit was merged in pull request #345.
This commit is contained in:
@@ -11,6 +11,7 @@ export default tseslint.config(
|
||||
'**/coverage/**',
|
||||
'**/drizzle.config.ts',
|
||||
'**/framework/**',
|
||||
'packages/mosaic/__tests__/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
109
packages/mosaic/__tests__/integration/full-wizard.test.ts
Normal file
109
packages/mosaic/__tests__/integration/full-wizard.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import {
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
writeFileSync,
|
||||
readFileSync,
|
||||
existsSync,
|
||||
rmSync,
|
||||
cpSync,
|
||||
} from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js';
|
||||
import { createConfigService } from '../../src/config/config-service.js';
|
||||
import { runWizard } from '../../src/wizard.js';
|
||||
|
||||
describe('Full Wizard (headless)', () => {
|
||||
let tmpDir: string;
|
||||
const repoRoot = join(import.meta.dirname, '..', '..');
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), 'mosaic-wizard-test-'));
|
||||
// Copy templates to tmp dir
|
||||
const templatesDir = join(repoRoot, 'templates');
|
||||
if (existsSync(templatesDir)) {
|
||||
cpSync(templatesDir, join(tmpDir, 'templates'), { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('quick start produces valid SOUL.md', async () => {
|
||||
const prompter = new HeadlessPrompter({
|
||||
'Installation mode': 'quick',
|
||||
'What name should agents use?': 'TestBot',
|
||||
'Communication style': 'direct',
|
||||
'Your name': 'Tester',
|
||||
'Your pronouns': 'They/Them',
|
||||
'Your timezone': 'UTC',
|
||||
});
|
||||
|
||||
await runWizard({
|
||||
mosaicHome: tmpDir,
|
||||
sourceDir: tmpDir,
|
||||
prompter,
|
||||
configService: createConfigService(tmpDir, tmpDir),
|
||||
});
|
||||
|
||||
const soulPath = join(tmpDir, 'SOUL.md');
|
||||
expect(existsSync(soulPath)).toBe(true);
|
||||
|
||||
const soul = readFileSync(soulPath, 'utf-8');
|
||||
expect(soul).toContain('You are **TestBot**');
|
||||
expect(soul).toContain('Be direct, concise, and concrete');
|
||||
expect(soul).toContain('execution partner and visibility engine');
|
||||
});
|
||||
|
||||
it('quick start produces valid USER.md', async () => {
|
||||
const prompter = new HeadlessPrompter({
|
||||
'Installation mode': 'quick',
|
||||
'What name should agents use?': 'TestBot',
|
||||
'Communication style': 'direct',
|
||||
'Your name': 'Tester',
|
||||
'Your pronouns': 'He/Him',
|
||||
'Your timezone': 'America/Chicago',
|
||||
});
|
||||
|
||||
await runWizard({
|
||||
mosaicHome: tmpDir,
|
||||
sourceDir: tmpDir,
|
||||
prompter,
|
||||
configService: createConfigService(tmpDir, tmpDir),
|
||||
});
|
||||
|
||||
const userPath = join(tmpDir, 'USER.md');
|
||||
expect(existsSync(userPath)).toBe(true);
|
||||
|
||||
const user = readFileSync(userPath, 'utf-8');
|
||||
expect(user).toContain('**Name:** Tester');
|
||||
expect(user).toContain('**Pronouns:** He/Him');
|
||||
expect(user).toContain('**Timezone:** America/Chicago');
|
||||
});
|
||||
|
||||
it('applies CLI overrides', async () => {
|
||||
const prompter = new HeadlessPrompter({
|
||||
'Installation mode': 'quick',
|
||||
'Your name': 'FromPrompt',
|
||||
});
|
||||
|
||||
await runWizard({
|
||||
mosaicHome: tmpDir,
|
||||
sourceDir: tmpDir,
|
||||
prompter,
|
||||
configService: createConfigService(tmpDir, tmpDir),
|
||||
cliOverrides: {
|
||||
soul: {
|
||||
agentName: 'FromCLI',
|
||||
communicationStyle: 'formal',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const soul = readFileSync(join(tmpDir, 'SOUL.md'), 'utf-8');
|
||||
expect(soul).toContain('You are **FromCLI**');
|
||||
expect(soul).toContain('Use professional, structured language');
|
||||
});
|
||||
});
|
||||
68
packages/mosaic/__tests__/stages/detect-install.test.ts
Normal file
68
packages/mosaic/__tests__/stages/detect-install.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js';
|
||||
import { detectInstallStage } from '../../src/stages/detect-install.js';
|
||||
import type { WizardState } from '../../src/types.js';
|
||||
import type { ConfigService } from '../../src/config/config-service.js';
|
||||
|
||||
function createState(mosaicHome: string): WizardState {
|
||||
return {
|
||||
mosaicHome,
|
||||
sourceDir: mosaicHome,
|
||||
mode: 'quick',
|
||||
installAction: 'fresh',
|
||||
soul: {},
|
||||
user: {},
|
||||
tools: {},
|
||||
runtimes: { detected: [], mcpConfigured: false },
|
||||
selectedSkills: [],
|
||||
};
|
||||
}
|
||||
|
||||
const mockConfig: ConfigService = {
|
||||
readSoul: async () => ({ agentName: 'TestAgent' }),
|
||||
readUser: async () => ({ userName: 'TestUser' }),
|
||||
readTools: async () => ({}),
|
||||
writeSoul: async () => {},
|
||||
writeUser: async () => {},
|
||||
writeTools: async () => {},
|
||||
syncFramework: async () => {},
|
||||
};
|
||||
|
||||
describe('detectInstallStage', () => {
|
||||
let tmpDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), 'mosaic-test-'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('sets fresh for empty directory', async () => {
|
||||
const p = new HeadlessPrompter({});
|
||||
const state = createState(join(tmpDir, 'nonexistent'));
|
||||
await detectInstallStage(p, state, mockConfig);
|
||||
|
||||
expect(state.installAction).toBe('fresh');
|
||||
});
|
||||
|
||||
it('detects existing install and offers choices', async () => {
|
||||
// Create a mock existing install
|
||||
mkdirSync(join(tmpDir, 'bin'), { recursive: true });
|
||||
writeFileSync(join(tmpDir, 'AGENTS.md'), '# Test');
|
||||
writeFileSync(join(tmpDir, 'SOUL.md'), 'You are **Jarvis** in this session.');
|
||||
|
||||
const p = new HeadlessPrompter({
|
||||
'What would you like to do?': 'keep',
|
||||
});
|
||||
const state = createState(tmpDir);
|
||||
await detectInstallStage(p, state, mockConfig);
|
||||
|
||||
expect(state.installAction).toBe('keep');
|
||||
expect(state.soul.agentName).toBe('TestAgent');
|
||||
});
|
||||
});
|
||||
72
packages/mosaic/__tests__/stages/soul-setup.test.ts
Normal file
72
packages/mosaic/__tests__/stages/soul-setup.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js';
|
||||
import { soulSetupStage } from '../../src/stages/soul-setup.js';
|
||||
import type { WizardState } from '../../src/types.js';
|
||||
|
||||
function createState(overrides: Partial<WizardState> = {}): WizardState {
|
||||
return {
|
||||
mosaicHome: '/tmp/test-mosaic',
|
||||
sourceDir: '/tmp/test-mosaic',
|
||||
mode: 'quick',
|
||||
installAction: 'fresh',
|
||||
soul: {},
|
||||
user: {},
|
||||
tools: {},
|
||||
runtimes: { detected: [], mcpConfigured: false },
|
||||
selectedSkills: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('soulSetupStage', () => {
|
||||
it('sets agent name and style in quick mode', async () => {
|
||||
const p = new HeadlessPrompter({
|
||||
'What name should agents use?': 'Jarvis',
|
||||
'Communication style': 'friendly',
|
||||
});
|
||||
|
||||
const state = createState({ mode: 'quick' });
|
||||
await soulSetupStage(p, state);
|
||||
|
||||
expect(state.soul.agentName).toBe('Jarvis');
|
||||
expect(state.soul.communicationStyle).toBe('friendly');
|
||||
expect(state.soul.roleDescription).toBe('execution partner and visibility engine');
|
||||
});
|
||||
|
||||
it('uses defaults in quick mode with no answers', async () => {
|
||||
const p = new HeadlessPrompter({});
|
||||
const state = createState({ mode: 'quick' });
|
||||
await soulSetupStage(p, state);
|
||||
|
||||
expect(state.soul.agentName).toBe('Assistant');
|
||||
expect(state.soul.communicationStyle).toBe('direct');
|
||||
});
|
||||
|
||||
it('skips when install action is keep', async () => {
|
||||
const p = new HeadlessPrompter({});
|
||||
const state = createState({ installAction: 'keep' });
|
||||
state.soul.agentName = 'Existing';
|
||||
await soulSetupStage(p, state);
|
||||
|
||||
expect(state.soul.agentName).toBe('Existing');
|
||||
});
|
||||
|
||||
it('asks for all fields in advanced mode', async () => {
|
||||
const p = new HeadlessPrompter({
|
||||
'What name should agents use?': 'Atlas',
|
||||
'Agent role description': 'memory keeper',
|
||||
'Communication style': 'formal',
|
||||
'Accessibility preferences': 'ADHD-friendly',
|
||||
'Custom guardrails (optional)': 'Never push to main',
|
||||
});
|
||||
|
||||
const state = createState({ mode: 'advanced' });
|
||||
await soulSetupStage(p, state);
|
||||
|
||||
expect(state.soul.agentName).toBe('Atlas');
|
||||
expect(state.soul.roleDescription).toBe('memory keeper');
|
||||
expect(state.soul.communicationStyle).toBe('formal');
|
||||
expect(state.soul.accessibility).toBe('ADHD-friendly');
|
||||
expect(state.soul.customGuardrails).toBe('Never push to main');
|
||||
});
|
||||
});
|
||||
60
packages/mosaic/__tests__/stages/user-setup.test.ts
Normal file
60
packages/mosaic/__tests__/stages/user-setup.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js';
|
||||
import { userSetupStage } from '../../src/stages/user-setup.js';
|
||||
import type { WizardState } from '../../src/types.js';
|
||||
|
||||
function createState(overrides: Partial<WizardState> = {}): WizardState {
|
||||
return {
|
||||
mosaicHome: '/tmp/test-mosaic',
|
||||
sourceDir: '/tmp/test-mosaic',
|
||||
mode: 'quick',
|
||||
installAction: 'fresh',
|
||||
soul: { communicationStyle: 'direct' },
|
||||
user: {},
|
||||
tools: {},
|
||||
runtimes: { detected: [], mcpConfigured: false },
|
||||
selectedSkills: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('userSetupStage', () => {
|
||||
it('collects basic info in quick mode', async () => {
|
||||
const p = new HeadlessPrompter({
|
||||
'Your name': 'Jason',
|
||||
'Your pronouns': 'He/Him',
|
||||
'Your timezone': 'America/Chicago',
|
||||
});
|
||||
|
||||
const state = createState({ mode: 'quick' });
|
||||
await userSetupStage(p, state);
|
||||
|
||||
expect(state.user.userName).toBe('Jason');
|
||||
expect(state.user.pronouns).toBe('He/Him');
|
||||
expect(state.user.timezone).toBe('America/Chicago');
|
||||
expect(state.user.communicationPrefs).toContain('Direct and concise');
|
||||
});
|
||||
|
||||
it('skips when install action is keep', async () => {
|
||||
const p = new HeadlessPrompter({});
|
||||
const state = createState({ installAction: 'keep' });
|
||||
state.user.userName = 'Existing';
|
||||
await userSetupStage(p, state);
|
||||
|
||||
expect(state.user.userName).toBe('Existing');
|
||||
});
|
||||
|
||||
it('derives communication prefs from soul style', async () => {
|
||||
const p = new HeadlessPrompter({
|
||||
'Your name': 'Test',
|
||||
});
|
||||
|
||||
const state = createState({
|
||||
mode: 'quick',
|
||||
soul: { communicationStyle: 'friendly' },
|
||||
});
|
||||
await userSetupStage(p, state);
|
||||
|
||||
expect(state.user.communicationPrefs).toContain('Warm and conversational');
|
||||
});
|
||||
});
|
||||
97
packages/mosaic/__tests__/template/builders.test.ts
Normal file
97
packages/mosaic/__tests__/template/builders.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
buildSoulTemplateVars,
|
||||
buildUserTemplateVars,
|
||||
buildToolsTemplateVars,
|
||||
} from '../../src/template/builders.js';
|
||||
|
||||
describe('buildSoulTemplateVars', () => {
|
||||
it('builds direct style correctly', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
agentName: 'Jarvis',
|
||||
communicationStyle: 'direct',
|
||||
});
|
||||
expect(vars.AGENT_NAME).toBe('Jarvis');
|
||||
expect(vars.BEHAVIORAL_PRINCIPLES).toContain('Clarity over performance theater');
|
||||
expect(vars.COMMUNICATION_STYLE).toContain('Be direct, concise, and concrete');
|
||||
});
|
||||
|
||||
it('builds friendly style correctly', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
communicationStyle: 'friendly',
|
||||
});
|
||||
expect(vars.BEHAVIORAL_PRINCIPLES).toContain('Be helpful and approachable');
|
||||
expect(vars.COMMUNICATION_STYLE).toContain('Be warm and conversational');
|
||||
});
|
||||
|
||||
it('builds formal style correctly', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
communicationStyle: 'formal',
|
||||
});
|
||||
expect(vars.BEHAVIORAL_PRINCIPLES).toContain('Maintain professional, structured');
|
||||
expect(vars.COMMUNICATION_STYLE).toContain('Use professional, structured language');
|
||||
});
|
||||
|
||||
it('appends accessibility to principles', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
communicationStyle: 'direct',
|
||||
accessibility: 'ADHD-friendly chunking',
|
||||
});
|
||||
expect(vars.BEHAVIORAL_PRINCIPLES).toContain('6. ADHD-friendly chunking.');
|
||||
});
|
||||
|
||||
it('does not append accessibility when "none"', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
communicationStyle: 'direct',
|
||||
accessibility: 'none',
|
||||
});
|
||||
expect(vars.BEHAVIORAL_PRINCIPLES).not.toContain('6.');
|
||||
});
|
||||
|
||||
it('formats custom guardrails', () => {
|
||||
const vars = buildSoulTemplateVars({
|
||||
customGuardrails: 'Never auto-commit',
|
||||
});
|
||||
expect(vars.CUSTOM_GUARDRAILS).toBe('- Never auto-commit');
|
||||
});
|
||||
|
||||
it('uses defaults when config is empty', () => {
|
||||
const vars = buildSoulTemplateVars({});
|
||||
expect(vars.AGENT_NAME).toBe('Assistant');
|
||||
expect(vars.ROLE_DESCRIPTION).toBe('execution partner and visibility engine');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildUserTemplateVars', () => {
|
||||
it('maps all fields', () => {
|
||||
const vars = buildUserTemplateVars({
|
||||
userName: 'Jason',
|
||||
pronouns: 'He/Him',
|
||||
timezone: 'America/Chicago',
|
||||
});
|
||||
expect(vars.USER_NAME).toBe('Jason');
|
||||
expect(vars.PRONOUNS).toBe('He/Him');
|
||||
expect(vars.TIMEZONE).toBe('America/Chicago');
|
||||
});
|
||||
|
||||
it('uses defaults for missing fields', () => {
|
||||
const vars = buildUserTemplateVars({});
|
||||
expect(vars.PRONOUNS).toBe('They/Them');
|
||||
expect(vars.TIMEZONE).toBe('UTC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildToolsTemplateVars', () => {
|
||||
it('builds git providers table', () => {
|
||||
const vars = buildToolsTemplateVars({
|
||||
gitProviders: [{ name: 'GitHub', url: 'https://github.com', cli: 'gh', purpose: 'OSS' }],
|
||||
});
|
||||
expect(vars.GIT_PROVIDERS_TABLE).toContain('| GitHub |');
|
||||
expect(vars.GIT_PROVIDERS_TABLE).toContain('`gh`');
|
||||
});
|
||||
|
||||
it('uses default table when no providers', () => {
|
||||
const vars = buildToolsTemplateVars({});
|
||||
expect(vars.GIT_PROVIDERS_TABLE).toContain('add your git providers here');
|
||||
});
|
||||
});
|
||||
52
packages/mosaic/__tests__/template/engine.test.ts
Normal file
52
packages/mosaic/__tests__/template/engine.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { renderTemplate } from '../../src/template/engine.js';
|
||||
|
||||
describe('renderTemplate', () => {
|
||||
it('replaces all placeholders', () => {
|
||||
const template = 'You are **{{AGENT_NAME}}**, role: {{ROLE_DESCRIPTION}}';
|
||||
const result = renderTemplate(template, {
|
||||
AGENT_NAME: 'Jarvis',
|
||||
ROLE_DESCRIPTION: 'steward',
|
||||
});
|
||||
expect(result).toBe('You are **Jarvis**, role: steward');
|
||||
});
|
||||
|
||||
it('preserves ${ENV_VAR} references', () => {
|
||||
const template = 'Path: ${HOME}/.config, Agent: {{AGENT_NAME}}';
|
||||
const result = renderTemplate(template, { AGENT_NAME: 'Test' });
|
||||
expect(result).toBe('Path: ${HOME}/.config, Agent: Test');
|
||||
});
|
||||
|
||||
it('handles multi-line values', () => {
|
||||
const template = '{{PRINCIPLES}}';
|
||||
const result = renderTemplate(template, {
|
||||
PRINCIPLES: '1. First\n2. Second\n3. Third',
|
||||
});
|
||||
expect(result).toBe('1. First\n2. Second\n3. Third');
|
||||
});
|
||||
|
||||
it('replaces unset vars with empty string by default', () => {
|
||||
const template = 'Before {{MISSING}} After';
|
||||
const result = renderTemplate(template, {});
|
||||
expect(result).toBe('Before After');
|
||||
});
|
||||
|
||||
it('throws in strict mode for missing vars', () => {
|
||||
const template = '{{MISSING}}';
|
||||
expect(() => renderTemplate(template, {}, { strict: true })).toThrow(
|
||||
'Template variable not provided: {{MISSING}}',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles multiple occurrences of same placeholder', () => {
|
||||
const template = '{{NAME}} says hello, {{NAME}}!';
|
||||
const result = renderTemplate(template, { NAME: 'Jarvis' });
|
||||
expect(result).toBe('Jarvis says hello, Jarvis!');
|
||||
});
|
||||
|
||||
it('preserves non-placeholder curly braces', () => {
|
||||
const template = 'const x = { foo: {{VALUE}} }';
|
||||
const result = renderTemplate(template, { VALUE: '"bar"' });
|
||||
expect(result).toBe('const x = { foo: "bar" }');
|
||||
});
|
||||
});
|
||||
17
packages/mosaic/framework/adapters/claude.md
Normal file
17
packages/mosaic/framework/adapters/claude.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Claude Adapter
|
||||
|
||||
Use this adapter when running Claude CLI sessions.
|
||||
|
||||
## Required Context
|
||||
|
||||
1. `~/.config/mosaic/STANDARDS.md`
|
||||
2. `<repo>/AGENTS.md`
|
||||
|
||||
## Command Wrapper
|
||||
|
||||
Use wrapper commands from `~/.config/mosaic/bin/` for lifecycle rituals.
|
||||
|
||||
## Migration Note
|
||||
|
||||
Project-local `.claude/commands/*.md` should call `scripts/agent/*.sh` so behavior stays runtime-neutral.
|
||||
Guides and tools should resolve to `~/.config/mosaic/guides` and `~/.config/mosaic/tools` (linked into `~/.claude` for compatibility).
|
||||
13
packages/mosaic/framework/adapters/codex.md
Normal file
13
packages/mosaic/framework/adapters/codex.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Codex Adapter
|
||||
|
||||
Use this adapter when running Codex CLI sessions.
|
||||
|
||||
## Required Context
|
||||
|
||||
1. `~/.config/mosaic/STANDARDS.md`
|
||||
2. `<repo>/AGENTS.md`
|
||||
|
||||
## Runtime Behavior
|
||||
|
||||
- Favor repo lifecycle scripts under `scripts/agent/` for start/end rituals.
|
||||
- Keep instructions and quality gates aligned with Mosaic standards.
|
||||
14
packages/mosaic/framework/adapters/generic.md
Normal file
14
packages/mosaic/framework/adapters/generic.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Generic Adapter
|
||||
|
||||
For runtimes without a first-class adapter yet.
|
||||
|
||||
## Required Context
|
||||
|
||||
1. Load `~/.config/mosaic/STANDARDS.md`
|
||||
2. Load project `AGENTS.md`
|
||||
|
||||
## Minimal Contract
|
||||
|
||||
- Use `scripts/agent/session-start.sh` at start if present.
|
||||
- Use `scripts/agent/session-end.sh` before completion if present.
|
||||
- If missing, run equivalent repo commands and report what was executed.
|
||||
36
packages/mosaic/framework/adapters/pi.md
Normal file
36
packages/mosaic/framework/adapters/pi.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Pi Adapter
|
||||
|
||||
Use this adapter when running Pi sessions via `mosaic pi`.
|
||||
|
||||
## Required Context
|
||||
|
||||
1. `~/.config/mosaic/STANDARDS.md`
|
||||
2. `<repo>/AGENTS.md`
|
||||
|
||||
## Integration
|
||||
|
||||
Pi is the native Mosaic agent runtime. The `mosaic pi` launcher:
|
||||
|
||||
1. Injects the full runtime contract via `--append-system-prompt`
|
||||
2. Loads Mosaic skills via `--skill` flags
|
||||
3. Loads the Mosaic extension via `--extension` for lifecycle hooks
|
||||
4. Detects active missions and injects initial prompts
|
||||
|
||||
## Capabilities vs Other Runtimes
|
||||
|
||||
- No permission restrictions (no yolo flag needed)
|
||||
- Native thinking levels replace sequential-thinking MCP
|
||||
- Native skill discovery compatible with Mosaic SKILL.md format
|
||||
- Native extension system for lifecycle hooks (TypeScript, not bash shims)
|
||||
- Native session persistence and resume
|
||||
- Model-agnostic (Anthropic, OpenAI, Google, Ollama, custom providers)
|
||||
|
||||
## Command Wrapper
|
||||
|
||||
```bash
|
||||
mosaic pi # Interactive session
|
||||
mosaic pi "Fix the auth bug" # With initial prompt
|
||||
mosaic yolo pi # Identical to mosaic pi
|
||||
mosaic coord --pi run # Coordinator-driven session
|
||||
mosaic prdy --pi init # PRD creation via Pi
|
||||
```
|
||||
283
packages/mosaic/framework/bin/mosaic-doctor.ps1
Normal file
283
packages/mosaic/framework/bin/mosaic-doctor.ps1
Normal file
@@ -0,0 +1,283 @@
|
||||
# mosaic-doctor.ps1
|
||||
# Audits Mosaic runtime state and detects drift across agent runtimes.
|
||||
# PowerShell equivalent of mosaic-doctor (bash).
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
param(
|
||||
[switch]$FailOnWarn,
|
||||
[switch]$Verbose,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
|
||||
if ($Help) {
|
||||
Write-Host @"
|
||||
Usage: mosaic-doctor.ps1 [-FailOnWarn] [-Verbose] [-Help]
|
||||
|
||||
Audit Mosaic runtime state and detect drift across agent runtimes.
|
||||
"@
|
||||
exit 0
|
||||
}
|
||||
|
||||
$script:warnCount = 0
|
||||
|
||||
function Warn {
|
||||
param([string]$Message)
|
||||
$script:warnCount++
|
||||
Write-Host "[WARN] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Pass {
|
||||
param([string]$Message)
|
||||
if ($Verbose) { Write-Host "[OK] $Message" -ForegroundColor Green }
|
||||
}
|
||||
|
||||
function Expect-Dir {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path $Path -PathType Container)) { Warn "Missing directory: $Path" }
|
||||
else { Pass "Directory present: $Path" }
|
||||
}
|
||||
|
||||
function Expect-File {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path $Path -PathType Leaf)) { Warn "Missing file: $Path" }
|
||||
else { Pass "File present: $Path" }
|
||||
}
|
||||
|
||||
function Check-RuntimeFileCopy {
|
||||
param([string]$Src, [string]$Dst)
|
||||
|
||||
if (-not (Test-Path $Src)) { return }
|
||||
|
||||
if (-not (Test-Path $Dst)) {
|
||||
Warn "Missing runtime file: $Dst"
|
||||
return
|
||||
}
|
||||
|
||||
$item = Get-Item $Dst -Force -ErrorAction SilentlyContinue
|
||||
if ($item -and ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
Warn "Runtime file should not be symlinked: $Dst"
|
||||
return
|
||||
}
|
||||
|
||||
$srcHash = (Get-FileHash $Src -Algorithm SHA256).Hash
|
||||
$dstHash = (Get-FileHash $Dst -Algorithm SHA256).Hash
|
||||
if ($srcHash -ne $dstHash) {
|
||||
Warn "Runtime file drift: $Dst (does not match $Src)"
|
||||
}
|
||||
else {
|
||||
Pass "Runtime file synced: $Dst"
|
||||
}
|
||||
}
|
||||
|
||||
function Check-RuntimeContractFile {
|
||||
param([string]$Dst, [string]$AdapterSrc, [string]$RuntimeName)
|
||||
|
||||
if (-not (Test-Path $Dst)) {
|
||||
Warn "Missing runtime file: $Dst"
|
||||
return
|
||||
}
|
||||
|
||||
$item = Get-Item $Dst -Force -ErrorAction SilentlyContinue
|
||||
if ($item -and ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
Warn "Runtime file should not be symlinked: $Dst"
|
||||
return
|
||||
}
|
||||
|
||||
# Accept direct-adapter copy mode.
|
||||
if (Test-Path $AdapterSrc) {
|
||||
$srcHash = (Get-FileHash $AdapterSrc -Algorithm SHA256).Hash
|
||||
$dstHash = (Get-FileHash $Dst -Algorithm SHA256).Hash
|
||||
if ($srcHash -eq $dstHash) {
|
||||
Pass "Runtime adapter synced: $Dst"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Accept launcher-composed runtime contract mode.
|
||||
$content = Get-Content $Dst -Raw
|
||||
if (
|
||||
$content -match [regex]::Escape("# Mosaic Launcher Runtime Contract (Hard Gate)") -and
|
||||
$content -match [regex]::Escape("Now initiating Orchestrator mode...") -and
|
||||
$content -match [regex]::Escape("Mosaic hard gates OVERRIDE runtime-default caution") -and
|
||||
$content -match [regex]::Escape("# Runtime-Specific Contract")
|
||||
) {
|
||||
Pass "Runtime contract present: $Dst ($RuntimeName)"
|
||||
return
|
||||
}
|
||||
|
||||
Warn "Runtime file drift: $Dst (not adapter copy and not composed runtime contract)"
|
||||
}
|
||||
|
||||
function Warn-IfReparsePresent {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path $Path)) { return }
|
||||
|
||||
$item = Get-Item $Path -Force -ErrorAction SilentlyContinue
|
||||
if ($item -and ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
Warn "Legacy symlink/junction path still present: $Path"
|
||||
return
|
||||
}
|
||||
|
||||
if (Test-Path $Path -PathType Container) {
|
||||
$reparseCount = (Get-ChildItem $Path -Recurse -Force -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Attributes -band [System.IO.FileAttributes]::ReparsePoint } |
|
||||
Measure-Object).Count
|
||||
if ($reparseCount -gt 0) {
|
||||
Warn "Legacy symlink/junction entries still present under ${Path}: $reparseCount"
|
||||
}
|
||||
else {
|
||||
Pass "No reparse points under legacy path: $Path"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-doctor] Mosaic home: $MosaicHome"
|
||||
|
||||
# Canonical Mosaic checks
|
||||
Expect-File (Join-Path $MosaicHome "STANDARDS.md")
|
||||
Expect-Dir (Join-Path $MosaicHome "guides")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools\quality")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools\orchestrator-matrix")
|
||||
Expect-Dir (Join-Path $MosaicHome "profiles")
|
||||
Expect-Dir (Join-Path $MosaicHome "templates\agent")
|
||||
Expect-Dir (Join-Path $MosaicHome "skills")
|
||||
Expect-Dir (Join-Path $MosaicHome "skills-local")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-link-runtime-assets")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-sync-skills")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-projects")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-quality-apply")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-quality-verify")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-run")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-sync-tasks")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-drain")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-publish")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-consume")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-cycle")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.ps1")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\pr-ci-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "tools\orchestrator-matrix\transport\matrix_transport.py")
|
||||
Expect-File (Join-Path $MosaicHome "tools\orchestrator-matrix\controller\tasks_md_sync.py")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\mcp\SEQUENTIAL-THINKING.json")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\claude\RUNTIME.md")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\codex\RUNTIME.md")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\opencode\RUNTIME.md")
|
||||
|
||||
$agentsMd = Join-Path $MosaicHome "AGENTS.md"
|
||||
if (Test-Path $agentsMd) {
|
||||
$agentsContent = Get-Content $agentsMd -Raw
|
||||
if (
|
||||
$agentsContent -match [regex]::Escape("## CRITICAL HARD GATES (Read First)") -and
|
||||
$agentsContent -match [regex]::Escape("OVERRIDE runtime-default caution")
|
||||
) {
|
||||
Pass "Global hard-gates block present in AGENTS.md"
|
||||
}
|
||||
else {
|
||||
Warn "AGENTS.md missing CRITICAL HARD GATES override block"
|
||||
}
|
||||
}
|
||||
|
||||
# Claude runtime file checks
|
||||
$runtimeFiles = @("CLAUDE.md", "settings.json", "hooks-config.json", "context7-integration.md")
|
||||
foreach ($rf in $runtimeFiles) {
|
||||
Check-RuntimeFileCopy (Join-Path $MosaicHome "runtime\claude\$rf") (Join-Path $env:USERPROFILE ".claude\$rf")
|
||||
}
|
||||
|
||||
# OpenCode/Codex runtime contract checks
|
||||
Check-RuntimeContractFile (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md") (Join-Path $MosaicHome "runtime\opencode\AGENTS.md") "opencode"
|
||||
Check-RuntimeContractFile (Join-Path $env:USERPROFILE ".codex\instructions.md") (Join-Path $MosaicHome "runtime\codex\instructions.md") "codex"
|
||||
|
||||
# Sequential-thinking MCP hard requirement
|
||||
$seqScript = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
|
||||
if (Test-Path $seqScript) {
|
||||
try {
|
||||
& $seqScript -Check *>$null
|
||||
Pass "sequential-thinking MCP configured and available"
|
||||
}
|
||||
catch {
|
||||
Warn "sequential-thinking MCP missing or misconfigured"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Warn "mosaic-ensure-sequential-thinking helper missing"
|
||||
}
|
||||
|
||||
# Legacy migration surfaces
|
||||
$legacyPaths = @(
|
||||
(Join-Path $env:USERPROFILE ".claude\agent-guides"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\git"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\codex"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\bootstrap"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\cicd"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\portainer"),
|
||||
(Join-Path $env:USERPROFILE ".claude\templates"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\domains"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\tech-stacks"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\workflows")
|
||||
)
|
||||
foreach ($p in $legacyPaths) {
|
||||
Warn-IfReparsePresent $p
|
||||
}
|
||||
|
||||
# Skills runtime checks (junctions or symlinks into runtime-specific dirs)
|
||||
$linkTargets = @(
|
||||
(Join-Path $env:USERPROFILE ".claude\skills"),
|
||||
(Join-Path $env:USERPROFILE ".codex\skills"),
|
||||
(Join-Path $env:USERPROFILE ".config\opencode\skills")
|
||||
)
|
||||
|
||||
$skillSources = @($MosaicHome + "\skills", $MosaicHome + "\skills-local")
|
||||
|
||||
foreach ($runtimeSkills in $linkTargets) {
|
||||
if (-not (Test-Path $runtimeSkills)) { continue }
|
||||
|
||||
foreach ($sourceDir in $skillSources) {
|
||||
if (-not (Test-Path $sourceDir)) { continue }
|
||||
|
||||
Get-ChildItem $sourceDir -Directory | Where-Object { -not $_.Name.StartsWith(".") } | ForEach-Object {
|
||||
$name = $_.Name
|
||||
$skillPath = $_.FullName
|
||||
$target = Join-Path $runtimeSkills $name
|
||||
|
||||
if (-not (Test-Path $target)) {
|
||||
Warn "Missing skill link: $target"
|
||||
return
|
||||
}
|
||||
|
||||
$item = Get-Item $target -Force -ErrorAction SilentlyContinue
|
||||
if (-not ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
Warn "Non-junction skill entry: $target"
|
||||
return
|
||||
}
|
||||
|
||||
$targetResolved = $item.Target
|
||||
if (-not $targetResolved -or (Resolve-Path $targetResolved -ErrorAction SilentlyContinue).Path -ne (Resolve-Path $skillPath -ErrorAction SilentlyContinue).Path) {
|
||||
Warn "Drifted skill link: $target (expected -> $skillPath)"
|
||||
}
|
||||
else {
|
||||
Pass "Linked skill: $target"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Broken junctions/symlinks in managed runtime skill dirs
|
||||
$brokenLinks = 0
|
||||
foreach ($d in $linkTargets) {
|
||||
if (-not (Test-Path $d)) { continue }
|
||||
Get-ChildItem $d -Force -ErrorAction SilentlyContinue | Where-Object {
|
||||
($_.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -and -not (Test-Path $_.FullName)
|
||||
} | ForEach-Object { $brokenLinks++ }
|
||||
}
|
||||
if ($brokenLinks -gt 0) {
|
||||
Warn "Broken skill junctions/symlinks detected: $brokenLinks"
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-doctor] warnings=$($script:warnCount)"
|
||||
if ($FailOnWarn -and $script:warnCount -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
114
packages/mosaic/framework/bin/mosaic-ensure-sequential-thinking.ps1
Executable file
114
packages/mosaic/framework/bin/mosaic-ensure-sequential-thinking.ps1
Executable file
@@ -0,0 +1,114 @@
|
||||
# mosaic-ensure-sequential-thinking.ps1
|
||||
param(
|
||||
[switch]$Check
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$Pkg = "@modelcontextprotocol/server-sequential-thinking"
|
||||
|
||||
function Require-Binary {
|
||||
param([string]$Name)
|
||||
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
||||
throw "Required binary missing: $Name"
|
||||
}
|
||||
}
|
||||
|
||||
function Warm-Package {
|
||||
$null = & npx -y $Pkg --help 2>$null
|
||||
}
|
||||
|
||||
function Set-ClaudeConfig {
|
||||
$path = Join-Path $env:USERPROFILE ".claude\settings.json"
|
||||
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
|
||||
|
||||
$data = @{}
|
||||
if (Test-Path $path) {
|
||||
try { $data = Get-Content $path -Raw | ConvertFrom-Json -AsHashtable } catch { $data = @{} }
|
||||
}
|
||||
if (-not $data.ContainsKey("mcpServers") -or -not ($data["mcpServers"] -is [hashtable])) {
|
||||
$data["mcpServers"] = @{}
|
||||
}
|
||||
$data["mcpServers"]["sequential-thinking"] = @{
|
||||
command = "npx"
|
||||
args = @("-y", "@modelcontextprotocol/server-sequential-thinking")
|
||||
}
|
||||
$data | ConvertTo-Json -Depth 20 | Set-Content -Path $path -Encoding UTF8
|
||||
}
|
||||
|
||||
function Set-CodexConfig {
|
||||
$path = Join-Path $env:USERPROFILE ".codex\config.toml"
|
||||
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
|
||||
if (-not (Test-Path $path)) { New-Item -ItemType File -Path $path -Force | Out-Null }
|
||||
|
||||
$content = Get-Content $path -Raw
|
||||
$content = [regex]::Replace($content, "(?ms)^\[mcp_servers\.(sequential-thinking|sequential_thinking)\].*?(?=^\[|\z)", "")
|
||||
$content = $content.TrimEnd() + "`n`n[mcp_servers.sequential-thinking]`ncommand = `"npx`"`nargs = [`"-y`", `"@modelcontextprotocol/server-sequential-thinking`"]`n"
|
||||
Set-Content -Path $path -Value $content -Encoding UTF8
|
||||
}
|
||||
|
||||
function Set-OpenCodeConfig {
|
||||
$path = Join-Path $env:USERPROFILE ".config\opencode\config.json"
|
||||
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
|
||||
|
||||
$data = @{}
|
||||
if (Test-Path $path) {
|
||||
try { $data = Get-Content $path -Raw | ConvertFrom-Json -AsHashtable } catch { $data = @{} }
|
||||
}
|
||||
if (-not $data.ContainsKey("mcp") -or -not ($data["mcp"] -is [hashtable])) {
|
||||
$data["mcp"] = @{}
|
||||
}
|
||||
$data["mcp"]["sequential-thinking"] = @{
|
||||
type = "local"
|
||||
command = @("npx", "-y", "@modelcontextprotocol/server-sequential-thinking")
|
||||
enabled = $true
|
||||
}
|
||||
$data | ConvertTo-Json -Depth 20 | Set-Content -Path $path -Encoding UTF8
|
||||
}
|
||||
|
||||
function Test-Configs {
|
||||
$claudeOk = $false
|
||||
$codexOk = $false
|
||||
$opencodeOk = $false
|
||||
|
||||
$claudePath = Join-Path $env:USERPROFILE ".claude\settings.json"
|
||||
if (Test-Path $claudePath) {
|
||||
try {
|
||||
$c = Get-Content $claudePath -Raw | ConvertFrom-Json -AsHashtable
|
||||
$claudeOk = $c.ContainsKey("mcpServers") -and $c["mcpServers"].ContainsKey("sequential-thinking")
|
||||
} catch {}
|
||||
}
|
||||
|
||||
$codexPath = Join-Path $env:USERPROFILE ".codex\config.toml"
|
||||
if (Test-Path $codexPath) {
|
||||
$raw = Get-Content $codexPath -Raw
|
||||
$codexOk = $raw -match "\[mcp_servers\.(sequential-thinking|sequential_thinking)\]" -and $raw -match "@modelcontextprotocol/server-sequential-thinking"
|
||||
}
|
||||
|
||||
$opencodePath = Join-Path $env:USERPROFILE ".config\opencode\config.json"
|
||||
if (Test-Path $opencodePath) {
|
||||
try {
|
||||
$o = Get-Content $opencodePath -Raw | ConvertFrom-Json -AsHashtable
|
||||
$opencodeOk = $o.ContainsKey("mcp") -and $o["mcp"].ContainsKey("sequential-thinking")
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (-not ($claudeOk -and $codexOk -and $opencodeOk)) {
|
||||
throw "Sequential-thinking MCP runtime config is incomplete"
|
||||
}
|
||||
}
|
||||
|
||||
Require-Binary node
|
||||
Require-Binary npx
|
||||
Warm-Package
|
||||
|
||||
if ($Check) {
|
||||
Test-Configs
|
||||
Write-Host "[mosaic-seq] sequential-thinking MCP is configured and available"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Set-ClaudeConfig
|
||||
Set-CodexConfig
|
||||
Set-OpenCodeConfig
|
||||
Write-Host "[mosaic-seq] sequential-thinking MCP configured for Claude, Codex, and OpenCode"
|
||||
144
packages/mosaic/framework/bin/mosaic-init.ps1
Normal file
144
packages/mosaic/framework/bin/mosaic-init.ps1
Normal file
@@ -0,0 +1,144 @@
|
||||
# mosaic-init.ps1 — Interactive SOUL.md generator (Windows)
|
||||
#
|
||||
# Usage:
|
||||
# mosaic-init.ps1 # Interactive mode
|
||||
# mosaic-init.ps1 -Name "Jarvis" -Style direct # Flag overrides
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Role,
|
||||
[ValidateSet("direct", "friendly", "formal")]
|
||||
[string]$Style,
|
||||
[string]$Accessibility,
|
||||
[string]$Guardrails,
|
||||
[switch]$NonInteractive,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$Template = Join-Path $MosaicHome "templates\SOUL.md.template"
|
||||
$Output = Join-Path $MosaicHome "SOUL.md"
|
||||
|
||||
if ($Help) {
|
||||
Write-Host @"
|
||||
Usage: mosaic-init.ps1 [-Name <name>] [-Role <desc>] [-Style direct|friendly|formal]
|
||||
[-Accessibility <prefs>] [-Guardrails <rules>] [-NonInteractive]
|
||||
|
||||
Generate ~/.config/mosaic/SOUL.md - the universal agent identity contract.
|
||||
Interactive by default. Use flags to skip prompts.
|
||||
"@
|
||||
exit 0
|
||||
}
|
||||
|
||||
function Prompt-IfEmpty {
|
||||
param([string]$Current, [string]$PromptText, [string]$Default = "")
|
||||
|
||||
if ($Current) { return $Current }
|
||||
|
||||
if ($NonInteractive) {
|
||||
if ($Default) { return $Default }
|
||||
Write-Host "[mosaic-init] ERROR: Value required in non-interactive mode: $PromptText" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$display = if ($Default) { "$PromptText [$Default]" } else { $PromptText }
|
||||
$value = Read-Host $display
|
||||
if (-not $value -and $Default) { return $Default }
|
||||
return $value
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-init] Generating SOUL.md - your universal agent identity contract"
|
||||
Write-Host ""
|
||||
|
||||
$Name = Prompt-IfEmpty $Name "What name should agents use" "Assistant"
|
||||
$Role = Prompt-IfEmpty $Role "Agent role description" "execution partner and visibility engine"
|
||||
|
||||
if (-not $Style) {
|
||||
if ($NonInteractive) {
|
||||
$Style = "direct"
|
||||
}
|
||||
else {
|
||||
Write-Host ""
|
||||
Write-Host "Communication style:"
|
||||
Write-Host " 1) direct - Concise, no fluff, actionable"
|
||||
Write-Host " 2) friendly - Warm but efficient, conversational"
|
||||
Write-Host " 3) formal - Professional, structured, thorough"
|
||||
$choice = Read-Host "Choose [1/2/3] (default: 1)"
|
||||
$Style = switch ($choice) {
|
||||
"2" { "friendly" }
|
||||
"3" { "formal" }
|
||||
default { "direct" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Accessibility = Prompt-IfEmpty $Accessibility "Accessibility preferences (or 'none')" "none"
|
||||
|
||||
if (-not $Guardrails -and -not $NonInteractive) {
|
||||
Write-Host ""
|
||||
$Guardrails = Read-Host "Custom guardrails (optional, press Enter to skip)"
|
||||
}
|
||||
|
||||
# Build behavioral principles
|
||||
$BehavioralPrinciples = switch ($Style) {
|
||||
"direct" {
|
||||
"1. Clarity over performance theater.`n2. Practical execution over abstract planning.`n3. Truthfulness over confidence: state uncertainty explicitly.`n4. Visible state over hidden assumptions."
|
||||
}
|
||||
"friendly" {
|
||||
"1. Be helpful and approachable while staying efficient.`n2. Provide context and explain reasoning when helpful.`n3. Truthfulness over confidence: state uncertainty explicitly.`n4. Visible state over hidden assumptions."
|
||||
}
|
||||
"formal" {
|
||||
"1. Maintain professional, structured communication.`n2. Provide thorough analysis with explicit tradeoffs.`n3. Truthfulness over confidence: state uncertainty explicitly.`n4. Document decisions and rationale clearly."
|
||||
}
|
||||
}
|
||||
|
||||
if ($Accessibility -and $Accessibility -ne "none") {
|
||||
$BehavioralPrinciples += "`n5. $Accessibility."
|
||||
}
|
||||
|
||||
# Build communication style
|
||||
$CommunicationStyle = switch ($Style) {
|
||||
"direct" {
|
||||
"- Be direct, concise, and concrete.`n- Avoid fluff, hype, and anthropomorphic roleplay.`n- Do not simulate certainty when facts are missing.`n- Prefer actionable next steps and explicit tradeoffs."
|
||||
}
|
||||
"friendly" {
|
||||
"- Be warm and conversational while staying focused.`n- Explain your reasoning when it helps the user.`n- Do not simulate certainty when facts are missing.`n- Prefer actionable next steps with clear context."
|
||||
}
|
||||
"formal" {
|
||||
"- Use professional, structured language.`n- Provide thorough explanations with supporting detail.`n- Do not simulate certainty when facts are missing.`n- Present options with explicit tradeoffs and recommendations."
|
||||
}
|
||||
}
|
||||
|
||||
# Format custom guardrails
|
||||
$FormattedGuardrails = if ($Guardrails) { "- $Guardrails" } else { "" }
|
||||
|
||||
# Verify template
|
||||
if (-not (Test-Path $Template)) {
|
||||
Write-Host "[mosaic-init] ERROR: Template not found: $Template" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Generate SOUL.md
|
||||
$content = Get-Content $Template -Raw
|
||||
$content = $content -replace '\{\{AGENT_NAME\}\}', $Name
|
||||
$content = $content -replace '\{\{ROLE_DESCRIPTION\}\}', $Role
|
||||
$content = $content -replace '\{\{BEHAVIORAL_PRINCIPLES\}\}', $BehavioralPrinciples
|
||||
$content = $content -replace '\{\{COMMUNICATION_STYLE\}\}', $CommunicationStyle
|
||||
$content = $content -replace '\{\{CUSTOM_GUARDRAILS\}\}', $FormattedGuardrails
|
||||
|
||||
Set-Content -Path $Output -Value $content -Encoding UTF8
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "[mosaic-init] Generated: $Output"
|
||||
Write-Host "[mosaic-init] Agent name: $Name"
|
||||
Write-Host "[mosaic-init] Style: $Style"
|
||||
|
||||
# Push to runtime adapters
|
||||
$linkScript = Join-Path $MosaicHome "bin\mosaic-link-runtime-assets.ps1"
|
||||
if (Test-Path $linkScript) {
|
||||
Write-Host "[mosaic-init] Updating runtime adapters..."
|
||||
& $linkScript
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-init] Done. Launch with: mosaic claude"
|
||||
111
packages/mosaic/framework/bin/mosaic-link-runtime-assets.ps1
Normal file
111
packages/mosaic/framework/bin/mosaic-link-runtime-assets.ps1
Normal file
@@ -0,0 +1,111 @@
|
||||
# mosaic-link-runtime-assets.ps1
|
||||
# Syncs Mosaic runtime config files into agent runtime directories.
|
||||
# PowerShell equivalent of mosaic-link-runtime-assets (bash).
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$BackupStamp = Get-Date -Format "yyyyMMddHHmmss"
|
||||
|
||||
function Copy-FileManaged {
|
||||
param([string]$Src, [string]$Dst)
|
||||
|
||||
$parent = Split-Path $Dst -Parent
|
||||
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null }
|
||||
|
||||
# Remove existing symlink/junction
|
||||
$item = Get-Item $Dst -Force -ErrorAction SilentlyContinue
|
||||
if ($item -and $item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
|
||||
Remove-Item $Dst -Force
|
||||
}
|
||||
|
||||
if (Test-Path $Dst) {
|
||||
$srcHash = (Get-FileHash $Src -Algorithm SHA256).Hash
|
||||
$dstHash = (Get-FileHash $Dst -Algorithm SHA256).Hash
|
||||
if ($srcHash -eq $dstHash) { return }
|
||||
Rename-Item $Dst "$Dst.mosaic-bak-$BackupStamp"
|
||||
}
|
||||
|
||||
Copy-Item $Src $Dst -Force
|
||||
}
|
||||
|
||||
function Remove-LegacyPath {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path $Path)) { return }
|
||||
|
||||
$item = Get-Item $Path -Force -ErrorAction SilentlyContinue
|
||||
if ($item -and $item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
|
||||
Remove-Item $Path -Force
|
||||
return
|
||||
}
|
||||
|
||||
if (Test-Path $Path -PathType Container) {
|
||||
# Remove symlinks/junctions inside, then empty dirs
|
||||
Get-ChildItem $Path -Recurse -Force | Where-Object {
|
||||
$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint
|
||||
} | Remove-Item -Force
|
||||
|
||||
Get-ChildItem $Path -Recurse -Directory -Force |
|
||||
Sort-Object { $_.FullName.Length } -Descending |
|
||||
Where-Object { (Get-ChildItem $_.FullName -Force | Measure-Object).Count -eq 0 } |
|
||||
Remove-Item -Force
|
||||
}
|
||||
}
|
||||
|
||||
# Remove legacy compatibility paths
|
||||
$legacyPaths = @(
|
||||
(Join-Path $env:USERPROFILE ".claude\agent-guides"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\git"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\codex"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\bootstrap"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\cicd"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\portainer"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\debug-hook.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-handler.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-stdin.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\qa-hook-wrapper.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\qa-queue-monitor.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\scripts\remediation-hook-handler.sh"),
|
||||
(Join-Path $env:USERPROFILE ".claude\templates"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\domains"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\tech-stacks"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\workflows"),
|
||||
(Join-Path $env:USERPROFILE ".claude\presets\jarvis-loop.json")
|
||||
)
|
||||
|
||||
foreach ($p in $legacyPaths) {
|
||||
Remove-LegacyPath $p
|
||||
}
|
||||
|
||||
# Claude-specific runtime files (settings, hooks — CLAUDE.md is now a thin pointer)
|
||||
$runtimeFiles = @("CLAUDE.md", "settings.json", "hooks-config.json", "context7-integration.md")
|
||||
foreach ($rf in $runtimeFiles) {
|
||||
$src = Join-Path $MosaicHome "runtime\claude\$rf"
|
||||
if (-not (Test-Path $src)) { continue }
|
||||
$dst = Join-Path $env:USERPROFILE ".claude\$rf"
|
||||
Copy-FileManaged $src $dst
|
||||
}
|
||||
|
||||
# OpenCode runtime adapter
|
||||
$opencodeSrc = Join-Path $MosaicHome "runtime\opencode\AGENTS.md"
|
||||
if (Test-Path $opencodeSrc) {
|
||||
$opencodeDst = Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md"
|
||||
Copy-FileManaged $opencodeSrc $opencodeDst
|
||||
}
|
||||
|
||||
# Codex runtime adapter
|
||||
$codexSrc = Join-Path $MosaicHome "runtime\codex\instructions.md"
|
||||
if (Test-Path $codexSrc) {
|
||||
$codexDir = Join-Path $env:USERPROFILE ".codex"
|
||||
if (-not (Test-Path $codexDir)) { New-Item -ItemType Directory -Path $codexDir -Force | Out-Null }
|
||||
$codexDst = Join-Path $codexDir "instructions.md"
|
||||
Copy-FileManaged $codexSrc $codexDst
|
||||
}
|
||||
|
||||
$seqScript = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
|
||||
if (Test-Path $seqScript) {
|
||||
& $seqScript
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-link] Runtime assets synced (non-symlink mode)"
|
||||
Write-Host "[mosaic-link] Canonical source: $MosaicHome"
|
||||
@@ -0,0 +1,90 @@
|
||||
# mosaic-migrate-local-skills.ps1
|
||||
# Migrates runtime-local skill directories to Mosaic-managed junctions.
|
||||
# Uses directory junctions (no elevation required) with fallback to copies.
|
||||
# PowerShell equivalent of mosaic-migrate-local-skills (bash).
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
param(
|
||||
[switch]$Apply,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$LocalSkillsDir = Join-Path $MosaicHome "skills-local"
|
||||
|
||||
if ($Help) {
|
||||
Write-Host @"
|
||||
Usage: mosaic-migrate-local-skills.ps1 [-Apply] [-Help]
|
||||
|
||||
Migrate runtime-local skill directories (e.g. ~/.claude/skills/jarvis) to
|
||||
Mosaic-managed skills by replacing local directories with junctions to
|
||||
~/.config/mosaic/skills-local.
|
||||
|
||||
Default mode is dry-run.
|
||||
"@
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not (Test-Path $LocalSkillsDir)) {
|
||||
Write-Host "[mosaic-local-skills] Missing local skills dir: $LocalSkillsDir" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$skillRoots = @(
|
||||
(Join-Path $env:USERPROFILE ".claude\skills"),
|
||||
(Join-Path $env:USERPROFILE ".codex\skills"),
|
||||
(Join-Path $env:USERPROFILE ".config\opencode\skills")
|
||||
)
|
||||
|
||||
$count = 0
|
||||
|
||||
Get-ChildItem $LocalSkillsDir -Directory | ForEach-Object {
|
||||
$name = $_.Name
|
||||
$src = $_.FullName
|
||||
|
||||
foreach ($root in $skillRoots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
$target = Join-Path $root $name
|
||||
|
||||
# Already a junction/symlink — check if it points to the right place
|
||||
$existing = Get-Item $target -Force -ErrorAction SilentlyContinue
|
||||
if ($existing -and ($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
$currentTarget = $existing.Target
|
||||
if ($currentTarget -and ($currentTarget -eq $src -or (Resolve-Path $currentTarget -ErrorAction SilentlyContinue).Path -eq (Resolve-Path $src -ErrorAction SilentlyContinue).Path)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# Only migrate local directories containing SKILL.md
|
||||
if ((Test-Path $target -PathType Container) -and
|
||||
(Test-Path (Join-Path $target "SKILL.md")) -and
|
||||
-not ($existing -and ($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint))) {
|
||||
|
||||
$count++
|
||||
if ($Apply) {
|
||||
$stamp = Get-Date -Format "yyyyMMddHHmmss"
|
||||
Rename-Item $target "$target.mosaic-bak-$stamp"
|
||||
try {
|
||||
New-Item -ItemType Junction -Path $target -Target $src -ErrorAction Stop | Out-Null
|
||||
Write-Host "[mosaic-local-skills] migrated: $target -> $src"
|
||||
}
|
||||
catch {
|
||||
Write-Host "[mosaic-local-skills] Junction failed for $name, falling back to copy"
|
||||
Copy-Item $src $target -Recurse -Force
|
||||
Write-Host "[mosaic-local-skills] copied: $target <- $src"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host "[mosaic-local-skills] would migrate: $target -> $src"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Apply) {
|
||||
Write-Host "[mosaic-local-skills] complete: migrated=$count"
|
||||
}
|
||||
else {
|
||||
Write-Host "[mosaic-local-skills] dry-run: migratable=$count"
|
||||
Write-Host "[mosaic-local-skills] re-run with -Apply to migrate"
|
||||
}
|
||||
65
packages/mosaic/framework/bin/mosaic-release-upgrade.ps1
Normal file
65
packages/mosaic/framework/bin/mosaic-release-upgrade.ps1
Normal file
@@ -0,0 +1,65 @@
|
||||
# mosaic-release-upgrade.ps1 — Upgrade installed Mosaic framework release (Windows)
|
||||
#
|
||||
# Usage:
|
||||
# mosaic-release-upgrade.ps1
|
||||
# mosaic-release-upgrade.ps1 -Ref main -Keep
|
||||
# mosaic-release-upgrade.ps1 -Ref v0.2.0 -Overwrite -Yes
|
||||
#
|
||||
param(
|
||||
[string]$Ref = $(if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" }),
|
||||
[switch]$Keep,
|
||||
[switch]$Overwrite,
|
||||
[switch]$Yes,
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$RemoteInstallerUrl = if ($env:MOSAIC_REMOTE_INSTALL_URL) {
|
||||
$env:MOSAIC_REMOTE_INSTALL_URL
|
||||
} else {
|
||||
"https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1"
|
||||
}
|
||||
|
||||
$installMode = if ($Overwrite) { "overwrite" } elseif ($Keep) { "keep" } elseif ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE } else { "keep" }
|
||||
if ($installMode -notin @("keep", "overwrite")) {
|
||||
Write-Host "[mosaic-release-upgrade] Invalid install mode: $installMode" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$currentVersion = "unknown"
|
||||
$mosaicCmd = Join-Path $MosaicHome "bin\mosaic.ps1"
|
||||
if (Test-Path $mosaicCmd) {
|
||||
try {
|
||||
$currentVersion = (& $mosaicCmd --version) -replace '^mosaic\s+', ''
|
||||
}
|
||||
catch {
|
||||
$currentVersion = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-release-upgrade] Current version: $currentVersion"
|
||||
Write-Host "[mosaic-release-upgrade] Target ref: $Ref"
|
||||
Write-Host "[mosaic-release-upgrade] Install mode: $installMode"
|
||||
Write-Host "[mosaic-release-upgrade] Installer URL: $RemoteInstallerUrl"
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Host "[mosaic-release-upgrade] Dry run: no changes applied."
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $Yes) {
|
||||
$confirmation = Read-Host "Proceed with Mosaic release upgrade? [y/N]"
|
||||
if ($confirmation -notin @("y", "Y", "yes", "YES")) {
|
||||
Write-Host "[mosaic-release-upgrade] Aborted."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$env:MOSAIC_BOOTSTRAP_REF = $Ref
|
||||
$env:MOSAIC_INSTALL_MODE = $installMode
|
||||
$env:MOSAIC_HOME = $MosaicHome
|
||||
|
||||
Invoke-RestMethod -Uri $RemoteInstallerUrl | Invoke-Expression
|
||||
|
||||
126
packages/mosaic/framework/bin/mosaic-sync-skills.ps1
Normal file
126
packages/mosaic/framework/bin/mosaic-sync-skills.ps1
Normal file
@@ -0,0 +1,126 @@
|
||||
# mosaic-sync-skills.ps1
|
||||
# Syncs canonical skills and links them into agent runtime skill directories.
|
||||
# Uses directory junctions (no elevation required) with fallback to copies.
|
||||
# PowerShell equivalent of mosaic-sync-skills (bash).
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
param(
|
||||
[switch]$LinkOnly,
|
||||
[switch]$NoLink,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$SkillsRepoUrl = if ($env:MOSAIC_SKILLS_REPO_URL) { $env:MOSAIC_SKILLS_REPO_URL } else { "https://git.mosaicstack.dev/mosaic/agent-skills.git" }
|
||||
$SkillsRepoDir = if ($env:MOSAIC_SKILLS_REPO_DIR) { $env:MOSAIC_SKILLS_REPO_DIR } else { Join-Path $MosaicHome "sources\agent-skills" }
|
||||
$MosaicSkillsDir = Join-Path $MosaicHome "skills"
|
||||
$MosaicLocalSkillsDir = Join-Path $MosaicHome "skills-local"
|
||||
|
||||
if ($Help) {
|
||||
Write-Host @"
|
||||
Usage: mosaic-sync-skills.ps1 [-LinkOnly] [-NoLink] [-Help]
|
||||
|
||||
Sync canonical skills into ~/.config/mosaic/skills and link all Mosaic skills
|
||||
into runtime skill directories using directory junctions.
|
||||
|
||||
Options:
|
||||
-LinkOnly Skip git clone/pull and only relink
|
||||
-NoLink Sync canonical skills but do not update runtime links
|
||||
-Help Show help
|
||||
"@
|
||||
exit 0
|
||||
}
|
||||
|
||||
foreach ($d in @($MosaicHome, $MosaicSkillsDir, $MosaicLocalSkillsDir)) {
|
||||
if (-not (Test-Path $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null }
|
||||
}
|
||||
|
||||
# Fetch skills from git
|
||||
if (-not $LinkOnly) {
|
||||
if (Test-Path (Join-Path $SkillsRepoDir ".git")) {
|
||||
Write-Host "[mosaic-skills] Updating skills source: $SkillsRepoDir"
|
||||
git -C $SkillsRepoDir pull --rebase
|
||||
}
|
||||
else {
|
||||
Write-Host "[mosaic-skills] Cloning skills source to: $SkillsRepoDir"
|
||||
$parentDir = Split-Path $SkillsRepoDir -Parent
|
||||
if (-not (Test-Path $parentDir)) { New-Item -ItemType Directory -Path $parentDir -Force | Out-Null }
|
||||
git clone $SkillsRepoUrl $SkillsRepoDir
|
||||
}
|
||||
|
||||
$sourceSkillsDir = Join-Path $SkillsRepoDir "skills"
|
||||
if (-not (Test-Path $sourceSkillsDir)) {
|
||||
Write-Host "[mosaic-skills] Missing source skills dir: $sourceSkillsDir" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Sync: remove old, copy new
|
||||
if (Test-Path $MosaicSkillsDir) {
|
||||
Get-ChildItem $MosaicSkillsDir | Remove-Item -Recurse -Force
|
||||
}
|
||||
Copy-Item "$sourceSkillsDir\*" $MosaicSkillsDir -Recurse -Force
|
||||
}
|
||||
|
||||
if (-not (Test-Path $MosaicSkillsDir)) {
|
||||
Write-Host "[mosaic-skills] Canonical skills dir missing: $MosaicSkillsDir" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($NoLink) {
|
||||
Write-Host "[mosaic-skills] Canonical sync completed (link update skipped)"
|
||||
exit 0
|
||||
}
|
||||
|
||||
function Link-SkillIntoTarget {
|
||||
param([string]$SkillPath, [string]$TargetDir)
|
||||
|
||||
$name = Split-Path $SkillPath -Leaf
|
||||
if ($name.StartsWith(".")) { return }
|
||||
|
||||
$linkPath = Join-Path $TargetDir $name
|
||||
|
||||
# Already a junction/symlink — recreate
|
||||
$existing = Get-Item $linkPath -Force -ErrorAction SilentlyContinue
|
||||
if ($existing -and ($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||
Remove-Item $linkPath -Force
|
||||
}
|
||||
elseif ($existing) {
|
||||
Write-Host "[mosaic-skills] Preserve existing runtime-specific entry: $linkPath"
|
||||
return
|
||||
}
|
||||
|
||||
# Try junction first, fall back to copy
|
||||
try {
|
||||
New-Item -ItemType Junction -Path $linkPath -Target $SkillPath -ErrorAction Stop | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-Host "[mosaic-skills] Junction failed for $name, falling back to copy"
|
||||
Copy-Item $SkillPath $linkPath -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
$linkTargets = @(
|
||||
(Join-Path $env:USERPROFILE ".claude\skills"),
|
||||
(Join-Path $env:USERPROFILE ".codex\skills"),
|
||||
(Join-Path $env:USERPROFILE ".config\opencode\skills")
|
||||
)
|
||||
|
||||
foreach ($target in $linkTargets) {
|
||||
if (-not (Test-Path $target)) { New-Item -ItemType Directory -Path $target -Force | Out-Null }
|
||||
|
||||
# Link canonical skills
|
||||
Get-ChildItem $MosaicSkillsDir -Directory | ForEach-Object {
|
||||
Link-SkillIntoTarget $_.FullName $target
|
||||
}
|
||||
|
||||
# Link local skills
|
||||
if (Test-Path $MosaicLocalSkillsDir) {
|
||||
Get-ChildItem $MosaicLocalSkillsDir -Directory | ForEach-Object {
|
||||
Link-SkillIntoTarget $_.FullName $target
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-skills] Linked skills into: $target"
|
||||
}
|
||||
|
||||
Write-Host "[mosaic-skills] Complete"
|
||||
437
packages/mosaic/framework/bin/mosaic.ps1
Normal file
437
packages/mosaic/framework/bin/mosaic.ps1
Normal file
@@ -0,0 +1,437 @@
|
||||
# mosaic.ps1 — Unified agent launcher and management CLI (Windows)
|
||||
#
|
||||
# AGENTS.md is the global policy source for all agent sessions.
|
||||
# The launcher injects a composed runtime contract (AGENTS + runtime reference).
|
||||
#
|
||||
# Usage:
|
||||
# mosaic claude [args...] Launch Claude Code with runtime contract injected
|
||||
# mosaic opencode [args...] Launch OpenCode with runtime contract injected
|
||||
# mosaic codex [args...] Launch Codex with runtime contract injected
|
||||
# mosaic yolo <runtime> [args...] Launch runtime in dangerous-permissions mode
|
||||
# mosaic --yolo <runtime> [args...] Alias for yolo
|
||||
# mosaic init [args...] Generate SOUL.md interactively
|
||||
# mosaic doctor [args...] Health audit
|
||||
# mosaic sync [args...] Sync skills
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$Version = "0.1.0"
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host @"
|
||||
mosaic $Version - Unified agent launcher
|
||||
|
||||
Usage: mosaic <command> [args...]
|
||||
|
||||
Agent Launchers:
|
||||
claude [args...] Launch Claude Code with runtime contract injected
|
||||
opencode [args...] Launch OpenCode with runtime contract injected
|
||||
codex [args...] Launch Codex with runtime contract injected
|
||||
yolo <runtime> [args...] Dangerous mode for claude|codex|opencode
|
||||
--yolo <runtime> [args...] Alias for yolo
|
||||
|
||||
Management:
|
||||
init [args...] Generate SOUL.md (agent identity contract)
|
||||
doctor [args...] Audit runtime state and detect drift
|
||||
sync [args...] Sync skills from canonical source
|
||||
bootstrap <path> Bootstrap a repo with Mosaic standards
|
||||
upgrade [mode] [args] Upgrade release (default) or project files
|
||||
upgrade check Check release upgrade status (no changes)
|
||||
release-upgrade [...] Upgrade installed Mosaic release
|
||||
project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project
|
||||
|
||||
Options:
|
||||
-h, --help Show this help
|
||||
-v, --version Show version
|
||||
"@
|
||||
}
|
||||
|
||||
function Assert-MosaicHome {
|
||||
if (-not (Test-Path $MosaicHome)) {
|
||||
Write-Host "[mosaic] ERROR: ~/.config/mosaic not found." -ForegroundColor Red
|
||||
Write-Host "[mosaic] Install with: irm https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1 | iex"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-AgentsMd {
|
||||
$agentsPath = Join-Path $MosaicHome "AGENTS.md"
|
||||
if (-not (Test-Path $agentsPath)) {
|
||||
Write-Host "[mosaic] ERROR: ~/.config/mosaic/AGENTS.md not found." -ForegroundColor Red
|
||||
Write-Host "[mosaic] Re-run the installer."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-Soul {
|
||||
$soulPath = Join-Path $MosaicHome "SOUL.md"
|
||||
if (-not (Test-Path $soulPath)) {
|
||||
Write-Host "[mosaic] SOUL.md not found. Running mosaic init..."
|
||||
& (Join-Path $MosaicHome "bin\mosaic-init.ps1")
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-Runtime {
|
||||
param([string]$Cmd)
|
||||
if (-not (Get-Command $Cmd -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "[mosaic] ERROR: '$Cmd' not found in PATH." -ForegroundColor Red
|
||||
Write-Host "[mosaic] Install $Cmd before launching."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-SequentialThinking {
|
||||
$checker = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
|
||||
if (-not (Test-Path $checker)) {
|
||||
Write-Host "[mosaic] ERROR: sequential-thinking checker missing: $checker" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
try {
|
||||
& $checker -Check *>$null
|
||||
}
|
||||
catch {
|
||||
Write-Host "[mosaic] ERROR: sequential-thinking MCP is required but not configured." -ForegroundColor Red
|
||||
Write-Host "[mosaic] Run: $checker"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ActiveMission {
|
||||
$missionFile = Join-Path (Get-Location) ".mosaic\orchestrator\mission.json"
|
||||
if (-not (Test-Path $missionFile)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
try {
|
||||
$mission = Get-Content $missionFile -Raw | ConvertFrom-Json
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
|
||||
$status = [string]$mission.status
|
||||
if ([string]::IsNullOrWhiteSpace($status)) {
|
||||
$status = "inactive"
|
||||
}
|
||||
if ($status -ne "active" -and $status -ne "paused") {
|
||||
return $null
|
||||
}
|
||||
|
||||
$name = [string]$mission.name
|
||||
if ([string]::IsNullOrWhiteSpace($name)) {
|
||||
$name = "unnamed"
|
||||
}
|
||||
|
||||
$id = [string]$mission.mission_id
|
||||
if ([string]::IsNullOrWhiteSpace($id)) {
|
||||
$id = ""
|
||||
}
|
||||
|
||||
$milestones = @($mission.milestones)
|
||||
$milestoneCount = $milestones.Count
|
||||
$milestoneCompleted = @($milestones | Where-Object { $_.status -eq "completed" }).Count
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Name = $name
|
||||
Id = $id
|
||||
Status = $status
|
||||
MilestoneCount = $milestoneCount
|
||||
MilestoneCompleted = $milestoneCompleted
|
||||
}
|
||||
}
|
||||
|
||||
function Get-MissionContractBlock {
|
||||
$mission = Get-ActiveMission
|
||||
if ($null -eq $mission) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return @"
|
||||
# ACTIVE MISSION — HARD GATE (Read Before Anything Else)
|
||||
|
||||
An active orchestration mission exists in this project. This is a BLOCKING requirement.
|
||||
|
||||
**Mission:** $($mission.Name)
|
||||
**ID:** $($mission.Id)
|
||||
**Status:** $($mission.Status)
|
||||
**Milestones:** $($mission.MilestoneCompleted) / $($mission.MilestoneCount) completed
|
||||
|
||||
## MANDATORY — Before ANY Response to the User
|
||||
|
||||
You MUST complete these steps before responding to any user message, including simple greetings:
|
||||
|
||||
1. Read `~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md` (mission lifecycle protocol)
|
||||
2. Read `docs/MISSION-MANIFEST.md` for full mission scope, milestones, and success criteria
|
||||
3. Read the latest scratchpad in `docs/scratchpads/` for session history, decisions, and corrections
|
||||
4. Read `docs/TASKS.md` for current task state (what is done, what is next)
|
||||
5. After reading all four, acknowledge the mission state to the user before proceeding
|
||||
|
||||
If the user gives a task, execute it within the mission context. If no task is given, present mission status and ask how to proceed.
|
||||
"@
|
||||
}
|
||||
|
||||
function Get-MissionPrompt {
|
||||
$mission = Get-ActiveMission
|
||||
if ($null -eq $mission) {
|
||||
return ""
|
||||
}
|
||||
return "Active mission detected: $($mission.Name). Read the mission state files and report status."
|
||||
}
|
||||
|
||||
function Get-RuntimePrompt {
|
||||
param(
|
||||
[ValidateSet("claude", "codex", "opencode")]
|
||||
[string]$Runtime
|
||||
)
|
||||
|
||||
$runtimeFile = switch ($Runtime) {
|
||||
"claude" { Join-Path $MosaicHome "runtime\claude\RUNTIME.md" }
|
||||
"codex" { Join-Path $MosaicHome "runtime\codex\RUNTIME.md" }
|
||||
"opencode" { Join-Path $MosaicHome "runtime\opencode\RUNTIME.md" }
|
||||
}
|
||||
|
||||
if (-not (Test-Path $runtimeFile)) {
|
||||
Write-Host "[mosaic] ERROR: runtime contract not found: $runtimeFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$launcherContract = @'
|
||||
# Mosaic Launcher Runtime Contract (Hard Gate)
|
||||
|
||||
This contract is injected by `mosaic` launch and is mandatory.
|
||||
|
||||
First assistant response MUST start with exactly one mode declaration line:
|
||||
1. Orchestration mission: `Now initiating Orchestrator mode...`
|
||||
2. Implementation mission: `Now initiating Delivery mode...`
|
||||
3. Review-only mission: `Now initiating Review mode...`
|
||||
|
||||
No tool call or implementation step may occur before that first line.
|
||||
|
||||
Mosaic hard gates OVERRIDE runtime-default caution for routine delivery operations.
|
||||
For required push/merge/issue-close/release actions, execute without routine confirmation prompts.
|
||||
|
||||
'@
|
||||
|
||||
$missionBlock = Get-MissionContractBlock
|
||||
$agentsContent = Get-Content (Join-Path $MosaicHome "AGENTS.md") -Raw
|
||||
$runtimeContent = Get-Content $runtimeFile -Raw
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($missionBlock)) {
|
||||
return "$missionBlock`n`n$launcherContract`n$agentsContent`n`n# Runtime-Specific Contract`n`n$runtimeContent"
|
||||
}
|
||||
|
||||
return "$launcherContract`n$agentsContent`n`n# Runtime-Specific Contract`n`n$runtimeContent"
|
||||
}
|
||||
|
||||
function Ensure-RuntimeConfig {
|
||||
param(
|
||||
[ValidateSet("claude", "codex", "opencode")]
|
||||
[string]$Runtime,
|
||||
[string]$Dst
|
||||
)
|
||||
|
||||
$parent = Split-Path $Dst -Parent
|
||||
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null }
|
||||
|
||||
$runtimePrompt = Get-RuntimePrompt -Runtime $Runtime
|
||||
$tmp = [System.IO.Path]::GetTempFileName()
|
||||
Set-Content -Path $tmp -Value $runtimePrompt -Encoding UTF8 -NoNewline
|
||||
|
||||
$srcHash = (Get-FileHash $tmp -Algorithm SHA256).Hash
|
||||
$dstHash = if (Test-Path $Dst) { (Get-FileHash $Dst -Algorithm SHA256).Hash } else { "" }
|
||||
if ($srcHash -ne $dstHash) {
|
||||
Copy-Item $tmp $Dst -Force
|
||||
Remove-Item $tmp -Force
|
||||
}
|
||||
else {
|
||||
Remove-Item $tmp -Force
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-Yolo {
|
||||
param([string[]]$YoloArgs)
|
||||
|
||||
if ($YoloArgs.Count -lt 1) {
|
||||
Write-Host "[mosaic] ERROR: yolo requires a runtime (claude|codex|opencode)." -ForegroundColor Red
|
||||
Write-Host "[mosaic] Example: mosaic yolo claude"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$runtime = $YoloArgs[0]
|
||||
$tail = if ($YoloArgs.Count -gt 1) { @($YoloArgs[1..($YoloArgs.Count - 1)]) } else { @() }
|
||||
|
||||
switch ($runtime) {
|
||||
"claude" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "claude"
|
||||
Assert-SequentialThinking
|
||||
$agentsContent = Get-RuntimePrompt -Runtime "claude"
|
||||
Write-Host "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
|
||||
& claude --dangerously-skip-permissions --append-system-prompt $agentsContent @tail
|
||||
return
|
||||
}
|
||||
"codex" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "codex"
|
||||
Assert-SequentialThinking
|
||||
Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md")
|
||||
$missionPrompt = Get-MissionPrompt
|
||||
if (-not [string]::IsNullOrWhiteSpace($missionPrompt) -and $tail.Count -eq 0) {
|
||||
Write-Host "[mosaic] Launching Codex in YOLO mode (active mission detected)..."
|
||||
& codex --dangerously-bypass-approvals-and-sandbox $missionPrompt
|
||||
}
|
||||
else {
|
||||
Write-Host "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
|
||||
& codex --dangerously-bypass-approvals-and-sandbox @tail
|
||||
}
|
||||
return
|
||||
}
|
||||
"opencode" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "opencode"
|
||||
Assert-SequentialThinking
|
||||
Ensure-RuntimeConfig -Runtime "opencode" -Dst (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
|
||||
Write-Host "[mosaic] Launching OpenCode in YOLO mode..."
|
||||
& opencode @tail
|
||||
return
|
||||
}
|
||||
default {
|
||||
Write-Host "[mosaic] ERROR: Unsupported yolo runtime '$runtime'. Use claude|codex|opencode." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($args.Count -eq 0) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
$command = $args[0]
|
||||
$remaining = if ($args.Count -gt 1) { @($args[1..($args.Count - 1)]) } else { @() }
|
||||
|
||||
switch ($command) {
|
||||
"claude" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "claude"
|
||||
Assert-SequentialThinking
|
||||
# Claude supports --append-system-prompt for direct injection
|
||||
$agentsContent = Get-RuntimePrompt -Runtime "claude"
|
||||
Write-Host "[mosaic] Launching Claude Code..."
|
||||
& claude --append-system-prompt $agentsContent @remaining
|
||||
}
|
||||
"opencode" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "opencode"
|
||||
Assert-SequentialThinking
|
||||
# OpenCode reads from ~/.config/opencode/AGENTS.md
|
||||
Ensure-RuntimeConfig -Runtime "opencode" -Dst (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
|
||||
Write-Host "[mosaic] Launching OpenCode..."
|
||||
& opencode @remaining
|
||||
}
|
||||
"codex" {
|
||||
Assert-MosaicHome
|
||||
Assert-AgentsMd
|
||||
Assert-Soul
|
||||
Assert-Runtime "codex"
|
||||
Assert-SequentialThinking
|
||||
# Codex reads from ~/.codex/instructions.md
|
||||
Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md")
|
||||
$missionPrompt = Get-MissionPrompt
|
||||
if (-not [string]::IsNullOrWhiteSpace($missionPrompt) -and $remaining.Count -eq 0) {
|
||||
Write-Host "[mosaic] Launching Codex (active mission detected)..."
|
||||
& codex $missionPrompt
|
||||
}
|
||||
else {
|
||||
Write-Host "[mosaic] Launching Codex..."
|
||||
& codex @remaining
|
||||
}
|
||||
}
|
||||
"yolo" {
|
||||
Invoke-Yolo -YoloArgs $remaining
|
||||
}
|
||||
"--yolo" {
|
||||
Invoke-Yolo -YoloArgs $remaining
|
||||
}
|
||||
"init" {
|
||||
Assert-MosaicHome
|
||||
& (Join-Path $MosaicHome "bin\mosaic-init.ps1") @remaining
|
||||
}
|
||||
"doctor" {
|
||||
Assert-MosaicHome
|
||||
& (Join-Path $MosaicHome "bin\mosaic-doctor.ps1") @remaining
|
||||
}
|
||||
"sync" {
|
||||
Assert-MosaicHome
|
||||
& (Join-Path $MosaicHome "bin\mosaic-sync-skills.ps1") @remaining
|
||||
}
|
||||
"bootstrap" {
|
||||
Assert-MosaicHome
|
||||
Write-Host "[mosaic] NOTE: mosaic-bootstrap-repo requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining
|
||||
}
|
||||
"upgrade" {
|
||||
Assert-MosaicHome
|
||||
if ($remaining.Count -eq 0) {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1")
|
||||
break
|
||||
}
|
||||
|
||||
$mode = $remaining[0]
|
||||
$tail = if ($remaining.Count -gt 1) { $remaining[1..($remaining.Count - 1)] } else { @() }
|
||||
|
||||
switch -Regex ($mode) {
|
||||
"^release$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @tail
|
||||
}
|
||||
"^check$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") -DryRun @tail
|
||||
}
|
||||
"^project$" {
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @tail
|
||||
}
|
||||
"^(--all|--root)$" {
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||
}
|
||||
"^(--dry-run|--ref|--keep|--overwrite|-y|--yes)$" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
"^-.*" {
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
default {
|
||||
Write-Host "[mosaic] NOTE: treating positional argument as project path." -ForegroundColor Yellow
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||
}
|
||||
}
|
||||
}
|
||||
"release-upgrade" {
|
||||
Assert-MosaicHome
|
||||
& (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining
|
||||
}
|
||||
"project-upgrade" {
|
||||
Assert-MosaicHome
|
||||
Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow
|
||||
& (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining
|
||||
}
|
||||
{ $_ -in "help", "-h", "--help" } { Show-Usage }
|
||||
{ $_ -in "version", "-v", "--version" } { Write-Host "mosaic $Version" }
|
||||
default {
|
||||
Write-Host "[mosaic] Unknown command: $command" -ForegroundColor Red
|
||||
Write-Host "[mosaic] Run 'mosaic --help' for usage."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
# Mosaic Framework Consistency Audit
|
||||
|
||||
Date: 2026-02-17
|
||||
Scope:
|
||||
|
||||
- `~/src/mosaic-bootstrap`
|
||||
- `~/src/agent-skills`
|
||||
- Installed runtime check: `~/.config/mosaic`
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Severity | Finding | Status |
|
||||
| ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
||||
| MF-001 | High | QA rails scripts referenced invalid paths (missing `/qa/` segment) for `qa-hook-handler.sh` calls. | Remediated |
|
||||
| MF-002 | Medium | Conditional-loading detection was biased to legacy `agent-guides` markers and missed Mosaic `guides`/`Conditional Context` phrasing. | Remediated |
|
||||
| MF-003 | Medium | Framework spec text still described detection as `agent-guides`-based only. | Remediated |
|
||||
| MF-004 | Medium | Runtime Claude overlay hardcoded skill count, which drifts over time. | Remediated |
|
||||
| MF-005 | High | `pr-reviewer` skill used legacy `~/.claude/scripts/git` and `~/.claude/skills` paths. | Remediated |
|
||||
| MF-006 | Medium | `using-git-worktrees` prioritized `CLAUDE.md` only and ignored Mosaic-first docs (`AGENTS.md`/`SOUL.md`). | Remediated |
|
||||
| MF-007 | Medium | Marketing skills used `.claude/product-marketing-context.md` instead of Mosaic repo-local path. | Remediated |
|
||||
| MF-008 | Low | `agent-skills/README.md` manual install/adaptation guidance referenced legacy Claude-only paths. | Remediated |
|
||||
| MF-009 | Low | Historical creation-log contained stale Claude-specific path examples. | Remediated |
|
||||
|
||||
## Remediation Details
|
||||
|
||||
### MF-001 (QA rails path correction)
|
||||
|
||||
Updated:
|
||||
|
||||
- `tools/qa/qa-hook-wrapper.sh`
|
||||
- `tools/qa/qa-hook-stdin.sh`
|
||||
- `tools/qa/qa-hook-handler.sh`
|
||||
- `tools/qa/remediation-hook-handler.sh`
|
||||
- `tools/qa/qa-queue-monitor.sh`
|
||||
|
||||
Change:
|
||||
|
||||
- Standardized handler paths to `~/.config/mosaic/tools/qa/...`.
|
||||
|
||||
### MF-002 + MF-003 (conditional loading/context detection)
|
||||
|
||||
Updated:
|
||||
|
||||
- `tools/bootstrap/agent-lint.sh`
|
||||
- `tools/bootstrap/agent-upgrade.sh`
|
||||
- `templates/agent/SPEC.md`
|
||||
|
||||
Change:
|
||||
|
||||
- Detection now recognizes both legacy and Mosaic patterns:
|
||||
- `agent-guides` (legacy compatibility)
|
||||
- `~/.config/mosaic/guides`
|
||||
- `Conditional Loading`
|
||||
- `Conditional Context`
|
||||
|
||||
### MF-004 (runtime overlay drift-proofing)
|
||||
|
||||
Updated:
|
||||
|
||||
- `runtime/claude/CLAUDE.md`
|
||||
|
||||
Change:
|
||||
|
||||
- Removed hardcoded skills count text.
|
||||
|
||||
### MF-005 (pr-reviewer skill path migration)
|
||||
|
||||
Updated:
|
||||
|
||||
- `skills/pr-reviewer/SKILL.md`
|
||||
|
||||
Change:
|
||||
|
||||
- Replaced all `~/.claude/scripts/git/...` with `~/.config/mosaic/tools/git/...`.
|
||||
- Replaced `~/.claude/skills/...` with `~/.config/mosaic/skills/...`.
|
||||
|
||||
### MF-006 (worktree skill docs hierarchy)
|
||||
|
||||
Updated:
|
||||
|
||||
- `skills/using-git-worktrees/SKILL.md`
|
||||
|
||||
Change:
|
||||
|
||||
- Replaced CLAUDE-only checks with `AGENTS.md` / `SOUL.md` (fallback: `CLAUDE.md`).
|
||||
|
||||
### MF-007 (marketing context path migration)
|
||||
|
||||
Updated:
|
||||
|
||||
- `skills/product-marketing-context/SKILL.md`
|
||||
- All skills referencing product-marketing-context path (bulk update)
|
||||
|
||||
Change:
|
||||
|
||||
- Standardized context path to `.mosaic/product-marketing-context.md`.
|
||||
|
||||
### MF-008 + MF-009 (repo guidance cleanup)
|
||||
|
||||
Updated:
|
||||
|
||||
- `README.md` (agent-skills)
|
||||
- `skills/writing-skills/SKILL.md`
|
||||
- `skills/writing-skills/examples/CLAUDE_MD_TESTING.md`
|
||||
- `skills/systematic-debugging/CREATION-LOG.md`
|
||||
|
||||
Change:
|
||||
|
||||
- Standardized references to `~/.config/mosaic/skills`.
|
||||
- Removed stale Claude-only wording.
|
||||
|
||||
## Residual Legacy References (Intentional)
|
||||
|
||||
The following legacy references remain in `mosaic-bootstrap` by design and are not inconsistencies:
|
||||
|
||||
- Runtime compatibility tooling for Claude home directory management:
|
||||
- `bin/mosaic-link-runtime-assets`
|
||||
- `bin/mosaic-clean-runtime`
|
||||
- `bin/mosaic-doctor`
|
||||
- `bin/mosaic-sync-skills`
|
||||
- `bin/mosaic-migrate-local-skills`
|
||||
- Documentation explaining Claude runtime overlays:
|
||||
- `README.md`
|
||||
- `profiles/README.md`
|
||||
- `adapters/claude.md`
|
||||
- `runtime/claude/settings-overlays/jarvis-loop.json`
|
||||
|
||||
These are required to support existing Claude runtime integration while keeping Mosaic as canonical source.
|
||||
|
||||
## Verification
|
||||
|
||||
Executed checks:
|
||||
|
||||
- `rg -n "~/.claude|\\.claude/|agent-guides" ~/src/agent-skills -S`
|
||||
- Result: no matches after remediation.
|
||||
- `rg -n "~/.config/mosaic/tools/(qa-hook|remediation-hook|qa-queue-monitor)" ~/src/mosaic-bootstrap -S`
|
||||
- Result: no invalid old-style QA rail paths remain.
|
||||
- Installed runtime validation:
|
||||
- `~/.config/mosaic` contains `tools/git`, `tools/portainer`, `tools/cicd`, `skills`, and `bin` tooling.
|
||||
193
packages/mosaic/framework/guides/AUTHENTICATION.md
Normal file
193
packages/mosaic/framework/guides/AUTHENTICATION.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Authentication & Authorization Guide
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Review existing auth implementation in codebase
|
||||
3. Review Vault secrets structure: `docs/vault-secrets-structure.md`
|
||||
|
||||
## Authentication Patterns
|
||||
|
||||
### JWT (JSON Web Tokens)
|
||||
|
||||
```
|
||||
Vault Path: secret-{env}/backend-api/jwt/signing-key
|
||||
Fields: key, algorithm, expiry_seconds
|
||||
```
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
- Use RS256 or ES256 (asymmetric) for distributed systems
|
||||
- Use HS256 (symmetric) only for single-service auth
|
||||
- Set reasonable expiry (15min-1hr for access tokens)
|
||||
- Include minimal claims (sub, exp, iat, roles)
|
||||
- Never store sensitive data in JWT payload
|
||||
|
||||
### Session-Based
|
||||
|
||||
```
|
||||
Vault Path: secret-{env}/{service}/session/secret
|
||||
Fields: secret, cookie_name, max_age
|
||||
```
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
- Use secure, httpOnly, sameSite cookies
|
||||
- Regenerate session ID on privilege change
|
||||
- Implement session timeout
|
||||
- Store sessions server-side (Redis/database)
|
||||
|
||||
### OAuth2/OIDC
|
||||
|
||||
```
|
||||
Vault Paths:
|
||||
- secret-{env}/{service}/oauth/{provider}/client_id
|
||||
- secret-{env}/{service}/oauth/{provider}/client_secret
|
||||
```
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
- Use PKCE for public clients
|
||||
- Validate state parameter
|
||||
- Verify token signatures
|
||||
- Check issuer and audience claims
|
||||
|
||||
## Authorization Patterns
|
||||
|
||||
### Role-Based Access Control (RBAC)
|
||||
|
||||
```python
|
||||
# Example middleware
|
||||
def require_role(roles: list):
|
||||
def decorator(handler):
|
||||
def wrapper(request):
|
||||
user_roles = get_user_roles(request.user_id)
|
||||
if not any(role in user_roles for role in roles):
|
||||
raise ForbiddenError()
|
||||
return handler(request)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
@require_role(['admin', 'moderator'])
|
||||
def delete_user(request):
|
||||
pass
|
||||
```
|
||||
|
||||
### Permission-Based
|
||||
|
||||
```python
|
||||
# Check specific permissions
|
||||
def check_permission(user_id, resource, action):
|
||||
permissions = get_user_permissions(user_id)
|
||||
return f"{resource}:{action}" in permissions
|
||||
```
|
||||
|
||||
## Security Requirements
|
||||
|
||||
### Password Handling
|
||||
|
||||
- Use bcrypt, scrypt, or Argon2 for hashing
|
||||
- Minimum 12 character passwords
|
||||
- Check against breached password lists
|
||||
- Implement account lockout after failed attempts
|
||||
|
||||
### Token Security
|
||||
|
||||
- Rotate secrets regularly
|
||||
- Implement token revocation
|
||||
- Use short-lived access tokens with refresh tokens
|
||||
- Store refresh tokens securely (httpOnly cookies or encrypted storage)
|
||||
|
||||
### Multi-Factor Authentication
|
||||
|
||||
- Support TOTP (Google Authenticator compatible)
|
||||
- Consider WebAuthn for passwordless
|
||||
- Require MFA for sensitive operations
|
||||
|
||||
## Testing Authentication
|
||||
|
||||
### Test Cases Required
|
||||
|
||||
```python
|
||||
class TestAuthentication:
|
||||
def test_login_success_returns_token(self):
|
||||
pass
|
||||
def test_login_failure_returns_401(self):
|
||||
pass
|
||||
def test_invalid_token_returns_401(self):
|
||||
pass
|
||||
def test_expired_token_returns_401(self):
|
||||
pass
|
||||
def test_missing_token_returns_401(self):
|
||||
pass
|
||||
def test_insufficient_permissions_returns_403(self):
|
||||
pass
|
||||
def test_token_refresh_works(self):
|
||||
pass
|
||||
def test_logout_invalidates_token(self):
|
||||
pass
|
||||
```
|
||||
|
||||
## Authentik SSO Administration
|
||||
|
||||
Authentik is the identity provider for the Mosaic Stack. Use the Authentik tool suite for administration.
|
||||
|
||||
### Tool Suite
|
||||
|
||||
```bash
|
||||
# System health
|
||||
~/.config/mosaic/tools/authentik/admin-status.sh
|
||||
|
||||
# User management
|
||||
~/.config/mosaic/tools/authentik/user-list.sh
|
||||
~/.config/mosaic/tools/authentik/user-create.sh -u <username> -n <name> -e <email>
|
||||
|
||||
# Group and app management
|
||||
~/.config/mosaic/tools/authentik/group-list.sh
|
||||
~/.config/mosaic/tools/authentik/app-list.sh
|
||||
~/.config/mosaic/tools/authentik/flow-list.sh
|
||||
```
|
||||
|
||||
### Registering an OAuth Application
|
||||
|
||||
1. Create an OAuth2 provider in Authentik admin (Applications > Providers)
|
||||
2. Create an application linked to the provider (Applications > Applications)
|
||||
3. Configure redirect URIs for the application
|
||||
4. Store client_id and client_secret in Vault: `secret-{env}/{service}/oauth/authentik/`
|
||||
5. Verify with: `~/.config/mosaic/tools/authentik/app-list.sh`
|
||||
|
||||
### API Reference
|
||||
|
||||
- Base URL: `https://auth.diversecanvas.com`
|
||||
- API prefix: `/api/v3/`
|
||||
- OpenAPI schema: `/api/v3/schema/`
|
||||
- Auth: Bearer token (obtained via `auth-token.sh`)
|
||||
|
||||
## Common Vulnerabilities to Avoid
|
||||
|
||||
1. **Broken Authentication**
|
||||
- Weak password requirements
|
||||
- Missing brute-force protection
|
||||
- Session fixation
|
||||
|
||||
2. **Broken Access Control**
|
||||
- Missing authorization checks
|
||||
- IDOR (Insecure Direct Object Reference)
|
||||
- Privilege escalation
|
||||
|
||||
3. **Security Misconfiguration**
|
||||
- Default credentials
|
||||
- Verbose error messages
|
||||
- Missing security headers
|
||||
|
||||
## Commit Format
|
||||
|
||||
```
|
||||
feat(#89): Implement JWT authentication
|
||||
|
||||
- Add /auth/login and /auth/refresh endpoints
|
||||
- Implement token validation middleware
|
||||
- Configure 15min access token expiry
|
||||
|
||||
Fixes #89
|
||||
```
|
||||
125
packages/mosaic/framework/guides/BACKEND.md
Normal file
125
packages/mosaic/framework/guides/BACKEND.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Backend Development Guide
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review API contracts and database schema
|
||||
|
||||
## Development Standards
|
||||
|
||||
### API Design
|
||||
|
||||
- Follow RESTful conventions (or GraphQL patterns if applicable)
|
||||
- Use consistent endpoint naming: `/api/v1/resource-name`
|
||||
- Return appropriate HTTP status codes
|
||||
- Include pagination for list endpoints
|
||||
- Document all endpoints (OpenAPI/Swagger preferred)
|
||||
|
||||
### Database
|
||||
|
||||
- Write migrations for schema changes
|
||||
- Use parameterized queries (prevent SQL injection)
|
||||
- Index frequently queried columns
|
||||
- Document relationships and constraints
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Return structured error responses
|
||||
- Log errors with context (request ID, user ID if applicable)
|
||||
- Never expose internal errors to clients
|
||||
- Use appropriate error codes
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "User-friendly message",
|
||||
"details": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security
|
||||
|
||||
- Validate all input at API boundaries
|
||||
- Implement rate limiting on public endpoints
|
||||
- Use secrets from Vault (see `docs/vault-secrets-structure.md`)
|
||||
- Never log sensitive data (passwords, tokens, PII)
|
||||
- Follow OWASP guidelines
|
||||
|
||||
### Authentication/Authorization
|
||||
|
||||
- Use project's established auth pattern
|
||||
- Validate tokens on every request
|
||||
- Check permissions before operations
|
||||
- See `~/.config/mosaic/guides/AUTHENTICATION.md` for details
|
||||
|
||||
## Testing Requirements (TDD)
|
||||
|
||||
1. Write tests BEFORE implementation
|
||||
2. Minimum 85% coverage
|
||||
3. Test categories:
|
||||
- Unit tests for business logic
|
||||
- Integration tests for API endpoints
|
||||
- Database tests with transactions/rollback
|
||||
|
||||
### Test Patterns
|
||||
|
||||
```python
|
||||
# API test example structure
|
||||
class TestResourceEndpoint:
|
||||
def test_create_returns_201(self):
|
||||
pass
|
||||
def test_create_validates_input(self):
|
||||
pass
|
||||
def test_get_returns_404_for_missing(self):
|
||||
pass
|
||||
def test_requires_authentication(self):
|
||||
pass
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow Google Style Guide for your language
|
||||
- **TypeScript: Follow `~/.config/mosaic/guides/TYPESCRIPT.md` — MANDATORY**
|
||||
- Use linter/formatter from project configuration
|
||||
- Keep functions focused and small
|
||||
- Document complex business logic
|
||||
|
||||
### TypeScript Quick Rules (see TYPESCRIPT.md for full guide)
|
||||
|
||||
- **NO `any`** — define explicit types always
|
||||
- **NO lazy `unknown`** — only for error catches and external data with validation
|
||||
- **Explicit return types** on all exported functions
|
||||
- **Explicit parameter types** always
|
||||
- **DTO files are REQUIRED** for module/API boundaries (`*.dto.ts`)
|
||||
- **Interface for DTOs** — never inline object types
|
||||
- **Typed errors** — use custom error classes
|
||||
|
||||
## Performance
|
||||
|
||||
- Use database connection pooling
|
||||
- Implement caching where appropriate
|
||||
- Profile slow endpoints
|
||||
- Use async operations for I/O
|
||||
|
||||
## Commit Format
|
||||
|
||||
```
|
||||
feat(#45): Add user registration endpoint
|
||||
|
||||
- POST /api/v1/users for registration
|
||||
- Email validation and uniqueness check
|
||||
- Password hashing with bcrypt
|
||||
|
||||
Fixes #45
|
||||
```
|
||||
|
||||
## Before Completing
|
||||
|
||||
1. Run full test suite
|
||||
2. Verify migrations work (up and down)
|
||||
3. Test API with curl/httpie
|
||||
4. Update scratchpad with completion notes
|
||||
5. Reference issue in commit
|
||||
487
packages/mosaic/framework/guides/BOOTSTRAP.md
Executable file
487
packages/mosaic/framework/guides/BOOTSTRAP.md
Executable file
@@ -0,0 +1,487 @@
|
||||
# Project Bootstrap Guide
|
||||
|
||||
> Load this guide when setting up a new project for AI-assisted development.
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers how to bootstrap a project so AI agents (Claude, Codex, etc.) can work on it effectively. Proper bootstrapping ensures:
|
||||
|
||||
1. Agents understand the project structure and conventions
|
||||
2. Orchestration works correctly with quality gates
|
||||
3. Independent code review and security review are configured
|
||||
4. Issue tracking is consistent across projects
|
||||
5. Documentation standards and API contracts are enforced from day one
|
||||
6. PRD requirements are established before coding begins
|
||||
7. Branching/merging is consistent: `branch -> main` via PR with squash-only merges
|
||||
8. Steered-autonomy execution is enabled so agents can run end-to-end with escalation-only human intervention
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Automated bootstrap (recommended)
|
||||
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||
--name "my-project" \
|
||||
--type "nestjs-nextjs" \
|
||||
--repo "https://git.mosaicstack.dev/owner/repo"
|
||||
|
||||
# Or manually using templates
|
||||
export PROJECT_NAME="My Project"
|
||||
export PROJECT_DESCRIPTION="What this project does"
|
||||
export TASK_PREFIX="MP"
|
||||
envsubst < ~/.config/mosaic/templates/agent/AGENTS.md.template > AGENTS.md
|
||||
envsubst < ~/.config/mosaic/templates/agent/CLAUDE.md.template > CLAUDE.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 0: Enforce Sequential-Thinking MCP (Hard Requirement)
|
||||
|
||||
`sequential-thinking` MCP must be installed and configured before project bootstrapping.
|
||||
|
||||
```bash
|
||||
# Auto-configure sequential-thinking MCP for installed runtimes
|
||||
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking
|
||||
|
||||
# Verification-only check
|
||||
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking --check
|
||||
```
|
||||
|
||||
If this step fails, STOP and remediate Mosaic runtime configuration before continuing.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Detect Project Type
|
||||
|
||||
Check what files exist in the project root to determine the type:
|
||||
|
||||
| File Present | Project Type | Template |
|
||||
| ------------------------------------------------------- | ------------------------- | ------------------------- |
|
||||
| `package.json` + `pnpm-workspace.yaml` + NestJS+Next.js | NestJS + Next.js Monorepo | `projects/nestjs-nextjs/` |
|
||||
| `pyproject.toml` + `manage.py` | Django | `projects/django/` |
|
||||
| `pyproject.toml` (no Django) | Python (generic) | Generic template |
|
||||
| `package.json` (no monorepo) | Node.js (generic) | Generic template |
|
||||
| Other | Generic | Generic template |
|
||||
|
||||
```bash
|
||||
# Auto-detect project type
|
||||
detect_project_type() {
|
||||
if [[ -f "pnpm-workspace.yaml" ]] && [[ -f "turbo.json" ]]; then
|
||||
# Check for NestJS + Next.js
|
||||
if grep -q "nestjs" package.json 2>/dev/null && grep -q "next" package.json 2>/dev/null; then
|
||||
echo "nestjs-nextjs"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [[ -f "manage.py" ]] && [[ -f "pyproject.toml" ]]; then
|
||||
echo "django"
|
||||
return
|
||||
fi
|
||||
if [[ -f "pyproject.toml" ]]; then
|
||||
echo "python"
|
||||
return
|
||||
fi
|
||||
if [[ -f "package.json" ]]; then
|
||||
echo "nodejs"
|
||||
return
|
||||
fi
|
||||
echo "generic"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create AGENTS.md (Primary Project Contract)
|
||||
|
||||
`AGENTS.md` is the primary project-level contract for all agent runtimes.
|
||||
It defines project-specific requirements, quality gates, patterns, and testing expectations.
|
||||
|
||||
### Using a Tech-Stack Template
|
||||
|
||||
```bash
|
||||
# Set variables
|
||||
export PROJECT_NAME="My Project"
|
||||
export PROJECT_DESCRIPTION="Multi-tenant SaaS platform"
|
||||
export PROJECT_DIR="my-project"
|
||||
export REPO_URL="https://git.mosaicstack.dev/owner/repo"
|
||||
export TASK_PREFIX="MP"
|
||||
|
||||
# Use tech-stack-specific template if available
|
||||
TYPE=$(detect_project_type)
|
||||
TEMPLATE_DIR="$HOME/.config/mosaic/templates/agent/projects/$TYPE"
|
||||
|
||||
if [[ -d "$TEMPLATE_DIR" ]]; then
|
||||
envsubst < "$TEMPLATE_DIR/AGENTS.md.template" > AGENTS.md
|
||||
else
|
||||
envsubst < "$HOME/.config/mosaic/templates/agent/AGENTS.md.template" > AGENTS.md
|
||||
fi
|
||||
```
|
||||
|
||||
### Using the Generic Template
|
||||
|
||||
```bash
|
||||
# Set all required variables
|
||||
export PROJECT_NAME="My Project"
|
||||
export PROJECT_DESCRIPTION="What this project does"
|
||||
export REPO_URL="https://git.mosaicstack.dev/owner/repo"
|
||||
export PROJECT_DIR="my-project"
|
||||
export SOURCE_DIR="src"
|
||||
export CONFIG_FILES="pyproject.toml / package.json"
|
||||
export FRONTEND_STACK="N/A"
|
||||
export BACKEND_STACK="Python / FastAPI"
|
||||
export DATABASE_STACK="PostgreSQL"
|
||||
export TESTING_STACK="pytest"
|
||||
export DEPLOYMENT_STACK="Docker"
|
||||
export BUILD_COMMAND="pip install -e ."
|
||||
export TEST_COMMAND="pytest tests/"
|
||||
export LINT_COMMAND="ruff check ."
|
||||
export TYPECHECK_COMMAND="mypy ."
|
||||
export QUALITY_GATES="ruff check . && mypy . && pytest tests/"
|
||||
|
||||
envsubst < ~/.config/mosaic/templates/agent/AGENTS.md.template > AGENTS.md
|
||||
```
|
||||
|
||||
### Required Sections
|
||||
|
||||
Every AGENTS.md should contain:
|
||||
|
||||
1. **Project description** — One-line summary
|
||||
2. **Quality gates** — Commands that must pass
|
||||
3. **Codebase patterns** — Reusable implementation rules
|
||||
4. **Common gotchas** — Non-obvious constraints
|
||||
5. **Testing approaches** — Project-specific test strategy
|
||||
6. **Testing policy** — Situational-first validation and risk-based TDD
|
||||
7. **Orchestrator integration** — Task prefix, worker checklist
|
||||
8. **Documentation contract** — Required documentation gates and update expectations
|
||||
9. **PRD requirement** — `docs/PRD.md` or `docs/PRD.json` required before coding
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Create Runtime Context File (Runtime-Specific)
|
||||
|
||||
Runtime context files are runtime adapters. They are not the primary project contract.
|
||||
Use `CLAUDE.md` for Claude runtime compatibility. Use other runtime adapters as required by your environment.
|
||||
|
||||
Claude runtime mandate (HARD RULE):
|
||||
|
||||
- `CLAUDE.md` MUST explicitly instruct Claude agents to read and use `AGENTS.md`.
|
||||
- `CLAUDE.md` MUST treat `AGENTS.md` as the authoritative project-level contract.
|
||||
- If `AGENTS.md` and runtime wording conflict, `AGENTS.md` project rules win.
|
||||
|
||||
```bash
|
||||
TYPE=$(detect_project_type)
|
||||
TEMPLATE_DIR="$HOME/.config/mosaic/templates/agent/projects/$TYPE"
|
||||
|
||||
if [[ -d "$TEMPLATE_DIR" ]]; then
|
||||
envsubst < "$TEMPLATE_DIR/CLAUDE.md.template" > CLAUDE.md
|
||||
else
|
||||
envsubst < "$HOME/.config/mosaic/templates/agent/CLAUDE.md.template" > CLAUDE.md
|
||||
fi
|
||||
```
|
||||
|
||||
### Required Runtime Sections
|
||||
|
||||
Every runtime context file should contain:
|
||||
|
||||
1. **AGENTS handoff rule** — Runtime MUST direct agents to read/use `AGENTS.md`
|
||||
2. **Conditional documentation loading** — Required guide loading map
|
||||
3. **Technology stack** — Runtime-facing architecture summary
|
||||
4. **Repository structure** — Important paths
|
||||
5. **Development workflow** — Build/test/lint/typecheck commands
|
||||
6. **Issue tracking** — Issue and commit conventions
|
||||
7. **Code review** — Required review process
|
||||
8. **Runtime notes** — Runtime-specific behavior references
|
||||
9. **Branch and merge policy** — Trunk workflow (`branch -> main` via PR, squash-only)
|
||||
10. **Autonomy and escalation policy** — Agent owns coding/review/PR/release/deploy lifecycle
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Create Directory Structure
|
||||
|
||||
```bash
|
||||
# Create standard directories
|
||||
mkdir -p docs/scratchpads
|
||||
mkdir -p docs/templates
|
||||
mkdir -p docs/reports/qa-automation/pending
|
||||
mkdir -p docs/reports/qa-automation/in-progress
|
||||
mkdir -p docs/reports/qa-automation/done
|
||||
mkdir -p docs/reports/qa-automation/escalated
|
||||
mkdir -p docs/reports/deferred
|
||||
mkdir -p docs/tasks
|
||||
mkdir -p docs/releases
|
||||
mkdir -p docs/USER-GUIDE docs/ADMIN-GUIDE docs/DEVELOPER-GUIDE docs/API
|
||||
|
||||
# Documentation baseline files
|
||||
touch docs/USER-GUIDE/README.md
|
||||
touch docs/ADMIN-GUIDE/README.md
|
||||
touch docs/DEVELOPER-GUIDE/README.md
|
||||
touch docs/API/OPENAPI.yaml
|
||||
touch docs/API/ENDPOINTS.md
|
||||
touch docs/SITEMAP.md
|
||||
|
||||
# PRD baseline file (requirements source before coding)
|
||||
cp ~/.config/mosaic/templates/docs/PRD.md.template docs/PRD.md
|
||||
|
||||
# TASKS baseline file (canonical tracking)
|
||||
cp ~/.config/mosaic/templates/docs/TASKS.md.template docs/TASKS.md
|
||||
|
||||
# Deployment baseline file (target/platform/runbook)
|
||||
touch docs/DEPLOYMENT.md
|
||||
```
|
||||
|
||||
Documentation root hygiene (HARD RULE):
|
||||
|
||||
- Keep `docs/` root clean.
|
||||
- Store reports in `docs/reports/`, archived task artifacts in `docs/tasks/`, releases in `docs/releases/`, and scratchpads in `docs/scratchpads/`.
|
||||
- Do not place ad-hoc report files directly under `docs/`.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Initialize Repository Labels & Milestones
|
||||
|
||||
```bash
|
||||
# Use the init script
|
||||
~/.config/mosaic/tools/bootstrap/init-repo-labels.sh
|
||||
|
||||
# Or manually create standard labels
|
||||
~/.config/mosaic/tools/git/issue-create.sh # (labels are created on first use)
|
||||
```
|
||||
|
||||
### Standard Labels
|
||||
|
||||
| Label | Color | Purpose |
|
||||
| --------------- | --------- | -------------------------------------- |
|
||||
| `epic` | `#3E4B9E` | Large feature spanning multiple issues |
|
||||
| `feature` | `#0E8A16` | New functionality |
|
||||
| `bug` | `#D73A4A` | Defect fix |
|
||||
| `task` | `#0075CA` | General work item |
|
||||
| `documentation` | `#0075CA` | Documentation updates |
|
||||
| `security` | `#B60205` | Security-related |
|
||||
| `breaking` | `#D93F0B` | Breaking change |
|
||||
|
||||
### Initial Milestone (Hard Rule)
|
||||
|
||||
Create the first pre-MVP milestone at `0.0.1`.
|
||||
Reserve `0.1.0` for the MVP release milestone.
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/git/milestone-create.sh -t "0.0.1" -d "Pre-MVP - Foundation Sprint"
|
||||
|
||||
# Create when MVP scope is complete and release-ready:
|
||||
~/.config/mosaic/tools/git/milestone-create.sh -t "0.1.0" -d "MVP - Minimum Viable Product"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5b: Configure Main Branch Protection (Hard Rule)
|
||||
|
||||
Apply equivalent settings in Gitea, GitHub, or GitLab:
|
||||
|
||||
1. Protect `main` from direct pushes.
|
||||
2. Require pull requests to merge into `main`.
|
||||
3. Require required CI/status checks to pass before merge.
|
||||
4. Require code review approval before merge.
|
||||
5. Allow **squash merge only** for PRs into `main` (disable merge commits and rebase merges for `main`).
|
||||
|
||||
This enforces one merge strategy across human and agent workflows.
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Set Up CI/CD Review Pipeline
|
||||
|
||||
### Woodpecker CI
|
||||
|
||||
```bash
|
||||
# Copy Codex review pipeline
|
||||
mkdir -p .woodpecker/schemas
|
||||
cp ~/.config/mosaic/tools/codex/woodpecker/codex-review.yml .woodpecker/
|
||||
cp ~/.config/mosaic/tools/codex/schemas/*.json .woodpecker/schemas/
|
||||
|
||||
# Add codex_api_key secret to Woodpecker CI dashboard
|
||||
```
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
For GitHub repos, use the official Codex GitHub Action instead:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/codex-review.yml
|
||||
uses: openai/codex-action@v1
|
||||
```
|
||||
|
||||
### Python Package Publishing (Gitea PyPI)
|
||||
|
||||
If the project publishes Python packages, use Gitea's PyPI registry.
|
||||
|
||||
```bash
|
||||
# Build and publish
|
||||
python -m pip install --upgrade build twine
|
||||
python -m build
|
||||
python -m twine upload \
|
||||
--repository-url "https://GITEA_HOST/api/packages/ORG/pypi" \
|
||||
--username "$GITEA_USERNAME" \
|
||||
--password "$GITEA_TOKEN" \
|
||||
dist/*
|
||||
```
|
||||
|
||||
Use the same `gitea_username` and `gitea_token` CI secrets used for container and npm publishing.
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Verify Bootstrap
|
||||
|
||||
After bootstrapping, verify everything works:
|
||||
|
||||
```bash
|
||||
# Check files exist
|
||||
ls AGENTS.md docs/scratchpads/
|
||||
ls docs/reports/qa-automation/pending docs/reports/deferred docs/tasks docs/releases
|
||||
ls docs/USER-GUIDE/README.md docs/ADMIN-GUIDE/README.md docs/DEVELOPER-GUIDE/README.md
|
||||
ls docs/API/OPENAPI.yaml docs/API/ENDPOINTS.md docs/SITEMAP.md
|
||||
ls docs/PRD.md
|
||||
ls docs/TASKS.md
|
||||
|
||||
# Verify AGENTS.md has required sections
|
||||
grep -c "Quality Gates" AGENTS.md
|
||||
grep -c "Orchestrator Integration" AGENTS.md
|
||||
grep -c "Testing Approaches" AGENTS.md
|
||||
grep -c "Testing Policy" AGENTS.md
|
||||
grep -c "Documentation Contract" AGENTS.md
|
||||
grep -c "PRD Requirement" AGENTS.md
|
||||
|
||||
# Verify runtime context file has required sections
|
||||
if [[ -f CLAUDE.md ]]; then
|
||||
grep -c "AGENTS.md" CLAUDE.md
|
||||
grep -c "Conditional Documentation Loading" CLAUDE.md
|
||||
grep -c "Technology Stack" CLAUDE.md
|
||||
grep -c "Code Review" CLAUDE.md
|
||||
elif [[ -f RUNTIME.md ]]; then
|
||||
grep -c "Conditional Documentation Loading" RUNTIME.md
|
||||
grep -c "Technology Stack" RUNTIME.md
|
||||
grep -c "Code Review" RUNTIME.md
|
||||
else
|
||||
echo "Missing runtime context file (CLAUDE.md or RUNTIME.md)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run quality gates from AGENTS.md
|
||||
# (execute the command block under "Quality Gates")
|
||||
|
||||
# Test Codex review (if configured)
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --help
|
||||
|
||||
# Verify sequential-thinking MCP remains configured
|
||||
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking --check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Available Templates
|
||||
|
||||
### Generic Templates
|
||||
|
||||
| Template | Path | Purpose |
|
||||
| ---------------------------- | ----------------------------------- | ------------------------------------------ |
|
||||
| `AGENTS.md.template` | `~/.config/mosaic/templates/agent/` | Primary project agent contract |
|
||||
| `CLAUDE.md.template` | `~/.config/mosaic/templates/agent/` | Runtime compatibility context (Claude) |
|
||||
| `DOCUMENTATION-CHECKLIST.md` | `~/.config/mosaic/templates/docs/` | Documentation completion gate |
|
||||
| `PRD.md.template` | `~/.config/mosaic/templates/docs/` | Requirements source template |
|
||||
| `TASKS.md.template` | `~/.config/mosaic/templates/docs/` | Canonical task and issue tracking template |
|
||||
|
||||
### Tech-Stack Templates
|
||||
|
||||
| Stack | Path | Includes |
|
||||
| ---------------- | ---------------------------------------------------------- | ------------------------------------ |
|
||||
| NestJS + Next.js | `~/.config/mosaic/templates/agent/projects/nestjs-nextjs/` | AGENTS.md + runtime context template |
|
||||
| Django | `~/.config/mosaic/templates/agent/projects/django/` | AGENTS.md + runtime context template |
|
||||
|
||||
### Orchestrator Templates
|
||||
|
||||
| Template | Path | Purpose |
|
||||
| -------------------------------------- | ------------------------------------------------- | ----------------------- |
|
||||
| `tasks.md.template` | `~/src/jarvis-brain/docs/templates/orchestrator/` | Task tracking |
|
||||
| `orchestrator-learnings.json.template` | `~/src/jarvis-brain/docs/templates/orchestrator/` | Variance tracking |
|
||||
| `phase-issue-body.md.template` | `~/src/jarvis-brain/docs/templates/orchestrator/` | Git provider issue body |
|
||||
| `scratchpad.md.template` | `~/src/jarvis-brain/docs/templates/` | Per-task working doc |
|
||||
|
||||
### Variables Reference
|
||||
|
||||
| Variable | Description | Example |
|
||||
| ------------------------ | --------------------------- | ------------------------------------------ |
|
||||
| `${PROJECT_NAME}` | Human-readable project name | "Mosaic Stack" |
|
||||
| `${PROJECT_DESCRIPTION}` | One-line description | "Multi-tenant platform" |
|
||||
| `${PROJECT_DIR}` | Directory name | "mosaic-stack" |
|
||||
| `${PROJECT_SLUG}` | Python package slug | "mosaic_stack" |
|
||||
| `${REPO_URL}` | Git remote URL | "https://git.mosaicstack.dev/mosaic/stack" |
|
||||
| `${TASK_PREFIX}` | Orchestrator task prefix | "MS" |
|
||||
| `${SOURCE_DIR}` | Source code directory | "src" or "apps" |
|
||||
| `${QUALITY_GATES}` | Quality gate commands | "pnpm typecheck && pnpm lint && pnpm test" |
|
||||
| `${BUILD_COMMAND}` | Build command | "pnpm build" |
|
||||
| `${TEST_COMMAND}` | Test command | "pnpm test" |
|
||||
| `${LINT_COMMAND}` | Lint command | "pnpm lint" |
|
||||
| `${TYPECHECK_COMMAND}` | Type check command | "pnpm typecheck" |
|
||||
| `${FRONTEND_STACK}` | Frontend technologies | "Next.js + React" |
|
||||
| `${BACKEND_STACK}` | Backend technologies | "NestJS + Prisma" |
|
||||
| `${DATABASE_STACK}` | Database technologies | "PostgreSQL" |
|
||||
| `${TESTING_STACK}` | Testing technologies | "Vitest + Playwright" |
|
||||
| `${DEPLOYMENT_STACK}` | Deployment technologies | "Docker" |
|
||||
| `${CONFIG_FILES}` | Key config files | "package.json, tsconfig.json" |
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap Scripts
|
||||
|
||||
### init-project.sh
|
||||
|
||||
Full project bootstrap with interactive and flag-based modes:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||
--name "My Project" \
|
||||
--type "nestjs-nextjs" \
|
||||
--repo "https://git.mosaicstack.dev/owner/repo" \
|
||||
--prefix "MP" \
|
||||
--description "Multi-tenant platform"
|
||||
```
|
||||
|
||||
### init-repo-labels.sh
|
||||
|
||||
Initialize standard labels and the first pre-MVP milestone:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/bootstrap/init-repo-labels.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist
|
||||
|
||||
After bootstrapping, verify:
|
||||
|
||||
- [ ] `AGENTS.md` exists and is the primary project contract
|
||||
- [ ] Runtime context file exists (`CLAUDE.md` or `RUNTIME.md`)
|
||||
- [ ] `docs/scratchpads/` directory exists
|
||||
- [ ] `docs/reports/qa-automation/pending` directory exists
|
||||
- [ ] `docs/reports/deferred/` directory exists
|
||||
- [ ] `docs/tasks/` directory exists
|
||||
- [ ] `docs/releases/` directory exists
|
||||
- [ ] `docs/USER-GUIDE/README.md` exists
|
||||
- [ ] `docs/ADMIN-GUIDE/README.md` exists
|
||||
- [ ] `docs/DEVELOPER-GUIDE/README.md` exists
|
||||
- [ ] `docs/API/OPENAPI.yaml` exists
|
||||
- [ ] `docs/API/ENDPOINTS.md` exists
|
||||
- [ ] `docs/SITEMAP.md` exists
|
||||
- [ ] `docs/PRD.md` or `docs/PRD.json` exists
|
||||
- [ ] `docs/TASKS.md` exists and is ready for active tracking
|
||||
- [ ] `docs/DEPLOYMENT.md` exists with target platform and rollback notes
|
||||
- [ ] `sequential-thinking` MCP is configured and verification check passes
|
||||
- [ ] Git labels created (epic, feature, bug, task, etc.)
|
||||
- [ ] Initial pre-MVP milestone created (0.0.1)
|
||||
- [ ] MVP milestone reserved for release (0.1.0)
|
||||
- [ ] `main` is protected from direct pushes
|
||||
- [ ] PRs into `main` are required
|
||||
- [ ] Merge method for `main` is squash-only
|
||||
- [ ] Quality gates run successfully
|
||||
- [ ] `.env.example` exists (if project uses env vars)
|
||||
- [ ] CI/CD pipeline configured (if using Woodpecker/GitHub Actions)
|
||||
- [ ] Python publish path configured in CI (if project ships Python packages)
|
||||
- [ ] Codex review scripts accessible (`~/.config/mosaic/tools/codex/`)
|
||||
1082
packages/mosaic/framework/guides/CI-CD-PIPELINES.md
Normal file
1082
packages/mosaic/framework/guides/CI-CD-PIPELINES.md
Normal file
File diff suppressed because it is too large
Load Diff
154
packages/mosaic/framework/guides/CODE-REVIEW.md
Executable file
154
packages/mosaic/framework/guides/CODE-REVIEW.md
Executable file
@@ -0,0 +1,154 @@
|
||||
# Code Review Guide
|
||||
|
||||
## Hard Requirement
|
||||
|
||||
If an agent modifies source code, code review is REQUIRED before completion.
|
||||
Do not mark code-change tasks done until review is completed and blockers are resolved or explicitly tracked.
|
||||
If code/config/API contract/auth behavior changed and required docs are missing, this is a BLOCKER.
|
||||
If tests pass but acceptance criteria are not verified by situational evidence, this is a BLOCKER.
|
||||
If implementation diverges from `docs/PRD.md` or `docs/PRD.json` without PRD updates, this is a BLOCKER.
|
||||
|
||||
Merge strategy enforcement (HARD RULE):
|
||||
|
||||
- PR target for delivery is `main`.
|
||||
- Direct pushes to `main` are prohibited.
|
||||
- Merge to `main` MUST be squash-only.
|
||||
- Use `~/.config/mosaic/tools/git/pr-merge.sh -n {PR_NUMBER} -m squash` (or PowerShell equivalent).
|
||||
|
||||
## Review Checklist
|
||||
|
||||
### 1. Correctness
|
||||
|
||||
- [ ] Code does what the issue/PR description says
|
||||
- [ ] Code aligns with active PRD requirements
|
||||
- [ ] Acceptance criteria are mapped to concrete verification evidence
|
||||
- [ ] Edge cases are handled
|
||||
- [ ] Error conditions are managed properly
|
||||
- [ ] No obvious bugs or logic errors
|
||||
|
||||
### 2. Security
|
||||
|
||||
- [ ] No hardcoded secrets or credentials
|
||||
- [ ] Input validation at boundaries
|
||||
- [ ] SQL injection prevention (parameterized queries)
|
||||
- [ ] XSS prevention (output encoding)
|
||||
- [ ] Authentication/authorization checks present
|
||||
- [ ] Sensitive data not logged
|
||||
- [ ] Secrets follow Vault structure (see `docs/vault-secrets-structure.md`)
|
||||
|
||||
### 2a. OWASP Coverage (Required)
|
||||
|
||||
- [ ] OWASP Top 10 categories were reviewed for change impact
|
||||
- [ ] Access control checks verified on protected actions
|
||||
- [ ] Cryptographic handling validated (keys, hashing, TLS assumptions)
|
||||
- [ ] Injection risks reviewed for all untrusted inputs
|
||||
- [ ] Security misconfiguration risks reviewed (headers, CORS, defaults)
|
||||
- [ ] Dependency/component risk reviewed (known vulnerable components)
|
||||
- [ ] Authentication/session flows reviewed for failure paths
|
||||
- [ ] Logging/monitoring preserves detection without leaking sensitive data
|
||||
|
||||
### 3. Testing
|
||||
|
||||
- [ ] Tests exist for new functionality
|
||||
- [ ] Tests cover happy path AND error cases
|
||||
- [ ] Situational tests cover all impacted change surfaces (primary gate)
|
||||
- [ ] Tests validate required behavior/outcomes, not only internal implementation details
|
||||
- [ ] TDD was applied when required by `~/.config/mosaic/guides/QA-TESTING.md`
|
||||
- [ ] Coverage meets 85% minimum
|
||||
- [ ] Tests are readable and maintainable
|
||||
- [ ] No flaky tests introduced
|
||||
|
||||
### 4. Code Quality
|
||||
|
||||
- [ ] Follows Google Style Guide for the language
|
||||
- [ ] Functions are focused and reasonably sized
|
||||
- [ ] No unnecessary complexity
|
||||
- [ ] DRY - no significant duplication
|
||||
- [ ] Clear naming for variables and functions
|
||||
- [ ] No dead code or commented-out code
|
||||
|
||||
### 4a. TypeScript Strict Typing (see `TYPESCRIPT.md`)
|
||||
|
||||
- [ ] **NO `any` types** — explicit types required everywhere
|
||||
- [ ] **NO lazy `unknown`** — only for error catches with immediate narrowing
|
||||
- [ ] **Explicit return types** on all exported/public functions
|
||||
- [ ] **Explicit parameter types** — never implicit any
|
||||
- [ ] **No type assertions** (`as Type`) — use type guards instead
|
||||
- [ ] **No non-null assertions** (`!`) — use proper null handling
|
||||
- [ ] **Interfaces for objects** — not inline types
|
||||
- [ ] **Discriminated unions** for variant types
|
||||
- [ ] **DTO files used at boundaries** — module/API contracts are in `*.dto.ts`, not inline payload types
|
||||
|
||||
### 5. Documentation
|
||||
|
||||
- [ ] Complex logic has explanatory comments
|
||||
- [ ] Required docs updated per `~/.config/mosaic/guides/DOCUMENTATION.md`
|
||||
- [ ] Public APIs are documented
|
||||
- [ ] Private/internal APIs are documented
|
||||
- [ ] API input/output schemas are documented
|
||||
- [ ] API permissions/auth requirements are documented
|
||||
- [ ] Site map updates are present when navigation changed
|
||||
- [ ] README updated if needed
|
||||
- [ ] Breaking changes noted
|
||||
|
||||
### 6. Performance
|
||||
|
||||
- [ ] No obvious N+1 queries
|
||||
- [ ] No blocking operations in hot paths
|
||||
- [ ] Resource cleanup (connections, file handles)
|
||||
- [ ] Reasonable memory usage
|
||||
|
||||
### 7. Dependencies
|
||||
|
||||
- [ ] No deprecated packages
|
||||
- [ ] No unnecessary new dependencies
|
||||
- [ ] Dependency versions pinned appropriately
|
||||
|
||||
## Review Process
|
||||
|
||||
Use `~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md` whenever code/API/auth/infra changes are present.
|
||||
|
||||
### Getting Context
|
||||
|
||||
```bash
|
||||
# List the issue being addressed
|
||||
~/.config/mosaic/tools/git/issue-list.sh -i {issue-number}
|
||||
|
||||
# View the changes
|
||||
git diff main...HEAD
|
||||
```
|
||||
|
||||
### Providing Feedback
|
||||
|
||||
- Be specific: point to exact lines/files
|
||||
- Explain WHY something is problematic
|
||||
- Suggest alternatives when possible
|
||||
- Distinguish between blocking issues and suggestions
|
||||
- Be constructive, not critical of the person
|
||||
|
||||
### Feedback Categories
|
||||
|
||||
- **Blocker**: Must fix before merge (security, bugs, test failures)
|
||||
- **Should Fix**: Important but not blocking (code quality, minor issues)
|
||||
- **Suggestion**: Optional improvements (style preferences, nice-to-haves)
|
||||
- **Question**: Seeking clarification
|
||||
|
||||
### Review Comment Format
|
||||
|
||||
```
|
||||
[BLOCKER] Line 42: SQL injection vulnerability
|
||||
The user input is directly interpolated into the query.
|
||||
Use parameterized queries instead:
|
||||
`db.query("SELECT * FROM users WHERE id = ?", [userId])`
|
||||
|
||||
[SUGGESTION] Line 78: Consider extracting to helper
|
||||
This pattern appears in 3 places. A shared helper would reduce duplication.
|
||||
```
|
||||
|
||||
## After Review
|
||||
|
||||
1. Update issue with review status
|
||||
2. If changes requested, assign back to author
|
||||
3. If approved, note approval in issue comments
|
||||
4. For merges, ensure CI passes first
|
||||
5. Merge PR to `main` with squash strategy only
|
||||
132
packages/mosaic/framework/guides/DOCUMENTATION.md
Normal file
132
packages/mosaic/framework/guides/DOCUMENTATION.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Documentation Standard (MANDATORY)
|
||||
|
||||
This guide defines REQUIRED documentation behavior for all Mosaic projects.
|
||||
If code, API contracts, auth, or infrastructure changes, documentation updates are REQUIRED before completion.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Documentation is a delivery gate. Missing required documentation is a BLOCKER.
|
||||
2. `docs/PRD.md` or `docs/PRD.json` is REQUIRED as the project requirements source before coding begins.
|
||||
3. API documentation is OpenAPI-first. `docs/API/OPENAPI.yaml` (or `.json`) is the canonical API contract.
|
||||
4. Public and private/internal endpoints MUST be documented.
|
||||
5. API input and output schemas MUST be documented.
|
||||
6. API authentication and permissions MUST be documented per endpoint.
|
||||
7. A current site map MUST exist at `docs/SITEMAP.md`.
|
||||
8. Documentation updates MUST be committed in the same logical change set as the code/API change.
|
||||
9. Generated publishing output (Docusaurus/VitePress/MkDocs artifacts) is not canonical unless the project explicitly declares it canonical.
|
||||
10. `docs/` root MUST stay clean. Reports and working artifacts MUST be stored in dedicated subdirectories, not dumped at `docs/` root.
|
||||
|
||||
## Required Documentation Structure
|
||||
|
||||
```text
|
||||
docs/
|
||||
PRD.md (or PRD.json)
|
||||
TASKS.md (active orchestrator tracking, when orchestrator is used)
|
||||
SITEMAP.md
|
||||
USER-GUIDE/
|
||||
ADMIN-GUIDE/
|
||||
DEVELOPER-GUIDE/
|
||||
API/
|
||||
OPENAPI.yaml
|
||||
ENDPOINTS.md
|
||||
scratchpads/
|
||||
reports/
|
||||
tasks/
|
||||
releases/
|
||||
templates/ (optional)
|
||||
```
|
||||
|
||||
Minimum requirements:
|
||||
|
||||
- `docs/PRD.md` or `docs/PRD.json`: authoritative requirements source for implementation and testing.
|
||||
- `docs/USER-GUIDE/`: End-user workflows, feature behavior, common troubleshooting.
|
||||
- `docs/ADMIN-GUIDE/`: Configuration, deployment, operations, incident/recovery procedures.
|
||||
- `docs/DEVELOPER-GUIDE/`: Architecture, local setup, contribution/testing workflow, design constraints.
|
||||
- `docs/API/OPENAPI.yaml`: API SSOT for all HTTP endpoints.
|
||||
- `docs/API/ENDPOINTS.md`: Human-readable index for API endpoints, permissions, and change notes.
|
||||
- `docs/SITEMAP.md`: Navigation index for all user/admin/developer/API documentation pages.
|
||||
- `docs/reports/`: Review outputs, QA automation reports, deferrals, and audit artifacts.
|
||||
- `docs/tasks/`: Archived task snapshots and orchestrator learnings.
|
||||
- `docs/releases/`: Release notes and release-specific documentation.
|
||||
- `docs/scratchpads/`: Active task-level working notes.
|
||||
|
||||
## Root Hygiene Rule (MANDATORY)
|
||||
|
||||
Allowed root documentation files are intentionally limited:
|
||||
|
||||
1. `docs/PRD.md` or `docs/PRD.json`
|
||||
2. `docs/TASKS.md` (active milestone only, when task orchestration is in use)
|
||||
3. `docs/SITEMAP.md`
|
||||
4. `docs/README.md` (optional index)
|
||||
|
||||
All other docs MUST be placed in scoped folders (`docs/reports/`, `docs/tasks/`, `docs/releases/`, `docs/scratchpads/`, `docs/API/`, guide books).
|
||||
|
||||
## Artifact Placement Rules
|
||||
|
||||
| Artifact Type | REQUIRED Location |
|
||||
| ------------------------------------------ | ---------------------------------------- |
|
||||
| Code review reports, QA reports, audits | `docs/reports/<category>/` |
|
||||
| Deferred error lists / unresolved findings | `docs/reports/deferred/` |
|
||||
| Archived milestone task snapshots | `docs/tasks/` |
|
||||
| Orchestrator learnings JSON | `docs/tasks/orchestrator-learnings.json` |
|
||||
| Release notes | `docs/releases/` |
|
||||
| Active scratchpads | `docs/scratchpads/` |
|
||||
|
||||
## API Documentation Contract (OpenAPI-First)
|
||||
|
||||
For every API endpoint, documentation MUST include:
|
||||
|
||||
1. visibility: `public` or `private/internal`
|
||||
2. method and path
|
||||
3. endpoint purpose
|
||||
4. request/input schema
|
||||
5. response/output schema(s)
|
||||
6. auth method and required permission/role/scope
|
||||
7. error status codes and behavior
|
||||
|
||||
If OpenAPI cannot fully express an internal constraint, document it in `docs/API/ENDPOINTS.md`.
|
||||
|
||||
## Book/Chapter/Page Structure
|
||||
|
||||
Use this structure for every guide:
|
||||
|
||||
1. Book: one root guide folder (`USER-GUIDE`, `ADMIN-GUIDE`, `DEVELOPER-GUIDE`)
|
||||
2. Chapter: one subdirectory per topic area
|
||||
3. Page: one focused markdown file per concern
|
||||
|
||||
Required index files:
|
||||
|
||||
1. `docs/USER-GUIDE/README.md`
|
||||
2. `docs/ADMIN-GUIDE/README.md`
|
||||
3. `docs/DEVELOPER-GUIDE/README.md`
|
||||
|
||||
Each index file MUST link to all chapters and pages in that book.
|
||||
|
||||
## Situational Documentation Matrix
|
||||
|
||||
| Change Surface | REQUIRED Documentation Updates |
|
||||
| ---------------------------------------------- | ----------------------------------------------------------- |
|
||||
| New feature or behavior change | User guide + developer guide + sitemap |
|
||||
| API endpoint added/changed/removed | OpenAPI + API endpoint index + sitemap |
|
||||
| Auth/RBAC/permission change | API auth/permission docs + admin guide + developer guide |
|
||||
| Database schema/migration change | Developer guide + admin operational notes if runbook impact |
|
||||
| CI/CD or deployment change | Admin guide + developer guide |
|
||||
| Incident, recovery, or security control change | Admin guide runbook + security notes + sitemap |
|
||||
|
||||
## Publishing Target Rule (MANDATORY)
|
||||
|
||||
If the user does not specify documentation publishing target, the agent MUST ask:
|
||||
|
||||
1. Publish in-app (embedded docs)
|
||||
2. Publish on external docs platform (for example: Docusaurus, VitePress, MkDocs)
|
||||
|
||||
Default behavior before publishing decision:
|
||||
|
||||
- Keep canonical docs in-repo under `docs/`.
|
||||
- Do not assume external publishing platform.
|
||||
|
||||
## Completion Gate
|
||||
|
||||
You MUST NOT declare completion until all required documentation updates are done.
|
||||
|
||||
Use `~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md` as the final gate.
|
||||
210
packages/mosaic/framework/guides/E2E-DELIVERY.md
Normal file
210
packages/mosaic/framework/guides/E2E-DELIVERY.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# E2E Delivery Procedure (MANDATORY)
|
||||
|
||||
This guide is REQUIRED for all agent sessions.
|
||||
|
||||
## 0. Mode Handshake (Before Any Action)
|
||||
|
||||
First response MUST declare mode before tool calls or implementation steps:
|
||||
|
||||
1. Orchestration mission: `Now initiating Orchestrator mode...`
|
||||
2. Implementation mission: `Now initiating Delivery mode...`
|
||||
3. Review-only mission: `Now initiating Review mode...`
|
||||
|
||||
## 1. PRD Gate (Before Coding)
|
||||
|
||||
1. Ensure `docs/PRD.md` or `docs/PRD.json` exists before coding.
|
||||
2. Load `~/.config/mosaic/guides/PRD.md`.
|
||||
3. Prepare/update PRD from user input and available project context.
|
||||
4. If requirements are missing:
|
||||
- proceed with best-guess assumptions by default,
|
||||
- mark each assumption with `ASSUMPTION:` and rationale,
|
||||
- escalate only when uncertainty is high-impact and cannot be bounded safely.
|
||||
5. Treat PRD as the requirement source for implementation, testing, and review.
|
||||
|
||||
## 1a. Tracking Gate (Before Coding)
|
||||
|
||||
1. For non-trivial work, `docs/TASKS.md` MUST exist before coding.
|
||||
2. If `docs/TASKS.md` is missing, create it from `~/.config/mosaic/templates/docs/TASKS.md.template`.
|
||||
3. Detect provider first via `~/.config/mosaic/tools/git/detect-platform.sh`.
|
||||
4. For issue/PR/milestone operations, use Mosaic wrappers first (`~/.config/mosaic/tools/git/*.sh`).
|
||||
5. If external git provider is available (Gitea/GitHub/GitLab), create or update issue(s) before coding.
|
||||
6. Record provider issue reference(s) in `docs/TASKS.md` (example: `#123`).
|
||||
7. If no external provider is available, use internal task refs in `docs/TASKS.md` (example: `TASKS:T1`).
|
||||
8. Scratchpad MUST reference both task ID and issue/internal ref.
|
||||
|
||||
## 2. Intake and Scope
|
||||
|
||||
> **COMPLEXITY TRAP WARNING:** Intake applies to ALL tasks regardless of perceived complexity. "Simple" tasks (commit, push, deploy) have caused the most severe framework violations because agents skip intake when they pattern-match a task as mechanical. The procedure is unconditional.
|
||||
|
||||
1. Define scope, constraints, and acceptance criteria.
|
||||
2. Identify affected surfaces (API, DB, UI, infra, auth, CI/CD, docs).
|
||||
3. **Deployment surface check (MANDATORY if task involves deploy, images, or containers):** Before ANY build or deploy action, check for CI/CD pipeline config (`.woodpecker/`, `.woodpecker.yml`, `.github/workflows/`). If pipelines exist, CI is the canonical build path — manual `docker build`/`docker push` is forbidden. Load `~/.config/mosaic/guides/CI-CD-PIPELINES.md` immediately.
|
||||
4. Identify required guides and load them before implementation.
|
||||
5. For code/API/auth/infra changes, load `~/.config/mosaic/guides/DOCUMENTATION.md`.
|
||||
6. Determine budget constraints:
|
||||
- if the user provided a plan limit or token budget, treat it as a HARD cap,
|
||||
- if budget is unknown, derive a working budget from estimates and runtime limits, then continue autonomously.
|
||||
7. Record budget assumptions and caps in the scratchpad before implementation starts.
|
||||
8. Track estimated vs used tokens per logical unit and adapt strategy to remain inside budget.
|
||||
9. If projected usage exceeds budget, auto-reduce scope/parallelism first; escalate only if cap still cannot be met.
|
||||
|
||||
## 2a. Steered Autonomy (Lights-Out)
|
||||
|
||||
1. Agent owns delivery end-to-end: planning, coding, testing, review, PR/repo operations, release/tag, and deployment (when in scope).
|
||||
2. Human intervention is escalation-only; do not pause for routine approvals or handoffs.
|
||||
3. Continue execution until completion criteria are met or an escalation trigger is hit.
|
||||
|
||||
## 3. Scratchpad Requirement
|
||||
|
||||
1. Create a task-specific scratchpad before implementation.
|
||||
2. Record:
|
||||
- objective
|
||||
- plan
|
||||
- progress checkpoints
|
||||
- tests run
|
||||
- risks/blockers
|
||||
- final verification evidence
|
||||
|
||||
## 4. Embedded Execution Cycle (MANDATORY)
|
||||
|
||||
For implementation work, you MUST run this cycle in order:
|
||||
|
||||
1. `plan` - map PRD requirements to concrete implementation steps.
|
||||
2. `code` - implement one logical unit.
|
||||
3. `test` - run required baseline and situational checks for that unit.
|
||||
4. `review` - perform independent code review on the current delta.
|
||||
5. `remediate` - fix all findings and any test failures.
|
||||
6. `review` - re-review remediated changes until blockers are cleared.
|
||||
7. `commit` - commit only when the logical unit passes tests and review.
|
||||
8. `pre-push queue guard` - before pushing, wait for running/queued project pipelines to clear: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push`.
|
||||
9. `push` - push immediately after queue guard passes.
|
||||
10. `PR integration` - if external git provider is available, create/update PR to `main` and merge with required strategy via Mosaic wrappers.
|
||||
11. `pre-merge queue guard` - before merging PR, wait for running/queued project pipelines to clear: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge`.
|
||||
12. `CI/pipeline verification` - wait for terminal CI status and require green before completion (`~/.config/mosaic/tools/git/pr-ci-wait.sh` for PR-based workflow).
|
||||
13. `issue closure` - close linked external issue (or close internal `docs/TASKS.md` task ref when provider is unavailable).
|
||||
14. `greenfield situational test` - validate required user flows in a clean environment/startup path (post-merge for trunk workflow changes).
|
||||
15. `deploy + post-deploy validation` - when deployment is in scope, deploy to configured target and run post-deploy health/smoke checks.
|
||||
16. `repeat` - continue until all acceptance criteria are complete.
|
||||
|
||||
### Post-PR Hard Gate (Execute Sequentially, No Exceptions)
|
||||
|
||||
1. `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge -B main`
|
||||
2. `~/.config/mosaic/tools/git/pr-merge.sh -n <PR_NUMBER> -m squash`
|
||||
3. `~/.config/mosaic/tools/git/pr-ci-wait.sh -n <PR_NUMBER>`
|
||||
4. `~/.config/mosaic/tools/git/issue-close.sh -i <ISSUE_NUMBER>` (or close internal `docs/TASKS.md` ref when no provider exists)
|
||||
5. If any step fails: set status `blocked`, report the exact failed wrapper command, and stop.
|
||||
6. Do not ask the human to perform routine merge/close operations.
|
||||
7. Do not claim completion before step 4 succeeds.
|
||||
|
||||
### Forbidden Anti-Patterns
|
||||
|
||||
**PR/Merge:**
|
||||
|
||||
1. Do NOT stop at "PR created" or "PR updated".
|
||||
2. Do NOT ask "should I merge?" for routine delivery PRs.
|
||||
3. Do NOT ask "should I close the issue?" after merge + green CI.
|
||||
|
||||
**Build/Deploy:** 4. Do NOT run `docker build` or `docker push` locally to deploy images when CI/CD pipelines exist in the repository. CI is the ONLY canonical build path. 5. Do NOT skip intake and surface identification because a task "seems simple." This is the #1 cause of framework violations. 6. Do NOT deploy without first verifying whether CI/CD pipelines exist (`.woodpecker/`, `.woodpecker.yml`, `.github/workflows/`). If they exist, use them. 7. If you are about to run `docker build` and have NOT loaded `ci-cd-pipelines.md`, STOP — you are violating the framework.
|
||||
|
||||
If any step fails, you MUST remediate and re-run from the relevant step before proceeding.
|
||||
If push-queue/merge-queue/PR merge/CI/issue closure fails, status is `blocked` (not complete) and you MUST report the exact failed wrapper command.
|
||||
|
||||
## 5. Testing Priority Model
|
||||
|
||||
Use this order of priority:
|
||||
|
||||
1. Situational tests are the PRIMARY gate and MUST prove changed behavior meets requirements.
|
||||
2. Baseline tests are REQUIRED safety checks and MUST run for all software changes.
|
||||
3. TDD is risk-based and REQUIRED only for specific high-risk change types.
|
||||
|
||||
## 6. Mandatory Test Baseline
|
||||
|
||||
For all software changes, you MUST run baseline checks applicable to the repo/toolchain:
|
||||
|
||||
1. lint (or equivalent static checks)
|
||||
2. type checks (if language/tooling supports it)
|
||||
3. unit tests for changed logic
|
||||
4. integration tests for changed boundaries
|
||||
|
||||
## 7. Situational Testing Matrix (PRIMARY GATE)
|
||||
|
||||
Run additional tests based on what changed:
|
||||
|
||||
| Change Surface | Required Situational Tests |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------- |
|
||||
| Authentication/authorization | auth failure-path tests, permission boundary tests, token/session validation |
|
||||
| Database schema/migrations | migration up/down validation, rollback safety, data integrity checks |
|
||||
| API contract changes | backward compatibility checks, consumer-impact tests, contract tests |
|
||||
| Frontend/UI workflow changes | end-to-end flow tests, accessibility sanity checks, state transition checks |
|
||||
| CI/CD or deployment changes | pipeline execution validation, artifact integrity checks, rollback path check |
|
||||
| Security-sensitive logic | abuse-case tests, input validation fuzzing/sanitization checks |
|
||||
| Performance-critical path | baseline comparison, regression threshold checks |
|
||||
|
||||
## 8. Risk-Based TDD Requirement
|
||||
|
||||
TDD is REQUIRED for:
|
||||
|
||||
1. bug fixes (write a reproducer test first)
|
||||
2. security/auth/permission logic changes
|
||||
3. critical business logic and data-mutation rules
|
||||
|
||||
TDD is RECOMMENDED (not mandatory) for low-risk UI, copy, styling, and mechanical refactors.
|
||||
If TDD is skipped for a non-required case, record the rationale in the scratchpad.
|
||||
|
||||
## 9. Mandatory Code Review Gate
|
||||
|
||||
If you modify source code, you MUST run an independent code review before completion.
|
||||
|
||||
1. Use automated review tooling when available.
|
||||
2. If automated tooling is unavailable, run manual review using `~/.config/mosaic/guides/CODE-REVIEW.md`.
|
||||
3. Any blocker or critical finding MUST be fixed or tracked as an explicit remediation task before closure.
|
||||
|
||||
## 10. Mandatory Documentation Gate
|
||||
|
||||
For code/API/auth/infra changes, documentation updates are REQUIRED before completion.
|
||||
|
||||
1. Apply the standard in `~/.config/mosaic/guides/DOCUMENTATION.md`.
|
||||
2. Update required docs in the same logical change set as implementation.
|
||||
3. Complete `~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md`.
|
||||
4. If publish platform is unspecified, ask the user to choose in-app or external platform before publishing.
|
||||
5. Missing required documentation is a BLOCKER.
|
||||
|
||||
## 11. Completion Gate (All Required)
|
||||
|
||||
You MUST satisfy all items before completion:
|
||||
|
||||
1. Acceptance criteria met.
|
||||
2. Baseline tests passed.
|
||||
3. Situational tests passed (primary gate), including required greenfield situational validation.
|
||||
4. PRD is current and implementation is aligned with PRD.
|
||||
5. Acceptance criteria mapped to verification evidence.
|
||||
6. Code review completed for source code changes.
|
||||
7. Required documentation updates completed and reviewed.
|
||||
8. Scratchpad updated with evidence.
|
||||
9. Known risks documented.
|
||||
10. No unresolved blocker hidden.
|
||||
11. If deployment is in scope, deployment target, release version, and post-deploy verification evidence are documented.
|
||||
12. `docs/TASKS.md` status and issue/internal references are updated to match delivered work.
|
||||
13. If source code changed and external provider is available: PR merged to `main` (squash), with merge evidence recorded.
|
||||
14. CI/pipeline status is terminal green for the merged PR/head commit.
|
||||
15. Linked external issue is closed (or internal task ref is closed when no provider exists).
|
||||
16. If any of items 13-15 fail due access/tooling, report `blocked` with exact failed wrapper command and do not claim completion.
|
||||
|
||||
## 12. Review and Reporting
|
||||
|
||||
Completion report MUST include:
|
||||
|
||||
1. what changed
|
||||
2. PRD alignment summary
|
||||
3. acceptance criteria to evidence mapping
|
||||
4. what was tested (baseline + situational)
|
||||
5. what was reviewed (code review scope)
|
||||
6. what documentation was updated
|
||||
7. command-level evidence summary
|
||||
8. residual risks
|
||||
9. deployment and post-deploy verification summary (if in scope)
|
||||
10. explicit pass/fail status
|
||||
11. tracking summary (`docs/TASKS.md` updates and issue/internal refs)
|
||||
12. PR lifecycle summary (PR number, merge commit, merge method)
|
||||
13. CI/pipeline summary (run/check URL, terminal status)
|
||||
14. issue closure summary (issue number/ref and close evidence)
|
||||
91
packages/mosaic/framework/guides/FRONTEND.md
Normal file
91
packages/mosaic/framework/guides/FRONTEND.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Frontend Development Guide
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue in git repo: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review existing components and patterns in the codebase
|
||||
|
||||
## Development Standards
|
||||
|
||||
### Framework Conventions
|
||||
|
||||
- Follow project's existing framework patterns (React, Vue, Svelte, etc.)
|
||||
- Use existing component library/design system if present
|
||||
- Maintain consistent file structure with existing code
|
||||
|
||||
### Styling
|
||||
|
||||
- Use project's established styling approach (CSS modules, Tailwind, styled-components, etc.)
|
||||
- Follow existing naming conventions for CSS classes
|
||||
- Ensure responsive design unless explicitly single-platform
|
||||
|
||||
### State Management
|
||||
|
||||
- Use project's existing state management solution
|
||||
- Keep component state local when possible
|
||||
- Document any new global state additions
|
||||
|
||||
### Accessibility
|
||||
|
||||
- Include proper ARIA labels
|
||||
- Ensure keyboard navigation works
|
||||
- Test with screen reader considerations
|
||||
- Maintain color contrast ratios (WCAG 2.1 AA minimum)
|
||||
|
||||
## Testing Requirements (TDD)
|
||||
|
||||
1. Write tests BEFORE implementation
|
||||
2. Minimum 85% coverage
|
||||
3. Test categories:
|
||||
- Unit tests for utility functions
|
||||
- Component tests for UI behavior
|
||||
- Integration tests for user flows
|
||||
|
||||
### Test Patterns
|
||||
|
||||
```javascript
|
||||
// Component test example structure
|
||||
describe('ComponentName', () => {
|
||||
it('renders without crashing', () => {});
|
||||
it('handles user interaction correctly', () => {});
|
||||
it('displays error states appropriately', () => {});
|
||||
it('is accessible', () => {});
|
||||
});
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow Google JavaScript/TypeScript Style Guide
|
||||
- **TypeScript: Follow `~/.config/mosaic/guides/TYPESCRIPT.md` — MANDATORY**
|
||||
- Use ESLint/Prettier configuration from project
|
||||
- Prefer functional components over class components (React)
|
||||
- TypeScript strict mode is REQUIRED, not optional
|
||||
|
||||
### TypeScript Quick Rules (see TYPESCRIPT.md for full guide)
|
||||
|
||||
- **NO `any`** — define explicit types always
|
||||
- **NO lazy `unknown`** — only for error catches and external data with validation
|
||||
- **Explicit return types** on all exported functions
|
||||
- **Explicit parameter types** always
|
||||
- **Interface for props** — never inline object types
|
||||
- **Event handlers** — use proper React event types
|
||||
|
||||
## Commit Format
|
||||
|
||||
```
|
||||
feat(#123): Add user profile component
|
||||
|
||||
- Implement avatar display
|
||||
- Add edit mode toggle
|
||||
- Include form validation
|
||||
|
||||
Refs #123
|
||||
```
|
||||
|
||||
## Before Completing
|
||||
|
||||
1. Run full test suite
|
||||
2. Verify build succeeds
|
||||
3. Update scratchpad with completion notes
|
||||
4. Reference issue in commit: `Fixes #N` or `Refs #N`
|
||||
339
packages/mosaic/framework/guides/INFRASTRUCTURE.md
Normal file
339
packages/mosaic/framework/guides/INFRASTRUCTURE.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# Infrastructure & DevOps Guide
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review existing infrastructure configuration
|
||||
|
||||
## Vault Secrets Management
|
||||
|
||||
**CRITICAL**: Follow canonical Vault structure for ALL secrets.
|
||||
|
||||
### Structure
|
||||
|
||||
```
|
||||
{mount}/{service}/{component}/{secret-name}
|
||||
|
||||
Examples:
|
||||
- secret-prod/postgres/database/app
|
||||
- secret-prod/redis/auth/default
|
||||
- secret-prod/authentik/admin/token
|
||||
```
|
||||
|
||||
### Environment Mounts
|
||||
|
||||
- `secret-dev/` - Development environment
|
||||
- `secret-staging/` - Staging environment
|
||||
- `secret-prod/` - Production environment
|
||||
|
||||
### Standard Field Names
|
||||
|
||||
- Credentials: `username`, `password`
|
||||
- Tokens: `token`
|
||||
- OAuth: `client_id`, `client_secret`
|
||||
- Connection strings: `url`, `host`, `port`
|
||||
|
||||
See `docs/vault-secrets-structure.md` for complete reference.
|
||||
|
||||
## Container Standards
|
||||
|
||||
### Dockerfile Best Practices
|
||||
|
||||
```dockerfile
|
||||
# Use specific version tags
|
||||
FROM node:20-alpine
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -S app && adduser -S app -G app
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependency files first (layer caching)
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy application code
|
||||
COPY --chown=app:app . .
|
||||
|
||||
# Switch to non-root user
|
||||
USER app
|
||||
|
||||
# Use exec form for CMD
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
### Container Security
|
||||
|
||||
- Use minimal base images (alpine, distroless)
|
||||
- Run as non-root user
|
||||
- Don't store secrets in images
|
||||
- Scan images for vulnerabilities
|
||||
- Pin dependency versions
|
||||
|
||||
## Kubernetes/Docker Compose
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Always set resource limits to prevent runaway containers:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: '128Mi'
|
||||
cpu: '100m'
|
||||
limits:
|
||||
memory: '256Mi'
|
||||
cpu: '500m'
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
```yaml
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 3
|
||||
```
|
||||
|
||||
## CI/CD Pipelines
|
||||
|
||||
### Pipeline Stages
|
||||
|
||||
1. **Lint**: Code style and static analysis
|
||||
2. **Test**: Unit and integration tests
|
||||
3. **Build**: Compile and package
|
||||
4. **Scan**: Security and vulnerability scanning
|
||||
5. **Deploy**: Environment-specific deployment
|
||||
|
||||
### Pipeline Security
|
||||
|
||||
- Use secrets management (not hardcoded)
|
||||
- Pin action/image versions
|
||||
- Implement approval gates for production
|
||||
- Audit pipeline access
|
||||
|
||||
## Steered-Autonomous Deployment (Hard Rule)
|
||||
|
||||
In lights-out mode, the agent owns deployment end-to-end when deployment is in scope.
|
||||
The human is escalation-only for missing access, hard policy conflicts, or irreversible risk.
|
||||
|
||||
### Deployment Target Selection
|
||||
|
||||
1. Use explicit target from `docs/PRD.md` / `docs/PRD.json` or `docs/DEPLOYMENT.md`.
|
||||
2. If unspecified, infer from existing project config/integration.
|
||||
3. If multiple targets exist, choose the target already wired in CI/CD and document rationale.
|
||||
|
||||
### Supported Targets
|
||||
|
||||
- **Portainer**: Deploy via `~/.config/mosaic/tools/portainer/stack-redeploy.sh`, then verify with `stack-status.sh`.
|
||||
- **Coolify**: Deploy via `~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>`, then verify with `service-status.sh`.
|
||||
- **Vercel**: Deploy via `vercel` CLI or connected Git integration, then verify preview/production URL health.
|
||||
- **Other SaaS providers**: Use provider CLI/API/runbook with the same validation and rollback gates.
|
||||
|
||||
### Coolify API Operations
|
||||
|
||||
```bash
|
||||
# List projects and services
|
||||
~/.config/mosaic/tools/coolify/project-list.sh
|
||||
~/.config/mosaic/tools/coolify/service-list.sh
|
||||
|
||||
# Check service status
|
||||
~/.config/mosaic/tools/coolify/service-status.sh -u <uuid>
|
||||
|
||||
# Set env vars (takes effect on next deploy)
|
||||
~/.config/mosaic/tools/coolify/env-set.sh -u <uuid> -k KEY -v VALUE
|
||||
|
||||
# Deploy
|
||||
~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>
|
||||
```
|
||||
|
||||
**Known Coolify Limitations:**
|
||||
|
||||
- FQDN updates on compose sub-apps not supported via API (DB workaround required)
|
||||
- Compose files must be base64-encoded in `docker_compose_raw` field
|
||||
- Magic variables (`SERVICE_FQDN_*`) require list-style env syntax, not dict-style
|
||||
- Rate limit: 200 requests per interval
|
||||
|
||||
### Cloudflare DNS Operations
|
||||
|
||||
Use the Cloudflare tools for any DNS configuration: pointing domains at services, adding TXT verification records, managing MX records, etc.
|
||||
|
||||
**Multi-instance support**: Credentials support named instances (e.g. `personal`, `work`). A `default` key in credentials.json determines which instance is used when `-a` is omitted. Pass `-a <instance>` to target a specific account.
|
||||
|
||||
```bash
|
||||
# List all zones (domains) in the account
|
||||
~/.config/mosaic/tools/cloudflare/zone-list.sh [-a instance]
|
||||
|
||||
# List DNS records for a zone (accepts zone name or ID)
|
||||
~/.config/mosaic/tools/cloudflare/record-list.sh -z <zone> [-t type] [-n name]
|
||||
|
||||
# Create a DNS record
|
||||
~/.config/mosaic/tools/cloudflare/record-create.sh -z <zone> -t <type> -n <name> -c <content> [-p] [-l ttl] [-P priority]
|
||||
|
||||
# Update a DNS record (requires record ID from record-list)
|
||||
~/.config/mosaic/tools/cloudflare/record-update.sh -z <zone> -r <record-id> -t <type> -n <name> -c <content> [-p]
|
||||
|
||||
# Delete a DNS record
|
||||
~/.config/mosaic/tools/cloudflare/record-delete.sh -z <zone> -r <record-id>
|
||||
```
|
||||
|
||||
**Flag reference:**
|
||||
|
||||
| Flag | Purpose |
|
||||
| ---- | ----------------------------------------------------------------------- |
|
||||
| `-z` | Zone name (e.g. `mosaicstack.dev`) or 32-char zone ID |
|
||||
| `-a` | Named Cloudflare instance (omit for default) |
|
||||
| `-t` | Record type: `A`, `AAAA`, `CNAME`, `MX`, `TXT`, `SRV`, etc. |
|
||||
| `-n` | Record name: short (`app`) or FQDN (`app.example.com`) |
|
||||
| `-c` | Record content/value (IP, hostname, TXT string, etc.) |
|
||||
| `-r` | Record ID (from `record-list.sh` output) |
|
||||
| `-p` | Enable Cloudflare proxy (orange cloud) — omit for DNS-only (grey cloud) |
|
||||
| `-l` | TTL in seconds (default: `1` = auto) |
|
||||
| `-P` | Priority for MX/SRV records |
|
||||
| `-f` | Output format: `table` (default) or `json` |
|
||||
|
||||
**Common workflows:**
|
||||
|
||||
```bash
|
||||
# Point a new subdomain at a server (proxied through Cloudflare)
|
||||
~/.config/mosaic/tools/cloudflare/record-create.sh \
|
||||
-z example.com -t A -n myapp -c 203.0.113.10 -p
|
||||
|
||||
# Add a TXT record for domain verification (never proxied)
|
||||
~/.config/mosaic/tools/cloudflare/record-create.sh \
|
||||
-z example.com -t TXT -n _verify -c "verification=abc123"
|
||||
|
||||
# Check what records exist before making changes
|
||||
~/.config/mosaic/tools/cloudflare/record-list.sh -z example.com -t CNAME
|
||||
|
||||
# Update an existing record (get record ID from record-list first)
|
||||
~/.config/mosaic/tools/cloudflare/record-update.sh \
|
||||
-z example.com -r <record-id> -t A -n myapp -c 10.0.0.5 -p
|
||||
```
|
||||
|
||||
**DNS + Deployment integration**: When deploying a new service via Coolify or Portainer that needs a public domain, the typical sequence is:
|
||||
|
||||
1. Create the DNS record pointing at the host IP (with `-p` for Cloudflare proxy if desired)
|
||||
2. Deploy the service via Coolify/Portainer
|
||||
3. Verify the domain resolves and the service is reachable
|
||||
|
||||
**Proxy (`-p`) guidance:**
|
||||
|
||||
- Use proxy (orange cloud) for web services — provides CDN, DDoS protection, and hides origin IP
|
||||
- Skip proxy (grey cloud) for non-HTTP services (mail, SSH), wildcard records, or when the service handles its own TLS termination and needs direct client IP visibility
|
||||
- Proxy is NOT compatible with non-standard ports outside Cloudflare's supported range
|
||||
|
||||
### Stack Health Check
|
||||
|
||||
Verify all infrastructure services are reachable:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/health/stack-health.sh
|
||||
```
|
||||
|
||||
### Image Tagging and Promotion (Hard Rule)
|
||||
|
||||
For containerized deployments:
|
||||
|
||||
1. Build immutable image tags: `sha-<shortsha>` and `v{base-version}-rc.{build}`.
|
||||
2. Use mutable environment tags only as pointers: `testing`, optional `staging`, and `prod`.
|
||||
3. Deploy by immutable digest, not by mutable tag alone.
|
||||
4. Promote the exact tested digest between environments (no rebuild between testing and prod).
|
||||
5. Do not use `latest` or `dev` as deployment references.
|
||||
|
||||
Blue-green is the default strategy for production promotion.
|
||||
Canary is allowed only when automated SLO/error-rate gates and auto-rollback triggers are implemented.
|
||||
|
||||
### Post-Deploy Validation (REQUIRED)
|
||||
|
||||
1. Health endpoints return expected status.
|
||||
2. Critical smoke tests pass in target environment.
|
||||
3. Running version and digest match the promoted release candidate.
|
||||
4. Observability signals (errors/latency) are within expected thresholds.
|
||||
|
||||
### Rollback Rule
|
||||
|
||||
If post-deploy validation fails:
|
||||
|
||||
1. Execute rollback/redeploy-safe path immediately.
|
||||
2. Mark deployment as blocked in `docs/TASKS.md`.
|
||||
3. Record failure evidence and next remediation step in scratchpad and release notes.
|
||||
|
||||
### Registry Retention and Cleanup
|
||||
|
||||
Cleanup MUST be automated.
|
||||
|
||||
- Keep all final release tags (`vX.Y.Z`) indefinitely.
|
||||
- Keep active environment digests (`prod`, `testing`, and active blue/green slots).
|
||||
- Keep recent RC tags (`vX.Y.Z-rc.N`) based on retention window.
|
||||
- Remove stale `sha-*` and RC tags outside retention window if they are not actively deployed.
|
||||
|
||||
## Monitoring & Logging
|
||||
|
||||
### Logging Standards
|
||||
|
||||
- Use structured logging (JSON)
|
||||
- Include correlation IDs
|
||||
- Log at appropriate levels (ERROR, WARN, INFO, DEBUG)
|
||||
- Never log sensitive data
|
||||
|
||||
### Metrics to Collect
|
||||
|
||||
- Request latency (p50, p95, p99)
|
||||
- Error rates
|
||||
- Resource utilization (CPU, memory)
|
||||
- Business metrics
|
||||
|
||||
### Alerting
|
||||
|
||||
- Define SLOs (Service Level Objectives)
|
||||
- Alert on symptoms, not causes
|
||||
- Include runbook links in alerts
|
||||
- Avoid alert fatigue
|
||||
|
||||
## Testing Infrastructure
|
||||
|
||||
### Test Categories
|
||||
|
||||
1. **Unit tests**: Terraform/Ansible logic
|
||||
2. **Integration tests**: Deployed resources work together
|
||||
3. **Smoke tests**: Critical paths after deployment
|
||||
4. **Chaos tests**: Failure mode validation
|
||||
|
||||
### Infrastructure Testing Tools
|
||||
|
||||
- Terraform: `terraform validate`, `terraform plan`
|
||||
- Ansible: `ansible-lint`, molecule
|
||||
- Kubernetes: `kubectl dry-run`, kubeval
|
||||
- General: Terratest, ServerSpec
|
||||
|
||||
## Commit Format
|
||||
|
||||
```
|
||||
chore(#67): Configure Redis cluster
|
||||
|
||||
- Add Redis StatefulSet with 3 replicas
|
||||
- Configure persistence with PVC
|
||||
- Add Vault secret for auth password
|
||||
|
||||
Refs #67
|
||||
```
|
||||
|
||||
## Before Completing
|
||||
|
||||
1. Validate configuration syntax
|
||||
2. Run infrastructure tests
|
||||
3. Test in dev/staging first
|
||||
4. Document any manual steps required
|
||||
5. Update scratchpad and close issue
|
||||
51
packages/mosaic/framework/guides/MEMORY.md
Normal file
51
packages/mosaic/framework/guides/MEMORY.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Memory and Retention Rules
|
||||
|
||||
## Primary Memory Layer: OpenBrain
|
||||
|
||||
**OpenBrain is the canonical shared memory for all Mosaic agents across all harnesses and sessions.**
|
||||
|
||||
Use the `capture` MCP tool (or REST `POST /v1/thoughts`) to store:
|
||||
|
||||
- Discovered gotchas and workarounds
|
||||
- Architectural decisions and rationale
|
||||
- Project state and context for handoffs
|
||||
- Anything a future agent should know
|
||||
|
||||
Use `search` or `recent` at session start to load prior context before acting.
|
||||
|
||||
This is not optional. An agent that uses local file-based memory instead of OpenBrain is a broken agent — its knowledge is invisible to every other agent on the platform.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Agent learnings MUST go to OpenBrain — not to any file-based memory location.
|
||||
2. You MUST NOT write to runtime-native memory silos (they are write-blocked by hook).
|
||||
3. Active execution state belongs in project `docs/` — not in memory files.
|
||||
4. `~/.config/mosaic/memory/` is for mosaic framework technical notes only, not project knowledge.
|
||||
|
||||
## Runtime-Native Memory Silos (WRITE-BLOCKED)
|
||||
|
||||
These locations are blocked by PreToolUse hooks. Attempting to write there fails at the tool level.
|
||||
|
||||
| Runtime | Blocked silo | Use instead |
|
||||
| ----------- | ---------------------------------- | ------------------- |
|
||||
| Claude Code | `~/.claude/projects/*/memory/*.md` | OpenBrain `capture` |
|
||||
| Codex | Runtime session memory | OpenBrain `capture` |
|
||||
| OpenCode | Runtime session memory | OpenBrain `capture` |
|
||||
|
||||
MEMORY.md files may only contain behavioral guardrails that must be injected at load-path — not knowledge.
|
||||
|
||||
## Project Continuity Files (MANDATORY)
|
||||
|
||||
| File | Purpose | Location |
|
||||
| -------------------------------- | ----------------------------------------- | --------------------------- |
|
||||
| `docs/PRD.md` or `docs/PRD.json` | Source of requirements | Project `docs/` |
|
||||
| `docs/TASKS.md` | Task tracking, milestones, issues, status | Project `docs/` |
|
||||
| `docs/scratchpads/<task>.md` | Task-specific working memory | Project `docs/scratchpads/` |
|
||||
| `AGENTS.md` | Project-local patterns and conventions | Project root |
|
||||
|
||||
## How the Block Works
|
||||
|
||||
`~/.config/mosaic/tools/qa/prevent-memory-write.sh` is registered as a `PreToolUse` hook in
|
||||
`~/.claude/settings.json`. It intercepts Write/Edit/MultiEdit calls and rejects any targeting
|
||||
`~/.claude/projects/*/memory/*.md` before the tool executes. Exit code 2 blocks the call and
|
||||
the agent sees a message directing it to OpenBrain instead.
|
||||
127
packages/mosaic/framework/guides/ORCHESTRATOR-LEARNINGS.md
Normal file
127
packages/mosaic/framework/guides/ORCHESTRATOR-LEARNINGS.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Orchestrator Learnings (Universal)
|
||||
|
||||
> Cross-project heuristic adjustments based on observed variance data.
|
||||
>
|
||||
> **Note:** This file contains generic patterns only. Project-specific evidence is stored in each project's `docs/tasks/orchestrator-learnings.json`.
|
||||
|
||||
## Task Type Multipliers
|
||||
|
||||
Apply these multipliers to base estimates from `ORCHESTRATOR.md`:
|
||||
|
||||
| Task Type | Base Estimate | Multiplier | Confidence | Samples | Last Updated |
|
||||
| --------------------- | ---------------- | ---------- | ---------- | ------- | ------------ |
|
||||
| STYLE_FIX | 3-5K | 0.64 | MEDIUM | n=1 | 2026-02-05 |
|
||||
| BULK_CLEANUP | file_count × 550 | 1.0 | MEDIUM | n=2 | 2026-02-05 |
|
||||
| GUARD_ADD | 5-8K | 1.0 | LOW | n=0 | - |
|
||||
| SECURITY_FIX | 8-12K | 2.5 | LOW | n=0 | - |
|
||||
| AUTH_ADD | 15-25K | 1.0 | HIGH | n=1 | 2026-02-05 |
|
||||
| REFACTOR | 10-15K | 1.0 | LOW | n=0 | - |
|
||||
| TEST_ADD | 15-25K | 1.0 | LOW | n=0 | - |
|
||||
| ERROR_HANDLING | 8-12K | 2.3 | MEDIUM | n=1 | 2026-02-05 |
|
||||
| CONFIG_DEFAULT_CHANGE | 5-10K | 1.8 | MEDIUM | n=1 | 2026-02-05 |
|
||||
| INPUT_VALIDATION | 5-8K | 1.7 | MEDIUM | n=1 | 2026-02-05 |
|
||||
|
||||
## Phase Factors
|
||||
|
||||
Apply to all estimates based on task position in milestone:
|
||||
|
||||
| Phase Position | Factor | Rationale |
|
||||
| ----------------- | ------ | -------------------------- |
|
||||
| Early (tasks 1-3) | 1.45 | Codebase learning overhead |
|
||||
| Mid (tasks 4-7) | 1.25 | Pattern recognition phase |
|
||||
| Late (tasks 8+) | 1.10 | Established patterns |
|
||||
|
||||
## Estimation Formula
|
||||
|
||||
```
|
||||
Final Estimate = Base Estimate × Type Multiplier × Phase Factor × TDD Overhead
|
||||
|
||||
Where:
|
||||
- Base Estimate: From ORCHESTRATOR.md task type table
|
||||
- Type Multiplier: From table above (default 1.0)
|
||||
- Phase Factor: 1.45 / 1.25 / 1.10 based on position
|
||||
- TDD Overhead: 1.20 if tests required
|
||||
```
|
||||
|
||||
## Known Patterns
|
||||
|
||||
### BULK_CLEANUP
|
||||
|
||||
**Pattern:** Multi-file cleanup tasks are severely underestimated.
|
||||
|
||||
**Why:** Iterative testing across many files, cascading fixes, and debugging compound the effort.
|
||||
|
||||
**Observed:** +112% to +276% variance when using fixed estimates.
|
||||
|
||||
**Recommendation:** Use `file_count × 550` instead of fixed estimate.
|
||||
|
||||
### ERROR_HANDLING
|
||||
|
||||
**Pattern:** Error handling changes that modify type interfaces cascade through the codebase.
|
||||
|
||||
**Why:** Adding fields to result types requires updating all callers, error messages, and tests.
|
||||
|
||||
**Observed:** +131% variance.
|
||||
|
||||
**Multiplier:** 2.3x base estimate when type interfaces are modified.
|
||||
|
||||
### CONFIG_DEFAULT_CHANGE
|
||||
|
||||
**Pattern:** Config default changes require more test coverage than expected.
|
||||
|
||||
**Why:** Security-sensitive defaults need validation tests, warning tests, and edge case coverage.
|
||||
|
||||
**Observed:** +80% variance.
|
||||
|
||||
**Multiplier:** 1.8x when config changes need security validation.
|
||||
|
||||
### INPUT_VALIDATION
|
||||
|
||||
**Pattern:** Security input validation with allowlists is more complex than simple validation.
|
||||
|
||||
**Why:** Comprehensive allowlists (e.g., OAuth error codes), encoding requirements, and security tests add up.
|
||||
|
||||
**Observed:** +70% variance.
|
||||
|
||||
**Multiplier:** 1.7x when security allowlists are involved.
|
||||
|
||||
### STYLE_FIX
|
||||
|
||||
**Pattern:** Pure formatting fixes are faster than estimated when isolated.
|
||||
|
||||
**Observed:** -36% variance.
|
||||
|
||||
**Multiplier:** 0.64x for isolated style-only fixes.
|
||||
|
||||
## Changelog
|
||||
|
||||
| Date | Change | Samples | Confidence |
|
||||
| ---------- | ------------------------------------------- | ------- | ---------- |
|
||||
| 2026-02-05 | Added BULK_CLEANUP category | n=2 | MEDIUM |
|
||||
| 2026-02-05 | Added STYLE_FIX multiplier 0.64 | n=1 | MEDIUM |
|
||||
| 2026-02-05 | Confirmed AUTH_ADD heuristic accurate | n=1 | HIGH |
|
||||
| 2026-02-05 | Added ERROR_HANDLING multiplier 2.3x | n=1 | MEDIUM |
|
||||
| 2026-02-05 | Added CONFIG_DEFAULT_CHANGE multiplier 1.8x | n=1 | MEDIUM |
|
||||
| 2026-02-05 | Added INPUT_VALIDATION multiplier 1.7x | n=1 | MEDIUM |
|
||||
|
||||
## Update Protocol
|
||||
|
||||
**Graduated Autonomy:**
|
||||
|
||||
| Phase | Condition | Action |
|
||||
| ---------------------- | ----------------------------------------- | -------------------------------------------- |
|
||||
| **Now** | All proposals | Human review required |
|
||||
| **After 3 milestones** | <30% change, n≥3 samples, HIGH confidence | Auto-update allowed |
|
||||
| **Mature** | All changes | Auto with notification, revert on regression |
|
||||
|
||||
**Validation Before Update:**
|
||||
|
||||
1. Minimum 3 samples for same task type
|
||||
2. Standard deviation < 30% of mean
|
||||
3. Outliers (>2σ) excluded
|
||||
4. New formula must not increase variance on historical data
|
||||
|
||||
## Where to Find Project-Specific Data
|
||||
|
||||
- **Project learnings:** `<project>/docs/tasks/orchestrator-learnings.json`
|
||||
- **Cross-project metrics:** `jarvis-brain/data/orchestrator-metrics.json`
|
||||
268
packages/mosaic/framework/guides/ORCHESTRATOR-PROTOCOL.md
Normal file
268
packages/mosaic/framework/guides/ORCHESTRATOR-PROTOCOL.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# Orchestrator Protocol — Mission Lifecycle Guide
|
||||
|
||||
> **Operational guide for agent sessions.** Distilled from the full specification at
|
||||
> `jarvis-brain/docs/protocols/ORCHESTRATOR-PROTOCOL.md` (1,066 lines).
|
||||
>
|
||||
> Load this guide when: active mission detected, multi-milestone orchestration, mission continuation.
|
||||
> Load `ORCHESTRATOR.md` for per-session execution protocol (planning, coding, review, commit cycle).
|
||||
|
||||
---
|
||||
|
||||
## 1. Relationship to ORCHESTRATOR.md
|
||||
|
||||
| Concern | Guide |
|
||||
| -------------------------------------------------------------------- | ----------------- |
|
||||
| How to execute within a session (plan, code, test, review, commit) | `ORCHESTRATOR.md` |
|
||||
| How to manage a mission across sessions (resume, continue, handoff) | **This guide** |
|
||||
| Both guides are active simultaneously during orchestration missions. |
|
||||
|
||||
---
|
||||
|
||||
## 2. Mission Manifest
|
||||
|
||||
**Location:** `docs/MISSION-MANIFEST.md`
|
||||
**Owner:** Orchestrator (sole writer)
|
||||
**Template:** `~/.config/mosaic/templates/docs/MISSION-MANIFEST.md.template`
|
||||
|
||||
The manifest is the persistent document tracking full mission scope, status, milestones, and session history. It survives session death.
|
||||
|
||||
### Update Rules
|
||||
|
||||
- Update **Phase** when transitioning (Intake → Planning → Execution → Continuation → Completion)
|
||||
- Update **Current Milestone** when starting a new milestone
|
||||
- Update **Progress** after each milestone completion
|
||||
- Append to **Session History** at session start and end
|
||||
- Update **Status** to `completed` only when ALL success criteria are verified
|
||||
|
||||
### Hard Rule
|
||||
|
||||
The manifest is the source of truth for mission scope. If the manifest says a milestone is done, it is done. If it says remaining, it remains.
|
||||
|
||||
---
|
||||
|
||||
## 3. Scratchpad Protocol
|
||||
|
||||
**Location:** `docs/scratchpads/{mission-id}.md`
|
||||
**Template:** `~/.config/mosaic/templates/docs/mission-scratchpad.md.template`
|
||||
|
||||
### Rules
|
||||
|
||||
1. **First action** — Before ANY planning or coding, write the mission prompt to the scratchpad
|
||||
2. **Append-only** — NEVER delete or overwrite previous entries
|
||||
3. **Session log** — Record session start, tasks done, and outcome at session end
|
||||
4. **Decisions** — Record all planning decisions with rationale
|
||||
5. **Corrections** — Record course corrections from human or coordinator
|
||||
6. **Never deleted** — Scratchpads survive mission completion (archival reference)
|
||||
|
||||
---
|
||||
|
||||
## 4. TASKS.md as Control Plane
|
||||
|
||||
**Location:** `docs/TASKS.md`
|
||||
**Owner:** Orchestrator (sole writer). Workers read but NEVER modify.
|
||||
|
||||
### Table Schema
|
||||
|
||||
```markdown
|
||||
| id | status | milestone | description | pr | notes |
|
||||
```
|
||||
|
||||
### Status Values
|
||||
|
||||
`not-started` → `in-progress` → `done` (or `blocked` / `failed`)
|
||||
|
||||
### Planning Tasks Are First-Class
|
||||
|
||||
Include explicit planning tasks (e.g., `PLAN-001: Break down milestone into tasks`). These count toward progress.
|
||||
|
||||
### Post-Merge Tasks Are Explicit
|
||||
|
||||
Include verification tasks after merge: CI check, deployment verification, Playwright test. Don't assume they happen automatically.
|
||||
|
||||
---
|
||||
|
||||
## 5. Session Resume Protocol
|
||||
|
||||
When starting a session and an active mission is detected, follow this checklist:
|
||||
|
||||
### Detection (5-point check)
|
||||
|
||||
1. `docs/MISSION-MANIFEST.md` exists → read Phase, Current Milestone, Progress
|
||||
2. `docs/scratchpads/*.md` exists → read latest scratchpad for decisions and corrections
|
||||
3. `docs/TASKS.md` exists → read task state (what's done, what's next)
|
||||
4. Git state → current branch, open PRs, recent commits
|
||||
5. Provider state → open issues, milestone status (if accessible)
|
||||
|
||||
### Resume Procedure
|
||||
|
||||
1. Read the mission manifest FIRST
|
||||
2. Read the scratchpad for session history and corrections
|
||||
3. Read TASKS.md for current task state
|
||||
4. Identify the next `not-started` or `in-progress` task
|
||||
5. Continue execution from that task
|
||||
6. Update Session History in the manifest
|
||||
|
||||
### Dirty State Recovery
|
||||
|
||||
| State | Recovery |
|
||||
| ------------------------ | ------------------------------------------------------------------- |
|
||||
| Dirty git working tree | Stash changes, log stash ref in scratchpad, resume clean |
|
||||
| Open PR in bad state | Check PR status, close if broken, re-create if needed |
|
||||
| Half-created issues | Audit issues against TASKS.md, reconcile |
|
||||
| Tasks marked in-progress | Check if work was committed; if so, mark done; if not, restart task |
|
||||
|
||||
### Hard Rule
|
||||
|
||||
Session state is NEVER automatically deleted. The coordinator (human or automated) must explicitly request cleanup.
|
||||
|
||||
---
|
||||
|
||||
## 6. Mission Continuation
|
||||
|
||||
When a milestone completes and more milestones remain:
|
||||
|
||||
### Agent Handoff (at ~55-60% context)
|
||||
|
||||
If context usage is high, produce a handoff message:
|
||||
|
||||
1. Update TASKS.md with final task statuses
|
||||
2. Update mission manifest with session results
|
||||
3. Append session summary to scratchpad
|
||||
4. Commit all state files
|
||||
5. The coordinator will generate a continuation prompt for the next session
|
||||
|
||||
### Continuation Prompt and Capsule Format
|
||||
|
||||
The coordinator generates this (via `mosaic coord continue`) and writes a machine-readable capsule at `.mosaic/orchestrator/next-task.json`:
|
||||
|
||||
```
|
||||
## Continuation Mission
|
||||
Continue **{mission}** from existing state.
|
||||
- Read docs/MISSION-MANIFEST.md for scope and status
|
||||
- Read docs/scratchpads/{id}.md for decisions
|
||||
- Read docs/TASKS.md for current state
|
||||
- Continue from task {next-task-id}
|
||||
```
|
||||
|
||||
### Between Sessions (r0 manual)
|
||||
|
||||
1. Agent stops (expected — this is the confirmed stamina limitation)
|
||||
2. Human runs `mosaic coord mission` to check status
|
||||
3. Human runs `mosaic coord continue` to generate continuation prompt
|
||||
4. Human launches new session and pastes the prompt
|
||||
5. New agent reads manifest, scratchpad, TASKS.md and continues
|
||||
|
||||
### Between Sessions (r0 assisted)
|
||||
|
||||
Use `mosaic coord run` to remove copy/paste steps:
|
||||
|
||||
1. Agent stops
|
||||
2. Human runs `mosaic coord run [--claude|--codex]`
|
||||
3. Coordinator regenerates continuation prompt + `next-task.json`
|
||||
4. Coordinator launches selected runtime with scoped kickoff context
|
||||
5. New session resumes from next task
|
||||
|
||||
---
|
||||
|
||||
## 7. Failure Taxonomy Quick Reference
|
||||
|
||||
| Code | Type | Recovery |
|
||||
| ---- | ---------------------- | ----------------------------------------------------- |
|
||||
| F1 | Premature Stop | Continuation prompt → new session (most common) |
|
||||
| F2 | Context Exhaustion | Handoff message → new session |
|
||||
| F3 | Session Crash | Check git state → `mosaic coord resume` → new session |
|
||||
| F4 | Error Spiral | Kill session, mark task blocked, skip to next |
|
||||
| F5 | Quality Gate Failure | Create QA remediation task |
|
||||
| F6 | Infrastructure Failure | Pause, retry when service recovers |
|
||||
| F7 | False Completion | Append correction to scratchpad, relaunch |
|
||||
| F8 | Scope Drift | Kill session, relaunch with scratchpad ref |
|
||||
| F9 | Subagent Failure | Orchestrator retries or creates remediation |
|
||||
| F10 | Deadlock | Escalate to human |
|
||||
|
||||
### F1: Premature Stop — Detailed Recovery
|
||||
|
||||
This is the confirmed, most common failure. Every session will eventually trigger F1.
|
||||
|
||||
1. Session ends with tasks remaining in TASKS.md
|
||||
2. Run `mosaic coord mission` — verify milestone status
|
||||
3. If milestone complete: verify CI green, deployed, issues closed
|
||||
4. Run `mosaic coord continue` — generates scoped continuation prompt
|
||||
5. Launch new session, paste prompt
|
||||
6. New session reads state and continues from next pending task
|
||||
|
||||
---
|
||||
|
||||
## 8. r0 Manual Coordinator Process
|
||||
|
||||
In r0, the Coordinator is Jason + shell scripts. No daemon. No automation.
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Purpose |
|
||||
| --------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------ |
|
||||
| `mosaic coord init --name "..." --milestones "..."` | Initialize a new mission |
|
||||
| `mosaic coord mission` | Show mission progress dashboard |
|
||||
| `mosaic coord status` | Check if agent session is still running |
|
||||
| `mosaic coord continue` | Generate continuation prompt for next session |
|
||||
| `mosaic coord run [--claude | --codex]` | Generate continuation context and launch runtime |
|
||||
| `mosaic coord resume` | Crash recovery (detect dirty state, generate fix) |
|
||||
| `mosaic coord resume --clean-lock` | Clear stale session lock after review |
|
||||
|
||||
### Typical Workflow
|
||||
|
||||
```
|
||||
init → launch agent → [agent works] → agent stops →
|
||||
status → mission → run → repeat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Operational Checklist
|
||||
|
||||
### Pre-Mission
|
||||
|
||||
- [ ] Mission initialized: `mosaic coord init`
|
||||
- [ ] docs/MISSION-MANIFEST.md exists with scope and milestones
|
||||
- [ ] docs/TASKS.md scaffolded
|
||||
- [ ] docs/scratchpads/{id}.md scaffolded
|
||||
- [ ] Success criteria defined in manifest
|
||||
|
||||
### Session Start
|
||||
|
||||
- [ ] Read manifest → know phase, milestone, progress
|
||||
- [ ] Read scratchpad → know decisions, corrections, history
|
||||
- [ ] Read TASKS.md → know what's done and what's next
|
||||
- [ ] Write session start to scratchpad
|
||||
- [ ] Update Session History in manifest
|
||||
|
||||
### Planning Gate (Hard Gate — No Coding Until Complete)
|
||||
|
||||
- [ ] Milestones created in provider (Gitea/GitHub)
|
||||
- [ ] Issues created for all milestone tasks
|
||||
- [ ] TASKS.md populated with all planned tasks (including planning + verification tasks)
|
||||
- [ ] All planning artifacts committed and pushed
|
||||
|
||||
### Per-Task
|
||||
|
||||
- [ ] Update task status to `in-progress` in TASKS.md
|
||||
- [ ] Execute task following ORCHESTRATOR.md cycle
|
||||
- [ ] Update task status to `done` (or `blocked`/`failed`)
|
||||
- [ ] Commit, push
|
||||
|
||||
### Milestone Completion
|
||||
|
||||
- [ ] All milestone tasks in TASKS.md are `done`
|
||||
- [ ] CI/pipeline green
|
||||
- [ ] PR merged to `main`
|
||||
- [ ] Issues closed
|
||||
- [ ] Update manifest: milestone status → completed
|
||||
- [ ] Update scratchpad: session log entry
|
||||
- [ ] If deployment target: verify accessible
|
||||
|
||||
### Mission Completion
|
||||
|
||||
- [ ] ALL milestones completed
|
||||
- [ ] ALL success criteria verified with evidence
|
||||
- [ ] manifest status → completed
|
||||
- [ ] Final scratchpad entry with completion evidence
|
||||
- [ ] Release tag created and pushed (if applicable)
|
||||
1175
packages/mosaic/framework/guides/ORCHESTRATOR.md
Normal file
1175
packages/mosaic/framework/guides/ORCHESTRATOR.md
Normal file
File diff suppressed because it is too large
Load Diff
63
packages/mosaic/framework/guides/PRD.md
Normal file
63
packages/mosaic/framework/guides/PRD.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# PRD Requirement Guide (MANDATORY)
|
||||
|
||||
This guide defines how requirements are captured before coding.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Before coding begins, `docs/PRD.md` or `docs/PRD.json` MUST exist.
|
||||
2. The PRD is the authoritative requirements source for implementation and testing.
|
||||
3. The main agent MUST prepare or update the PRD using user input and available project context before implementation starts.
|
||||
4. The agent MUST NOT invent requirements silently.
|
||||
5. In steered autonomy mode, best-guess decisions are REQUIRED when needed; each guessed decision MUST be marked with `ASSUMPTION:` and rationale.
|
||||
|
||||
## PRD Format
|
||||
|
||||
Allowed canonical formats:
|
||||
|
||||
1. `docs/PRD.md`
|
||||
2. `docs/PRD.json`
|
||||
|
||||
Either format is valid. Both may exist if one is a transformed representation of the other.
|
||||
For markdown PRDs, start from `~/.config/mosaic/templates/docs/PRD.md.template`.
|
||||
|
||||
## Best-Guess Mode
|
||||
|
||||
Steered autonomy is the default operating mode.
|
||||
|
||||
1. Agent SHOULD fill missing decisions in the PRD without waiting for routine confirmation.
|
||||
2. Agent MUST mark each guessed decision with `ASSUMPTION:` and rationale.
|
||||
3. If user explicitly requests strict-confirmation mode, the agent MUST ask before unresolved decisions are finalized.
|
||||
4. For high-impact security/compliance/release uncertainty, escalate only if the decision cannot be safely constrained with rollback-ready defaults.
|
||||
|
||||
## Minimum PRD Content
|
||||
|
||||
Every PRD MUST include:
|
||||
|
||||
1. Problem statement and objective
|
||||
2. In-scope and out-of-scope
|
||||
3. User/stakeholder requirements
|
||||
4. Functional requirements
|
||||
5. Non-functional requirements (security, performance, reliability, observability)
|
||||
6. Acceptance criteria
|
||||
7. Constraints and dependencies
|
||||
8. Risks and open questions
|
||||
9. Testing and verification expectations
|
||||
10. Delivery/milestone intent
|
||||
|
||||
## Pre-Coding Gate
|
||||
|
||||
Coding MUST NOT begin until:
|
||||
|
||||
1. PRD file exists (`docs/PRD.md` or `docs/PRD.json`)
|
||||
2. PRD has required sections
|
||||
3. Unresolved decisions are captured as explicit `ASSUMPTION:` entries with rationale and planned validation
|
||||
|
||||
## Change Control
|
||||
|
||||
When requirements materially change:
|
||||
|
||||
1. Update PRD first.
|
||||
2. Then update implementation plan/tasks.
|
||||
3. Then implement code changes.
|
||||
|
||||
Implementation that diverges from PRD without PRD updates is a blocker.
|
||||
125
packages/mosaic/framework/guides/QA-TESTING.md
Normal file
125
packages/mosaic/framework/guides/QA-TESTING.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# QA & Testing Guide
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review `docs/PRD.md` or `docs/PRD.json` as the requirements source.
|
||||
4. Review acceptance criteria and affected change surfaces.
|
||||
|
||||
## Testing Policy (Hard Rules)
|
||||
|
||||
1. Situational testing is the PRIMARY validation gate.
|
||||
2. Baseline testing is REQUIRED for all software changes.
|
||||
3. TDD is risk-based and REQUIRED only for defined high-risk change types.
|
||||
4. Tests MUST validate requirements and behavior, not only internal implementation details.
|
||||
|
||||
## Priority Order
|
||||
|
||||
1. Situational tests: prove requirements and real behavior on changed surfaces.
|
||||
2. Baseline tests: lint/type/unit/integration safety checks.
|
||||
3. TDD discipline: applied where risk justifies test-first workflow.
|
||||
|
||||
## Risk-Based TDD Requirement
|
||||
|
||||
| Change Type | TDD Requirement | Required Action |
|
||||
| ---------------------------------------------- | --------------- | ---------------------------------------------------------------------- |
|
||||
| Bug fix | REQUIRED | Write a failing reproducer test first, then fix. |
|
||||
| Security/auth/permission logic | REQUIRED | Write failing security/permission-path test first. |
|
||||
| Critical business logic or data mutation rules | REQUIRED | Write failing rule/invariant test first. |
|
||||
| API behavior regression | REQUIRED | Write failing contract/behavior test first. |
|
||||
| Low-risk UI copy/style/layout | OPTIONAL | Add verification tests as appropriate; TDD recommended, not mandatory. |
|
||||
| Mechanical refactor with unchanged behavior | OPTIONAL | Ensure regression/smoke coverage and situational evidence. |
|
||||
|
||||
If TDD is not required and skipped, record rationale in scratchpad.
|
||||
If TDD is required and skipped, task is NOT complete.
|
||||
|
||||
## Baseline Test Requirements
|
||||
|
||||
For all software changes, run baseline checks applicable to the repo:
|
||||
|
||||
1. lint/static checks
|
||||
2. type checks
|
||||
3. unit tests for changed logic
|
||||
4. integration tests for changed boundaries
|
||||
|
||||
## Situational Testing Matrix (Primary Gate)
|
||||
|
||||
| Change Surface | Required Situational Tests |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------- |
|
||||
| Authentication/authorization | auth failure-path tests, permission boundary tests, token/session validation |
|
||||
| Database schema/migrations | migration up/down validation, rollback safety, data integrity checks |
|
||||
| API contract changes | backward compatibility checks, consumer-impact tests, contract tests |
|
||||
| Frontend/UI workflow changes | end-to-end flow tests, accessibility sanity checks, state transition checks |
|
||||
| CI/CD or deployment changes | pipeline execution validation, artifact integrity checks, rollback path check |
|
||||
| Security-sensitive logic | abuse-case tests, input validation fuzzing/sanitization checks |
|
||||
| Performance-critical path | baseline comparison, regression threshold checks |
|
||||
|
||||
## Coverage Requirements
|
||||
|
||||
### Minimum Standards
|
||||
|
||||
- Overall Coverage: 85% minimum
|
||||
- Critical Paths: 95% minimum (auth, payments, data mutations)
|
||||
- New Code: 90% minimum
|
||||
|
||||
Coverage is necessary but NOT sufficient. Passing coverage does not replace situational verification.
|
||||
|
||||
## Requirements-to-Evidence Mapping (Mandatory)
|
||||
|
||||
Before completion, map each acceptance criterion to concrete evidence.
|
||||
Acceptance criteria MUST come from the active PRD.
|
||||
|
||||
Template:
|
||||
|
||||
```markdown
|
||||
| Acceptance Criterion | Verification Method | Evidence |
|
||||
| -------------------- | ------------------------------------------------------ | ---------------- |
|
||||
| AC-1: ... | Situational test / baseline test / manual verification | command + result |
|
||||
| AC-2: ... | ... | ... |
|
||||
```
|
||||
|
||||
## Browser Automation (Hard Rule)
|
||||
|
||||
All browser automation (Playwright, Cypress, Puppeteer) MUST run in **headless mode**.
|
||||
Launching a visible browser collides with the user's display and active session.
|
||||
|
||||
- Playwright: use `headless: true` in config or `--headed` must NOT be passed
|
||||
- Cypress: use `cypress run` (headless by default), never `cypress open`
|
||||
- Puppeteer: use `headless: true` (default)
|
||||
|
||||
If a project's `playwright.config.ts` does not explicitly set `headless: true`, add it before running tests.
|
||||
|
||||
## Test Quality Rules
|
||||
|
||||
1. Test behavior and outcomes, not private implementation details.
|
||||
2. Include failure-path and edge-case assertions for changed behavior.
|
||||
3. Keep tests deterministic; no new flaky tests.
|
||||
4. Keep tests isolated; no dependency on execution order.
|
||||
|
||||
## Anti-Gaming Rules
|
||||
|
||||
1. Do NOT stop at "tests pass" if acceptance criteria are not verified.
|
||||
2. Do NOT write narrow tests that only satisfy assertions while missing real workflow behavior.
|
||||
3. Do NOT claim completion without situational evidence for impacted surfaces.
|
||||
|
||||
## Reporting
|
||||
|
||||
QA report MUST include:
|
||||
|
||||
1. baseline tests run and outcomes
|
||||
2. situational tests run and outcomes
|
||||
3. TDD usage decision (required/applied or optional/skipped with rationale)
|
||||
4. acceptance-criteria-to-evidence mapping
|
||||
5. coverage results
|
||||
6. residual risk notes
|
||||
|
||||
## Before Completing
|
||||
|
||||
1. Baseline tests pass.
|
||||
2. Required situational tests pass.
|
||||
3. TDD obligations met for required change types.
|
||||
4. Acceptance criteria mapped to evidence.
|
||||
5. No flaky tests introduced.
|
||||
6. CI pipeline passes (if available).
|
||||
7. Scratchpad updated with results.
|
||||
440
packages/mosaic/framework/guides/TYPESCRIPT.md
Normal file
440
packages/mosaic/framework/guides/TYPESCRIPT.md
Normal file
@@ -0,0 +1,440 @@
|
||||
# TypeScript Style Guide
|
||||
|
||||
**Authority**: This guide is MANDATORY for all TypeScript code. No exceptions without explicit approval.
|
||||
|
||||
Based on Google TypeScript Style Guide with stricter enforcement.
|
||||
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Explicit over implicit** — Always declare types, never rely on inference for public APIs
|
||||
2. **Specific over generic** — Use the narrowest type that works
|
||||
3. **Safe over convenient** — Type safety is not negotiable
|
||||
4. **Contract-first boundaries** — Cross-module and API payloads MUST use dedicated DTO files
|
||||
|
||||
---
|
||||
|
||||
## DTO Contract (MANDATORY)
|
||||
|
||||
DTO files are REQUIRED for TypeScript module boundaries to preserve shared context and consistency.
|
||||
|
||||
Hard requirements:
|
||||
|
||||
1. Input and output payloads crossing module boundaries MUST be defined in `*.dto.ts` files.
|
||||
2. Controller/service boundary payloads MUST use DTO types; inline object literal types are NOT allowed.
|
||||
3. Public API request/response contracts MUST use DTO files and remain stable across modules.
|
||||
4. Shared DTOs used by multiple modules MUST live in a shared location (for example `src/shared/dto/` or `packages/shared/dto/`).
|
||||
5. ORM/entity models MUST NOT be exposed directly across module boundaries; map them to DTOs.
|
||||
6. DTO changes MUST be reflected in tests and documentation when contracts change.
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG: inline payload contract at boundary
|
||||
export function createUser(payload: { email: string; role: string }): Promise<User> {}
|
||||
|
||||
// ✅ CORRECT: dedicated DTO file contract
|
||||
// user-create.dto.ts
|
||||
export interface UserCreateDto {
|
||||
email: string;
|
||||
role: UserRole;
|
||||
}
|
||||
|
||||
// user-response.dto.ts
|
||||
export interface UserResponseDto {
|
||||
id: string;
|
||||
email: string;
|
||||
role: UserRole;
|
||||
}
|
||||
|
||||
// service.ts
|
||||
export function createUser(payload: UserCreateDto): Promise<UserResponseDto> {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Forbidden Patterns (NEVER USE)
|
||||
|
||||
### `any` Type — FORBIDDEN
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER
|
||||
function process(data: any) {}
|
||||
const result: any = fetchData();
|
||||
Record<string, any>;
|
||||
|
||||
// ✅ ALWAYS define explicit types
|
||||
interface UserData {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
function process(data: UserData) {}
|
||||
```
|
||||
|
||||
### `unknown` as Lazy Typing — FORBIDDEN
|
||||
|
||||
`unknown` is only acceptable in these specific cases:
|
||||
|
||||
1. Error catch blocks (then immediately narrow)
|
||||
2. JSON.parse results (then validate with Zod/schema)
|
||||
3. External API responses before validation
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER - using unknown to avoid typing
|
||||
function getData(): unknown {}
|
||||
const config: Record<string, unknown> = {};
|
||||
|
||||
// ✅ ACCEPTABLE - error handling with immediate narrowing
|
||||
try {
|
||||
riskyOperation();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
logger.error(error.message);
|
||||
} else {
|
||||
logger.error('Unknown error', { error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ ACCEPTABLE - external data with validation
|
||||
const raw: unknown = JSON.parse(response);
|
||||
const validated = UserSchema.parse(raw); // Zod validation
|
||||
```
|
||||
|
||||
### Implicit `any` — FORBIDDEN
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER - implicit any from missing types
|
||||
function process(data) {} // Parameter has implicit any
|
||||
const handler = (e) => {}; // Parameter has implicit any
|
||||
|
||||
// ✅ ALWAYS - explicit types
|
||||
function process(data: RequestPayload): ProcessedResult {}
|
||||
const handler = (e: React.MouseEvent<HTMLButtonElement>): void => {};
|
||||
```
|
||||
|
||||
### Type Assertions to Bypass Safety — FORBIDDEN
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER - lying to the compiler
|
||||
const user = data as User;
|
||||
const element = document.getElementById('app') as HTMLDivElement;
|
||||
|
||||
// ✅ USE - type guards and narrowing
|
||||
function isUser(data: unknown): data is User {
|
||||
return typeof data === 'object' && data !== null && 'id' in data;
|
||||
}
|
||||
if (isUser(data)) {
|
||||
console.log(data.id); // Safe
|
||||
}
|
||||
|
||||
// ✅ USE - null checks
|
||||
const element = document.getElementById('app');
|
||||
if (element instanceof HTMLDivElement) {
|
||||
element.style.display = 'none'; // Safe
|
||||
}
|
||||
```
|
||||
|
||||
### Non-null Assertion (`!`) — FORBIDDEN (except tests)
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER in production code
|
||||
const name = user!.name;
|
||||
const element = document.getElementById('app')!;
|
||||
|
||||
// ✅ USE - proper null handling
|
||||
const name = user?.name ?? 'Anonymous';
|
||||
const element = document.getElementById('app');
|
||||
if (element) {
|
||||
// Safe to use element
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Required Patterns
|
||||
|
||||
### Explicit Return Types — REQUIRED for all public functions
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - missing return type
|
||||
export function calculateTotal(items: Item[]) {
|
||||
return items.reduce((sum, item) => sum + item.price, 0);
|
||||
}
|
||||
|
||||
// ✅ CORRECT - explicit return type
|
||||
export function calculateTotal(items: Item[]): number {
|
||||
return items.reduce((sum, item) => sum + item.price, 0);
|
||||
}
|
||||
```
|
||||
|
||||
### Explicit Parameter Types — REQUIRED always
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG
|
||||
const multiply = (a, b) => a * b;
|
||||
users.map((user) => user.name); // If user type isn't inferred
|
||||
|
||||
// ✅ CORRECT
|
||||
const multiply = (a: number, b: number): number => a * b;
|
||||
users.map((user: User): string => user.name);
|
||||
```
|
||||
|
||||
### Interface Over Type Alias — PREFERRED for objects
|
||||
|
||||
```typescript
|
||||
// ✅ PREFERRED - interface (extendable, better error messages)
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
// ✅ ACCEPTABLE - type alias for unions, intersections, primitives
|
||||
type Status = 'active' | 'inactive' | 'pending';
|
||||
type ID = string | number;
|
||||
```
|
||||
|
||||
### Const Assertions for Literals — REQUIRED
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - loses literal types
|
||||
const config = {
|
||||
endpoint: '/api/users',
|
||||
method: 'GET',
|
||||
};
|
||||
// config.method is string, not 'GET'
|
||||
|
||||
// ✅ CORRECT - preserves literal types
|
||||
const config = {
|
||||
endpoint: '/api/users',
|
||||
method: 'GET',
|
||||
} as const;
|
||||
// config.method is 'GET'
|
||||
```
|
||||
|
||||
### Discriminated Unions — REQUIRED for variants
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - optional properties for variants
|
||||
interface ApiResponse {
|
||||
success: boolean;
|
||||
data?: User;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// ✅ CORRECT - discriminated union
|
||||
interface SuccessResponse {
|
||||
success: true;
|
||||
data: User;
|
||||
}
|
||||
interface ErrorResponse {
|
||||
success: false;
|
||||
error: string;
|
||||
}
|
||||
type ApiResponse = SuccessResponse | ErrorResponse;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Generic Constraints
|
||||
|
||||
### Meaningful Constraints — REQUIRED
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - unconstrained generic
|
||||
function merge<T>(a: T, b: T): T {}
|
||||
|
||||
// ✅ CORRECT - constrained generic
|
||||
function merge<T extends object>(a: T, b: Partial<T>): T {}
|
||||
```
|
||||
|
||||
### Default Generic Parameters — USE SPECIFIC TYPES
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG
|
||||
interface Repository<T = unknown> {}
|
||||
|
||||
// ✅ CORRECT - no default if type should be explicit
|
||||
interface Repository<T extends Entity> {}
|
||||
|
||||
// ✅ ACCEPTABLE - meaningful default
|
||||
interface Cache<T extends Serializable = JsonValue> {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## React/JSX Specific
|
||||
|
||||
### Event Handlers — EXPLICIT TYPES REQUIRED
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG
|
||||
const handleClick = (e) => {};
|
||||
const handleChange = (e) => {};
|
||||
|
||||
// ✅ CORRECT
|
||||
const handleClick = (e: React.MouseEvent<HTMLButtonElement>): void => {};
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {};
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {};
|
||||
```
|
||||
|
||||
### Component Props — INTERFACE REQUIRED
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - inline types
|
||||
function Button({ label, onClick }: { label: string; onClick: () => void }) { }
|
||||
|
||||
// ✅ CORRECT - named interface
|
||||
interface ButtonProps {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function Button({ label, onClick, disabled = false }: ButtonProps): JSX.Element {
|
||||
return <button onClick={onClick} disabled={disabled}>{label}</button>;
|
||||
}
|
||||
```
|
||||
|
||||
### Children Prop — USE React.ReactNode
|
||||
|
||||
```typescript
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
sidebar?: React.ReactNode;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Response Typing
|
||||
|
||||
### Define Explicit Response Types
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG
|
||||
const response = await fetch('/api/users');
|
||||
const data = await response.json(); // data is any
|
||||
|
||||
// ✅ CORRECT
|
||||
interface UsersResponse {
|
||||
users: User[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/users');
|
||||
const data: UsersResponse = await response.json();
|
||||
|
||||
// ✅ BEST - with runtime validation
|
||||
const response = await fetch('/api/users');
|
||||
const raw = await response.json();
|
||||
const data = UsersResponseSchema.parse(raw); // Zod validates at runtime
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Typed Error Classes — REQUIRED for domain errors
|
||||
|
||||
```typescript
|
||||
class ValidationError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly field: string,
|
||||
public readonly code: string,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundError extends Error {
|
||||
constructor(
|
||||
public readonly resource: string,
|
||||
public readonly id: string,
|
||||
) {
|
||||
super(`${resource} with id ${id} not found`);
|
||||
this.name = 'NotFoundError';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Narrowing — REQUIRED
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await saveUser(user);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof ValidationError) {
|
||||
return { error: error.message, field: error.field };
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return { error: 'Not found', resource: error.resource };
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
logger.error('Unexpected error', { message: error.message, stack: error.stack });
|
||||
return { error: 'Internal error' };
|
||||
}
|
||||
logger.error('Unknown error type', { error: String(error) });
|
||||
return { error: 'Internal error' };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ESLint Rules — ENFORCE THESE
|
||||
|
||||
```javascript
|
||||
{
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": ["error", {
|
||||
"allowExpressions": true,
|
||||
"allowTypedFunctionExpressions": true
|
||||
}],
|
||||
"@typescript-eslint/explicit-module-boundary-types": "error",
|
||||
"@typescript-eslint/no-inferrable-types": "off", // Allow explicit primitives
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/strict-boolean-expressions": "error",
|
||||
"@typescript-eslint/no-unsafe-assignment": "error",
|
||||
"@typescript-eslint/no-unsafe-member-access": "error",
|
||||
"@typescript-eslint/no-unsafe-call": "error",
|
||||
"@typescript-eslint/no-unsafe-return": "error"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TSConfig Strict Mode — REQUIRED
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary: The Type Safety Hierarchy
|
||||
|
||||
From best to worst:
|
||||
|
||||
1. **Explicit specific type** (interface/type) — REQUIRED
|
||||
2. **Generic with constraints** — ACCEPTABLE
|
||||
3. **`unknown` with immediate validation** — ONLY for external data
|
||||
4. **`any`** — FORBIDDEN
|
||||
|
||||
**When in doubt, define an interface.**
|
||||
205
packages/mosaic/framework/guides/VAULT-SECRETS.md
Normal file
205
packages/mosaic/framework/guides/VAULT-SECRETS.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Vault Secrets Management Guide
|
||||
|
||||
This guide applies when the project uses HashiCorp Vault for secrets management.
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Verify Vault access: `vault status`
|
||||
2. Authenticate: `vault login` (method depends on environment)
|
||||
3. Check your permissions for the required paths
|
||||
|
||||
## Canonical Structure
|
||||
|
||||
**ALL Vault secrets MUST follow this structure:**
|
||||
|
||||
```
|
||||
{mount}/{service}/{component}/{secret-name}
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
- **mount**: Environment-specific mount point
|
||||
- **service**: The service or application name
|
||||
- **component**: Logical grouping (database, api, oauth, etc.)
|
||||
- **secret-name**: Specific secret identifier
|
||||
|
||||
## Environment Mounts
|
||||
|
||||
| Mount | Environment | Usage |
|
||||
| ----------------- | ----------- | ---------------------- |
|
||||
| `secret-dev/` | Development | Local dev, CI |
|
||||
| `secret-staging/` | Staging | Pre-production testing |
|
||||
| `secret-prod/` | Production | Live systems |
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Database credentials
|
||||
secret-prod/postgres/database/app
|
||||
secret-prod/mysql/database/readonly
|
||||
secret-staging/redis/auth/default
|
||||
|
||||
# API tokens
|
||||
secret-prod/authentik/admin/token
|
||||
secret-prod/stripe/api/live-key
|
||||
secret-dev/sendgrid/api/test-key
|
||||
|
||||
# JWT/Authentication
|
||||
secret-prod/backend-api/jwt/signing-key
|
||||
secret-prod/auth-service/session/secret
|
||||
|
||||
# OAuth providers
|
||||
secret-prod/backend-api/oauth/google
|
||||
secret-prod/backend-api/oauth/github
|
||||
|
||||
# Internal services
|
||||
secret-prod/loki/read-auth/admin
|
||||
secret-prod/grafana/admin/password
|
||||
```
|
||||
|
||||
## Standard Field Names
|
||||
|
||||
Use consistent field names within secrets:
|
||||
|
||||
| Purpose | Fields |
|
||||
| ----------- | ---------------------------- |
|
||||
| Credentials | `username`, `password` |
|
||||
| Tokens | `token` |
|
||||
| OAuth | `client_id`, `client_secret` |
|
||||
| Connection | `url`, `host`, `port` |
|
||||
| Keys | `public_key`, `private_key` |
|
||||
|
||||
### Example Secret Structure
|
||||
|
||||
```json
|
||||
// secret-prod/postgres/database/app
|
||||
{
|
||||
"username": "app_user",
|
||||
"password": "secure-password-here",
|
||||
"host": "db.example.com",
|
||||
"port": "5432",
|
||||
"database": "myapp"
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
1. **DO NOT GUESS** secret paths - Always verify the path exists
|
||||
2. **Use helper scripts** in `scripts/vault/` when available
|
||||
3. **All lowercase, hyphenated** (kebab-case) for all path segments
|
||||
4. **Standard field names** - Use the conventions above
|
||||
5. **No sensitive data in path names** - Path itself should not reveal secrets
|
||||
6. **Environment separation** - Never reference prod secrets from dev
|
||||
|
||||
## Deprecated Paths (DO NOT USE)
|
||||
|
||||
These legacy patterns are deprecated and should be migrated:
|
||||
|
||||
| Deprecated | Migrate To |
|
||||
| ------------------------- | ------------------------------------------- |
|
||||
| `secret/infrastructure/*` | `secret-{env}/{service}/...` |
|
||||
| `secret/oauth/*` | `secret-{env}/{service}/oauth/{provider}` |
|
||||
| `secret/database/*` | `secret-{env}/{service}/database/{user}` |
|
||||
| `secret/credentials/*` | `secret-{env}/{service}/{component}/{name}` |
|
||||
|
||||
## Reading Secrets
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# Read a secret
|
||||
vault kv get secret-prod/postgres/database/app
|
||||
|
||||
# Get specific field
|
||||
vault kv get -field=password secret-prod/postgres/database/app
|
||||
|
||||
# JSON output
|
||||
vault kv get -format=json secret-prod/postgres/database/app
|
||||
```
|
||||
|
||||
### Application Code
|
||||
|
||||
**Python (hvac):**
|
||||
|
||||
```python
|
||||
import hvac
|
||||
|
||||
client = hvac.Client(url='https://vault.example.com')
|
||||
secret = client.secrets.kv.v2.read_secret_version(
|
||||
path='postgres/database/app',
|
||||
mount_point='secret-prod'
|
||||
)
|
||||
password = secret['data']['data']['password']
|
||||
```
|
||||
|
||||
**Node.js (node-vault):**
|
||||
|
||||
```javascript
|
||||
const vault = require('node-vault')({ endpoint: 'https://vault.example.com' });
|
||||
const secret = await vault.read('secret-prod/data/postgres/database/app');
|
||||
const password = secret.data.data.password;
|
||||
```
|
||||
|
||||
**Go:**
|
||||
|
||||
```go
|
||||
secret, err := client.Logical().Read("secret-prod/data/postgres/database/app")
|
||||
password := secret.Data["data"].(map[string]interface{})["password"].(string)
|
||||
```
|
||||
|
||||
## Writing Secrets
|
||||
|
||||
Only authorized personnel should write secrets. If you need a new secret:
|
||||
|
||||
1. Request through proper channels (ticket, PR to IaC repo)
|
||||
2. Follow the canonical structure
|
||||
3. Document the secret's purpose
|
||||
4. Set appropriate access policies
|
||||
|
||||
```bash
|
||||
# Example (requires write permissions)
|
||||
vault kv put secret-dev/myapp/database/app \
|
||||
username="dev_user" \
|
||||
password="dev-password" \
|
||||
host="localhost" \
|
||||
port="5432"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Permission Denied
|
||||
|
||||
```
|
||||
Error: permission denied
|
||||
```
|
||||
|
||||
- Verify your token has read access to the path
|
||||
- Check if you're using the correct mount point
|
||||
- Confirm the secret path exists
|
||||
|
||||
### Secret Not Found
|
||||
|
||||
```
|
||||
Error: no value found at secret-prod/data/service/component/name
|
||||
```
|
||||
|
||||
- Verify the exact path (use `vault kv list` to explore)
|
||||
- Check for typos in service/component names
|
||||
- Confirm you're using the correct environment mount
|
||||
|
||||
### Token Expired
|
||||
|
||||
```
|
||||
Error: token expired
|
||||
```
|
||||
|
||||
- Re-authenticate: `vault login`
|
||||
- Check token TTL: `vault token lookup`
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Least privilege** - Request only the permissions you need
|
||||
2. **Short-lived tokens** - Use tokens with appropriate TTLs
|
||||
3. **Audit logging** - All access is logged; act accordingly
|
||||
4. **No local copies** - Don't store secrets in files or env vars long-term
|
||||
5. **Rotate on compromise** - Immediately rotate any exposed secrets
|
||||
231
packages/mosaic/framework/install.ps1
Normal file
231
packages/mosaic/framework/install.ps1
Normal file
@@ -0,0 +1,231 @@
|
||||
# Mosaic Bootstrap — Windows Installer
|
||||
# PowerShell equivalent of install.sh
|
||||
#
|
||||
# Usage:
|
||||
# powershell -ExecutionPolicy Bypass -File install.ps1
|
||||
#
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$SourceDir = $PSScriptRoot
|
||||
$TargetDir = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" }
|
||||
$InstallMode = if ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE.ToLowerInvariant() } else { "prompt" } # prompt|keep|overwrite
|
||||
$PreservePaths = @("SOUL.md", "memory")
|
||||
|
||||
function Write-Ok { param([string]$Msg) Write-Host " ✓ " -ForegroundColor Green -NoNewline; Write-Host $Msg }
|
||||
function Write-Warn { param([string]$Msg) Write-Host " ⚠ " -ForegroundColor Yellow -NoNewline; Write-Host $Msg }
|
||||
function Write-Fail { param([string]$Msg) Write-Host " ✗ " -ForegroundColor Red -NoNewline; Write-Host $Msg }
|
||||
function Write-Step { param([string]$Msg) Write-Host ""; Write-Host $Msg -ForegroundColor White -BackgroundColor DarkGray }
|
||||
|
||||
function Test-ExistingInstall {
|
||||
if (-not (Test-Path $TargetDir)) { return $false }
|
||||
return (Test-Path (Join-Path $TargetDir "bin\mosaic")) -or (Test-Path (Join-Path $TargetDir "AGENTS.md")) -or (Test-Path (Join-Path $TargetDir "SOUL.md"))
|
||||
}
|
||||
|
||||
function Select-InstallMode {
|
||||
switch ($InstallMode) {
|
||||
"prompt" { }
|
||||
"keep" { return }
|
||||
"overwrite" { return }
|
||||
default {
|
||||
Write-Fail "Invalid MOSAIC_INSTALL_MODE '$InstallMode'. Use: prompt, keep, overwrite."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-ExistingInstall)) {
|
||||
$script:InstallMode = "overwrite"
|
||||
return
|
||||
}
|
||||
|
||||
if (-not [Environment]::UserInteractive) {
|
||||
Write-Warn "Existing install detected without interactive input; defaulting to keep local files."
|
||||
$script:InstallMode = "keep"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Existing Mosaic install detected at: $TargetDir"
|
||||
Write-Host "Choose reinstall mode:"
|
||||
Write-Host " 1) keep Keep local files (SOUL.md, memory/) while updating framework"
|
||||
Write-Host " 2) overwrite Replace everything in $TargetDir"
|
||||
Write-Host " 3) cancel Abort install"
|
||||
$selection = Read-Host "Selection [1/2/3] (default: 1)"
|
||||
|
||||
$normalizedSelection = if ($null -eq $selection) { "" } else { $selection.ToLowerInvariant() }
|
||||
switch ($normalizedSelection) {
|
||||
"" { $script:InstallMode = "keep" }
|
||||
"1" { $script:InstallMode = "keep" }
|
||||
"k" { $script:InstallMode = "keep" }
|
||||
"keep" { $script:InstallMode = "keep" }
|
||||
"2" { $script:InstallMode = "overwrite" }
|
||||
"o" { $script:InstallMode = "overwrite" }
|
||||
"overwrite" { $script:InstallMode = "overwrite" }
|
||||
"3" { Write-Fail "Install cancelled."; exit 1 }
|
||||
"c" { Write-Fail "Install cancelled."; exit 1 }
|
||||
"cancel" { Write-Fail "Install cancelled."; exit 1 }
|
||||
default {
|
||||
Write-Warn "Unrecognized selection '$selection'; defaulting to keep."
|
||||
$script:InstallMode = "keep"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Install framework ────────────────────────────────────────
|
||||
Write-Step " Installing Mosaic framework "
|
||||
|
||||
if (-not (Test-Path $TargetDir)) { New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null }
|
||||
Select-InstallMode
|
||||
|
||||
if ($InstallMode -eq "keep") {
|
||||
Write-Ok "Install mode: keep local SOUL.md/memory while updating framework"
|
||||
}
|
||||
else {
|
||||
Write-Ok "Install mode: overwrite existing files"
|
||||
}
|
||||
|
||||
$preserveTmp = $null
|
||||
if ($InstallMode -eq "keep") {
|
||||
$preserveTmp = Join-Path ([System.IO.Path]::GetTempPath()) ("mosaic-preserve-" + [Guid]::NewGuid().ToString("N"))
|
||||
New-Item -ItemType Directory -Path $preserveTmp -Force | Out-Null
|
||||
foreach ($relPath in $PreservePaths) {
|
||||
$src = Join-Path $TargetDir $relPath
|
||||
if (Test-Path $src) {
|
||||
$dstParent = Join-Path $preserveTmp (Split-Path $relPath -Parent)
|
||||
if (-not [string]::IsNullOrEmpty($dstParent) -and -not (Test-Path $dstParent)) {
|
||||
New-Item -ItemType Directory -Path $dstParent -Force | Out-Null
|
||||
}
|
||||
Copy-Item $src (Join-Path $preserveTmp $relPath) -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get-ChildItem $TargetDir -Exclude ".git" | Remove-Item -Recurse -Force
|
||||
Get-ChildItem $SourceDir -Exclude ".git" | ForEach-Object {
|
||||
$dest = Join-Path $TargetDir $_.Name
|
||||
if ($_.PSIsContainer) {
|
||||
Copy-Item $_.FullName $dest -Recurse -Force
|
||||
}
|
||||
else {
|
||||
Copy-Item $_.FullName $dest -Force
|
||||
}
|
||||
}
|
||||
|
||||
if ($InstallMode -eq "keep" -and $null -ne $preserveTmp) {
|
||||
foreach ($relPath in $PreservePaths) {
|
||||
$src = Join-Path $preserveTmp $relPath
|
||||
if (Test-Path $src) {
|
||||
$dst = Join-Path $TargetDir $relPath
|
||||
if (Test-Path $dst) {
|
||||
Remove-Item $dst -Recurse -Force
|
||||
}
|
||||
$dstParent = Split-Path $dst -Parent
|
||||
if (-not (Test-Path $dstParent)) {
|
||||
New-Item -ItemType Directory -Path $dstParent -Force | Out-Null
|
||||
}
|
||||
Copy-Item $src $dst -Recurse -Force
|
||||
}
|
||||
}
|
||||
Remove-Item -Path $preserveTmp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Ok "Framework installed to $TargetDir"
|
||||
|
||||
# ── Post-install tasks ───────────────────────────────────────
|
||||
Write-Step " Post-install tasks "
|
||||
|
||||
$binDir = Join-Path $TargetDir "bin"
|
||||
|
||||
try {
|
||||
& "$binDir\mosaic-link-runtime-assets.ps1" *>$null
|
||||
Write-Ok "Runtime assets linked"
|
||||
}
|
||||
catch {
|
||||
Write-Warn "Runtime asset linking failed (non-fatal)"
|
||||
}
|
||||
|
||||
try {
|
||||
& "$binDir\mosaic-ensure-sequential-thinking.ps1" *>$null
|
||||
Write-Ok "sequential-thinking MCP configured"
|
||||
}
|
||||
catch {
|
||||
if ($env:MOSAIC_ALLOW_MISSING_SEQUENTIAL_THINKING -eq "1") {
|
||||
Write-Warn "sequential-thinking MCP setup failed but bypassed (MOSAIC_ALLOW_MISSING_SEQUENTIAL_THINKING=1)"
|
||||
}
|
||||
else {
|
||||
Write-Fail "sequential-thinking MCP setup failed (hard requirement)."
|
||||
Write-Fail "Set MOSAIC_ALLOW_MISSING_SEQUENTIAL_THINKING=1 only for temporary bypass scenarios."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($env:MOSAIC_SKIP_SKILLS_SYNC -eq "1") {
|
||||
Write-Ok "Skills sync skipped (MOSAIC_SKIP_SKILLS_SYNC=1)"
|
||||
}
|
||||
else {
|
||||
try {
|
||||
& "$binDir\mosaic-sync-skills.ps1" *>$null
|
||||
Write-Ok "Skills synced"
|
||||
}
|
||||
catch {
|
||||
Write-Warn "Skills sync failed (non-fatal)"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
& "$binDir\mosaic-migrate-local-skills.ps1" -Apply *>$null
|
||||
Write-Ok "Local skills migrated"
|
||||
}
|
||||
catch {
|
||||
Write-Warn "Local skill migration failed (non-fatal)"
|
||||
}
|
||||
|
||||
try {
|
||||
& "$binDir\mosaic-doctor.ps1" *>$null
|
||||
Write-Ok "Health audit passed"
|
||||
}
|
||||
catch {
|
||||
Write-Warn "Health audit reported issues — run 'mosaic doctor' for details"
|
||||
}
|
||||
|
||||
# ── PATH configuration ───────────────────────────────────────
|
||||
Write-Step " PATH configuration "
|
||||
|
||||
$mosaicBin = Join-Path $TargetDir "bin"
|
||||
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
$pathChanged = $false
|
||||
|
||||
if ($userPath -and $userPath.Split(";") -contains $mosaicBin) {
|
||||
Write-Ok "Already in User PATH"
|
||||
}
|
||||
else {
|
||||
$newPath = if ($userPath) { "$mosaicBin;$userPath" } else { $mosaicBin }
|
||||
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
|
||||
$env:Path = "$mosaicBin;$env:Path"
|
||||
Write-Ok "Added to User PATH"
|
||||
$pathChanged = $true
|
||||
}
|
||||
|
||||
# ── Summary ──────────────────────────────────────────────────
|
||||
Write-Host ""
|
||||
Write-Host " Mosaic installed successfully." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
$nextSteps = @()
|
||||
|
||||
if ($pathChanged) {
|
||||
$nextSteps += "Open a new terminal (or log out and back in) to activate PATH."
|
||||
}
|
||||
|
||||
$soulPath = Join-Path $TargetDir "SOUL.md"
|
||||
if (-not (Test-Path $soulPath)) {
|
||||
$nextSteps += "Run 'mosaic init' to set up your agent identity (SOUL.md)."
|
||||
}
|
||||
|
||||
if ($nextSteps.Count -gt 0) {
|
||||
Write-Host " Next steps:" -ForegroundColor White
|
||||
for ($i = 0; $i -lt $nextSteps.Count; $i++) {
|
||||
Write-Host " $($i + 1). " -NoNewline
|
||||
Write-Host $nextSteps[$i] -ForegroundColor Cyan
|
||||
}
|
||||
Write-Host ""
|
||||
}
|
||||
0
packages/mosaic/framework/memory/.gitkeep
Normal file
0
packages/mosaic/framework/memory/.gitkeep
Normal file
22
packages/mosaic/framework/profiles/README.md
Normal file
22
packages/mosaic/framework/profiles/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Mosaic Profiles
|
||||
|
||||
Profiles are runtime-neutral context packs that can be consumed by any agent runtime.
|
||||
|
||||
## Layout
|
||||
|
||||
- `domains/`: regulated-domain and security context (HIPAA, fintech, crypto, etc.)
|
||||
- `tech-stacks/`: stack-specific conventions and quality checks
|
||||
- `workflows/`: reusable execution workflows
|
||||
|
||||
## Runtime Split
|
||||
|
||||
- Runtime-neutral content belongs here under `~/.config/mosaic/profiles`.
|
||||
- Runtime-specific settings belong under `~/.config/mosaic/runtime/<runtime>/...`.
|
||||
|
||||
Current runtime overlay example:
|
||||
|
||||
- `~/.config/mosaic/runtime/claude/settings-overlays/jarvis-loop.json`
|
||||
|
||||
## Claude Compatibility
|
||||
|
||||
`mosaic-link-runtime-assets` prunes legacy preset symlink trees from `~/.claude` so Mosaic remains canonical and Claude uses runtime overlays that reference Mosaic paths directly.
|
||||
190
packages/mosaic/framework/profiles/domains/crypto-web3.json
Normal file
190
packages/mosaic/framework/profiles/domains/crypto-web3.json
Normal file
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"name": "Cryptocurrency & Web3 Security",
|
||||
"description": "Security patterns for blockchain, cryptocurrency, and Web3 applications",
|
||||
"domainKeywords": [
|
||||
"crypto",
|
||||
"blockchain",
|
||||
"web3",
|
||||
"defi",
|
||||
"nft",
|
||||
"wallet",
|
||||
"smart contract",
|
||||
"ethereum"
|
||||
],
|
||||
"compliance": {
|
||||
"regulations": ["AML", "KYC", "FATF", "BSA", "Regional crypto regulations"],
|
||||
"scope": "Applications handling cryptocurrencies and digital assets",
|
||||
"requirements": [
|
||||
"Secure private key management",
|
||||
"Anti-money laundering (AML) compliance",
|
||||
"Know Your Customer (KYC) verification",
|
||||
"Transaction monitoring and reporting",
|
||||
"Wallet security and multi-signature",
|
||||
"Smart contract security audits"
|
||||
]
|
||||
},
|
||||
"securityPatterns": {
|
||||
"walletSecurity": {
|
||||
"privateKeys": "Never store private keys in plaintext",
|
||||
"keyDerivation": "Use BIP32/BIP44 for key derivation",
|
||||
"storage": "Hardware Security Modules (HSMs) for production",
|
||||
"backup": "Secure backup and recovery procedures",
|
||||
"multiSig": "Multi-signature wallets for high-value transactions"
|
||||
},
|
||||
"smartContracts": {
|
||||
"auditing": "Professional security audits required",
|
||||
"testing": "Comprehensive test coverage including edge cases",
|
||||
"upgradeability": "Consider proxy patterns for upgradeable contracts",
|
||||
"accessControl": "Role-based access control in contracts",
|
||||
"gasOptimization": "Optimize for gas efficiency and DoS protection"
|
||||
},
|
||||
"transactionSecurity": {
|
||||
"validation": "Multi-layer transaction validation",
|
||||
"monitoring": "Real-time transaction monitoring",
|
||||
"limits": "Configurable transaction limits",
|
||||
"timelock": "Time-delayed execution for large transactions",
|
||||
"approval": "Multi-party approval workflows"
|
||||
},
|
||||
"apiSecurity": {
|
||||
"authentication": "Strong API authentication (JWT + API keys)",
|
||||
"rateLimit": "Aggressive rate limiting for trading APIs",
|
||||
"signing": "Request signing for sensitive operations",
|
||||
"websockets": "Secure WebSocket connections for real-time data"
|
||||
}
|
||||
},
|
||||
"implementationPatterns": {
|
||||
"backend": {
|
||||
"walletIntegration": {
|
||||
"abstraction": "Abstract wallet operations behind service layer",
|
||||
"keyManagement": "Separate key management from application logic",
|
||||
"transactions": "Queue and batch transactions for efficiency",
|
||||
"monitoring": "Monitor blockchain for transaction confirmations"
|
||||
},
|
||||
"tradingEngine": {
|
||||
"orderMatching": "Secure order matching algorithms",
|
||||
"balanceTracking": "Accurate balance tracking with locks",
|
||||
"riskManagement": "Position limits and risk controls",
|
||||
"latency": "Low-latency execution for competitive trading"
|
||||
},
|
||||
"compliance": {
|
||||
"kyc": "Identity verification workflows",
|
||||
"aml": "Automated AML screening and monitoring",
|
||||
"reporting": "Suspicious activity reporting (SAR)",
|
||||
"sanctions": "OFAC and sanctions list screening"
|
||||
}
|
||||
},
|
||||
"frontend": {
|
||||
"walletConnection": {
|
||||
"webWallets": "Support for MetaMask, WalletConnect, etc.",
|
||||
"security": "Validate wallet signatures and addresses",
|
||||
"persistence": "Secure session management",
|
||||
"switching": "Handle network and account switching"
|
||||
},
|
||||
"trading": {
|
||||
"realTime": "Real-time price and order book updates",
|
||||
"charting": "Advanced charting capabilities",
|
||||
"orderTypes": "Support for various order types",
|
||||
"riskWarnings": "Clear risk disclosures and warnings"
|
||||
}
|
||||
}
|
||||
},
|
||||
"blockchainIntegration": {
|
||||
"ethereum": {
|
||||
"web3": "Use ethers.js or web3.js for blockchain interaction",
|
||||
"infura": "Reliable node access via Infura/Alchemy",
|
||||
"events": "Event listening and log parsing",
|
||||
"gasManagement": "Dynamic gas price management"
|
||||
},
|
||||
"bitcoin": {
|
||||
"addresses": "Support for multiple address types",
|
||||
"utxo": "UTXO management and coin selection",
|
||||
"fees": "Dynamic fee estimation",
|
||||
"scripting": "Advanced scripting for complex transactions"
|
||||
},
|
||||
"multiChain": {
|
||||
"abstraction": "Chain-agnostic service interfaces",
|
||||
"bridging": "Cross-chain bridge integrations",
|
||||
"networks": "Support for testnets and multiple networks",
|
||||
"consensus": "Handle different consensus mechanisms"
|
||||
}
|
||||
},
|
||||
"testingRequirements": {
|
||||
"coverage": {
|
||||
"minimum": "95% for financial logic modules",
|
||||
"focus": "Security-critical components and edge cases"
|
||||
},
|
||||
"security": [
|
||||
"Smart contract security audits",
|
||||
"Penetration testing for web interfaces",
|
||||
"Key management security testing",
|
||||
"Transaction flow security validation",
|
||||
"API security testing"
|
||||
],
|
||||
"blockchain": [
|
||||
"Test on multiple networks (mainnet, testnet)",
|
||||
"Handle network congestion scenarios",
|
||||
"Test transaction failure and retry logic",
|
||||
"Validate gas estimation accuracy",
|
||||
"Test blockchain reorganization handling"
|
||||
]
|
||||
},
|
||||
"context7Libraries": [
|
||||
"ethers",
|
||||
"web3",
|
||||
"@metamask/providers",
|
||||
"bitcoinjs-lib",
|
||||
"@walletconnect/client",
|
||||
"bip32",
|
||||
"bip39"
|
||||
],
|
||||
"codeTemplates": {
|
||||
"walletService": {
|
||||
"description": "Secure wallet service interface",
|
||||
"template": "@Injectable()\nexport class WalletService {\n async signTransaction(transaction: Transaction, keyId: string): Promise<string> {\n const privateKey = await this.keyManager.getKey(keyId);\n return this.signer.sign(transaction, privateKey);\n }\n\n async validateAddress(address: string, network: Network): Promise<boolean> {\n return this.validator.isValid(address, network);\n }\n}"
|
||||
},
|
||||
"transactionMonitor": {
|
||||
"description": "Blockchain transaction monitoring",
|
||||
"template": "this.web3.eth.subscribe('pendingTransactions', (txHash) => {\n this.web3.eth.getTransaction(txHash).then(tx => {\n if (this.isWatchedAddress(tx.to)) {\n this.processIncomingTransaction(tx);\n }\n });\n});"
|
||||
},
|
||||
"smartContractInteraction": {
|
||||
"description": "Safe smart contract interaction",
|
||||
"template": "const contract = new ethers.Contract(address, abi, signer);\nconst gasEstimate = await contract.estimateGas.transfer(to, amount);\nconst tx = await contract.transfer(to, amount, {\n gasLimit: gasEstimate.mul(110).div(100), // 10% buffer\n gasPrice: await this.getOptimalGasPrice()\n});"
|
||||
}
|
||||
},
|
||||
"complianceChecklist": [
|
||||
"Know Your Customer (KYC) procedures implemented",
|
||||
"Anti-Money Laundering (AML) monitoring in place",
|
||||
"Suspicious activity reporting (SAR) procedures",
|
||||
"OFAC and sanctions screening implemented",
|
||||
"Transaction monitoring and analysis tools",
|
||||
"Customer due diligence (CDD) procedures",
|
||||
"Enhanced due diligence (EDD) for high-risk customers",
|
||||
"Record keeping and data retention policies",
|
||||
"Compliance training for staff",
|
||||
"Regular compliance audits and reviews"
|
||||
],
|
||||
"securityBestPractices": [
|
||||
"Never store private keys in application code",
|
||||
"Use hardware security modules (HSMs) for key storage",
|
||||
"Implement multi-signature wallets for treasury management",
|
||||
"Conduct regular security audits of smart contracts",
|
||||
"Use time-locked transactions for large amounts",
|
||||
"Implement comprehensive transaction monitoring",
|
||||
"Use secure random number generation",
|
||||
"Validate all blockchain data independently",
|
||||
"Implement proper access controls and authentication",
|
||||
"Maintain detailed audit logs of all operations"
|
||||
],
|
||||
"riskAssessment": [
|
||||
"Private key compromise and theft",
|
||||
"Smart contract vulnerabilities and exploits",
|
||||
"Exchange hacks and loss of user funds",
|
||||
"Regulatory compliance failures",
|
||||
"Market manipulation and fraud",
|
||||
"Technical failures and system outages",
|
||||
"Insider threats and malicious employees",
|
||||
"Third-party service provider risks",
|
||||
"Quantum computing threats to cryptography",
|
||||
"Cross-chain bridge vulnerabilities"
|
||||
]
|
||||
}
|
||||
190
packages/mosaic/framework/profiles/domains/fintech-security.json
Normal file
190
packages/mosaic/framework/profiles/domains/fintech-security.json
Normal file
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"name": "Fintech Security Compliance",
|
||||
"description": "PCI DSS and financial security requirements for fintech applications",
|
||||
"domainKeywords": [
|
||||
"payment",
|
||||
"financial",
|
||||
"banking",
|
||||
"credit",
|
||||
"debit",
|
||||
"transaction",
|
||||
"pci",
|
||||
"fintech"
|
||||
],
|
||||
"compliance": {
|
||||
"regulations": ["PCI DSS", "PSD2", "SOX", "KYC", "AML"],
|
||||
"scope": "Applications processing payment card data",
|
||||
"requirements": [
|
||||
"Secure cardholder data",
|
||||
"Encrypt transmission of cardholder data",
|
||||
"Protect stored cardholder data",
|
||||
"Maintain vulnerability management program",
|
||||
"Implement strong access control measures",
|
||||
"Regularly monitor and test networks",
|
||||
"Maintain information security policy"
|
||||
]
|
||||
},
|
||||
"dataClassification": {
|
||||
"pan": {
|
||||
"definition": "Primary Account Number (Credit/Debit card number)",
|
||||
"storage": "Never store full PAN unless absolutely necessary",
|
||||
"masking": "Show only last 4 digits",
|
||||
"encryption": "AES-256 if storage required",
|
||||
"transmission": "Always encrypted with TLS 1.2+"
|
||||
},
|
||||
"sadData": {
|
||||
"definition": "Sensitive Authentication Data",
|
||||
"types": ["CVV2", "PIN", "Track data"],
|
||||
"storage": "Never store SAD after authorization",
|
||||
"handling": "Process but do not retain"
|
||||
},
|
||||
"cardholderData": {
|
||||
"definition": "PAN + cardholder name, service code, expiration date",
|
||||
"minimization": "Store only if business need exists",
|
||||
"retention": "Purge when no longer needed",
|
||||
"access": "Restrict access to authorized personnel only"
|
||||
}
|
||||
},
|
||||
"securityPatterns": {
|
||||
"encryption": {
|
||||
"algorithm": "AES-256 for data at rest",
|
||||
"keyManagement": "Hardware Security Modules (HSMs) preferred",
|
||||
"transmission": "TLS 1.2+ for data in transit",
|
||||
"tokenization": "Replace PAN with non-sensitive tokens"
|
||||
},
|
||||
"authentication": {
|
||||
"mfa": "Multi-factor authentication mandatory",
|
||||
"passwordPolicy": "Complex passwords, regular rotation",
|
||||
"sessionManagement": "Secure session handling with timeout",
|
||||
"biometric": "Support for biometric authentication"
|
||||
},
|
||||
"authorization": {
|
||||
"rbac": "Role-based access control",
|
||||
"segregationOfDuties": "Separate roles for sensitive operations",
|
||||
"leastPrivilege": "Minimum necessary access principle",
|
||||
"approval": "Multi-person approval for high-value transactions"
|
||||
},
|
||||
"fraudPrevention": {
|
||||
"riskScoring": "Real-time transaction risk assessment",
|
||||
"monitoring": "Anomaly detection and behavioral analytics",
|
||||
"alerts": "Immediate alerts for suspicious activities",
|
||||
"blocking": "Automatic blocking of fraudulent transactions"
|
||||
}
|
||||
},
|
||||
"implementationPatterns": {
|
||||
"backend": {
|
||||
"paymentProcessing": {
|
||||
"tokenization": "Use payment tokens instead of card data",
|
||||
"validation": "Validate all payment inputs",
|
||||
"logging": "Log transactions without sensitive data",
|
||||
"encryption": "Encrypt cardholder data before storage"
|
||||
},
|
||||
"apiSecurity": {
|
||||
"rateLimit": "Implement rate limiting",
|
||||
"apiKeys": "Secure API key management",
|
||||
"signing": "Request signing for sensitive operations",
|
||||
"monitoring": "Monitor API usage patterns"
|
||||
},
|
||||
"database": {
|
||||
"encryption": "Database-level encryption for sensitive fields",
|
||||
"access": "Database access controls and monitoring",
|
||||
"backup": "Encrypted backups with secure key management",
|
||||
"masking": "Data masking for non-production environments"
|
||||
}
|
||||
},
|
||||
"frontend": {
|
||||
"paymentForms": {
|
||||
"https": "Always use HTTPS for payment pages",
|
||||
"validation": "Client-side validation with server confirmation",
|
||||
"autocomplete": "Disable autocomplete for sensitive fields",
|
||||
"iframes": "Use secure iframes for payment card input"
|
||||
},
|
||||
"dataHandling": {
|
||||
"noStorage": "Never store payment data in browser",
|
||||
"masking": "Mask card numbers in UI",
|
||||
"timeout": "Session timeout for payment pages",
|
||||
"clearData": "Clear payment data from memory after use"
|
||||
}
|
||||
}
|
||||
},
|
||||
"testingRequirements": {
|
||||
"coverage": {
|
||||
"minimum": "90% for payment processing modules",
|
||||
"focus": "Security controls and fraud prevention"
|
||||
},
|
||||
"security": [
|
||||
"Penetration testing quarterly",
|
||||
"Vulnerability scanning monthly",
|
||||
"Code review for all payment code",
|
||||
"Test encryption implementation",
|
||||
"Validate tokenization process"
|
||||
],
|
||||
"compliance": [
|
||||
"PCI DSS compliance validation",
|
||||
"Test access controls",
|
||||
"Validate audit logging",
|
||||
"Test incident response procedures",
|
||||
"Verify data retention policies"
|
||||
]
|
||||
},
|
||||
"context7Libraries": [
|
||||
"stripe",
|
||||
"bcrypt",
|
||||
"jsonwebtoken",
|
||||
"helmet",
|
||||
"express-rate-limit",
|
||||
"crypto"
|
||||
],
|
||||
"codeTemplates": {
|
||||
"paymentEntity": {
|
||||
"description": "Payment entity with tokenization",
|
||||
"template": "@Entity()\nexport class Payment {\n @Tokenized()\n @Column()\n cardToken: string;\n\n @Column()\n lastFourDigits: string;\n\n @Encrypted()\n @Column()\n amount: number;\n}"
|
||||
},
|
||||
"transactionLog": {
|
||||
"description": "Secure transaction logging",
|
||||
"template": "await this.auditService.logTransaction({\n transactionId: transaction.id,\n userId: user.id,\n amount: transaction.amount,\n currency: transaction.currency,\n status: 'COMPLETED',\n riskScore: riskAssessment.score,\n timestamp: new Date()\n});"
|
||||
},
|
||||
"fraudCheck": {
|
||||
"description": "Fraud prevention check",
|
||||
"template": "const riskScore = await this.fraudService.assessRisk({\n userId: user.id,\n amount: transaction.amount,\n location: transaction.location,\n deviceFingerprint: request.deviceId\n});\n\nif (riskScore > FRAUD_THRESHOLD) {\n await this.alertService.triggerFraudAlert(transaction);\n}"
|
||||
}
|
||||
},
|
||||
"complianceChecklist": [
|
||||
"Cardholder data is encrypted at rest and in transit",
|
||||
"Sensitive authentication data is not stored",
|
||||
"Access to cardholder data is restricted and monitored",
|
||||
"Strong cryptography and security protocols are used",
|
||||
"Antivirus software is maintained",
|
||||
"Secure systems and applications are developed",
|
||||
"Access to data is restricted by business need-to-know",
|
||||
"Unique IDs are assigned to each person with computer access",
|
||||
"Physical access to cardholder data is restricted",
|
||||
"All access to network resources is logged and monitored",
|
||||
"Security systems and processes are regularly tested",
|
||||
"Information security policy is maintained"
|
||||
],
|
||||
"riskAssessment": [
|
||||
"Unauthorized access to payment data",
|
||||
"Data breaches and card data theft",
|
||||
"Fraud and unauthorized transactions",
|
||||
"System vulnerabilities and exploits",
|
||||
"Insider threats and malicious employees",
|
||||
"Third-party payment processor risks",
|
||||
"Network security vulnerabilities",
|
||||
"Application security weaknesses",
|
||||
"Physical security of payment systems",
|
||||
"Business continuity and disaster recovery"
|
||||
],
|
||||
"regulatoryReporting": [
|
||||
"PCI DSS compliance reports",
|
||||
"Suspicious activity reports (SARs)",
|
||||
"Currency transaction reports (CTRs)",
|
||||
"Know Your Customer (KYC) documentation",
|
||||
"Anti-Money Laundering (AML) compliance",
|
||||
"Data breach notification requirements",
|
||||
"Consumer privacy disclosures",
|
||||
"Financial audit requirements",
|
||||
"Incident response documentation",
|
||||
"Third-party risk assessments"
|
||||
]
|
||||
}
|
||||
189
packages/mosaic/framework/profiles/domains/healthcare-hipaa.json
Normal file
189
packages/mosaic/framework/profiles/domains/healthcare-hipaa.json
Normal file
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"name": "Healthcare HIPAA Compliance",
|
||||
"description": "HIPAA compliance requirements for healthcare applications handling PHI",
|
||||
"domainKeywords": ["health", "medical", "patient", "hipaa", "phi", "healthcare"],
|
||||
"compliance": {
|
||||
"regulation": "HIPAA (Health Insurance Portability and Accountability Act)",
|
||||
"scope": "All applications handling Protected Health Information (PHI)",
|
||||
"requirements": [
|
||||
"Encrypt PHI at rest and in transit",
|
||||
"Implement access controls for PHI",
|
||||
"Audit all access to PHI",
|
||||
"Ensure data integrity",
|
||||
"Implement proper user authentication",
|
||||
"Maintain data minimization practices"
|
||||
]
|
||||
},
|
||||
"dataClassification": {
|
||||
"phi": {
|
||||
"definition": "Individually identifiable health information",
|
||||
"examples": [
|
||||
"Names, addresses, birth dates",
|
||||
"Phone numbers, email addresses",
|
||||
"Social Security numbers",
|
||||
"Medical record numbers",
|
||||
"Health plan beneficiary numbers",
|
||||
"Account numbers",
|
||||
"Certificate/license numbers",
|
||||
"Vehicle identifiers and serial numbers",
|
||||
"Device identifiers and serial numbers",
|
||||
"Web Universal Resource Locators (URLs)",
|
||||
"Internet Protocol (IP) address numbers",
|
||||
"Biometric identifiers",
|
||||
"Full face photographic images",
|
||||
"Medical diagnoses and treatment information",
|
||||
"Lab results and vital signs"
|
||||
],
|
||||
"encryption": "AES-256 encryption required",
|
||||
"storage": "Must be encrypted at rest",
|
||||
"transmission": "Must be encrypted in transit (TLS 1.2+)"
|
||||
}
|
||||
},
|
||||
"securityPatterns": {
|
||||
"encryption": {
|
||||
"algorithm": "AES-256",
|
||||
"keyManagement": "Use AWS KMS, Azure Key Vault, or similar",
|
||||
"implementation": "Field-level encryption for PHI columns",
|
||||
"example": "@Encrypted decorator for entity fields"
|
||||
},
|
||||
"authentication": {
|
||||
"method": "Multi-factor authentication required",
|
||||
"tokenType": "JWT with refresh tokens",
|
||||
"sessionTimeout": "Maximum 15 minutes inactive timeout",
|
||||
"passwordPolicy": "Minimum 8 characters, complexity requirements"
|
||||
},
|
||||
"authorization": {
|
||||
"model": "Role-Based Access Control (RBAC)",
|
||||
"principle": "Minimum necessary access",
|
||||
"implementation": "Care group permissions with data segmentation",
|
||||
"auditTrail": "Log all authorization decisions"
|
||||
},
|
||||
"auditLogging": {
|
||||
"requirement": "All PHI access must be logged",
|
||||
"fields": [
|
||||
"User ID",
|
||||
"Patient ID",
|
||||
"Action performed",
|
||||
"Timestamp",
|
||||
"IP address",
|
||||
"Success/failure",
|
||||
"Data accessed"
|
||||
],
|
||||
"retention": "6 years minimum",
|
||||
"integrity": "Logs must be tamper-evident"
|
||||
}
|
||||
},
|
||||
"implementationPatterns": {
|
||||
"backend": {
|
||||
"entities": {
|
||||
"phiFields": "Mark PHI fields with @PHIEncrypted decorator",
|
||||
"auditables": "Extend BaseAuditableEntity for PHI entities",
|
||||
"relationships": "Implement proper access control on relationships"
|
||||
},
|
||||
"controllers": {
|
||||
"authentication": "All PHI endpoints require authentication",
|
||||
"authorization": "Check user permissions before PHI access",
|
||||
"logging": "Log all PHI access attempts",
|
||||
"validation": "Validate all inputs to prevent injection"
|
||||
},
|
||||
"services": {
|
||||
"encryption": "Encrypt PHI before database storage",
|
||||
"decryption": "Decrypt PHI only for authorized access",
|
||||
"minimization": "Return only necessary PHI fields",
|
||||
"auditing": "Create audit log entries for all PHI operations"
|
||||
}
|
||||
},
|
||||
"frontend": {
|
||||
"dataHandling": {
|
||||
"localStorage": "Never store PHI in localStorage",
|
||||
"sessionStorage": "Only encrypted session data allowed",
|
||||
"memory": "Clear PHI from component state on unmount",
|
||||
"logging": "Never log PHI to console or external services"
|
||||
},
|
||||
"ui": {
|
||||
"masking": "Mask sensitive data by default",
|
||||
"permissions": "Hide UI elements based on user roles",
|
||||
"timeout": "Implement session timeout with warnings",
|
||||
"accessibility": "Ensure screen readers don't expose PHI inappropriately"
|
||||
}
|
||||
}
|
||||
},
|
||||
"testingRequirements": {
|
||||
"coverage": {
|
||||
"minimum": "80% for all PHI-handling modules",
|
||||
"focus": "Security and privacy controls"
|
||||
},
|
||||
"security": [
|
||||
"Test for PHI leakage in API responses",
|
||||
"Verify encryption of PHI fields",
|
||||
"Test authorization controls",
|
||||
"Validate audit logging functionality",
|
||||
"Test session timeout behavior"
|
||||
],
|
||||
"compliance": [
|
||||
"Verify minimum necessary access",
|
||||
"Test audit log completeness",
|
||||
"Validate encryption implementation",
|
||||
"Test user access controls",
|
||||
"Verify data retention policies"
|
||||
]
|
||||
},
|
||||
"context7Libraries": [
|
||||
"@nestjs/jwt",
|
||||
"bcrypt",
|
||||
"helmet",
|
||||
"crypto",
|
||||
"jsonwebtoken",
|
||||
"express-rate-limit"
|
||||
],
|
||||
"codeTemplates": {
|
||||
"phiEntity": {
|
||||
"description": "Entity with PHI fields",
|
||||
"template": "@Entity()\nexport class Patient {\n @PHIEncrypted()\n @Column()\n firstName: string;\n\n @AuditableField()\n @Column()\n medicalRecordNumber: string;\n}"
|
||||
},
|
||||
"auditLog": {
|
||||
"description": "Audit log entry",
|
||||
"template": "await this.auditService.log({\n userId: user.id,\n action: 'VIEW_PATIENT',\n resourceType: 'Patient',\n resourceId: patientId,\n ipAddress: request.ip,\n timestamp: new Date()\n});"
|
||||
},
|
||||
"authGuard": {
|
||||
"description": "HIPAA auth guard",
|
||||
"template": "@UseGuards(JwtAuthGuard, RolesGuard)\n@RequirePermission('view_patient_phi')\n@ApiSecurity('bearer')"
|
||||
}
|
||||
},
|
||||
"complianceChecklist": [
|
||||
"All PHI fields are encrypted at rest",
|
||||
"All PHI transmission uses TLS 1.2+",
|
||||
"User authentication is implemented with MFA",
|
||||
"Role-based access control is enforced",
|
||||
"All PHI access is logged and auditable",
|
||||
"Session timeout is configured (max 15 minutes)",
|
||||
"Password policies meet HIPAA requirements",
|
||||
"Data backup and recovery procedures are secure",
|
||||
"Incident response procedures are documented",
|
||||
"Employee access is based on minimum necessary principle"
|
||||
],
|
||||
"riskAssessment": [
|
||||
"Unauthorized access to PHI",
|
||||
"Data breaches due to weak encryption",
|
||||
"Insider threats and inappropriate access",
|
||||
"Data loss due to inadequate backups",
|
||||
"System vulnerabilities and exploits",
|
||||
"Third-party vendor security risks",
|
||||
"Physical security of systems and data",
|
||||
"Network security and access controls",
|
||||
"Application security vulnerabilities",
|
||||
"Business continuity and disaster recovery"
|
||||
],
|
||||
"incidentResponse": [
|
||||
"Identify and contain the incident",
|
||||
"Assess the scope and severity",
|
||||
"Notify affected individuals if required",
|
||||
"Report to HHS if breach affects 500+ individuals",
|
||||
"Implement corrective actions",
|
||||
"Document all incident response activities",
|
||||
"Conduct post-incident review and lessons learned",
|
||||
"Update security policies and procedures",
|
||||
"Provide additional training if needed",
|
||||
"Monitor for similar incidents"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"name": "NestJS Backend",
|
||||
"description": "NestJS backend with TypeORM, PostgreSQL, and comprehensive testing",
|
||||
"filePatterns": ["*.ts", "*.js"],
|
||||
"excludePatterns": ["*.spec.ts", "*.test.ts", "*.d.ts"],
|
||||
"techStack": {
|
||||
"framework": "NestJS",
|
||||
"language": "TypeScript",
|
||||
"database": "TypeORM + PostgreSQL",
|
||||
"validation": "class-validator + class-transformer",
|
||||
"testing": "Jest + Supertest",
|
||||
"documentation": "Swagger/OpenAPI",
|
||||
"caching": "Redis + cache-manager",
|
||||
"queues": "Bull + Redis"
|
||||
},
|
||||
"conventions": {
|
||||
"naming": {
|
||||
"variables": "camelCase",
|
||||
"functions": "camelCase",
|
||||
"classes": "PascalCase",
|
||||
"interfaces": "PascalCase with I prefix",
|
||||
"types": "PascalCase with T prefix",
|
||||
"enums": "PascalCase",
|
||||
"constants": "UPPER_SNAKE_CASE"
|
||||
},
|
||||
"fileStructure": {
|
||||
"modules": "Feature-based modules in src/{feature}/",
|
||||
"controllers": "{feature}.controller.ts",
|
||||
"services": "{feature}.service.ts",
|
||||
"entities": "{feature}.entity.ts",
|
||||
"dtos": "dto/{feature}.dto.ts",
|
||||
"tests": "{feature}.controller.spec.ts, {feature}.service.spec.ts"
|
||||
},
|
||||
"imports": {
|
||||
"style": "Absolute imports with @ prefix when available",
|
||||
"grouping": "Third-party, @nestjs, internal, relative",
|
||||
"sorting": "Alphabetical within groups"
|
||||
}
|
||||
},
|
||||
"qualityChecks": {
|
||||
"lint": {
|
||||
"command": "npx eslint --fix",
|
||||
"config": "Google TypeScript ESLint config",
|
||||
"autoFix": true
|
||||
},
|
||||
"format": {
|
||||
"command": "npx prettier --write",
|
||||
"config": "80 character line limit",
|
||||
"autoFix": true
|
||||
},
|
||||
"build": {
|
||||
"command": "npm run build",
|
||||
"checkTypes": true,
|
||||
"failOnError": true
|
||||
},
|
||||
"test": {
|
||||
"unit": "npm run test:unit",
|
||||
"integration": "npm run test:integration",
|
||||
"coverage": "npm run test:cov",
|
||||
"minimumCoverage": 40
|
||||
}
|
||||
},
|
||||
"codePatterns": {
|
||||
"controller": {
|
||||
"decorators": ["@Controller", "@ApiTags", "@UseGuards"],
|
||||
"methods": ["@Get", "@Post", "@Put", "@Delete", "@Patch"],
|
||||
"responses": ["@ApiResponse", "@ApiOperation"],
|
||||
"validation": ["@Body", "@Param", "@Query with DTOs"],
|
||||
"errorHandling": "Use HttpException and custom exception filters"
|
||||
},
|
||||
"service": {
|
||||
"injection": "Constructor dependency injection with @Injectable",
|
||||
"methods": "Async methods with proper error handling",
|
||||
"database": "Use TypeORM repository pattern",
|
||||
"transactions": "@Transaction decorator for data consistency"
|
||||
},
|
||||
"entity": {
|
||||
"decorators": ["@Entity", "@PrimaryGeneratedColumn", "@Column"],
|
||||
"relationships": ["@ManyToOne", "@OneToMany", "@ManyToMany"],
|
||||
"validation": "class-validator decorators on fields",
|
||||
"timestamps": "Include createdAt, updatedAt with @CreateDateColumn"
|
||||
},
|
||||
"dto": {
|
||||
"validation": "class-validator decorators (@IsString, @IsOptional)",
|
||||
"transformation": "class-transformer decorators (@Transform, @Type)",
|
||||
"swagger": "Swagger decorators (@ApiProperty, @ApiPropertyOptional)",
|
||||
"inheritance": "Use PartialType, PickType for variations"
|
||||
},
|
||||
"testing": {
|
||||
"unit": "Test services and controllers independently with mocks",
|
||||
"integration": "Test complete request/response cycles",
|
||||
"mocking": "Use jest.mock for dependencies",
|
||||
"coverage": "Focus on business logic and edge cases"
|
||||
}
|
||||
},
|
||||
"context7Libraries": [
|
||||
"@nestjs/common",
|
||||
"@nestjs/core",
|
||||
"@nestjs/typeorm",
|
||||
"@nestjs/swagger",
|
||||
"@nestjs/jwt",
|
||||
"@nestjs/passport",
|
||||
"@nestjs/cache-manager",
|
||||
"@nestjs/throttler",
|
||||
"typeorm",
|
||||
"class-validator",
|
||||
"class-transformer",
|
||||
"jest"
|
||||
],
|
||||
"commonImports": {
|
||||
"controller": [
|
||||
"import { Controller, Get, Post, Put, Delete, Patch, Body, Param, Query, UseGuards, HttpException, HttpStatus } from '@nestjs/common';",
|
||||
"import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';"
|
||||
],
|
||||
"service": [
|
||||
"import { Injectable } from '@nestjs/common';",
|
||||
"import { InjectRepository } from '@nestjs/typeorm';",
|
||||
"import { Repository } from 'typeorm';"
|
||||
],
|
||||
"entity": [
|
||||
"import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';",
|
||||
"import { IsString, IsOptional, IsNumber, IsBoolean, IsDate } from 'class-validator';"
|
||||
],
|
||||
"dto": [
|
||||
"import { IsString, IsOptional, IsNumber, IsBoolean, IsEmail, IsArray } from 'class-validator';",
|
||||
"import { Transform, Type } from 'class-transformer';",
|
||||
"import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';"
|
||||
]
|
||||
},
|
||||
"bestPractices": [
|
||||
"Use dependency injection for all services and repositories",
|
||||
"Validate all input data using DTOs with class-validator",
|
||||
"Document all API endpoints with Swagger decorators",
|
||||
"Implement proper error handling with custom exception filters",
|
||||
"Use TypeORM repositories for database operations",
|
||||
"Write unit tests for all services and integration tests for controllers",
|
||||
"Use environment variables for configuration",
|
||||
"Implement rate limiting and security guards",
|
||||
"Use transactions for operations affecting multiple entities",
|
||||
"Follow REST API conventions for endpoint naming"
|
||||
],
|
||||
"securityConsiderations": [
|
||||
"Validate and sanitize all inputs",
|
||||
"Use JWT authentication with proper token validation",
|
||||
"Implement role-based access control (RBAC)",
|
||||
"Use HTTPS in production environments",
|
||||
"Implement rate limiting to prevent abuse",
|
||||
"Hash passwords using bcrypt",
|
||||
"Use parameterized queries to prevent SQL injection",
|
||||
"Implement proper CORS configuration",
|
||||
"Log security-relevant events for auditing",
|
||||
"Use environment variables for sensitive configuration"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"name": "Next.js Fullstack",
|
||||
"description": "Next.js 14+ with App Router, TypeScript, Tailwind CSS, and modern fullstack development",
|
||||
"filePatterns": ["*.tsx", "*.ts", "*.jsx", "*.js"],
|
||||
"excludePatterns": ["*.test.tsx", "*.test.ts", "*.spec.tsx", "*.spec.ts", "*.d.ts"],
|
||||
"techStack": {
|
||||
"framework": "Next.js 14+ with App Router",
|
||||
"language": "TypeScript",
|
||||
"styling": "Tailwind CSS",
|
||||
"database": "Prisma + PostgreSQL",
|
||||
"authentication": "NextAuth.js",
|
||||
"stateManagement": "Zustand + React Query",
|
||||
"testing": "Jest + React Testing Library",
|
||||
"deployment": "Vercel",
|
||||
"api": "Next.js API Routes / Server Actions"
|
||||
},
|
||||
"conventions": {
|
||||
"naming": {
|
||||
"components": "PascalCase (UserProfile.tsx)",
|
||||
"pages": "lowercase with hyphens (user-profile/page.tsx)",
|
||||
"apiRoutes": "lowercase with hyphens (api/user-profile/route.ts)",
|
||||
"hooks": "camelCase with use prefix (useAuth.ts)",
|
||||
"utilities": "camelCase (formatDate.ts)",
|
||||
"constants": "UPPER_SNAKE_CASE",
|
||||
"types": "PascalCase with T prefix"
|
||||
},
|
||||
"fileStructure": {
|
||||
"appRouter": "app/{route}/page.tsx, layout.tsx",
|
||||
"apiRoutes": "app/api/{endpoint}/route.ts",
|
||||
"components": "components/{feature}/{ComponentName}.tsx",
|
||||
"hooks": "hooks/use{HookName}.ts",
|
||||
"libs": "lib/{utility}.ts",
|
||||
"types": "types/{feature}.types.ts",
|
||||
"prisma": "prisma/schema.prisma, prisma/migrations/",
|
||||
"tests": "__tests__/{ComponentName}.test.tsx"
|
||||
},
|
||||
"imports": {
|
||||
"style": "Absolute imports with @ prefix",
|
||||
"grouping": "React/Next, third-party, internal, relative",
|
||||
"sorting": "Alphabetical within groups"
|
||||
}
|
||||
},
|
||||
"qualityChecks": {
|
||||
"lint": {
|
||||
"command": "npx eslint --fix",
|
||||
"config": "Next.js ESLint + TypeScript",
|
||||
"autoFix": true
|
||||
},
|
||||
"format": {
|
||||
"command": "npx prettier --write",
|
||||
"config": "80 character line limit",
|
||||
"autoFix": true
|
||||
},
|
||||
"build": {
|
||||
"command": "npm run build",
|
||||
"checkTypes": true,
|
||||
"failOnError": true
|
||||
},
|
||||
"test": {
|
||||
"unit": "npm test",
|
||||
"coverage": "npm run test:coverage",
|
||||
"minimumCoverage": 75
|
||||
}
|
||||
},
|
||||
"codePatterns": {
|
||||
"page": {
|
||||
"structure": "Default export function with metadata",
|
||||
"metadata": "Use generateMetadata for dynamic SEO",
|
||||
"loading": "Create loading.tsx for loading states",
|
||||
"error": "Create error.tsx for error boundaries",
|
||||
"notFound": "Create not-found.tsx for 404 handling"
|
||||
},
|
||||
"layout": {
|
||||
"structure": "Root layout with html and body tags",
|
||||
"metadata": "Define default metadata and viewport",
|
||||
"providers": "Wrap children with necessary providers",
|
||||
"fonts": "Use next/font for font optimization"
|
||||
},
|
||||
"component": {
|
||||
"client": "Use 'use client' directive for client components",
|
||||
"server": "Default to server components when possible",
|
||||
"props": "Define TypeScript interfaces for props",
|
||||
"memo": "Use React.memo for performance when needed"
|
||||
},
|
||||
"apiRoute": {
|
||||
"structure": "Export named functions (GET, POST, etc.)",
|
||||
"params": "Use typed params and searchParams",
|
||||
"responses": "Return NextResponse with proper status codes",
|
||||
"middleware": "Use middleware for auth and validation"
|
||||
},
|
||||
"serverActions": {
|
||||
"directive": "Use 'use server' directive",
|
||||
"validation": "Validate input data with zod",
|
||||
"revalidation": "Use revalidatePath/revalidateTag",
|
||||
"errors": "Handle errors gracefully"
|
||||
},
|
||||
"database": {
|
||||
"prisma": "Use Prisma Client for database operations",
|
||||
"transactions": "Use Prisma transactions for complex operations",
|
||||
"migrations": "Use Prisma migrate for schema changes",
|
||||
"seeding": "Create seed scripts for development data"
|
||||
}
|
||||
},
|
||||
"context7Libraries": [
|
||||
"next",
|
||||
"react",
|
||||
"@next/font",
|
||||
"next-auth",
|
||||
"@prisma/client",
|
||||
"prisma",
|
||||
"tailwindcss",
|
||||
"zustand",
|
||||
"@tanstack/react-query",
|
||||
"zod"
|
||||
],
|
||||
"commonImports": {
|
||||
"page": ["import { Metadata } from 'next';", "import { notFound } from 'next/navigation';"],
|
||||
"component": [
|
||||
"import React from 'react';",
|
||||
"import Link from 'next/link';",
|
||||
"import Image from 'next/image';"
|
||||
],
|
||||
"apiRoute": [
|
||||
"import { NextRequest, NextResponse } from 'next/server';",
|
||||
"import { getServerSession } from 'next-auth';"
|
||||
],
|
||||
"serverAction": [
|
||||
"import { revalidatePath } from 'next/cache';",
|
||||
"import { redirect } from 'next/navigation';"
|
||||
]
|
||||
},
|
||||
"bestPractices": [
|
||||
"Use App Router instead of Pages Router for new projects",
|
||||
"Default to Server Components, use Client Components only when needed",
|
||||
"Use Next.js Image component for optimized images",
|
||||
"Implement proper SEO with metadata API",
|
||||
"Use Server Actions for form handling and mutations",
|
||||
"Implement proper error handling with error boundaries",
|
||||
"Use Prisma for type-safe database operations",
|
||||
"Implement proper authentication with NextAuth.js",
|
||||
"Use Tailwind CSS for styling with design system approach",
|
||||
"Implement proper loading states and skeleton screens"
|
||||
],
|
||||
"seoOptimization": [
|
||||
"Use generateMetadata for dynamic meta tags",
|
||||
"Implement proper Open Graph and Twitter Card tags",
|
||||
"Use structured data (JSON-LD) where appropriate",
|
||||
"Implement proper canonical URLs",
|
||||
"Use Next.js Image component with alt text",
|
||||
"Implement proper heading hierarchy",
|
||||
"Use semantic HTML elements",
|
||||
"Generate sitemap.xml and robots.txt",
|
||||
"Implement proper internal linking",
|
||||
"Optimize Core Web Vitals"
|
||||
],
|
||||
"performanceOptimizations": [
|
||||
"Use Next.js Image component with proper sizing",
|
||||
"Implement code splitting with dynamic imports",
|
||||
"Use React.lazy and Suspense for component lazy loading",
|
||||
"Optimize fonts with next/font",
|
||||
"Use streaming with loading.tsx files",
|
||||
"Implement proper caching strategies",
|
||||
"Use ISR (Incremental Static Regeneration) when appropriate",
|
||||
"Optimize bundle size with proper imports",
|
||||
"Use web workers for heavy computations",
|
||||
"Implement proper database query optimization"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"name": "Python FastAPI",
|
||||
"description": "FastAPI with SQLAlchemy, Pydantic, and modern Python development practices",
|
||||
"filePatterns": ["*.py"],
|
||||
"excludePatterns": ["*_test.py", "*test*.py", "__pycache__/*"],
|
||||
"techStack": {
|
||||
"framework": "FastAPI",
|
||||
"language": "Python 3.9+",
|
||||
"database": "SQLAlchemy + PostgreSQL",
|
||||
"validation": "Pydantic",
|
||||
"testing": "Pytest + httpx",
|
||||
"documentation": "OpenAPI/Swagger (auto-generated)",
|
||||
"async": "asyncio + asyncpg",
|
||||
"serialization": "Pydantic models"
|
||||
},
|
||||
"conventions": {
|
||||
"naming": {
|
||||
"variables": "snake_case",
|
||||
"functions": "snake_case",
|
||||
"classes": "PascalCase",
|
||||
"constants": "UPPER_SNAKE_CASE",
|
||||
"modules": "lowercase_with_underscores",
|
||||
"packages": "lowercase"
|
||||
},
|
||||
"fileStructure": {
|
||||
"routers": "app/routers/{feature}.py",
|
||||
"models": "app/models/{feature}.py",
|
||||
"schemas": "app/schemas/{feature}.py",
|
||||
"services": "app/services/{feature}.py",
|
||||
"database": "app/database.py",
|
||||
"tests": "tests/test_{feature}.py"
|
||||
},
|
||||
"imports": {
|
||||
"style": "Absolute imports from project root",
|
||||
"grouping": "Standard library, third-party, local",
|
||||
"sorting": "Alphabetical within groups"
|
||||
}
|
||||
},
|
||||
"qualityChecks": {
|
||||
"lint": {
|
||||
"command": "flake8 .",
|
||||
"config": "PEP 8 compliance",
|
||||
"autoFix": false
|
||||
},
|
||||
"format": {
|
||||
"command": "black .",
|
||||
"config": "88 character line limit",
|
||||
"autoFix": true
|
||||
},
|
||||
"typeCheck": {
|
||||
"command": "mypy .",
|
||||
"config": "Strict type checking",
|
||||
"autoFix": false
|
||||
},
|
||||
"build": {
|
||||
"command": "python -m compileall .",
|
||||
"checkSyntax": true,
|
||||
"failOnError": true
|
||||
},
|
||||
"test": {
|
||||
"unit": "pytest tests/",
|
||||
"coverage": "pytest --cov=app tests/",
|
||||
"minimumCoverage": 80
|
||||
}
|
||||
},
|
||||
"codePatterns": {
|
||||
"router": {
|
||||
"structure": "Use APIRouter with proper prefixes and tags",
|
||||
"endpoints": "Async functions with proper HTTP methods",
|
||||
"dependencies": "Use Depends() for dependency injection",
|
||||
"responses": "Type-annotated response models",
|
||||
"errors": "Use HTTPException for error handling"
|
||||
},
|
||||
"model": {
|
||||
"sqlalchemy": "Use SQLAlchemy declarative base",
|
||||
"relationships": "Properly define foreign keys and relationships",
|
||||
"validation": "Include proper field constraints",
|
||||
"timestamps": "Include created_at, updated_at fields"
|
||||
},
|
||||
"schema": {
|
||||
"pydantic": "Use Pydantic BaseModel for request/response schemas",
|
||||
"validation": "Include proper field validation",
|
||||
"serialization": "Configure proper serialization options",
|
||||
"inheritance": "Use inheritance for variations (Create, Update, Response)"
|
||||
},
|
||||
"service": {
|
||||
"async": "Use async/await for database operations",
|
||||
"transactions": "Implement proper transaction handling",
|
||||
"error_handling": "Comprehensive error handling with custom exceptions",
|
||||
"logging": "Structured logging for debugging and monitoring"
|
||||
},
|
||||
"testing": {
|
||||
"fixtures": "Use pytest fixtures for test setup",
|
||||
"client": "Use TestClient for endpoint testing",
|
||||
"database": "Use separate test database",
|
||||
"mocking": "Mock external dependencies and services"
|
||||
}
|
||||
},
|
||||
"context7Libraries": [
|
||||
"fastapi",
|
||||
"sqlalchemy",
|
||||
"pydantic",
|
||||
"pytest",
|
||||
"httpx",
|
||||
"asyncpg",
|
||||
"uvicorn",
|
||||
"alembic"
|
||||
],
|
||||
"commonImports": {
|
||||
"router": [
|
||||
"from fastapi import APIRouter, Depends, HTTPException, status",
|
||||
"from sqlalchemy.orm import Session",
|
||||
"from app.database import get_db"
|
||||
],
|
||||
"model": [
|
||||
"from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean",
|
||||
"from sqlalchemy.ext.declarative import declarative_base",
|
||||
"from sqlalchemy.orm import relationship",
|
||||
"from datetime import datetime"
|
||||
],
|
||||
"schema": [
|
||||
"from pydantic import BaseModel, EmailStr, validator",
|
||||
"from typing import Optional, List",
|
||||
"from datetime import datetime"
|
||||
],
|
||||
"service": [
|
||||
"from sqlalchemy.orm import Session",
|
||||
"from sqlalchemy.exc import IntegrityError",
|
||||
"from fastapi import HTTPException, status"
|
||||
]
|
||||
},
|
||||
"bestPractices": [
|
||||
"Use async/await for I/O operations",
|
||||
"Implement proper dependency injection with Depends()",
|
||||
"Use Pydantic models for request/response validation",
|
||||
"Follow PEP 8 style guidelines",
|
||||
"Use type hints for all functions and variables",
|
||||
"Implement proper error handling with HTTP status codes",
|
||||
"Use SQLAlchemy for database operations with proper relationships",
|
||||
"Write comprehensive tests with pytest",
|
||||
"Use environment variables for configuration",
|
||||
"Implement proper logging for debugging and monitoring"
|
||||
],
|
||||
"securityConsiderations": [
|
||||
"Validate and sanitize all input data using Pydantic",
|
||||
"Use proper authentication and authorization mechanisms",
|
||||
"Hash passwords using secure algorithms (bcrypt)",
|
||||
"Implement rate limiting to prevent abuse",
|
||||
"Use HTTPS in production environments",
|
||||
"Validate JWT tokens properly",
|
||||
"Use parameterized queries to prevent SQL injection",
|
||||
"Implement proper CORS configuration",
|
||||
"Log security-relevant events for auditing",
|
||||
"Use environment variables for sensitive configuration"
|
||||
],
|
||||
"asyncPatterns": [
|
||||
"Use async def for route handlers that perform I/O",
|
||||
"Use asyncio.gather() for concurrent operations",
|
||||
"Implement proper connection pooling for database",
|
||||
"Use async context managers for resource management",
|
||||
"Handle exceptions properly in async functions",
|
||||
"Use asyncio.create_task() for background tasks",
|
||||
"Implement proper shutdown handling for async resources",
|
||||
"Use async generators for streaming responses",
|
||||
"Avoid blocking operations in async functions",
|
||||
"Use proper async testing patterns with pytest-asyncio"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"name": "React Frontend",
|
||||
"description": "React 18+ with TypeScript, Tailwind CSS, and modern development practices",
|
||||
"filePatterns": ["*.tsx", "*.ts", "*.jsx", "*.js"],
|
||||
"excludePatterns": ["*.test.tsx", "*.test.ts", "*.spec.tsx", "*.spec.ts", "*.d.ts"],
|
||||
"techStack": {
|
||||
"framework": "React 18+",
|
||||
"language": "TypeScript",
|
||||
"styling": "Tailwind CSS",
|
||||
"stateManagement": "React Query + Context API",
|
||||
"routing": "React Router",
|
||||
"testing": "React Testing Library + Jest",
|
||||
"bundler": "Create React App / Vite",
|
||||
"icons": "Heroicons + Lucide React",
|
||||
"charts": "Chart.js + react-chartjs-2"
|
||||
},
|
||||
"conventions": {
|
||||
"naming": {
|
||||
"components": "PascalCase (UserProfile.tsx)",
|
||||
"hooks": "camelCase with use prefix (useAuth.ts)",
|
||||
"utilities": "camelCase (formatDate.ts)",
|
||||
"constants": "UPPER_SNAKE_CASE",
|
||||
"types": "PascalCase with T prefix",
|
||||
"interfaces": "PascalCase with I prefix"
|
||||
},
|
||||
"fileStructure": {
|
||||
"components": "src/components/{feature}/{ComponentName}.tsx",
|
||||
"hooks": "src/hooks/use{HookName}.ts",
|
||||
"services": "src/services/{feature}.service.ts",
|
||||
"types": "src/types/{feature}.types.ts",
|
||||
"contexts": "src/contexts/{Feature}Context.tsx",
|
||||
"pages": "src/pages/{PageName}.tsx",
|
||||
"tests": "src/components/{feature}/__tests__/{ComponentName}.test.tsx"
|
||||
},
|
||||
"imports": {
|
||||
"style": "Absolute imports with @ prefix when available",
|
||||
"grouping": "React, third-party, internal, relative",
|
||||
"sorting": "Alphabetical within groups"
|
||||
}
|
||||
},
|
||||
"qualityChecks": {
|
||||
"lint": {
|
||||
"command": "npx eslint --fix",
|
||||
"config": "ESLint React + TypeScript + a11y",
|
||||
"autoFix": true
|
||||
},
|
||||
"format": {
|
||||
"command": "npx prettier --write",
|
||||
"config": "80 character line limit, single quotes",
|
||||
"autoFix": true
|
||||
},
|
||||
"build": {
|
||||
"command": "npm run build",
|
||||
"checkTypes": true,
|
||||
"failOnError": true
|
||||
},
|
||||
"test": {
|
||||
"unit": "npm test",
|
||||
"coverage": "npm run test:coverage",
|
||||
"minimumCoverage": 70
|
||||
}
|
||||
},
|
||||
"codePatterns": {
|
||||
"component": {
|
||||
"structure": "Functional components with TypeScript interfaces",
|
||||
"props": "Define interface for component props",
|
||||
"state": "Use useState, useReducer for local state",
|
||||
"effects": "Use useEffect with proper cleanup",
|
||||
"memo": "Use React.memo for performance optimization when needed",
|
||||
"forwardRef": "Use forwardRef for components that need ref access"
|
||||
},
|
||||
"hooks": {
|
||||
"custom": "Extract reusable logic into custom hooks",
|
||||
"naming": "Always start with 'use' prefix",
|
||||
"dependencies": "Properly declare useEffect dependencies",
|
||||
"cleanup": "Return cleanup functions from useEffect when needed"
|
||||
},
|
||||
"styling": {
|
||||
"tailwind": "Use Tailwind utility classes",
|
||||
"responsive": "Mobile-first responsive design",
|
||||
"darkMode": "Support dark mode with Tailwind dark: prefix",
|
||||
"accessibility": "Include proper ARIA labels and keyboard navigation"
|
||||
},
|
||||
"stateManagement": {
|
||||
"local": "useState for component-local state",
|
||||
"global": "Context API for app-wide state",
|
||||
"server": "React Query for server state management",
|
||||
"forms": "Controlled components with validation"
|
||||
},
|
||||
"testing": {
|
||||
"render": "Use @testing-library/react render method",
|
||||
"queries": "Use semantic queries (getByRole, getByLabelText)",
|
||||
"userEvents": "Use @testing-library/user-event for interactions",
|
||||
"mocking": "Mock external dependencies and API calls",
|
||||
"accessibility": "Test with screen reader and keyboard navigation"
|
||||
}
|
||||
},
|
||||
"context7Libraries": [
|
||||
"react",
|
||||
"react-dom",
|
||||
"react-router-dom",
|
||||
"@tanstack/react-query",
|
||||
"tailwindcss",
|
||||
"@testing-library/react",
|
||||
"@testing-library/user-event",
|
||||
"@heroicons/react",
|
||||
"lucide-react",
|
||||
"chart.js"
|
||||
],
|
||||
"commonImports": {
|
||||
"component": [
|
||||
"import React, { useState, useEffect, useCallback, useMemo } from 'react';",
|
||||
"import { useNavigate, useParams } from 'react-router-dom';"
|
||||
],
|
||||
"hook": [
|
||||
"import { useState, useEffect, useCallback, useContext } from 'react';",
|
||||
"import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';"
|
||||
],
|
||||
"test": [
|
||||
"import { render, screen, fireEvent, waitFor } from '@testing-library/react';",
|
||||
"import userEvent from '@testing-library/user-event';",
|
||||
"import { BrowserRouter } from 'react-router-dom';"
|
||||
]
|
||||
},
|
||||
"bestPractices": [
|
||||
"Use functional components with hooks instead of class components",
|
||||
"Extract custom hooks for reusable stateful logic",
|
||||
"Use React.memo for performance optimization when appropriate",
|
||||
"Implement proper error boundaries for error handling",
|
||||
"Use React Query for server state management",
|
||||
"Follow accessibility guidelines (WCAG 2.1)",
|
||||
"Implement responsive design with mobile-first approach",
|
||||
"Use TypeScript for type safety",
|
||||
"Write comprehensive tests for components and hooks",
|
||||
"Optimize bundle size with code splitting and lazy loading"
|
||||
],
|
||||
"accessibilityRequirements": [
|
||||
"Provide meaningful alt text for images",
|
||||
"Use semantic HTML elements",
|
||||
"Ensure proper heading hierarchy (h1, h2, h3)",
|
||||
"Include ARIA labels for interactive elements",
|
||||
"Support keyboard navigation for all interactive elements",
|
||||
"Maintain sufficient color contrast ratios",
|
||||
"Provide focus indicators for keyboard users",
|
||||
"Use role attributes when semantic HTML is insufficient",
|
||||
"Test with screen readers",
|
||||
"Ensure form fields have associated labels"
|
||||
],
|
||||
"performanceOptimizations": [
|
||||
"Use React.lazy for code splitting",
|
||||
"Implement virtualization for long lists",
|
||||
"Use useMemo and useCallback to prevent unnecessary re-renders",
|
||||
"Optimize images with proper formats and sizes",
|
||||
"Use React.memo for components that receive stable props",
|
||||
"Implement proper error boundaries",
|
||||
"Use React Query for efficient data fetching and caching",
|
||||
"Minimize bundle size by importing only needed modules",
|
||||
"Use web workers for heavy computations",
|
||||
"Implement proper loading states and skeleton screens"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"name": "API Development Workflow",
|
||||
"description": "Standardized workflow for REST/GraphQL API endpoint development",
|
||||
"workflowType": "api-development",
|
||||
"applicablePatterns": ["REST", "GraphQL", "WebSocket"],
|
||||
"phases": {
|
||||
"planning": {
|
||||
"description": "API design and specification phase",
|
||||
"activities": [
|
||||
"Define API contract and OpenAPI specification",
|
||||
"Design request/response schemas",
|
||||
"Plan error handling and status codes",
|
||||
"Consider rate limiting and pagination",
|
||||
"Document authentication and authorization requirements"
|
||||
]
|
||||
},
|
||||
"implementation": {
|
||||
"description": "Core API implementation phase",
|
||||
"activities": [
|
||||
"Create controller/resolver with proper routing",
|
||||
"Implement service layer with business logic",
|
||||
"Add input validation and sanitization",
|
||||
"Implement proper error handling",
|
||||
"Add authentication and authorization guards"
|
||||
]
|
||||
},
|
||||
"testing": {
|
||||
"description": "Comprehensive API testing phase",
|
||||
"activities": [
|
||||
"Write unit tests for service layer",
|
||||
"Create integration tests for endpoints",
|
||||
"Test error scenarios and edge cases",
|
||||
"Validate API documentation accuracy",
|
||||
"Perform security testing"
|
||||
]
|
||||
},
|
||||
"documentation": {
|
||||
"description": "API documentation and examples",
|
||||
"activities": [
|
||||
"Generate/update OpenAPI documentation",
|
||||
"Create usage examples and tutorials",
|
||||
"Document rate limits and quotas",
|
||||
"Add error code documentation",
|
||||
"Update API versioning information"
|
||||
]
|
||||
}
|
||||
},
|
||||
"implementationPatterns": {
|
||||
"controller": {
|
||||
"structure": "Thin controller with business logic in services",
|
||||
"validation": "Use DTOs for input validation",
|
||||
"responses": "Standardized response format",
|
||||
"errors": "Consistent error handling middleware",
|
||||
"documentation": "Comprehensive API documentation decorators"
|
||||
},
|
||||
"service": {
|
||||
"business_logic": "Core business logic implementation",
|
||||
"data_access": "Repository pattern for data operations",
|
||||
"transactions": "Database transaction management",
|
||||
"caching": "Implement caching where appropriate",
|
||||
"external_apis": "Handle external API integrations"
|
||||
},
|
||||
"validation": {
|
||||
"input": "Validate all input parameters and body data",
|
||||
"sanitization": "Sanitize inputs to prevent injection attacks",
|
||||
"authorization": "Verify user permissions for operations",
|
||||
"rate_limiting": "Implement appropriate rate limiting",
|
||||
"idempotency": "Support idempotent operations where needed"
|
||||
},
|
||||
"responses": {
|
||||
"success": "Consistent success response format",
|
||||
"errors": "Standardized error response structure",
|
||||
"pagination": "Implement cursor or offset pagination",
|
||||
"filtering": "Support query filtering and sorting",
|
||||
"versioning": "Handle API versioning appropriately"
|
||||
}
|
||||
},
|
||||
"qualityGates": {
|
||||
"pre_implementation": [
|
||||
"API specification reviewed and approved",
|
||||
"Data models and schemas defined",
|
||||
"Authentication requirements clarified",
|
||||
"Rate limiting strategy determined",
|
||||
"Error handling approach documented"
|
||||
],
|
||||
"implementation": [
|
||||
"Code follows established patterns and conventions",
|
||||
"Input validation implemented for all parameters",
|
||||
"Proper error handling and logging added",
|
||||
"Authentication and authorization enforced",
|
||||
"Business logic separated from controller logic"
|
||||
],
|
||||
"testing": [
|
||||
"Unit tests cover all service methods",
|
||||
"Integration tests validate API contracts",
|
||||
"Error scenarios properly tested",
|
||||
"Performance tests pass acceptance criteria",
|
||||
"Security tests identify no critical vulnerabilities"
|
||||
],
|
||||
"deployment": [
|
||||
"API documentation is accurate and complete",
|
||||
"Monitoring and alerting configured",
|
||||
"Database migrations applied successfully",
|
||||
"Configuration validated in target environment",
|
||||
"Rollback procedures documented and tested"
|
||||
]
|
||||
},
|
||||
"testingStrategy": {
|
||||
"unit_tests": {
|
||||
"scope": "Individual service methods and business logic",
|
||||
"mocking": "Mock external dependencies and database",
|
||||
"coverage": "Minimum 80% code coverage",
|
||||
"focus": "Business logic and edge cases"
|
||||
},
|
||||
"integration_tests": {
|
||||
"scope": "Full API endpoint testing with real database",
|
||||
"scenarios": "Happy path and error scenarios",
|
||||
"data": "Use test fixtures and factories",
|
||||
"cleanup": "Clean up test data after each test"
|
||||
},
|
||||
"contract_tests": {
|
||||
"scope": "API contract validation",
|
||||
"tools": "OpenAPI validation and contract testing",
|
||||
"versioning": "Backward compatibility testing",
|
||||
"documentation": "Ensure examples work correctly"
|
||||
},
|
||||
"performance_tests": {
|
||||
"scope": "Load and stress testing",
|
||||
"metrics": "Response time, throughput, resource usage",
|
||||
"scenarios": "Normal and peak load conditions",
|
||||
"bottlenecks": "Identify and address performance issues"
|
||||
},
|
||||
"security_tests": {
|
||||
"scope": "Authentication, authorization, and input validation",
|
||||
"scenarios": "SQL injection, XSS, authentication bypass",
|
||||
"tools": "Automated security scanning",
|
||||
"compliance": "Ensure regulatory compliance requirements"
|
||||
}
|
||||
},
|
||||
"codeTemplates": {
|
||||
"restController": {
|
||||
"framework": "universal",
|
||||
"template": "// REST Controller Template\n@Controller('/api/v1/users')\n@ApiTags('users')\nexport class UsersController {\n constructor(private usersService: UsersService) {}\n\n @Get()\n @ApiOperation({ summary: 'Get all users' })\n @ApiResponse({ status: 200, description: 'Users retrieved successfully' })\n async getUsers(@Query() query: GetUsersDto): Promise<ApiResponse<User[]>> {\n const users = await this.usersService.getUsers(query);\n return { data: users, message: 'Users retrieved successfully' };\n }\n\n @Post()\n @ApiOperation({ summary: 'Create new user' })\n @ApiResponse({ status: 201, description: 'User created successfully' })\n async createUser(@Body() createUserDto: CreateUserDto): Promise<ApiResponse<User>> {\n const user = await this.usersService.createUser(createUserDto);\n return { data: user, message: 'User created successfully' };\n }\n}"
|
||||
},
|
||||
"serviceLayer": {
|
||||
"framework": "universal",
|
||||
"template": "// Service Layer Template\n@Injectable()\nexport class UsersService {\n constructor(private usersRepository: UsersRepository) {}\n\n async getUsers(query: GetUsersDto): Promise<User[]> {\n try {\n const users = await this.usersRepository.findWithFilters(query);\n return users;\n } catch (error) {\n throw new ServiceException('Failed to retrieve users', error);\n }\n }\n\n async createUser(createUserDto: CreateUserDto): Promise<User> {\n try {\n const existingUser = await this.usersRepository.findByEmail(createUserDto.email);\n if (existingUser) {\n throw new ConflictException('User with this email already exists');\n }\n \n const user = await this.usersRepository.create(createUserDto);\n return user;\n } catch (error) {\n throw new ServiceException('Failed to create user', error);\n }\n }\n}"
|
||||
},
|
||||
"integrationTest": {
|
||||
"framework": "universal",
|
||||
"template": "// Integration Test Template\ndescribe('Users API', () => {\n let app: TestingModule;\n let httpServer: any;\n\n beforeAll(async () => {\n app = await Test.createTestingModule({\n imports: [AppModule],\n }).compile();\n \n httpServer = app.createNestApplication();\n await httpServer.init();\n });\n\n describe('GET /api/v1/users', () => {\n it('should return users list', async () => {\n const response = await request(httpServer)\n .get('/api/v1/users')\n .expect(200);\n\n expect(response.body.data).toBeInstanceOf(Array);\n expect(response.body.message).toBe('Users retrieved successfully');\n });\n\n it('should handle pagination', async () => {\n const response = await request(httpServer)\n .get('/api/v1/users?page=1&limit=10')\n .expect(200);\n\n expect(response.body.data.length).toBeLessThanOrEqual(10);\n });\n });\n\n describe('POST /api/v1/users', () => {\n it('should create new user', async () => {\n const newUser = {\n name: 'John Doe',\n email: 'john@example.com'\n };\n\n const response = await request(httpServer)\n .post('/api/v1/users')\n .send(newUser)\n .expect(201);\n\n expect(response.body.data.name).toBe(newUser.name);\n expect(response.body.data.email).toBe(newUser.email);\n });\n\n it('should validate required fields', async () => {\n const response = await request(httpServer)\n .post('/api/v1/users')\n .send({})\n .expect(400);\n\n expect(response.body.errors).toBeDefined();\n });\n });\n});"
|
||||
}
|
||||
},
|
||||
"bestPractices": [
|
||||
"Use consistent REST conventions for endpoint naming",
|
||||
"Implement proper HTTP status codes for different scenarios",
|
||||
"Add comprehensive input validation and sanitization",
|
||||
"Use DTOs for request/response data structures",
|
||||
"Implement proper error handling with meaningful messages",
|
||||
"Add rate limiting to prevent API abuse",
|
||||
"Use pagination for endpoints returning large datasets",
|
||||
"Implement API versioning strategy from the start",
|
||||
"Add comprehensive logging for debugging and monitoring",
|
||||
"Use dependency injection for better testability",
|
||||
"Implement proper authentication and authorization",
|
||||
"Add API documentation with examples and use cases"
|
||||
],
|
||||
"commonPitfalls": [
|
||||
"Putting business logic directly in controllers",
|
||||
"Not validating input parameters properly",
|
||||
"Inconsistent error handling and response formats",
|
||||
"Missing or outdated API documentation",
|
||||
"Not implementing proper pagination",
|
||||
"Ignoring rate limiting and abuse prevention",
|
||||
"Poor error messages that don't help clients",
|
||||
"Not versioning APIs properly",
|
||||
"Missing or inadequate logging",
|
||||
"Not testing error scenarios thoroughly",
|
||||
"Exposing sensitive information in error responses",
|
||||
"Not handling database connection failures gracefully"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"name": "Frontend Component Development",
|
||||
"description": "Standardized workflow for React/Vue component development with accessibility and testing",
|
||||
"workflowType": "frontend-component",
|
||||
"applicablePatterns": ["React", "Vue", "Angular", "Web Components"],
|
||||
"phases": {
|
||||
"design": {
|
||||
"description": "Component design and specification phase",
|
||||
"activities": [
|
||||
"Define component API and props interface",
|
||||
"Create component design system documentation",
|
||||
"Plan responsive behavior and breakpoints",
|
||||
"Design accessibility features and ARIA labels",
|
||||
"Consider component composition and reusability"
|
||||
]
|
||||
},
|
||||
"implementation": {
|
||||
"description": "Core component implementation phase",
|
||||
"activities": [
|
||||
"Create component with TypeScript interfaces",
|
||||
"Implement responsive styling with CSS/Tailwind",
|
||||
"Add accessibility features (ARIA, keyboard navigation)",
|
||||
"Implement component state management",
|
||||
"Add proper error boundaries and loading states"
|
||||
]
|
||||
},
|
||||
"testing": {
|
||||
"description": "Comprehensive component testing phase",
|
||||
"activities": [
|
||||
"Write unit tests for component logic",
|
||||
"Create integration tests with user interactions",
|
||||
"Test accessibility with screen readers",
|
||||
"Validate responsive behavior across devices",
|
||||
"Test component with different prop combinations"
|
||||
]
|
||||
},
|
||||
"documentation": {
|
||||
"description": "Component documentation and examples",
|
||||
"activities": [
|
||||
"Create Storybook stories for all variants",
|
||||
"Document component API and usage examples",
|
||||
"Add accessibility guidelines and best practices",
|
||||
"Create interactive documentation",
|
||||
"Document component performance characteristics"
|
||||
]
|
||||
}
|
||||
},
|
||||
"implementationPatterns": {
|
||||
"structure": {
|
||||
"functional": "Use functional components with hooks",
|
||||
"typescript": "Define proper TypeScript interfaces for props",
|
||||
"composition": "Design for component composition and reusability",
|
||||
"separation": "Separate logic, presentation, and styling concerns",
|
||||
"naming": "Use descriptive and consistent naming conventions"
|
||||
},
|
||||
"styling": {
|
||||
"responsive": "Mobile-first responsive design approach",
|
||||
"design_tokens": "Use design tokens for consistency",
|
||||
"css_modules": "Scoped styling to prevent conflicts",
|
||||
"accessibility": "Ensure sufficient color contrast and focus indicators",
|
||||
"dark_mode": "Support light and dark theme variations"
|
||||
},
|
||||
"accessibility": {
|
||||
"semantic_html": "Use semantic HTML elements when possible",
|
||||
"aria_labels": "Add appropriate ARIA labels and descriptions",
|
||||
"keyboard_nav": "Implement full keyboard navigation support",
|
||||
"screen_readers": "Ensure screen reader compatibility",
|
||||
"focus_management": "Proper focus management and indicators"
|
||||
},
|
||||
"state_management": {
|
||||
"local_state": "Use useState for component-local state",
|
||||
"side_effects": "Use useEffect with proper cleanup",
|
||||
"performance": "Use useMemo and useCallback for optimization",
|
||||
"context": "Use React Context for component tree state",
|
||||
"forms": "Controlled components with proper validation"
|
||||
},
|
||||
"error_handling": {
|
||||
"boundaries": "Implement error boundaries for error containment",
|
||||
"validation": "Input validation with user-friendly messages",
|
||||
"loading_states": "Proper loading and skeleton states",
|
||||
"fallbacks": "Graceful degradation for component failures",
|
||||
"user_feedback": "Clear feedback for user actions"
|
||||
}
|
||||
},
|
||||
"qualityGates": {
|
||||
"design": [
|
||||
"Component API designed with reusability in mind",
|
||||
"Accessibility requirements identified and documented",
|
||||
"Responsive behavior planned for all breakpoints",
|
||||
"Design tokens and styling approach determined",
|
||||
"Component composition strategy defined"
|
||||
],
|
||||
"implementation": [
|
||||
"TypeScript interfaces defined for all props",
|
||||
"Component implements planned accessibility features",
|
||||
"Responsive behavior works across all target devices",
|
||||
"Component follows established coding patterns",
|
||||
"Error handling and edge cases addressed"
|
||||
],
|
||||
"testing": [
|
||||
"Unit tests cover all component logic and edge cases",
|
||||
"Accessibility tests pass with screen reader testing",
|
||||
"Integration tests validate user interaction flows",
|
||||
"Visual regression tests prevent styling issues",
|
||||
"Performance tests meet established benchmarks"
|
||||
],
|
||||
"documentation": [
|
||||
"Storybook stories demonstrate all component variants",
|
||||
"API documentation is complete and accurate",
|
||||
"Usage examples and best practices documented",
|
||||
"Accessibility guidelines provided",
|
||||
"Performance characteristics documented"
|
||||
]
|
||||
},
|
||||
"testingStrategy": {
|
||||
"unit_tests": {
|
||||
"scope": "Component logic, prop handling, and state changes",
|
||||
"tools": "React Testing Library, Jest",
|
||||
"coverage": "Minimum 85% code coverage",
|
||||
"focus": "User interactions and business logic"
|
||||
},
|
||||
"accessibility_tests": {
|
||||
"scope": "ARIA labels, keyboard navigation, screen reader compatibility",
|
||||
"tools": "axe-core, @testing-library/jest-dom",
|
||||
"manual": "Manual testing with actual screen readers",
|
||||
"standards": "WCAG 2.1 AA compliance"
|
||||
},
|
||||
"visual_tests": {
|
||||
"scope": "Component appearance across different states",
|
||||
"tools": "Chromatic, Percy, or similar visual testing",
|
||||
"devices": "Test across multiple device sizes",
|
||||
"themes": "Test light/dark theme variations"
|
||||
},
|
||||
"integration_tests": {
|
||||
"scope": "Component behavior within larger application context",
|
||||
"user_flows": "End-to-end user interaction scenarios",
|
||||
"data_flow": "Test with real or realistic data",
|
||||
"performance": "Component performance under load"
|
||||
},
|
||||
"responsive_tests": {
|
||||
"scope": "Component behavior across different screen sizes",
|
||||
"breakpoints": "Test all defined responsive breakpoints",
|
||||
"orientation": "Portrait and landscape orientations",
|
||||
"devices": "Physical device testing when possible"
|
||||
}
|
||||
},
|
||||
"codeTemplates": {
|
||||
"reactComponent": {
|
||||
"framework": "React",
|
||||
"template": "import React, { useState, useEffect, useCallback } from 'react';\nimport { cn } from '@/lib/utils';\n\ninterface ComponentNameProps {\n /** Primary content for the component */\n children?: React.ReactNode;\n /** Additional CSS class names */\n className?: string;\n /** Component variant */\n variant?: 'primary' | 'secondary' | 'outline';\n /** Component size */\n size?: 'sm' | 'md' | 'lg';\n /** Disabled state */\n disabled?: boolean;\n /** Click handler */\n onClick?: () => void;\n}\n\n/**\n * ComponentName - Brief description of what this component does\n * \n * @example\n * <ComponentName variant=\"primary\" size=\"md\">\n * Content goes here\n * </ComponentName>\n */\nexport const ComponentName: React.FC<ComponentNameProps> = ({\n children,\n className,\n variant = 'primary',\n size = 'md',\n disabled = false,\n onClick,\n ...props\n}) => {\n const [isActive, setIsActive] = useState(false);\n\n const handleClick = useCallback(() => {\n if (!disabled && onClick) {\n onClick();\n }\n }, [disabled, onClick]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleClick();\n }\n }, [handleClick]);\n\n return (\n <button\n className={cn(\n // Base styles\n 'inline-flex items-center justify-center rounded-md font-medium transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n \n // Variant styles\n {\n 'bg-primary text-primary-foreground hover:bg-primary/90': variant === 'primary',\n 'bg-secondary text-secondary-foreground hover:bg-secondary/80': variant === 'secondary',\n 'border border-input hover:bg-accent hover:text-accent-foreground': variant === 'outline',\n },\n \n // Size styles\n {\n 'h-8 px-3 text-sm': size === 'sm',\n 'h-10 px-4 py-2': size === 'md',\n 'h-12 px-6 text-lg': size === 'lg',\n },\n \n // State styles\n {\n 'opacity-50 cursor-not-allowed': disabled,\n },\n \n className\n )}\n disabled={disabled}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n {...props}\n >\n {children}\n </button>\n );\n};\n\nComponentName.displayName = 'ComponentName';"
|
||||
},
|
||||
"componentTest": {
|
||||
"framework": "React Testing Library",
|
||||
"template": "import { render, screen, fireEvent, waitFor } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { axe, toHaveNoViolations } from 'jest-axe';\nimport { ComponentName } from './ComponentName';\n\n// Extend Jest matchers\nexpect.extend(toHaveNoViolations);\n\ndescribe('ComponentName', () => {\n const user = userEvent.setup();\n\n it('renders with default props', () => {\n render(<ComponentName>Test Content</ComponentName>);\n \n const button = screen.getByRole('button', { name: 'Test Content' });\n expect(button).toBeInTheDocument();\n expect(button).toHaveClass('bg-primary'); // default variant\n });\n\n it('handles click events', async () => {\n const handleClick = jest.fn();\n render(\n <ComponentName onClick={handleClick}>\n Click me\n </ComponentName>\n );\n\n const button = screen.getByRole('button', { name: 'Click me' });\n await user.click(button);\n \n expect(handleClick).toHaveBeenCalledTimes(1);\n });\n\n it('supports keyboard navigation', async () => {\n const handleClick = jest.fn();\n render(\n <ComponentName onClick={handleClick}>\n Press Enter\n </ComponentName>\n );\n\n const button = screen.getByRole('button', { name: 'Press Enter' });\n button.focus();\n \n await user.keyboard('{Enter}');\n expect(handleClick).toHaveBeenCalledTimes(1);\n \n await user.keyboard(' ');\n expect(handleClick).toHaveBeenCalledTimes(2);\n });\n\n it('handles disabled state correctly', async () => {\n const handleClick = jest.fn();\n render(\n <ComponentName disabled onClick={handleClick}>\n Disabled\n </ComponentName>\n );\n\n const button = screen.getByRole('button', { name: 'Disabled' });\n expect(button).toBeDisabled();\n expect(button).toHaveAttribute('aria-disabled', 'true');\n \n await user.click(button);\n expect(handleClick).not.toHaveBeenCalled();\n });\n\n it('applies correct variant styles', () => {\n const { rerender } = render(\n <ComponentName variant=\"secondary\">\n Secondary\n </ComponentName>\n );\n \n let button = screen.getByRole('button');\n expect(button).toHaveClass('bg-secondary');\n \n rerender(\n <ComponentName variant=\"outline\">\n Outline\n </ComponentName>\n );\n \n button = screen.getByRole('button');\n expect(button).toHaveClass('border');\n });\n\n it('has no accessibility violations', async () => {\n const { container } = render(\n <ComponentName>\n Accessible Button\n </ComponentName>\n );\n \n const results = await axe(container);\n expect(results).toHaveNoViolations();\n });\n\n it('supports custom className', () => {\n render(\n <ComponentName className=\"custom-class\">\n Custom\n </ComponentName>\n );\n \n const button = screen.getByRole('button');\n expect(button).toHaveClass('custom-class');\n });\n});"
|
||||
},
|
||||
"storybookStory": {
|
||||
"framework": "Storybook",
|
||||
"template": "import type { Meta, StoryObj } from '@storybook/react';\nimport { ComponentName } from './ComponentName';\n\nconst meta: Meta<typeof ComponentName> = {\n title: 'Components/ComponentName',\n component: ComponentName,\n parameters: {\n layout: 'centered',\n docs: {\n description: {\n component: 'A versatile button component with multiple variants and sizes.',\n },\n },\n },\n argTypes: {\n variant: {\n control: 'select',\n options: ['primary', 'secondary', 'outline'],\n description: 'The visual variant of the button',\n },\n size: {\n control: 'select', \n options: ['sm', 'md', 'lg'],\n description: 'The size of the button',\n },\n disabled: {\n control: 'boolean',\n description: 'Whether the button is disabled',\n },\n onClick: {\n action: 'clicked',\n description: 'Function called when button is clicked',\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\n// Default story\nexport const Default: Story = {\n args: {\n children: 'Button',\n variant: 'primary',\n size: 'md',\n disabled: false,\n },\n};\n\n// Variants showcase\nexport const Variants: Story = {\n render: () => (\n <div className=\"flex gap-4\">\n <ComponentName variant=\"primary\">Primary</ComponentName>\n <ComponentName variant=\"secondary\">Secondary</ComponentName>\n <ComponentName variant=\"outline\">Outline</ComponentName>\n </div>\n ),\n};\n\n// Sizes showcase\nexport const Sizes: Story = {\n render: () => (\n <div className=\"flex items-center gap-4\">\n <ComponentName size=\"sm\">Small</ComponentName>\n <ComponentName size=\"md\">Medium</ComponentName>\n <ComponentName size=\"lg\">Large</ComponentName>\n </div>\n ),\n};\n\n// Disabled state\nexport const Disabled: Story = {\n args: {\n children: 'Disabled Button',\n disabled: true,\n },\n};\n\n// Interactive example\nexport const Interactive: Story = {\n render: () => {\n const [count, setCount] = React.useState(0);\n return (\n <div className=\"text-center\">\n <p className=\"mb-4\">Count: {count}</p>\n <ComponentName onClick={() => setCount(count + 1)}>\n Increment\n </ComponentName>\n </div>\n );\n },\n};"
|
||||
}
|
||||
},
|
||||
"accessibilityRequirements": [
|
||||
"Use semantic HTML elements when possible (button, input, etc.)",
|
||||
"Provide meaningful alt text for images",
|
||||
"Ensure sufficient color contrast (4.5:1 for normal text)",
|
||||
"Support keyboard navigation for all interactive elements",
|
||||
"Use ARIA labels and descriptions where needed",
|
||||
"Implement proper focus management and indicators",
|
||||
"Support screen readers with appropriate ARIA attributes",
|
||||
"Test with actual assistive technologies",
|
||||
"Provide skip links for navigation",
|
||||
"Use proper heading hierarchy",
|
||||
"Ensure form labels are properly associated",
|
||||
"Implement error states with clear messaging"
|
||||
],
|
||||
"performanceOptimizations": [
|
||||
"Use React.memo for components that receive stable props",
|
||||
"Implement useMemo for expensive calculations",
|
||||
"Use useCallback for event handlers passed to child components",
|
||||
"Optimize images with proper formats and lazy loading",
|
||||
"Implement virtualization for large lists",
|
||||
"Use code splitting for large components",
|
||||
"Minimize bundle size by importing only needed modules",
|
||||
"Use CSS-in-JS efficiently to avoid style recalculations",
|
||||
"Implement proper error boundaries to prevent crashes",
|
||||
"Monitor component re-render frequency and optimize"
|
||||
],
|
||||
"bestPractices": [
|
||||
"Design components for reusability and composition",
|
||||
"Use TypeScript for better development experience and catch errors",
|
||||
"Follow accessibility guidelines from the start",
|
||||
"Write comprehensive tests including accessibility tests",
|
||||
"Document components with clear examples and usage",
|
||||
"Use consistent naming conventions across components",
|
||||
"Implement proper error handling and loading states",
|
||||
"Consider mobile-first responsive design",
|
||||
"Use design tokens for consistent styling",
|
||||
"Optimize for performance without premature optimization",
|
||||
"Follow the principle of least privilege for component APIs",
|
||||
"Use proper semantic HTML for better accessibility"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
38
packages/mosaic/framework/remote-install.ps1
Normal file
38
packages/mosaic/framework/remote-install.ps1
Normal file
@@ -0,0 +1,38 @@
|
||||
# Mosaic Bootstrap — Remote Installer (Windows PowerShell)
|
||||
#
|
||||
# One-liner:
|
||||
# irm https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1 | iex
|
||||
#
|
||||
# Or explicit:
|
||||
# powershell -ExecutionPolicy Bypass -File remote-install.ps1
|
||||
#
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$BootstrapRef = if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" }
|
||||
$ArchiveUrl = "https://git.mosaicstack.dev/mosaic/bootstrap/archive/$BootstrapRef.zip"
|
||||
$WorkDir = Join-Path $env:TEMP "mosaic-bootstrap-$PID"
|
||||
$ZipPath = "$WorkDir.zip"
|
||||
|
||||
try {
|
||||
Write-Host "[mosaic] Downloading bootstrap archive (ref: $BootstrapRef)..."
|
||||
New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null
|
||||
Invoke-WebRequest -Uri $ArchiveUrl -OutFile $ZipPath -UseBasicParsing
|
||||
|
||||
Write-Host "[mosaic] Extracting..."
|
||||
Expand-Archive -Path $ZipPath -DestinationPath $WorkDir -Force
|
||||
|
||||
$InstallScript = Join-Path $WorkDir "bootstrap\install.ps1"
|
||||
if (-not (Test-Path $InstallScript)) {
|
||||
throw "install.ps1 not found in archive"
|
||||
}
|
||||
|
||||
Write-Host "[mosaic] Running install..."
|
||||
& $InstallScript
|
||||
|
||||
Write-Host "[mosaic] Done."
|
||||
}
|
||||
finally {
|
||||
Write-Host "[mosaic] Cleaning up temporary files..."
|
||||
Remove-Item -Path $ZipPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $WorkDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Reference in New Issue
Block a user