@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.
Files changed (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/src/config/index.d.ts +3 -0
  4. package/dist/src/config/index.d.ts.map +1 -0
  5. package/dist/src/config/index.js +1 -0
  6. package/dist/src/config/repository-config.d.ts +44 -0
  7. package/dist/src/config/repository-config.d.ts.map +1 -0
  8. package/dist/src/config/repository-config.js +88 -0
  9. package/dist/src/config/repository-config.test.d.ts +2 -0
  10. package/dist/src/config/repository-config.test.d.ts.map +1 -0
  11. package/dist/src/config/repository-config.test.js +249 -0
  12. package/dist/src/deployment/deployment-checker.d.ts +110 -0
  13. package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
  14. package/dist/src/deployment/deployment-checker.js +242 -0
  15. package/dist/src/deployment/index.d.ts +3 -0
  16. package/dist/src/deployment/index.d.ts.map +1 -0
  17. package/dist/src/deployment/index.js +2 -0
  18. package/dist/src/frontend/index.d.ts +2 -0
  19. package/dist/src/frontend/index.d.ts.map +1 -0
  20. package/dist/src/frontend/index.js +1 -0
  21. package/dist/src/frontend/types.d.ts +106 -0
  22. package/dist/src/frontend/types.d.ts.map +1 -0
  23. package/dist/src/frontend/types.js +11 -0
  24. package/dist/src/governor/decision-engine.d.ts +52 -0
  25. package/dist/src/governor/decision-engine.d.ts.map +1 -0
  26. package/dist/src/governor/decision-engine.js +220 -0
  27. package/dist/src/governor/decision-engine.test.d.ts +2 -0
  28. package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
  29. package/dist/src/governor/decision-engine.test.js +629 -0
  30. package/dist/src/governor/event-bus.d.ts +43 -0
  31. package/dist/src/governor/event-bus.d.ts.map +1 -0
  32. package/dist/src/governor/event-bus.js +8 -0
  33. package/dist/src/governor/event-deduplicator.d.ts +43 -0
  34. package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
  35. package/dist/src/governor/event-deduplicator.js +53 -0
  36. package/dist/src/governor/event-driven-governor.d.ts +131 -0
  37. package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
  38. package/dist/src/governor/event-driven-governor.js +379 -0
  39. package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
  40. package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
  41. package/dist/src/governor/event-driven-governor.test.js +673 -0
  42. package/dist/src/governor/event-types.d.ts +78 -0
  43. package/dist/src/governor/event-types.d.ts.map +1 -0
  44. package/dist/src/governor/event-types.js +32 -0
  45. package/dist/src/governor/governor-types.d.ts +82 -0
  46. package/dist/src/governor/governor-types.d.ts.map +1 -0
  47. package/dist/src/governor/governor-types.js +21 -0
  48. package/dist/src/governor/governor.d.ts +100 -0
  49. package/dist/src/governor/governor.d.ts.map +1 -0
  50. package/dist/src/governor/governor.js +262 -0
  51. package/dist/src/governor/governor.test.d.ts +2 -0
  52. package/dist/src/governor/governor.test.d.ts.map +1 -0
  53. package/dist/src/governor/governor.test.js +514 -0
  54. package/dist/src/governor/human-touchpoints.d.ts +131 -0
  55. package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
  56. package/dist/src/governor/human-touchpoints.js +251 -0
  57. package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
  58. package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
  59. package/dist/src/governor/human-touchpoints.test.js +366 -0
  60. package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
  61. package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
  62. package/dist/src/governor/in-memory-event-bus.js +79 -0
  63. package/dist/src/governor/index.d.ts +14 -0
  64. package/dist/src/governor/index.d.ts.map +1 -0
  65. package/dist/src/governor/index.js +13 -0
  66. package/dist/src/governor/override-parser.d.ts +60 -0
  67. package/dist/src/governor/override-parser.d.ts.map +1 -0
  68. package/dist/src/governor/override-parser.js +98 -0
  69. package/dist/src/governor/override-parser.test.d.ts +2 -0
  70. package/dist/src/governor/override-parser.test.d.ts.map +1 -0
  71. package/dist/src/governor/override-parser.test.js +312 -0
  72. package/dist/src/governor/platform-adapter.d.ts +69 -0
  73. package/dist/src/governor/platform-adapter.d.ts.map +1 -0
  74. package/dist/src/governor/platform-adapter.js +11 -0
  75. package/dist/src/governor/processing-state.d.ts +66 -0
  76. package/dist/src/governor/processing-state.d.ts.map +1 -0
  77. package/dist/src/governor/processing-state.js +43 -0
  78. package/dist/src/governor/processing-state.test.d.ts +2 -0
  79. package/dist/src/governor/processing-state.test.d.ts.map +1 -0
  80. package/dist/src/governor/processing-state.test.js +96 -0
  81. package/dist/src/governor/top-of-funnel.d.ts +118 -0
  82. package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
  83. package/dist/src/governor/top-of-funnel.js +168 -0
  84. package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
  85. package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
  86. package/dist/src/governor/top-of-funnel.test.js +331 -0
  87. package/dist/src/index.d.ts +11 -0
  88. package/dist/src/index.d.ts.map +1 -0
  89. package/dist/src/index.js +10 -0
  90. package/dist/src/linear-cli.d.ts +38 -0
  91. package/dist/src/linear-cli.d.ts.map +1 -0
  92. package/dist/src/linear-cli.js +674 -0
  93. package/dist/src/logger.d.ts +117 -0
  94. package/dist/src/logger.d.ts.map +1 -0
  95. package/dist/src/logger.js +430 -0
  96. package/dist/src/manifest/generate.d.ts +20 -0
  97. package/dist/src/manifest/generate.d.ts.map +1 -0
  98. package/dist/src/manifest/generate.js +65 -0
  99. package/dist/src/manifest/index.d.ts +4 -0
  100. package/dist/src/manifest/index.d.ts.map +1 -0
  101. package/dist/src/manifest/index.js +2 -0
  102. package/dist/src/manifest/route-manifest.d.ts +34 -0
  103. package/dist/src/manifest/route-manifest.d.ts.map +1 -0
  104. package/dist/src/manifest/route-manifest.js +148 -0
  105. package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
  106. package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
  107. package/dist/src/orchestrator/activity-emitter.js +306 -0
  108. package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
  109. package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
  110. package/dist/src/orchestrator/api-activity-emitter.js +417 -0
  111. package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
  112. package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
  113. package/dist/src/orchestrator/heartbeat-writer.js +137 -0
  114. package/dist/src/orchestrator/index.d.ts +20 -0
  115. package/dist/src/orchestrator/index.d.ts.map +1 -0
  116. package/dist/src/orchestrator/index.js +22 -0
  117. package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
  118. package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
  119. package/dist/src/orchestrator/log-analyzer.js +572 -0
  120. package/dist/src/orchestrator/log-config.d.ts +39 -0
  121. package/dist/src/orchestrator/log-config.d.ts.map +1 -0
  122. package/dist/src/orchestrator/log-config.js +45 -0
  123. package/dist/src/orchestrator/orchestrator.d.ts +316 -0
  124. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
  125. package/dist/src/orchestrator/orchestrator.js +3290 -0
  126. package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
  127. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
  128. package/dist/src/orchestrator/parse-work-result.js +135 -0
  129. package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
  130. package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
  131. package/dist/src/orchestrator/parse-work-result.test.js +234 -0
  132. package/dist/src/orchestrator/progress-logger.d.ts +72 -0
  133. package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
  134. package/dist/src/orchestrator/progress-logger.js +135 -0
  135. package/dist/src/orchestrator/session-logger.d.ts +159 -0
  136. package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
  137. package/dist/src/orchestrator/session-logger.js +275 -0
  138. package/dist/src/orchestrator/state-recovery.d.ts +96 -0
  139. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
  140. package/dist/src/orchestrator/state-recovery.js +302 -0
  141. package/dist/src/orchestrator/state-types.d.ts +165 -0
  142. package/dist/src/orchestrator/state-types.d.ts.map +1 -0
  143. package/dist/src/orchestrator/state-types.js +7 -0
  144. package/dist/src/orchestrator/stream-parser.d.ts +151 -0
  145. package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
  146. package/dist/src/orchestrator/stream-parser.js +137 -0
  147. package/dist/src/orchestrator/types.d.ts +232 -0
  148. package/dist/src/orchestrator/types.d.ts.map +1 -0
  149. package/dist/src/orchestrator/types.js +4 -0
  150. package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
  151. package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
  152. package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
  153. package/dist/src/providers/a2a-auth.d.ts +81 -0
  154. package/dist/src/providers/a2a-auth.d.ts.map +1 -0
  155. package/dist/src/providers/a2a-auth.js +188 -0
  156. package/dist/src/providers/a2a-auth.test.d.ts +2 -0
  157. package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
  158. package/dist/src/providers/a2a-auth.test.js +232 -0
  159. package/dist/src/providers/a2a-provider.d.ts +254 -0
  160. package/dist/src/providers/a2a-provider.d.ts.map +1 -0
  161. package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
  162. package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
  163. package/dist/src/providers/a2a-provider.integration.test.js +665 -0
  164. package/dist/src/providers/a2a-provider.js +811 -0
  165. package/dist/src/providers/a2a-provider.test.d.ts +2 -0
  166. package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
  167. package/dist/src/providers/a2a-provider.test.js +681 -0
  168. package/dist/src/providers/amp-provider.d.ts +20 -0
  169. package/dist/src/providers/amp-provider.d.ts.map +1 -0
  170. package/dist/src/providers/amp-provider.js +24 -0
  171. package/dist/src/providers/claude-provider.d.ts +18 -0
  172. package/dist/src/providers/claude-provider.d.ts.map +1 -0
  173. package/dist/src/providers/claude-provider.js +437 -0
  174. package/dist/src/providers/codex-provider.d.ts +133 -0
  175. package/dist/src/providers/codex-provider.d.ts.map +1 -0
  176. package/dist/src/providers/codex-provider.js +381 -0
  177. package/dist/src/providers/codex-provider.test.d.ts +2 -0
  178. package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
  179. package/dist/src/providers/codex-provider.test.js +387 -0
  180. package/dist/src/providers/index.d.ts +44 -0
  181. package/dist/src/providers/index.d.ts.map +1 -0
  182. package/dist/src/providers/index.js +85 -0
  183. package/dist/src/providers/spring-ai-provider.d.ts +90 -0
  184. package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
  185. package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
  186. package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
  187. package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
  188. package/dist/src/providers/spring-ai-provider.js +317 -0
  189. package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
  190. package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
  191. package/dist/src/providers/spring-ai-provider.test.js +200 -0
  192. package/dist/src/providers/types.d.ts +165 -0
  193. package/dist/src/providers/types.d.ts.map +1 -0
  194. package/dist/src/providers/types.js +13 -0
  195. package/dist/src/templates/adapters.d.ts +51 -0
  196. package/dist/src/templates/adapters.d.ts.map +1 -0
  197. package/dist/src/templates/adapters.js +104 -0
  198. package/dist/src/templates/adapters.test.d.ts +2 -0
  199. package/dist/src/templates/adapters.test.d.ts.map +1 -0
  200. package/dist/src/templates/adapters.test.js +165 -0
  201. package/dist/src/templates/agent-definition.d.ts +85 -0
  202. package/dist/src/templates/agent-definition.d.ts.map +1 -0
  203. package/dist/src/templates/agent-definition.js +97 -0
  204. package/dist/src/templates/agent-definition.test.d.ts +2 -0
  205. package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
  206. package/dist/src/templates/agent-definition.test.js +209 -0
  207. package/dist/src/templates/index.d.ts +14 -0
  208. package/dist/src/templates/index.d.ts.map +1 -0
  209. package/dist/src/templates/index.js +11 -0
  210. package/dist/src/templates/loader.d.ts +41 -0
  211. package/dist/src/templates/loader.d.ts.map +1 -0
  212. package/dist/src/templates/loader.js +114 -0
  213. package/dist/src/templates/registry.d.ts +80 -0
  214. package/dist/src/templates/registry.d.ts.map +1 -0
  215. package/dist/src/templates/registry.js +177 -0
  216. package/dist/src/templates/registry.test.d.ts +2 -0
  217. package/dist/src/templates/registry.test.d.ts.map +1 -0
  218. package/dist/src/templates/registry.test.js +198 -0
  219. package/dist/src/templates/renderer.d.ts +29 -0
  220. package/dist/src/templates/renderer.d.ts.map +1 -0
  221. package/dist/src/templates/renderer.js +35 -0
  222. package/dist/src/templates/strategy-templates.test.d.ts +2 -0
  223. package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
  224. package/dist/src/templates/strategy-templates.test.js +619 -0
  225. package/dist/src/templates/types.d.ts +233 -0
  226. package/dist/src/templates/types.d.ts.map +1 -0
  227. package/dist/src/templates/types.js +127 -0
  228. package/dist/src/templates/types.test.d.ts +2 -0
  229. package/dist/src/templates/types.test.d.ts.map +1 -0
  230. package/dist/src/templates/types.test.js +232 -0
  231. package/dist/src/tools/index.d.ts +6 -0
  232. package/dist/src/tools/index.d.ts.map +1 -0
  233. package/dist/src/tools/index.js +3 -0
  234. package/dist/src/tools/linear-runner.d.ts +34 -0
  235. package/dist/src/tools/linear-runner.d.ts.map +1 -0
  236. package/dist/src/tools/linear-runner.js +700 -0
  237. package/dist/src/tools/plugins/linear.d.ts +9 -0
  238. package/dist/src/tools/plugins/linear.d.ts.map +1 -0
  239. package/dist/src/tools/plugins/linear.js +138 -0
  240. package/dist/src/tools/registry.d.ts +9 -0
  241. package/dist/src/tools/registry.d.ts.map +1 -0
  242. package/dist/src/tools/registry.js +18 -0
  243. package/dist/src/tools/types.d.ts +18 -0
  244. package/dist/src/tools/types.d.ts.map +1 -0
  245. package/dist/src/tools/types.js +1 -0
  246. package/package.json +78 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Template Loader
3
+ *
4
+ * Discovers, parses, and validates workflow templates from YAML files.
5
+ * Supports layered resolution:
6
+ * 1. Built-in defaults (packages/core/src/templates/defaults/)
7
+ * 2. Project-level overrides (.agentfactory/templates/ in repo root)
8
+ * 3. Inline config overrides (programmatic)
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { parse as parseYaml } from 'yaml';
13
+ import { validateWorkflowTemplate, validatePartialTemplate } from './types.js';
14
+ /**
15
+ * Load all workflow templates from a directory.
16
+ * Only processes .yaml and .yml files at the top level.
17
+ *
18
+ * The map key is determined by `metadata.name` when it differs from `metadata.workType`,
19
+ * enabling strategy-specific compound keys like "refinement-context-enriched".
20
+ * When `metadata.name` equals `metadata.workType`, the key is simply the work type.
21
+ */
22
+ export function loadTemplatesFromDir(dir) {
23
+ const templates = new Map();
24
+ if (!fs.existsSync(dir)) {
25
+ return templates;
26
+ }
27
+ const files = fs.readdirSync(dir).filter(f => (f.endsWith('.yaml') || f.endsWith('.yml')) && !f.startsWith('_'));
28
+ for (const file of files) {
29
+ const filePath = path.join(dir, file);
30
+ const template = loadTemplateFile(filePath);
31
+ if (template) {
32
+ // Use metadata.name as the key when it differs from workType (strategy templates),
33
+ // otherwise fall back to workType for backward compatibility.
34
+ const key = template.metadata.name !== template.metadata.workType
35
+ ? template.metadata.name
36
+ : template.metadata.workType;
37
+ templates.set(key, template);
38
+ }
39
+ }
40
+ return templates;
41
+ }
42
+ /**
43
+ * Load and validate a single workflow template YAML file.
44
+ */
45
+ export function loadTemplateFile(filePath) {
46
+ try {
47
+ const content = fs.readFileSync(filePath, 'utf-8');
48
+ const data = parseYaml(content);
49
+ return validateWorkflowTemplate(data, filePath);
50
+ }
51
+ catch (error) {
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ throw new Error(`Failed to load template ${filePath}: ${message}`);
54
+ }
55
+ }
56
+ /**
57
+ * Load all partial templates from a directory (including subdirectories).
58
+ * Returns a map of partial name → content string.
59
+ *
60
+ * Partial names are derived from file paths relative to the partials directory:
61
+ * partials/cli-instructions.yaml → "cli-instructions"
62
+ * partials/frontend/linear-cli.yaml → "frontend/linear-cli"
63
+ */
64
+ export function loadPartialsFromDir(dir, frontend) {
65
+ const partials = new Map();
66
+ if (!fs.existsSync(dir)) {
67
+ return partials;
68
+ }
69
+ loadPartialsRecursive(dir, dir, partials, frontend);
70
+ return partials;
71
+ }
72
+ function loadPartialsRecursive(baseDir, currentDir, partials, frontend) {
73
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
74
+ for (const entry of entries) {
75
+ const fullPath = path.join(currentDir, entry.name);
76
+ if (entry.isDirectory()) {
77
+ loadPartialsRecursive(baseDir, fullPath, partials, frontend);
78
+ continue;
79
+ }
80
+ if (!entry.name.endsWith('.yaml') && !entry.name.endsWith('.yml')) {
81
+ continue;
82
+ }
83
+ try {
84
+ const content = fs.readFileSync(fullPath, 'utf-8');
85
+ const data = parseYaml(content);
86
+ const partial = validatePartialTemplate(data, fullPath);
87
+ // Skip frontend-specific partials that don't match the current frontend
88
+ if (partial.metadata.frontend && frontend && partial.metadata.frontend !== frontend) {
89
+ continue;
90
+ }
91
+ // Derive partial name from relative path (without extension)
92
+ const relativePath = path.relative(baseDir, fullPath);
93
+ const name = relativePath.replace(/\.(yaml|yml)$/, '').replace(/\\/g, '/');
94
+ partials.set(name, partial.content);
95
+ }
96
+ catch (error) {
97
+ const message = error instanceof Error ? error.message : String(error);
98
+ throw new Error(`Failed to load partial ${fullPath}: ${message}`);
99
+ }
100
+ }
101
+ }
102
+ /**
103
+ * Get the path to the built-in default templates directory.
104
+ */
105
+ export function getBuiltinDefaultsDir() {
106
+ // Resolve relative to this file's location
107
+ return path.join(path.dirname(new URL(import.meta.url).pathname), 'defaults');
108
+ }
109
+ /**
110
+ * Get the path to the built-in default partials directory.
111
+ */
112
+ export function getBuiltinPartialsDir() {
113
+ return path.join(getBuiltinDefaultsDir(), 'partials');
114
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Template Registry
3
+ *
4
+ * In-memory registry that resolves workflow templates by work type.
5
+ * Supports layered resolution with built-in defaults, project overrides,
6
+ * and inline config overrides.
7
+ */
8
+ import type { AgentWorkType } from '@renseiai/agentfactory-linear';
9
+ import type { WorkflowTemplate, TemplateContext, TemplateRegistryConfig, ToolPermission, ToolPermissionAdapter } from './types.js';
10
+ /**
11
+ * Template Registry manages workflow templates and renders prompts.
12
+ *
13
+ * Supports strategy-aware template resolution: given a (workType, strategy) tuple,
14
+ * the registry first looks for a strategy-specific template (e.g., "refinement-context-enriched")
15
+ * and falls back to the base work type template (e.g., "refinement").
16
+ */
17
+ export declare class TemplateRegistry {
18
+ /**
19
+ * Internal map uses string keys to support both base work types (e.g., "development")
20
+ * and strategy-specific compound keys (e.g., "refinement-context-enriched").
21
+ */
22
+ private templates;
23
+ private handlebars;
24
+ private toolPermissionAdapter?;
25
+ constructor();
26
+ /**
27
+ * Create and initialize a registry from configuration.
28
+ */
29
+ static create(config?: TemplateRegistryConfig): TemplateRegistry;
30
+ /**
31
+ * Initialize the registry by loading templates from configured sources.
32
+ * Resolution order (later sources override earlier):
33
+ * 1. Built-in defaults
34
+ * 2. Each directory in templateDirs (in order)
35
+ * 3. Inline template overrides
36
+ */
37
+ initialize(config?: TemplateRegistryConfig): void;
38
+ /**
39
+ * Set the tool permission adapter for provider-specific translation.
40
+ */
41
+ setToolPermissionAdapter(adapter: ToolPermissionAdapter): void;
42
+ /**
43
+ * Look up a template by work type, with optional strategy for compound key resolution.
44
+ *
45
+ * Resolution order:
46
+ * 1. "{workType}-{strategy}" (e.g., "refinement-context-enriched")
47
+ * 2. "{workType}" (e.g., "refinement" -- fallback)
48
+ *
49
+ * Returns undefined if no template is registered.
50
+ */
51
+ getTemplate(workType: AgentWorkType, strategy?: string): WorkflowTemplate | undefined;
52
+ /**
53
+ * Check if a template is registered for a work type (optionally with strategy).
54
+ */
55
+ hasTemplate(workType: AgentWorkType, strategy?: string): boolean;
56
+ /**
57
+ * Get all registered template keys (base work types and strategy-specific keys).
58
+ */
59
+ getRegisteredWorkTypes(): string[];
60
+ /**
61
+ * Render a template for a work type with the given context variables.
62
+ * Optionally specify a strategy for strategy-aware template resolution.
63
+ * Returns null if no template is registered for the work type.
64
+ */
65
+ renderPrompt(workType: AgentWorkType, context: TemplateContext, strategy?: string): string | null;
66
+ /**
67
+ * Get the translated tool permissions for a work type (optionally with strategy).
68
+ * Returns undefined if no template or no tool permissions defined.
69
+ */
70
+ getToolPermissions(workType: AgentWorkType, strategy?: string): string[] | undefined;
71
+ /**
72
+ * Get disallowed tools for a work type (optionally with strategy).
73
+ */
74
+ getDisallowedTools(workType: AgentWorkType, strategy?: string): ToolPermission[] | undefined;
75
+ /**
76
+ * Register a partial template for use in Handlebars rendering.
77
+ */
78
+ registerPartial(name: string, content: string): void;
79
+ }
80
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAQnB;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,qBAAqB,CAAC,CAAuB;;IAcrD;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,GAAE,sBAA2B,GAAG,gBAAgB;IAMpE;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,GAAE,sBAA2B,GAAG,IAAI;IA4CrD;;OAEG;IACH,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAI9D;;;;;;;;OAQG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASrF;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhE;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkBjG;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAgBpF;;OAEG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,SAAS;IAK5F;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;CAGrD"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Template Registry
3
+ *
4
+ * In-memory registry that resolves workflow templates by work type.
5
+ * Supports layered resolution with built-in defaults, project overrides,
6
+ * and inline config overrides.
7
+ */
8
+ import Handlebars from 'handlebars';
9
+ import { loadTemplatesFromDir, loadPartialsFromDir, getBuiltinDefaultsDir, getBuiltinPartialsDir, } from './loader.js';
10
+ /**
11
+ * Template Registry manages workflow templates and renders prompts.
12
+ *
13
+ * Supports strategy-aware template resolution: given a (workType, strategy) tuple,
14
+ * the registry first looks for a strategy-specific template (e.g., "refinement-context-enriched")
15
+ * and falls back to the base work type template (e.g., "refinement").
16
+ */
17
+ export class TemplateRegistry {
18
+ /**
19
+ * Internal map uses string keys to support both base work types (e.g., "development")
20
+ * and strategy-specific compound keys (e.g., "refinement-context-enriched").
21
+ */
22
+ templates = new Map();
23
+ handlebars;
24
+ toolPermissionAdapter;
25
+ constructor() {
26
+ this.handlebars = Handlebars.create();
27
+ // Register custom helpers
28
+ this.handlebars.registerHelper('eq', function (a, b) {
29
+ return a === b;
30
+ });
31
+ this.handlebars.registerHelper('neq', function (a, b) {
32
+ return a !== b;
33
+ });
34
+ }
35
+ /**
36
+ * Create and initialize a registry from configuration.
37
+ */
38
+ static create(config = {}) {
39
+ const registry = new TemplateRegistry();
40
+ registry.initialize(config);
41
+ return registry;
42
+ }
43
+ /**
44
+ * Initialize the registry by loading templates from configured sources.
45
+ * Resolution order (later sources override earlier):
46
+ * 1. Built-in defaults
47
+ * 2. Each directory in templateDirs (in order)
48
+ * 3. Inline template overrides
49
+ */
50
+ initialize(config = {}) {
51
+ const { templateDirs = [], templates, useBuiltinDefaults = true, frontend } = config;
52
+ // Layer 1: Built-in defaults
53
+ if (useBuiltinDefaults) {
54
+ const builtinDir = getBuiltinDefaultsDir();
55
+ const builtinTemplates = loadTemplatesFromDir(builtinDir);
56
+ for (const [key, template] of builtinTemplates) {
57
+ this.templates.set(key, template);
58
+ }
59
+ // Load built-in partials
60
+ const builtinPartialsDir = getBuiltinPartialsDir();
61
+ const builtinPartials = loadPartialsFromDir(builtinPartialsDir, frontend);
62
+ for (const [name, content] of builtinPartials) {
63
+ this.handlebars.registerPartial(`partials/${name}`, content);
64
+ }
65
+ }
66
+ // Layer 2: Project-level overrides (each dir in order)
67
+ for (const dir of templateDirs) {
68
+ const dirTemplates = loadTemplatesFromDir(dir);
69
+ for (const [key, template] of dirTemplates) {
70
+ this.templates.set(key, template);
71
+ }
72
+ // Load partials from each dir's partials subdirectory
73
+ const partialsDir = `${dir}/partials`;
74
+ const partials = loadPartialsFromDir(partialsDir, frontend);
75
+ for (const [name, content] of partials) {
76
+ this.handlebars.registerPartial(`partials/${name}`, content);
77
+ }
78
+ }
79
+ // Layer 3: Inline config overrides (highest priority)
80
+ if (templates) {
81
+ for (const [workType, template] of Object.entries(templates)) {
82
+ if (template) {
83
+ this.templates.set(workType, template);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ /**
89
+ * Set the tool permission adapter for provider-specific translation.
90
+ */
91
+ setToolPermissionAdapter(adapter) {
92
+ this.toolPermissionAdapter = adapter;
93
+ }
94
+ /**
95
+ * Look up a template by work type, with optional strategy for compound key resolution.
96
+ *
97
+ * Resolution order:
98
+ * 1. "{workType}-{strategy}" (e.g., "refinement-context-enriched")
99
+ * 2. "{workType}" (e.g., "refinement" -- fallback)
100
+ *
101
+ * Returns undefined if no template is registered.
102
+ */
103
+ getTemplate(workType, strategy) {
104
+ if (strategy) {
105
+ const strategyKey = `${workType}-${strategy}`;
106
+ const strategyTemplate = this.templates.get(strategyKey);
107
+ if (strategyTemplate)
108
+ return strategyTemplate;
109
+ }
110
+ return this.templates.get(workType);
111
+ }
112
+ /**
113
+ * Check if a template is registered for a work type (optionally with strategy).
114
+ */
115
+ hasTemplate(workType, strategy) {
116
+ if (strategy) {
117
+ const strategyKey = `${workType}-${strategy}`;
118
+ if (this.templates.has(strategyKey))
119
+ return true;
120
+ }
121
+ return this.templates.has(workType);
122
+ }
123
+ /**
124
+ * Get all registered template keys (base work types and strategy-specific keys).
125
+ */
126
+ getRegisteredWorkTypes() {
127
+ return Array.from(this.templates.keys());
128
+ }
129
+ /**
130
+ * Render a template for a work type with the given context variables.
131
+ * Optionally specify a strategy for strategy-aware template resolution.
132
+ * Returns null if no template is registered for the work type.
133
+ */
134
+ renderPrompt(workType, context, strategy) {
135
+ const template = this.getTemplate(workType, strategy);
136
+ if (!template) {
137
+ return null;
138
+ }
139
+ try {
140
+ const compiledTemplate = this.handlebars.compile(template.prompt, { noEscape: true });
141
+ return compiledTemplate(context);
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ const key = strategy ? `${workType}-${strategy}` : workType;
146
+ throw new Error(`Failed to render template for work type "${key}": ${message}`);
147
+ }
148
+ }
149
+ /**
150
+ * Get the translated tool permissions for a work type (optionally with strategy).
151
+ * Returns undefined if no template or no tool permissions defined.
152
+ */
153
+ getToolPermissions(workType, strategy) {
154
+ const template = this.getTemplate(workType, strategy);
155
+ if (!template?.tools?.allow) {
156
+ return undefined;
157
+ }
158
+ if (this.toolPermissionAdapter) {
159
+ return this.toolPermissionAdapter.translatePermissions(template.tools.allow);
160
+ }
161
+ // Without an adapter, return string permissions as-is
162
+ return template.tools.allow.map(p => typeof p === 'string' ? p : p.shell);
163
+ }
164
+ /**
165
+ * Get disallowed tools for a work type (optionally with strategy).
166
+ */
167
+ getDisallowedTools(workType, strategy) {
168
+ const template = this.getTemplate(workType, strategy);
169
+ return template?.tools?.disallow;
170
+ }
171
+ /**
172
+ * Register a partial template for use in Handlebars rendering.
173
+ */
174
+ registerPartial(name, content) {
175
+ this.handlebars.registerPartial(name, content);
176
+ }
177
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,198 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { TemplateRegistry } from './registry.js';
3
+ import { ClaudeToolPermissionAdapter } from './adapters.js';
4
+ describe('TemplateRegistry', () => {
5
+ let registry;
6
+ beforeEach(() => {
7
+ registry = new TemplateRegistry();
8
+ });
9
+ describe('basic operations', () => {
10
+ it('returns undefined for unregistered work type', () => {
11
+ expect(registry.getTemplate('development')).toBeUndefined();
12
+ expect(registry.hasTemplate('development')).toBe(false);
13
+ });
14
+ it('returns empty list of registered work types initially', () => {
15
+ expect(registry.getRegisteredWorkTypes()).toEqual([]);
16
+ });
17
+ it('returns null when rendering unregistered work type', () => {
18
+ expect(registry.renderPrompt('development', { identifier: 'SUP-123' })).toBeNull();
19
+ });
20
+ });
21
+ describe('inline templates', () => {
22
+ const devTemplate = {
23
+ apiVersion: 'v1',
24
+ kind: 'WorkflowTemplate',
25
+ metadata: { name: 'development', workType: 'development' },
26
+ tools: { allow: [{ shell: 'pnpm *' }] },
27
+ prompt: 'Start work on {{identifier}}.',
28
+ };
29
+ it('registers and renders inline templates', () => {
30
+ registry.initialize({ templates: { development: devTemplate }, useBuiltinDefaults: false });
31
+ expect(registry.hasTemplate('development')).toBe(true);
32
+ const result = registry.renderPrompt('development', { identifier: 'SUP-123' });
33
+ expect(result).toBe('Start work on SUP-123.');
34
+ });
35
+ it('handles mentionContext conditional', () => {
36
+ const template = {
37
+ apiVersion: 'v1',
38
+ kind: 'WorkflowTemplate',
39
+ metadata: { name: 'dev', workType: 'development' },
40
+ prompt: 'Work on {{identifier}}.{{#if mentionContext}}\nContext: {{mentionContext}}{{/if}}',
41
+ };
42
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
43
+ // Without mentionContext
44
+ expect(registry.renderPrompt('development', { identifier: 'SUP-1' }))
45
+ .toBe('Work on SUP-1.');
46
+ // With mentionContext
47
+ expect(registry.renderPrompt('development', { identifier: 'SUP-1', mentionContext: 'fix bug' }))
48
+ .toBe('Work on SUP-1.\nContext: fix bug');
49
+ });
50
+ });
51
+ describe('partials', () => {
52
+ it('renders templates with registered partials', () => {
53
+ const template = {
54
+ apiVersion: 'v1',
55
+ kind: 'WorkflowTemplate',
56
+ metadata: { name: 'dev', workType: 'development' },
57
+ prompt: 'Work on {{identifier}}.{{> partials/test-partial}}',
58
+ };
59
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
60
+ registry.registerPartial('partials/test-partial', '\nTest partial content.');
61
+ const result = registry.renderPrompt('development', { identifier: 'SUP-1' });
62
+ expect(result).toBe('Work on SUP-1.\nTest partial content.');
63
+ });
64
+ it('throws on missing partial', () => {
65
+ const template = {
66
+ apiVersion: 'v1',
67
+ kind: 'WorkflowTemplate',
68
+ metadata: { name: 'dev', workType: 'development' },
69
+ prompt: 'Work on {{identifier}}.{{> partials/missing}}',
70
+ };
71
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
72
+ expect(() => registry.renderPrompt('development', { identifier: 'SUP-1' }))
73
+ .toThrow('Failed to render template');
74
+ });
75
+ });
76
+ describe('tool permissions', () => {
77
+ it('returns undefined when no tools defined', () => {
78
+ const template = {
79
+ apiVersion: 'v1',
80
+ kind: 'WorkflowTemplate',
81
+ metadata: { name: 'dev', workType: 'development' },
82
+ prompt: 'test',
83
+ };
84
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
85
+ expect(registry.getToolPermissions('development')).toBeUndefined();
86
+ });
87
+ it('returns raw permissions without adapter', () => {
88
+ const template = {
89
+ apiVersion: 'v1',
90
+ kind: 'WorkflowTemplate',
91
+ metadata: { name: 'dev', workType: 'development' },
92
+ tools: { allow: [{ shell: 'pnpm *' }, 'Read'] },
93
+ prompt: 'test',
94
+ };
95
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
96
+ const perms = registry.getToolPermissions('development');
97
+ expect(perms).toEqual(['pnpm *', 'Read']);
98
+ });
99
+ it('translates permissions with Claude adapter', () => {
100
+ const template = {
101
+ apiVersion: 'v1',
102
+ kind: 'WorkflowTemplate',
103
+ metadata: { name: 'dev', workType: 'development' },
104
+ tools: {
105
+ allow: [{ shell: 'pnpm *' }, { shell: 'git commit *' }],
106
+ disallow: ['user-input'],
107
+ },
108
+ prompt: 'test',
109
+ };
110
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
111
+ registry.setToolPermissionAdapter(new ClaudeToolPermissionAdapter());
112
+ const perms = registry.getToolPermissions('development');
113
+ expect(perms).toEqual(['Bash(pnpm:*)', 'Bash(git commit:*)']);
114
+ });
115
+ it('returns disallowed tools', () => {
116
+ const template = {
117
+ apiVersion: 'v1',
118
+ kind: 'WorkflowTemplate',
119
+ metadata: { name: 'dev', workType: 'development' },
120
+ tools: { disallow: ['user-input'] },
121
+ prompt: 'test',
122
+ };
123
+ registry.initialize({ templates: { development: template }, useBuiltinDefaults: false });
124
+ expect(registry.getDisallowedTools('development')).toEqual(['user-input']);
125
+ });
126
+ });
127
+ describe('built-in defaults', () => {
128
+ it('loads built-in default templates when useBuiltinDefaults is true', () => {
129
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
130
+ // Should have loaded templates for all 11 base work types + 5 strategy templates
131
+ const workTypes = fullRegistry.getRegisteredWorkTypes();
132
+ expect(workTypes.length).toBe(16);
133
+ expect(workTypes).toContain('development');
134
+ expect(workTypes).toContain('qa');
135
+ expect(workTypes).toContain('coordination');
136
+ // Strategy-specific templates
137
+ expect(workTypes).toContain('refinement-context-enriched');
138
+ expect(workTypes).toContain('refinement-decompose');
139
+ expect(workTypes).toContain('development-retry');
140
+ expect(workTypes).toContain('qa-retry');
141
+ expect(workTypes).toContain('qa-native');
142
+ });
143
+ it('renders a built-in template with variables', () => {
144
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
145
+ const result = fullRegistry.renderPrompt('development', { identifier: 'SUP-999' });
146
+ expect(result).toContain('SUP-999');
147
+ expect(result).toContain('Implement the feature/fix');
148
+ });
149
+ it('built-in templates include CLI instructions partial', () => {
150
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
151
+ const result = fullRegistry.renderPrompt('development', { identifier: 'SUP-1', linearCli: 'pnpm af-linear', packageManager: 'pnpm' });
152
+ expect(result).toContain('pnpm af-linear');
153
+ expect(result).toContain('LINEAR CLI (CRITICAL)');
154
+ });
155
+ it('built-in QA template includes work result marker', () => {
156
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
157
+ const result = fullRegistry.renderPrompt('qa', { identifier: 'SUP-1' });
158
+ expect(result).toContain('WORK_RESULT:passed');
159
+ expect(result).toContain('WORK_RESULT:failed');
160
+ });
161
+ it('built-in coordination template includes shared worktree safety', () => {
162
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
163
+ const result = fullRegistry.renderPrompt('coordination', { identifier: 'SUP-1' });
164
+ expect(result).toContain('SHARED WORKTREE');
165
+ expect(result).toContain('git worktree remove');
166
+ });
167
+ it('built-in templates handle mentionContext', () => {
168
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
169
+ const result = fullRegistry.renderPrompt('development', {
170
+ identifier: 'SUP-1',
171
+ mentionContext: 'Fix the login bug',
172
+ });
173
+ expect(result).toContain('Fix the login bug');
174
+ expect(result).toContain('Additional context');
175
+ });
176
+ it('built-in templates omit mentionContext when not provided', () => {
177
+ const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
178
+ const result = fullRegistry.renderPrompt('development', { identifier: 'SUP-1' });
179
+ expect(result).not.toContain('Additional context');
180
+ });
181
+ });
182
+ describe('layer override', () => {
183
+ it('inline templates override built-in defaults', () => {
184
+ const customTemplate = {
185
+ apiVersion: 'v1',
186
+ kind: 'WorkflowTemplate',
187
+ metadata: { name: 'custom-dev', workType: 'development' },
188
+ prompt: 'Custom prompt for {{identifier}}.',
189
+ };
190
+ const customRegistry = TemplateRegistry.create({
191
+ useBuiltinDefaults: true,
192
+ templates: { development: customTemplate },
193
+ });
194
+ const result = customRegistry.renderPrompt('development', { identifier: 'SUP-1' });
195
+ expect(result).toBe('Custom prompt for SUP-1.');
196
+ });
197
+ });
198
+ });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Template Renderer
3
+ *
4
+ * Bridge between the template registry and the existing prompt generation
5
+ * functions. Provides a drop-in replacement for generatePromptForWorkType
6
+ * that uses templates when available and falls back to hardcoded prompts.
7
+ */
8
+ import type { AgentWorkType } from '@renseiai/agentfactory-linear';
9
+ import type { TemplateContext } from './types.js';
10
+ import type { TemplateRegistry } from './registry.js';
11
+ /**
12
+ * Render a prompt from a template registry with fallback support.
13
+ *
14
+ * Supports strategy-aware resolution: when a strategy is provided,
15
+ * the registry first looks for a strategy-specific template
16
+ * (e.g., "refinement-context-enriched") before falling back to the base template.
17
+ *
18
+ * @param registry - The template registry to use
19
+ * @param workType - The work type to generate a prompt for
20
+ * @param context - Template variables
21
+ * @param fallback - Fallback function when no template is available
22
+ * @param strategy - Optional escalation strategy for strategy-aware resolution
23
+ * @returns The rendered prompt string
24
+ */
25
+ export declare function renderPromptWithFallback(registry: TemplateRegistry | null, workType: AgentWorkType, context: TemplateContext, fallback: (identifier: string, workType: AgentWorkType, options?: {
26
+ parentContext?: string;
27
+ mentionContext?: string;
28
+ }) => string, strategy?: string): string;
29
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/templates/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,EACjC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,EAChI,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAcR"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Template Renderer
3
+ *
4
+ * Bridge between the template registry and the existing prompt generation
5
+ * functions. Provides a drop-in replacement for generatePromptForWorkType
6
+ * that uses templates when available and falls back to hardcoded prompts.
7
+ */
8
+ /**
9
+ * Render a prompt from a template registry with fallback support.
10
+ *
11
+ * Supports strategy-aware resolution: when a strategy is provided,
12
+ * the registry first looks for a strategy-specific template
13
+ * (e.g., "refinement-context-enriched") before falling back to the base template.
14
+ *
15
+ * @param registry - The template registry to use
16
+ * @param workType - The work type to generate a prompt for
17
+ * @param context - Template variables
18
+ * @param fallback - Fallback function when no template is available
19
+ * @param strategy - Optional escalation strategy for strategy-aware resolution
20
+ * @returns The rendered prompt string
21
+ */
22
+ export function renderPromptWithFallback(registry, workType, context, fallback, strategy) {
23
+ // If registry exists and has a template, use it
24
+ if (registry?.hasTemplate(workType, strategy)) {
25
+ const rendered = registry.renderPrompt(workType, context, strategy);
26
+ if (rendered !== null) {
27
+ return rendered;
28
+ }
29
+ }
30
+ // Fall back to hardcoded prompt generation
31
+ return fallback(context.identifier, workType, {
32
+ parentContext: context.parentContext,
33
+ mentionContext: context.mentionContext,
34
+ });
35
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=strategy-templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy-templates.test.d.ts","sourceRoot":"","sources":["../../../src/templates/strategy-templates.test.ts"],"names":[],"mappings":""}