@renseiai/agentfactory 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +1 -0
- package/dist/src/config/repository-config.d.ts +44 -0
- package/dist/src/config/repository-config.d.ts.map +1 -0
- package/dist/src/config/repository-config.js +88 -0
- package/dist/src/config/repository-config.test.d.ts +2 -0
- package/dist/src/config/repository-config.test.d.ts.map +1 -0
- package/dist/src/config/repository-config.test.js +249 -0
- package/dist/src/deployment/deployment-checker.d.ts +110 -0
- package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
- package/dist/src/deployment/deployment-checker.js +242 -0
- package/dist/src/deployment/index.d.ts +3 -0
- package/dist/src/deployment/index.d.ts.map +1 -0
- package/dist/src/deployment/index.js +2 -0
- package/dist/src/frontend/index.d.ts +2 -0
- package/dist/src/frontend/index.d.ts.map +1 -0
- package/dist/src/frontend/index.js +1 -0
- package/dist/src/frontend/types.d.ts +106 -0
- package/dist/src/frontend/types.d.ts.map +1 -0
- package/dist/src/frontend/types.js +11 -0
- package/dist/src/governor/decision-engine.d.ts +52 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -0
- package/dist/src/governor/decision-engine.js +220 -0
- package/dist/src/governor/decision-engine.test.d.ts +2 -0
- package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
- package/dist/src/governor/decision-engine.test.js +629 -0
- package/dist/src/governor/event-bus.d.ts +43 -0
- package/dist/src/governor/event-bus.d.ts.map +1 -0
- package/dist/src/governor/event-bus.js +8 -0
- package/dist/src/governor/event-deduplicator.d.ts +43 -0
- package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
- package/dist/src/governor/event-deduplicator.js +53 -0
- package/dist/src/governor/event-driven-governor.d.ts +131 -0
- package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
- package/dist/src/governor/event-driven-governor.js +379 -0
- package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
- package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
- package/dist/src/governor/event-driven-governor.test.js +673 -0
- package/dist/src/governor/event-types.d.ts +78 -0
- package/dist/src/governor/event-types.d.ts.map +1 -0
- package/dist/src/governor/event-types.js +32 -0
- package/dist/src/governor/governor-types.d.ts +82 -0
- package/dist/src/governor/governor-types.d.ts.map +1 -0
- package/dist/src/governor/governor-types.js +21 -0
- package/dist/src/governor/governor.d.ts +100 -0
- package/dist/src/governor/governor.d.ts.map +1 -0
- package/dist/src/governor/governor.js +262 -0
- package/dist/src/governor/governor.test.d.ts +2 -0
- package/dist/src/governor/governor.test.d.ts.map +1 -0
- package/dist/src/governor/governor.test.js +514 -0
- package/dist/src/governor/human-touchpoints.d.ts +131 -0
- package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
- package/dist/src/governor/human-touchpoints.js +251 -0
- package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
- package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
- package/dist/src/governor/human-touchpoints.test.js +366 -0
- package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
- package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
- package/dist/src/governor/in-memory-event-bus.js +79 -0
- package/dist/src/governor/index.d.ts +14 -0
- package/dist/src/governor/index.d.ts.map +1 -0
- package/dist/src/governor/index.js +13 -0
- package/dist/src/governor/override-parser.d.ts +60 -0
- package/dist/src/governor/override-parser.d.ts.map +1 -0
- package/dist/src/governor/override-parser.js +98 -0
- package/dist/src/governor/override-parser.test.d.ts +2 -0
- package/dist/src/governor/override-parser.test.d.ts.map +1 -0
- package/dist/src/governor/override-parser.test.js +312 -0
- package/dist/src/governor/platform-adapter.d.ts +69 -0
- package/dist/src/governor/platform-adapter.d.ts.map +1 -0
- package/dist/src/governor/platform-adapter.js +11 -0
- package/dist/src/governor/processing-state.d.ts +66 -0
- package/dist/src/governor/processing-state.d.ts.map +1 -0
- package/dist/src/governor/processing-state.js +43 -0
- package/dist/src/governor/processing-state.test.d.ts +2 -0
- package/dist/src/governor/processing-state.test.d.ts.map +1 -0
- package/dist/src/governor/processing-state.test.js +96 -0
- package/dist/src/governor/top-of-funnel.d.ts +118 -0
- package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
- package/dist/src/governor/top-of-funnel.js +168 -0
- package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
- package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
- package/dist/src/governor/top-of-funnel.test.js +331 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/linear-cli.d.ts +38 -0
- package/dist/src/linear-cli.d.ts.map +1 -0
- package/dist/src/linear-cli.js +674 -0
- package/dist/src/logger.d.ts +117 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +430 -0
- package/dist/src/manifest/generate.d.ts +20 -0
- package/dist/src/manifest/generate.d.ts.map +1 -0
- package/dist/src/manifest/generate.js +65 -0
- package/dist/src/manifest/index.d.ts +4 -0
- package/dist/src/manifest/index.d.ts.map +1 -0
- package/dist/src/manifest/index.js +2 -0
- package/dist/src/manifest/route-manifest.d.ts +34 -0
- package/dist/src/manifest/route-manifest.d.ts.map +1 -0
- package/dist/src/manifest/route-manifest.js +148 -0
- package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
- package/dist/src/orchestrator/activity-emitter.js +306 -0
- package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
- package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
- package/dist/src/orchestrator/api-activity-emitter.js +417 -0
- package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
- package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
- package/dist/src/orchestrator/heartbeat-writer.js +137 -0
- package/dist/src/orchestrator/index.d.ts +20 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -0
- package/dist/src/orchestrator/index.js +22 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
- package/dist/src/orchestrator/log-analyzer.js +572 -0
- package/dist/src/orchestrator/log-config.d.ts +39 -0
- package/dist/src/orchestrator/log-config.d.ts.map +1 -0
- package/dist/src/orchestrator/log-config.js +45 -0
- package/dist/src/orchestrator/orchestrator.d.ts +316 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/src/orchestrator/orchestrator.js +3290 -0
- package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
- package/dist/src/orchestrator/parse-work-result.js +135 -0
- package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
- package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
- package/dist/src/orchestrator/parse-work-result.test.js +234 -0
- package/dist/src/orchestrator/progress-logger.d.ts +72 -0
- package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
- package/dist/src/orchestrator/progress-logger.js +135 -0
- package/dist/src/orchestrator/session-logger.d.ts +159 -0
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
- package/dist/src/orchestrator/session-logger.js +275 -0
- package/dist/src/orchestrator/state-recovery.d.ts +96 -0
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
- package/dist/src/orchestrator/state-recovery.js +302 -0
- package/dist/src/orchestrator/state-types.d.ts +165 -0
- package/dist/src/orchestrator/state-types.d.ts.map +1 -0
- package/dist/src/orchestrator/state-types.js +7 -0
- package/dist/src/orchestrator/stream-parser.d.ts +151 -0
- package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
- package/dist/src/orchestrator/stream-parser.js +137 -0
- package/dist/src/orchestrator/types.d.ts +232 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -0
- package/dist/src/orchestrator/types.js +4 -0
- package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
- package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
- package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
- package/dist/src/providers/a2a-auth.d.ts +81 -0
- package/dist/src/providers/a2a-auth.d.ts.map +1 -0
- package/dist/src/providers/a2a-auth.js +188 -0
- package/dist/src/providers/a2a-auth.test.d.ts +2 -0
- package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-auth.test.js +232 -0
- package/dist/src/providers/a2a-provider.d.ts +254 -0
- package/dist/src/providers/a2a-provider.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
- package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.integration.test.js +665 -0
- package/dist/src/providers/a2a-provider.js +811 -0
- package/dist/src/providers/a2a-provider.test.d.ts +2 -0
- package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.test.js +681 -0
- package/dist/src/providers/amp-provider.d.ts +20 -0
- package/dist/src/providers/amp-provider.d.ts.map +1 -0
- package/dist/src/providers/amp-provider.js +24 -0
- package/dist/src/providers/claude-provider.d.ts +18 -0
- package/dist/src/providers/claude-provider.d.ts.map +1 -0
- package/dist/src/providers/claude-provider.js +437 -0
- package/dist/src/providers/codex-provider.d.ts +133 -0
- package/dist/src/providers/codex-provider.d.ts.map +1 -0
- package/dist/src/providers/codex-provider.js +381 -0
- package/dist/src/providers/codex-provider.test.d.ts +2 -0
- package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
- package/dist/src/providers/codex-provider.test.js +387 -0
- package/dist/src/providers/index.d.ts +44 -0
- package/dist/src/providers/index.d.ts.map +1 -0
- package/dist/src/providers/index.js +85 -0
- package/dist/src/providers/spring-ai-provider.d.ts +90 -0
- package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
- package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
- package/dist/src/providers/spring-ai-provider.js +317 -0
- package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
- package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.test.js +200 -0
- package/dist/src/providers/types.d.ts +165 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +13 -0
- package/dist/src/templates/adapters.d.ts +51 -0
- package/dist/src/templates/adapters.d.ts.map +1 -0
- package/dist/src/templates/adapters.js +104 -0
- package/dist/src/templates/adapters.test.d.ts +2 -0
- package/dist/src/templates/adapters.test.d.ts.map +1 -0
- package/dist/src/templates/adapters.test.js +165 -0
- package/dist/src/templates/agent-definition.d.ts +85 -0
- package/dist/src/templates/agent-definition.d.ts.map +1 -0
- package/dist/src/templates/agent-definition.js +97 -0
- package/dist/src/templates/agent-definition.test.d.ts +2 -0
- package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
- package/dist/src/templates/agent-definition.test.js +209 -0
- package/dist/src/templates/index.d.ts +14 -0
- package/dist/src/templates/index.d.ts.map +1 -0
- package/dist/src/templates/index.js +11 -0
- package/dist/src/templates/loader.d.ts +41 -0
- package/dist/src/templates/loader.d.ts.map +1 -0
- package/dist/src/templates/loader.js +114 -0
- package/dist/src/templates/registry.d.ts +80 -0
- package/dist/src/templates/registry.d.ts.map +1 -0
- package/dist/src/templates/registry.js +177 -0
- package/dist/src/templates/registry.test.d.ts +2 -0
- package/dist/src/templates/registry.test.d.ts.map +1 -0
- package/dist/src/templates/registry.test.js +198 -0
- package/dist/src/templates/renderer.d.ts +29 -0
- package/dist/src/templates/renderer.d.ts.map +1 -0
- package/dist/src/templates/renderer.js +35 -0
- package/dist/src/templates/strategy-templates.test.d.ts +2 -0
- package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
- package/dist/src/templates/strategy-templates.test.js +619 -0
- package/dist/src/templates/types.d.ts +233 -0
- package/dist/src/templates/types.d.ts.map +1 -0
- package/dist/src/templates/types.js +127 -0
- package/dist/src/templates/types.test.d.ts +2 -0
- package/dist/src/templates/types.test.d.ts.map +1 -0
- package/dist/src/templates/types.test.js +232 -0
- package/dist/src/tools/index.d.ts +6 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +3 -0
- package/dist/src/tools/linear-runner.d.ts +34 -0
- package/dist/src/tools/linear-runner.d.ts.map +1 -0
- package/dist/src/tools/linear-runner.js +700 -0
- package/dist/src/tools/plugins/linear.d.ts +9 -0
- package/dist/src/tools/plugins/linear.d.ts.map +1 -0
- package/dist/src/tools/plugins/linear.js +138 -0
- package/dist/src/tools/registry.d.ts +9 -0
- package/dist/src/tools/registry.d.ts.map +1 -0
- package/dist/src/tools/registry.js +18 -0
- package/dist/src/tools/types.d.ts +18 -0
- package/dist/src/tools/types.d.ts.map +1 -0
- package/dist/src/tools/types.js +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { TemplateRegistry } from './registry.js';
|
|
3
|
+
describe('Strategy-Aware Template Selection', () => {
|
|
4
|
+
describe('strategy-specific template loading', () => {
|
|
5
|
+
it('loads strategy-specific templates from built-in defaults', () => {
|
|
6
|
+
const registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
7
|
+
const workTypes = registry.getRegisteredWorkTypes();
|
|
8
|
+
// Should include strategy-specific compound keys
|
|
9
|
+
expect(workTypes).toContain('refinement-context-enriched');
|
|
10
|
+
expect(workTypes).toContain('refinement-decompose');
|
|
11
|
+
expect(workTypes).toContain('development-retry');
|
|
12
|
+
expect(workTypes).toContain('qa-retry');
|
|
13
|
+
expect(workTypes).toContain('qa-native');
|
|
14
|
+
// Should still include base work types
|
|
15
|
+
expect(workTypes).toContain('development');
|
|
16
|
+
expect(workTypes).toContain('qa');
|
|
17
|
+
expect(workTypes).toContain('refinement');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('getTemplate with strategy parameter', () => {
|
|
21
|
+
let registry;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
24
|
+
});
|
|
25
|
+
it('returns strategy-specific template when strategy is provided', () => {
|
|
26
|
+
const template = registry.getTemplate('refinement', 'context-enriched');
|
|
27
|
+
expect(template).toBeDefined();
|
|
28
|
+
expect(template.metadata.name).toBe('refinement-context-enriched');
|
|
29
|
+
expect(template.metadata.workType).toBe('refinement');
|
|
30
|
+
});
|
|
31
|
+
it('returns decompose strategy template', () => {
|
|
32
|
+
const template = registry.getTemplate('refinement', 'decompose');
|
|
33
|
+
expect(template).toBeDefined();
|
|
34
|
+
expect(template.metadata.name).toBe('refinement-decompose');
|
|
35
|
+
});
|
|
36
|
+
it('returns development-retry strategy template', () => {
|
|
37
|
+
const template = registry.getTemplate('development', 'retry');
|
|
38
|
+
expect(template).toBeDefined();
|
|
39
|
+
expect(template.metadata.name).toBe('development-retry');
|
|
40
|
+
});
|
|
41
|
+
it('returns qa-retry strategy template', () => {
|
|
42
|
+
const template = registry.getTemplate('qa', 'retry');
|
|
43
|
+
expect(template).toBeDefined();
|
|
44
|
+
expect(template.metadata.name).toBe('qa-retry');
|
|
45
|
+
});
|
|
46
|
+
it('falls back to base template when strategy template does not exist', () => {
|
|
47
|
+
const template = registry.getTemplate('refinement', 'nonexistent-strategy');
|
|
48
|
+
expect(template).toBeDefined();
|
|
49
|
+
expect(template.metadata.name).toBe('refinement');
|
|
50
|
+
expect(template.metadata.workType).toBe('refinement');
|
|
51
|
+
});
|
|
52
|
+
it('falls back to base template when strategy is undefined', () => {
|
|
53
|
+
const template = registry.getTemplate('refinement');
|
|
54
|
+
expect(template).toBeDefined();
|
|
55
|
+
expect(template.metadata.name).toBe('refinement');
|
|
56
|
+
});
|
|
57
|
+
it('returns undefined when neither strategy nor base template exists', () => {
|
|
58
|
+
const emptyRegistry = TemplateRegistry.create({ useBuiltinDefaults: false });
|
|
59
|
+
expect(emptyRegistry.getTemplate('refinement', 'context-enriched')).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('hasTemplate with strategy parameter', () => {
|
|
63
|
+
let registry;
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
66
|
+
});
|
|
67
|
+
it('returns true for existing strategy template', () => {
|
|
68
|
+
expect(registry.hasTemplate('refinement', 'context-enriched')).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
it('returns true when strategy template does not exist but base does', () => {
|
|
71
|
+
expect(registry.hasTemplate('refinement', 'nonexistent')).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
it('returns false when no templates exist for work type', () => {
|
|
74
|
+
const emptyRegistry = TemplateRegistry.create({ useBuiltinDefaults: false });
|
|
75
|
+
expect(emptyRegistry.hasTemplate('refinement', 'context-enriched')).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('renderPrompt with strategy', () => {
|
|
79
|
+
let registry;
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
82
|
+
});
|
|
83
|
+
it('renders strategy-specific template with WorkflowState context variables', () => {
|
|
84
|
+
const context = {
|
|
85
|
+
identifier: 'SUP-100',
|
|
86
|
+
cycleCount: 3,
|
|
87
|
+
strategy: 'context-enriched',
|
|
88
|
+
failureSummary: 'Tests failed: missing null check in handler',
|
|
89
|
+
};
|
|
90
|
+
const result = registry.renderPrompt('refinement', context, 'context-enriched');
|
|
91
|
+
expect(result).toContain('SUP-100');
|
|
92
|
+
expect(result).toContain('FAILED QA 3 times');
|
|
93
|
+
expect(result).toContain('context-enriched retry');
|
|
94
|
+
expect(result).toContain('missing null check in handler');
|
|
95
|
+
expect(result).toContain('ROOT CAUSE');
|
|
96
|
+
});
|
|
97
|
+
it('renders decompose template with failure history', () => {
|
|
98
|
+
const context = {
|
|
99
|
+
identifier: 'SUP-200',
|
|
100
|
+
cycleCount: 4,
|
|
101
|
+
failureSummary: 'Multiple subsystems failing independently',
|
|
102
|
+
team: 'Engineering',
|
|
103
|
+
linearCli: 'pnpm af-linear',
|
|
104
|
+
packageManager: 'pnpm',
|
|
105
|
+
};
|
|
106
|
+
const result = registry.renderPrompt('refinement', context, 'decompose');
|
|
107
|
+
expect(result).toContain('SUP-200');
|
|
108
|
+
expect(result).toContain('FAILED QA 4 times');
|
|
109
|
+
expect(result).toContain('DECOMPOSE');
|
|
110
|
+
expect(result).toContain('Multiple subsystems failing independently');
|
|
111
|
+
expect(result).toContain('pnpm af-linear create-issue');
|
|
112
|
+
});
|
|
113
|
+
it('renders development-retry template with previous failure reasons', () => {
|
|
114
|
+
const context = {
|
|
115
|
+
identifier: 'SUP-300',
|
|
116
|
+
attemptNumber: 3,
|
|
117
|
+
previousFailureReasons: [
|
|
118
|
+
'TypeScript error in utils.ts',
|
|
119
|
+
'Missing import statement',
|
|
120
|
+
],
|
|
121
|
+
failureSummary: 'Two previous attempts failed due to type errors',
|
|
122
|
+
};
|
|
123
|
+
const result = registry.renderPrompt('development', context, 'retry');
|
|
124
|
+
expect(result).toContain('SUP-300');
|
|
125
|
+
expect(result).toContain('attempt 3');
|
|
126
|
+
expect(result).toContain('DO NOT REPEAT THESE MISTAKES');
|
|
127
|
+
expect(result).toContain('TypeScript error in utils.ts');
|
|
128
|
+
expect(result).toContain('Missing import statement');
|
|
129
|
+
expect(result).toContain('Two previous attempts failed');
|
|
130
|
+
});
|
|
131
|
+
it('renders qa-retry template with previous QA failures', () => {
|
|
132
|
+
const context = {
|
|
133
|
+
identifier: 'SUP-400',
|
|
134
|
+
attemptNumber: 2,
|
|
135
|
+
previousFailureReasons: [
|
|
136
|
+
'Test suite timeout on large fixture',
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
const result = registry.renderPrompt('qa', context, 'retry');
|
|
140
|
+
expect(result).toContain('SUP-400');
|
|
141
|
+
expect(result).toContain('QA attempt 2');
|
|
142
|
+
expect(result).toContain('Test suite timeout on large fixture');
|
|
143
|
+
expect(result).toContain('WORK_RESULT');
|
|
144
|
+
});
|
|
145
|
+
it('falls back to base template when strategy template not found', () => {
|
|
146
|
+
const context = {
|
|
147
|
+
identifier: 'SUP-500',
|
|
148
|
+
cycleCount: 1,
|
|
149
|
+
};
|
|
150
|
+
const result = registry.renderPrompt('refinement', context, 'unknown-strategy');
|
|
151
|
+
expect(result).toContain('SUP-500');
|
|
152
|
+
// Should render the base refinement template, which has "Refine" in the prompt
|
|
153
|
+
expect(result).toContain('Refine');
|
|
154
|
+
});
|
|
155
|
+
it('returns null when no template found for work type or strategy', () => {
|
|
156
|
+
const emptyRegistry = TemplateRegistry.create({ useBuiltinDefaults: false });
|
|
157
|
+
const result = emptyRegistry.renderPrompt('refinement', { identifier: 'SUP-1' }, 'context-enriched');
|
|
158
|
+
expect(result).toBeNull();
|
|
159
|
+
});
|
|
160
|
+
it('renders template without failureSummary showing fallback text', () => {
|
|
161
|
+
const context = {
|
|
162
|
+
identifier: 'SUP-600',
|
|
163
|
+
cycleCount: 2,
|
|
164
|
+
};
|
|
165
|
+
const result = registry.renderPrompt('refinement', context, 'context-enriched');
|
|
166
|
+
expect(result).toContain('No failure details available.');
|
|
167
|
+
});
|
|
168
|
+
it('renders development-retry without previousFailureReasons', () => {
|
|
169
|
+
const context = {
|
|
170
|
+
identifier: 'SUP-700',
|
|
171
|
+
attemptNumber: 2,
|
|
172
|
+
};
|
|
173
|
+
const result = registry.renderPrompt('development', context, 'retry');
|
|
174
|
+
expect(result).toContain('SUP-700');
|
|
175
|
+
expect(result).toContain('attempt 2');
|
|
176
|
+
// Should not contain the "Previous Failures" section
|
|
177
|
+
expect(result).not.toContain('DO NOT REPEAT THESE MISTAKES');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('getToolPermissions with strategy', () => {
|
|
181
|
+
let registry;
|
|
182
|
+
beforeEach(() => {
|
|
183
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
184
|
+
});
|
|
185
|
+
it('returns strategy-specific tool permissions', () => {
|
|
186
|
+
const perms = registry.getToolPermissions('refinement', 'context-enriched');
|
|
187
|
+
expect(perms).toBeDefined();
|
|
188
|
+
// Strategy template allows pnpm, git commit, git diff, git log
|
|
189
|
+
expect(perms.length).toBeGreaterThanOrEqual(4);
|
|
190
|
+
});
|
|
191
|
+
it('falls back to base template tool permissions when strategy not found', () => {
|
|
192
|
+
const perms = registry.getToolPermissions('refinement', 'nonexistent');
|
|
193
|
+
expect(perms).toBeDefined();
|
|
194
|
+
// Base refinement template has pnpm af-linear
|
|
195
|
+
expect(perms.some(p => p.includes('pnpm'))).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
describe('getDisallowedTools with strategy', () => {
|
|
199
|
+
let registry;
|
|
200
|
+
beforeEach(() => {
|
|
201
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
202
|
+
});
|
|
203
|
+
it('returns strategy-specific disallowed tools', () => {
|
|
204
|
+
const disallowed = registry.getDisallowedTools('refinement', 'context-enriched');
|
|
205
|
+
expect(disallowed).toBeDefined();
|
|
206
|
+
expect(disallowed).toContain('user-input');
|
|
207
|
+
});
|
|
208
|
+
it('falls back to base template disallowed tools when strategy not found', () => {
|
|
209
|
+
const disallowed = registry.getDisallowedTools('refinement', 'nonexistent');
|
|
210
|
+
expect(disallowed).toBeDefined();
|
|
211
|
+
expect(disallowed).toContain('user-input');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe('inline strategy template override', () => {
|
|
215
|
+
it('supports strategy templates via inline config', () => {
|
|
216
|
+
const customStrategyTemplate = {
|
|
217
|
+
apiVersion: 'v1',
|
|
218
|
+
kind: 'WorkflowTemplate',
|
|
219
|
+
metadata: { name: 'development-retry', workType: 'development' },
|
|
220
|
+
prompt: 'Custom retry for {{identifier}} attempt {{attemptNumber}}.',
|
|
221
|
+
};
|
|
222
|
+
const registry = TemplateRegistry.create({
|
|
223
|
+
useBuiltinDefaults: false,
|
|
224
|
+
templates: { 'development-retry': customStrategyTemplate },
|
|
225
|
+
});
|
|
226
|
+
const result = registry.renderPrompt('development', {
|
|
227
|
+
identifier: 'SUP-1',
|
|
228
|
+
attemptNumber: 3,
|
|
229
|
+
}, 'retry');
|
|
230
|
+
expect(result).toBe('Custom retry for SUP-1 attempt 3.');
|
|
231
|
+
});
|
|
232
|
+
it('inline strategy template overrides built-in strategy template', () => {
|
|
233
|
+
const customTemplate = {
|
|
234
|
+
apiVersion: 'v1',
|
|
235
|
+
kind: 'WorkflowTemplate',
|
|
236
|
+
metadata: { name: 'refinement-context-enriched', workType: 'refinement' },
|
|
237
|
+
prompt: 'Overridden: {{identifier}} cycle {{cycleCount}}.',
|
|
238
|
+
};
|
|
239
|
+
const registry = TemplateRegistry.create({
|
|
240
|
+
useBuiltinDefaults: true,
|
|
241
|
+
templates: { 'refinement-context-enriched': customTemplate },
|
|
242
|
+
});
|
|
243
|
+
const result = registry.renderPrompt('refinement', {
|
|
244
|
+
identifier: 'SUP-1',
|
|
245
|
+
cycleCount: 5,
|
|
246
|
+
}, 'context-enriched');
|
|
247
|
+
expect(result).toBe('Overridden: SUP-1 cycle 5.');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
describe('new TemplateContext variables', () => {
|
|
251
|
+
it('cycleCount is available in templates', () => {
|
|
252
|
+
const registry = new TemplateRegistry();
|
|
253
|
+
const template = {
|
|
254
|
+
apiVersion: 'v1',
|
|
255
|
+
kind: 'WorkflowTemplate',
|
|
256
|
+
metadata: { name: 'test', workType: 'development' },
|
|
257
|
+
prompt: 'Cycle: {{cycleCount}}',
|
|
258
|
+
};
|
|
259
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
260
|
+
const result = registry.renderPrompt('development', { identifier: 'X', cycleCount: 3 });
|
|
261
|
+
expect(result).toBe('Cycle: 3');
|
|
262
|
+
});
|
|
263
|
+
it('strategy is available in templates', () => {
|
|
264
|
+
const registry = new TemplateRegistry();
|
|
265
|
+
const template = {
|
|
266
|
+
apiVersion: 'v1',
|
|
267
|
+
kind: 'WorkflowTemplate',
|
|
268
|
+
metadata: { name: 'test', workType: 'development' },
|
|
269
|
+
prompt: 'Strategy: {{strategy}}',
|
|
270
|
+
};
|
|
271
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
272
|
+
const result = registry.renderPrompt('development', { identifier: 'X', strategy: 'context-enriched' });
|
|
273
|
+
expect(result).toBe('Strategy: context-enriched');
|
|
274
|
+
});
|
|
275
|
+
it('failureSummary with triple-stache renders unescaped HTML', () => {
|
|
276
|
+
const registry = new TemplateRegistry();
|
|
277
|
+
const template = {
|
|
278
|
+
apiVersion: 'v1',
|
|
279
|
+
kind: 'WorkflowTemplate',
|
|
280
|
+
metadata: { name: 'test', workType: 'development' },
|
|
281
|
+
prompt: '{{{failureSummary}}}',
|
|
282
|
+
};
|
|
283
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
284
|
+
const result = registry.renderPrompt('development', {
|
|
285
|
+
identifier: 'X',
|
|
286
|
+
failureSummary: '**Bold** and <code>inline</code>',
|
|
287
|
+
});
|
|
288
|
+
expect(result).toBe('**Bold** and <code>inline</code>');
|
|
289
|
+
});
|
|
290
|
+
it('attemptNumber is available in templates', () => {
|
|
291
|
+
const registry = new TemplateRegistry();
|
|
292
|
+
const template = {
|
|
293
|
+
apiVersion: 'v1',
|
|
294
|
+
kind: 'WorkflowTemplate',
|
|
295
|
+
metadata: { name: 'test', workType: 'development' },
|
|
296
|
+
prompt: 'Attempt {{attemptNumber}}',
|
|
297
|
+
};
|
|
298
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
299
|
+
const result = registry.renderPrompt('development', { identifier: 'X', attemptNumber: 2 });
|
|
300
|
+
expect(result).toBe('Attempt 2');
|
|
301
|
+
});
|
|
302
|
+
it('previousFailureReasons are iterable via #each', () => {
|
|
303
|
+
const registry = new TemplateRegistry();
|
|
304
|
+
const template = {
|
|
305
|
+
apiVersion: 'v1',
|
|
306
|
+
kind: 'WorkflowTemplate',
|
|
307
|
+
metadata: { name: 'test', workType: 'development' },
|
|
308
|
+
prompt: '{{#each previousFailureReasons}}{{this}};{{/each}}',
|
|
309
|
+
};
|
|
310
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
311
|
+
const result = registry.renderPrompt('development', {
|
|
312
|
+
identifier: 'X',
|
|
313
|
+
previousFailureReasons: ['reason1', 'reason2'],
|
|
314
|
+
});
|
|
315
|
+
expect(result).toBe('reason1;reason2;');
|
|
316
|
+
});
|
|
317
|
+
it('totalCostUsd is available in templates', () => {
|
|
318
|
+
const registry = new TemplateRegistry();
|
|
319
|
+
const template = {
|
|
320
|
+
apiVersion: 'v1',
|
|
321
|
+
kind: 'WorkflowTemplate',
|
|
322
|
+
metadata: { name: 'test', workType: 'development' },
|
|
323
|
+
prompt: 'Cost: ${{totalCostUsd}}',
|
|
324
|
+
};
|
|
325
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
326
|
+
const result = registry.renderPrompt('development', { identifier: 'X', totalCostUsd: 1.5 });
|
|
327
|
+
expect(result).toBe('Cost: $1.5');
|
|
328
|
+
});
|
|
329
|
+
it('blockerIdentifier is available in templates', () => {
|
|
330
|
+
const registry = new TemplateRegistry();
|
|
331
|
+
const template = {
|
|
332
|
+
apiVersion: 'v1',
|
|
333
|
+
kind: 'WorkflowTemplate',
|
|
334
|
+
metadata: { name: 'test', workType: 'development' },
|
|
335
|
+
prompt: 'Blocker: {{blockerIdentifier}}',
|
|
336
|
+
};
|
|
337
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
338
|
+
const result = registry.renderPrompt('development', { identifier: 'X', blockerIdentifier: 'SUP-999' });
|
|
339
|
+
expect(result).toBe('Blocker: SUP-999');
|
|
340
|
+
});
|
|
341
|
+
it('team is available in templates', () => {
|
|
342
|
+
const registry = new TemplateRegistry();
|
|
343
|
+
const template = {
|
|
344
|
+
apiVersion: 'v1',
|
|
345
|
+
kind: 'WorkflowTemplate',
|
|
346
|
+
metadata: { name: 'test', workType: 'development' },
|
|
347
|
+
prompt: 'Team: {{team}}',
|
|
348
|
+
};
|
|
349
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
350
|
+
const result = registry.renderPrompt('development', { identifier: 'X', team: 'Engineering' });
|
|
351
|
+
expect(result).toBe('Team: Engineering');
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
describe('governor notification partials', () => {
|
|
355
|
+
let registry;
|
|
356
|
+
beforeEach(() => {
|
|
357
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
358
|
+
});
|
|
359
|
+
it('renders review-request partial', () => {
|
|
360
|
+
const template = {
|
|
361
|
+
apiVersion: 'v1',
|
|
362
|
+
kind: 'WorkflowTemplate',
|
|
363
|
+
metadata: { name: 'test-gov', workType: 'development' },
|
|
364
|
+
prompt: '{{> partials/governor/review-request}}',
|
|
365
|
+
};
|
|
366
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
367
|
+
const result = registry.renderPrompt('development', {
|
|
368
|
+
identifier: 'SUP-1',
|
|
369
|
+
cycleCount: 3,
|
|
370
|
+
failureSummary: 'Tests keep failing on auth module',
|
|
371
|
+
totalCostUsd: 2.50,
|
|
372
|
+
});
|
|
373
|
+
expect(result).toContain('Review Requested: Repeated QA Failure');
|
|
374
|
+
expect(result).toContain('3 development cycles');
|
|
375
|
+
expect(result).toContain('Tests keep failing on auth module');
|
|
376
|
+
expect(result).toContain('$2.5');
|
|
377
|
+
expect(result).toContain('HOLD');
|
|
378
|
+
expect(result).toContain('DECOMPOSE');
|
|
379
|
+
});
|
|
380
|
+
it('renders review-request partial without failure summary', () => {
|
|
381
|
+
const template = {
|
|
382
|
+
apiVersion: 'v1',
|
|
383
|
+
kind: 'WorkflowTemplate',
|
|
384
|
+
metadata: { name: 'test-gov', workType: 'development' },
|
|
385
|
+
prompt: '{{> partials/governor/review-request}}',
|
|
386
|
+
};
|
|
387
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
388
|
+
const result = registry.renderPrompt('development', {
|
|
389
|
+
identifier: 'SUP-1',
|
|
390
|
+
cycleCount: 2,
|
|
391
|
+
});
|
|
392
|
+
expect(result).toContain('No failure details available.');
|
|
393
|
+
});
|
|
394
|
+
it('renders decomposition-proposal partial', () => {
|
|
395
|
+
const template = {
|
|
396
|
+
apiVersion: 'v1',
|
|
397
|
+
kind: 'WorkflowTemplate',
|
|
398
|
+
metadata: { name: 'test-gov', workType: 'development' },
|
|
399
|
+
prompt: '{{> partials/governor/decomposition-proposal}}',
|
|
400
|
+
};
|
|
401
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
402
|
+
const result = registry.renderPrompt('development', {
|
|
403
|
+
identifier: 'SUP-1',
|
|
404
|
+
cycleCount: 4,
|
|
405
|
+
totalCostUsd: 5.00,
|
|
406
|
+
});
|
|
407
|
+
expect(result).toContain('Decomposition Proposed');
|
|
408
|
+
expect(result).toContain('failed QA 4 times');
|
|
409
|
+
expect(result).toContain('$5');
|
|
410
|
+
expect(result).toContain('HOLD');
|
|
411
|
+
});
|
|
412
|
+
it('renders escalation-alert partial', () => {
|
|
413
|
+
const template = {
|
|
414
|
+
apiVersion: 'v1',
|
|
415
|
+
kind: 'WorkflowTemplate',
|
|
416
|
+
metadata: { name: 'test-gov', workType: 'development' },
|
|
417
|
+
prompt: '{{> partials/governor/escalation-alert}}',
|
|
418
|
+
};
|
|
419
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
420
|
+
const result = registry.renderPrompt('development', {
|
|
421
|
+
identifier: 'SUP-1',
|
|
422
|
+
cycleCount: 5,
|
|
423
|
+
totalCostUsd: 10.00,
|
|
424
|
+
blockerIdentifier: 'SUP-999',
|
|
425
|
+
failureSummary: 'All automated retries exhausted',
|
|
426
|
+
});
|
|
427
|
+
expect(result).toContain('Human Intervention Required');
|
|
428
|
+
expect(result).toContain('5 cycles');
|
|
429
|
+
expect(result).toContain('$10');
|
|
430
|
+
expect(result).toContain('SUP-999');
|
|
431
|
+
expect(result).toContain('All automated retries exhausted');
|
|
432
|
+
});
|
|
433
|
+
it('renders escalation-alert without blocker identifier', () => {
|
|
434
|
+
const template = {
|
|
435
|
+
apiVersion: 'v1',
|
|
436
|
+
kind: 'WorkflowTemplate',
|
|
437
|
+
metadata: { name: 'test-gov', workType: 'development' },
|
|
438
|
+
prompt: '{{> partials/governor/escalation-alert}}',
|
|
439
|
+
};
|
|
440
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
441
|
+
const result = registry.renderPrompt('development', {
|
|
442
|
+
identifier: 'SUP-1',
|
|
443
|
+
cycleCount: 5,
|
|
444
|
+
});
|
|
445
|
+
expect(result).toContain('Human Intervention Required');
|
|
446
|
+
expect(result).not.toContain('blocker has been created');
|
|
447
|
+
expect(result).toContain('No failure details recorded.');
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
describe('qa-native strategy template', () => {
|
|
451
|
+
let registry;
|
|
452
|
+
beforeEach(() => {
|
|
453
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
454
|
+
});
|
|
455
|
+
it('loads qa-native as a strategy template', () => {
|
|
456
|
+
const template = registry.getTemplate('qa', 'native');
|
|
457
|
+
expect(template).toBeDefined();
|
|
458
|
+
expect(template.metadata.name).toBe('qa-native');
|
|
459
|
+
expect(template.metadata.workType).toBe('qa');
|
|
460
|
+
});
|
|
461
|
+
it('renders qa-native template with identifier', () => {
|
|
462
|
+
const context = {
|
|
463
|
+
identifier: 'SUP-800',
|
|
464
|
+
};
|
|
465
|
+
const result = registry.renderPrompt('qa', context, 'native');
|
|
466
|
+
expect(result).toContain('SUP-800');
|
|
467
|
+
expect(result).toContain('native/compiled project');
|
|
468
|
+
expect(result).toContain('WORK_RESULT');
|
|
469
|
+
expect(result).toContain('Build verification');
|
|
470
|
+
expect(result).toContain('memory safety');
|
|
471
|
+
});
|
|
472
|
+
it('renders qa-native with custom build commands', () => {
|
|
473
|
+
const context = {
|
|
474
|
+
identifier: 'SUP-801',
|
|
475
|
+
buildCommand: 'cargo build --release',
|
|
476
|
+
testCommand: 'cargo test',
|
|
477
|
+
validateCommand: 'cargo clippy -- -D warnings',
|
|
478
|
+
};
|
|
479
|
+
const result = registry.renderPrompt('qa', context, 'native');
|
|
480
|
+
expect(result).toContain('cargo build --release');
|
|
481
|
+
expect(result).toContain('cargo test');
|
|
482
|
+
expect(result).toContain('cargo clippy -- -D warnings');
|
|
483
|
+
});
|
|
484
|
+
it('renders qa-native with auto-detect fallback when no commands provided', () => {
|
|
485
|
+
const context = {
|
|
486
|
+
identifier: 'SUP-802',
|
|
487
|
+
};
|
|
488
|
+
const result = registry.renderPrompt('qa', context, 'native');
|
|
489
|
+
expect(result).toContain('Detect the build system');
|
|
490
|
+
expect(result).toContain('Detect the test framework');
|
|
491
|
+
});
|
|
492
|
+
it('includes native-specific tool permissions', () => {
|
|
493
|
+
const perms = registry.getToolPermissions('qa', 'native');
|
|
494
|
+
expect(perms).toBeDefined();
|
|
495
|
+
expect(perms.some(p => p.includes('cargo') || p.includes('make') || p.includes('cmake'))).toBe(true);
|
|
496
|
+
});
|
|
497
|
+
it('disallows user-input in native QA template', () => {
|
|
498
|
+
const disallowed = registry.getDisallowedTools('qa', 'native');
|
|
499
|
+
expect(disallowed).toBeDefined();
|
|
500
|
+
expect(disallowed).toContain('user-input');
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
describe('configurable build/test commands in base qa template', () => {
|
|
504
|
+
let registry;
|
|
505
|
+
beforeEach(() => {
|
|
506
|
+
registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
507
|
+
});
|
|
508
|
+
it('renders base qa template with custom buildCommand', () => {
|
|
509
|
+
const context = {
|
|
510
|
+
identifier: 'SUP-900',
|
|
511
|
+
buildCommand: 'make -j8',
|
|
512
|
+
};
|
|
513
|
+
const result = registry.renderPrompt('qa', context);
|
|
514
|
+
expect(result).toContain('`make -j8`');
|
|
515
|
+
expect(result).toContain('WORK_RESULT');
|
|
516
|
+
});
|
|
517
|
+
it('renders base qa template with custom testCommand', () => {
|
|
518
|
+
const context = {
|
|
519
|
+
identifier: 'SUP-901',
|
|
520
|
+
testCommand: 'go test ./...',
|
|
521
|
+
};
|
|
522
|
+
const result = registry.renderPrompt('qa', context);
|
|
523
|
+
expect(result).toContain('`go test ./...`');
|
|
524
|
+
});
|
|
525
|
+
it('renders base qa template with custom validateCommand', () => {
|
|
526
|
+
const context = {
|
|
527
|
+
identifier: 'SUP-902',
|
|
528
|
+
validateCommand: 'cargo clippy',
|
|
529
|
+
};
|
|
530
|
+
const result = registry.renderPrompt('qa', context);
|
|
531
|
+
expect(result).toContain('`cargo clippy`');
|
|
532
|
+
});
|
|
533
|
+
it('uses pnpm defaults when no custom commands provided', () => {
|
|
534
|
+
const context = {
|
|
535
|
+
identifier: 'SUP-903',
|
|
536
|
+
packageManager: 'pnpm',
|
|
537
|
+
};
|
|
538
|
+
const result = registry.renderPrompt('qa', context);
|
|
539
|
+
expect(result).toContain('`pnpm typecheck` or `pnpm build`');
|
|
540
|
+
// Should NOT contain custom command sections
|
|
541
|
+
expect(result).not.toContain('Build: `');
|
|
542
|
+
});
|
|
543
|
+
it('uses custom buildCommand in HARD FAIL RULE when provided', () => {
|
|
544
|
+
const context = {
|
|
545
|
+
identifier: 'SUP-904',
|
|
546
|
+
buildCommand: 'cmake --build build',
|
|
547
|
+
};
|
|
548
|
+
const result = registry.renderPrompt('qa', context);
|
|
549
|
+
expect(result).toContain('`cmake --build build`');
|
|
550
|
+
// Should NOT contain the pnpm default in the hard fail rule
|
|
551
|
+
expect(result).not.toContain('`pnpm typecheck` or `pnpm build`');
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
describe('build/test command context variables', () => {
|
|
555
|
+
it('buildCommand is available in templates', () => {
|
|
556
|
+
const registry = new TemplateRegistry();
|
|
557
|
+
const template = {
|
|
558
|
+
apiVersion: 'v1',
|
|
559
|
+
kind: 'WorkflowTemplate',
|
|
560
|
+
metadata: { name: 'test', workType: 'development' },
|
|
561
|
+
prompt: 'Build: {{buildCommand}}',
|
|
562
|
+
};
|
|
563
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
564
|
+
const result = registry.renderPrompt('development', {
|
|
565
|
+
identifier: 'X',
|
|
566
|
+
buildCommand: 'cargo build',
|
|
567
|
+
});
|
|
568
|
+
expect(result).toBe('Build: cargo build');
|
|
569
|
+
});
|
|
570
|
+
it('testCommand is available in templates', () => {
|
|
571
|
+
const registry = new TemplateRegistry();
|
|
572
|
+
const template = {
|
|
573
|
+
apiVersion: 'v1',
|
|
574
|
+
kind: 'WorkflowTemplate',
|
|
575
|
+
metadata: { name: 'test', workType: 'development' },
|
|
576
|
+
prompt: 'Test: {{testCommand}}',
|
|
577
|
+
};
|
|
578
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
579
|
+
const result = registry.renderPrompt('development', {
|
|
580
|
+
identifier: 'X',
|
|
581
|
+
testCommand: 'go test ./...',
|
|
582
|
+
});
|
|
583
|
+
expect(result).toBe('Test: go test ./...');
|
|
584
|
+
});
|
|
585
|
+
it('validateCommand is available in templates', () => {
|
|
586
|
+
const registry = new TemplateRegistry();
|
|
587
|
+
const template = {
|
|
588
|
+
apiVersion: 'v1',
|
|
589
|
+
kind: 'WorkflowTemplate',
|
|
590
|
+
metadata: { name: 'test', workType: 'development' },
|
|
591
|
+
prompt: 'Validate: {{validateCommand}}',
|
|
592
|
+
};
|
|
593
|
+
registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
|
|
594
|
+
const result = registry.renderPrompt('development', {
|
|
595
|
+
identifier: 'X',
|
|
596
|
+
validateCommand: 'cargo clippy',
|
|
597
|
+
});
|
|
598
|
+
expect(result).toBe('Validate: cargo clippy');
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
describe('native-build-validation partial', () => {
|
|
602
|
+
it('renders native-build-validation partial', () => {
|
|
603
|
+
const registry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
604
|
+
const template = {
|
|
605
|
+
apiVersion: 'v1',
|
|
606
|
+
kind: 'WorkflowTemplate',
|
|
607
|
+
metadata: { name: 'test-native', workType: 'development' },
|
|
608
|
+
prompt: '{{> partials/native-build-validation}}',
|
|
609
|
+
};
|
|
610
|
+
registry.initialize({ useBuiltinDefaults: true, templates: { development: template } });
|
|
611
|
+
const result = registry.renderPrompt('development', { identifier: 'SUP-1' });
|
|
612
|
+
expect(result).toContain('NATIVE BUILD VALIDATION');
|
|
613
|
+
expect(result).toContain('Cargo.toml');
|
|
614
|
+
expect(result).toContain('CMakeLists.txt');
|
|
615
|
+
expect(result).toContain('go.mod');
|
|
616
|
+
expect(result).toContain('Memory safety');
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
});
|