@entelligentsia/forgecli 1.0.10 → 1.0.20

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 (183) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/dist/CHANGELOG-forge-plugin.md +211 -0
  3. package/dist/bin/forge.js +0 -0
  4. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  5. package/dist/extensions/forgecli/context-governor-compaction.d.ts +83 -0
  6. package/dist/extensions/forgecli/context-governor-compaction.js +302 -0
  7. package/dist/extensions/forgecli/context-governor-compaction.js.map +1 -0
  8. package/dist/extensions/forgecli/context-governor.d.ts +173 -0
  9. package/dist/extensions/forgecli/context-governor.js +618 -0
  10. package/dist/extensions/forgecli/context-governor.js.map +1 -0
  11. package/dist/extensions/forgecli/dashboard/component.d.ts +105 -0
  12. package/dist/extensions/forgecli/dashboard/component.js +861 -0
  13. package/dist/extensions/forgecli/dashboard/component.js.map +1 -0
  14. package/dist/extensions/forgecli/dashboard/register.d.ts +2 -0
  15. package/dist/extensions/forgecli/dashboard/register.js +31 -0
  16. package/dist/extensions/forgecli/dashboard/register.js.map +1 -0
  17. package/dist/extensions/forgecli/dashboard/theme.d.ts +27 -0
  18. package/dist/extensions/forgecli/dashboard/theme.js +91 -0
  19. package/dist/extensions/forgecli/dashboard/theme.js.map +1 -0
  20. package/dist/extensions/forgecli/dashboard/view-model.d.ts +35 -0
  21. package/dist/extensions/forgecli/dashboard/view-model.js +54 -0
  22. package/dist/extensions/forgecli/dashboard/view-model.js.map +1 -0
  23. package/dist/extensions/forgecli/fix-bug.js +126 -7
  24. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  25. package/dist/extensions/forgecli/forge-artifact-tool.js +2 -1
  26. package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
  27. package/dist/extensions/forgecli/forge-commands.js +1 -0
  28. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  29. package/dist/extensions/forgecli/forge-init/phase4-register.js +53 -0
  30. package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
  31. package/dist/extensions/forgecli/forge-subagent.d.ts +20 -1
  32. package/dist/extensions/forgecli/forge-subagent.js +23 -7
  33. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  34. package/dist/extensions/forgecli/forge-tools.js +3 -1
  35. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  36. package/dist/extensions/forgecli/hook-dispatcher.d.ts +3 -1
  37. package/dist/extensions/forgecli/hook-dispatcher.js +37 -3
  38. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  39. package/dist/extensions/forgecli/index.js +38 -1
  40. package/dist/extensions/forgecli/index.js.map +1 -1
  41. package/dist/extensions/forgecli/lib/halt-advisor.d.ts +59 -0
  42. package/dist/extensions/forgecli/lib/halt-advisor.js +113 -0
  43. package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -0
  44. package/dist/extensions/forgecli/migration-engine.js +25 -12
  45. package/dist/extensions/forgecli/migration-engine.js.map +1 -1
  46. package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +26 -0
  47. package/dist/extensions/forgecli/orchestrator-status-bar.js +213 -0
  48. package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -0
  49. package/dist/extensions/forgecli/orchestrator-tree.d.ts +96 -0
  50. package/dist/extensions/forgecli/orchestrator-tree.js +390 -0
  51. package/dist/extensions/forgecli/orchestrator-tree.js.map +1 -0
  52. package/dist/extensions/forgecli/project-orientation.js +12 -8
  53. package/dist/extensions/forgecli/project-orientation.js.map +1 -1
  54. package/dist/extensions/forgecli/regenerate.d.ts +16 -0
  55. package/dist/extensions/forgecli/regenerate.js +110 -0
  56. package/dist/extensions/forgecli/regenerate.js.map +1 -1
  57. package/dist/extensions/forgecli/run-sprint.d.ts +3 -1
  58. package/dist/extensions/forgecli/run-sprint.js +34 -3
  59. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  60. package/dist/extensions/forgecli/run-task.d.ts +66 -1
  61. package/dist/extensions/forgecli/run-task.js +323 -12
  62. package/dist/extensions/forgecli/run-task.js.map +1 -1
  63. package/dist/extensions/forgecli/thread-switcher.d.ts +4 -1
  64. package/dist/extensions/forgecli/thread-switcher.js +118 -762
  65. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  66. package/dist/extensions/forgecli/viewport-events.js +32 -0
  67. package/dist/extensions/forgecli/viewport-events.js.map +1 -1
  68. package/dist/forge-payload/.base-pack/commands/fix-bug.md +1 -1
  69. package/dist/forge-payload/.base-pack/commands/run-sprint.md +1 -1
  70. package/dist/forge-payload/.base-pack/commands/run-task.md +1 -1
  71. package/dist/forge-payload/.base-pack/personas/architect.md +1 -1
  72. package/dist/forge-payload/.base-pack/personas/bug-fixer.md +1 -1
  73. package/dist/forge-payload/.base-pack/personas/collator.md +3 -3
  74. package/dist/forge-payload/.base-pack/personas/engineer.md +1 -1
  75. package/dist/forge-payload/.base-pack/personas/librarian.md +1 -1
  76. package/dist/forge-payload/.base-pack/personas/orchestrator.md +1 -1
  77. package/dist/forge-payload/.base-pack/personas/product-manager.md +1 -1
  78. package/dist/forge-payload/.base-pack/personas/qa-engineer.md +1 -1
  79. package/dist/forge-payload/.base-pack/personas/supervisor.md +1 -1
  80. package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +1 -1
  81. package/dist/forge-payload/.base-pack/workflows/_fragments/friction-emit.md +1 -1
  82. package/dist/forge-payload/.base-pack/workflows/_fragments/iron-laws.md +1 -1
  83. package/dist/forge-payload/.base-pack/workflows/_fragments/progress-reporting.md +2 -2
  84. package/dist/forge-payload/.base-pack/workflows/_fragments/store-cli-verbs.md +11 -2
  85. package/dist/forge-payload/.base-pack/workflows/architect_approve.md +6 -7
  86. package/dist/forge-payload/.base-pack/workflows/architect_review_sprint_completion.md +2 -2
  87. package/dist/forge-payload/.base-pack/workflows/architect_sprint_intake.md +2 -2
  88. package/dist/forge-payload/.base-pack/workflows/architect_sprint_plan.md +5 -5
  89. package/dist/forge-payload/.base-pack/workflows/collator_agent.md +4 -6
  90. package/dist/forge-payload/.base-pack/workflows/commit_task.md +5 -6
  91. package/dist/forge-payload/.base-pack/workflows/enhance.md +5 -5
  92. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +15 -7
  93. package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +12 -13
  94. package/dist/forge-payload/.base-pack/workflows/plan_task.md +12 -6
  95. package/dist/forge-payload/.base-pack/workflows/review_code.md +12 -11
  96. package/dist/forge-payload/.base-pack/workflows/review_plan.md +12 -11
  97. package/dist/forge-payload/.base-pack/workflows/sprint_retrospective.md +3 -3
  98. package/dist/forge-payload/.base-pack/workflows/triage.md +12 -9
  99. package/dist/forge-payload/.base-pack/workflows/update_implementation.md +2 -2
  100. package/dist/forge-payload/.base-pack/workflows/update_plan.md +2 -2
  101. package/dist/forge-payload/.base-pack/workflows/validate_task.md +9 -9
  102. package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +490 -0
  103. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-sprint.js +416 -0
  104. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +608 -0
  105. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  106. package/dist/forge-payload/.schemas/config.schema.json +2 -3
  107. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  108. package/dist/forge-payload/.schemas/event.schema.json +16 -0
  109. package/dist/forge-payload/.schemas/migrations.json +359 -18
  110. package/dist/forge-payload/commands/health.md +29 -0
  111. package/dist/forge-payload/commands/rebuild.md +143 -15
  112. package/dist/forge-payload/commands/update.md +28 -27
  113. package/dist/forge-payload/hooks/preflight-session.cjs +99 -0
  114. package/dist/forge-payload/init/phases/phase-3-materialize.md +18 -5
  115. package/dist/forge-payload/integrity.json +7 -6
  116. package/dist/forge-payload/meta/fragments/tool-discipline.md +1 -1
  117. package/dist/forge-payload/meta/personas/meta-architect.md +1 -1
  118. package/dist/forge-payload/meta/personas/meta-bug-fixer.md +1 -1
  119. package/dist/forge-payload/meta/personas/meta-collator.md +7 -7
  120. package/dist/forge-payload/meta/personas/meta-engineer.md +1 -1
  121. package/dist/forge-payload/meta/personas/meta-orchestrator.md +1 -1
  122. package/dist/forge-payload/meta/personas/meta-supervisor.md +1 -1
  123. package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +1 -1
  124. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +1 -1
  125. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +1 -1
  126. package/dist/forge-payload/meta/workflows/_fragments/iron-laws.md +1 -1
  127. package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +2 -2
  128. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +11 -2
  129. package/dist/forge-payload/meta/workflows/meta-approve.md +6 -7
  130. package/dist/forge-payload/meta/workflows/meta-bug-triage.md +12 -9
  131. package/dist/forge-payload/meta/workflows/meta-collate.md +5 -7
  132. package/dist/forge-payload/meta/workflows/meta-commit.md +5 -6
  133. package/dist/forge-payload/meta/workflows/meta-enhance.md +5 -5
  134. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +35 -11
  135. package/dist/forge-payload/meta/workflows/meta-implement.md +15 -7
  136. package/dist/forge-payload/meta/workflows/meta-migrate.md +13 -14
  137. package/dist/forge-payload/meta/workflows/meta-new-sprint.md +3 -3
  138. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +138 -39
  139. package/dist/forge-payload/meta/workflows/meta-plan-sprint.md +6 -6
  140. package/dist/forge-payload/meta/workflows/meta-plan-task.md +12 -6
  141. package/dist/forge-payload/meta/workflows/meta-retro.md +4 -4
  142. package/dist/forge-payload/meta/workflows/meta-retrospective.md +4 -4
  143. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +12 -11
  144. package/dist/forge-payload/meta/workflows/meta-review-plan.md +12 -11
  145. package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +3 -3
  146. package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +3 -3
  147. package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +6 -6
  148. package/dist/forge-payload/meta/workflows/meta-update-implementation.md +2 -2
  149. package/dist/forge-payload/meta/workflows/meta-update-plan.md +2 -2
  150. package/dist/forge-payload/meta/workflows/meta-validate.md +9 -9
  151. package/dist/forge-payload/schemas/config.schema.json +2 -3
  152. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  153. package/dist/forge-payload/schemas/event.schema.json +16 -0
  154. package/dist/forge-payload/schemas/structure-manifest.json +75 -73
  155. package/dist/forge-payload/skills/refresh-kb-links/SKILL.md +14 -7
  156. package/dist/forge-payload/tools/banners.cjs +29 -10
  157. package/dist/forge-payload/tools/check-structure.cjs +88 -7
  158. package/dist/forge-payload/tools/collate.cjs +48 -2
  159. package/dist/forge-payload/tools/manage-config.cjs +5 -7
  160. package/dist/forge-payload/tools/parse-gates.cjs +73 -1
  161. package/dist/forge-payload/tools/postflight-gate.cjs +298 -0
  162. package/dist/forge-payload/tools/preflight-gate.cjs +47 -0
  163. package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -4
  164. package/dist/forge-payload/tools/verify-phase.cjs +17 -0
  165. package/package.json +2 -2
  166. package/dist/bin/forgecli.d.ts +0 -2
  167. package/dist/bin/forgecli.js +0 -6
  168. package/dist/bin/forgecli.js.map +0 -1
  169. package/dist/extensions/forgecli/config-tui/index.d.ts +0 -5
  170. package/dist/extensions/forgecli/config-tui/index.js +0 -5
  171. package/dist/extensions/forgecli/config-tui/index.js.map +0 -1
  172. package/dist/extensions/forgecli/loaders/persona-skill-loader.d.ts +0 -45
  173. package/dist/extensions/forgecli/loaders/persona-skill-loader.js +0 -227
  174. package/dist/extensions/forgecli/loaders/persona-skill-loader.js.map +0 -1
  175. package/dist/extensions/forgecli/loaders/template-render.d.ts +0 -20
  176. package/dist/extensions/forgecli/loaders/template-render.js +0 -85
  177. package/dist/extensions/forgecli/loaders/template-render.js.map +0 -1
  178. package/dist/extensions/forgecli/loaders/workflow-loader.d.ts +0 -41
  179. package/dist/extensions/forgecli/loaders/workflow-loader.js +0 -164
  180. package/dist/extensions/forgecli/loaders/workflow-loader.js.map +0 -1
  181. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +0 -446
  182. package/dist/forge-payload/.base-pack/workflows/orchestrate_task.md +0 -928
  183. package/dist/forge-payload/.base-pack/workflows/run_sprint.md +0 -225
@@ -349,9 +349,56 @@ if (require.main === module) {
349
349
  if (result.ok) process.exit(0);
350
350
  process.stderr.write(`Gate failed for phase "${args.phase}":\n`);
351
351
  for (const m of result.missing) process.stderr.write(` - ${m}\n`);
352
+ // Emit structured JSON on stdout for orchestrators to parse and render.
353
+ // Shape: { phase, reasonCode, detail, remediation }
354
+ const structured = buildStructuredFailure(args.phase, result.missing);
355
+ process.stdout.write(JSON.stringify(structured) + '\n');
352
356
  process.exit(1);
353
357
  }
354
358
 
359
+ // Build a structured gate-failure object for orchestrators.
360
+ // Maps the human-readable `missing[]` strings to a typed { phase, reasonCode, detail, remediation }.
361
+ // reasonCode is derived from the dominant failure pattern:
362
+ // artifact-missing — artifact missing / too small
363
+ // predecessor-verdict-missing — after-clause verdict absent or wrong
364
+ // illegal-status — require/forbid predicate fired
365
+ // tool-error — internal error / unrecognised pattern
366
+ // When multiple failures exist, reasonCode reflects the first recognised pattern;
367
+ // detail combines all messages; only a single JSON object is ever emitted.
368
+ function buildStructuredFailure(phase, missing) {
369
+ let reasonCode = 'tool-error';
370
+ const detailParts = [];
371
+
372
+ for (const m of missing) {
373
+ detailParts.push(m);
374
+ if (reasonCode === 'tool-error') {
375
+ if (/^artifact (missing|too small)/i.test(m)) {
376
+ reasonCode = 'artifact-missing';
377
+ } else if (/^predecessor verdict (missing|unreadable)/i.test(m) || /verdict is "/i.test(m)) {
378
+ reasonCode = 'predecessor-verdict-missing';
379
+ } else if (/^(require failed|forbid triggered)/i.test(m)) {
380
+ reasonCode = 'illegal-status';
381
+ }
382
+ }
383
+ }
384
+
385
+ const detail = detailParts.join('; ');
386
+
387
+ const remediationMap = {
388
+ 'artifact-missing': `Re-run the phase that produces this artifact (e.g. /forge:plan or /forge:implement), then retry.`,
389
+ 'predecessor-verdict-missing': `Ensure the predecessor review phase completed and recorded a verdict via set-summary, then retry.`,
390
+ 'illegal-status': `Correct the task/bug status (use store-cli update-status) so it satisfies the gate predicate, then retry.`,
391
+ 'tool-error': `Check the gate configuration and store records for this task; run node .forge/tools/preflight-gate.cjs manually for diagnostics.`,
392
+ };
393
+
394
+ return {
395
+ phase,
396
+ reasonCode,
397
+ detail,
398
+ remediation: remediationMap[reasonCode],
399
+ };
400
+ }
401
+
355
402
  function parseArgs(argv) {
356
403
  const out = {};
357
404
  for (let i = 0; i < argv.length; i++) {
@@ -120,10 +120,11 @@ const RUNTIME_PASSTHROUGH_KEYS = new Set([
120
120
  * project prefix via getCommandsSubdir() — see walkBasePack.
121
121
  */
122
122
  const SUBDIR_OUTPUT_MAP = {
123
- personas: path.join('.forge', 'personas'),
124
- skills: path.join('.forge', 'skills'),
125
- workflows: path.join('.forge', 'workflows'),
126
- templates: path.join('.forge', 'templates'),
123
+ personas: path.join('.forge', 'personas'),
124
+ skills: path.join('.forge', 'skills'),
125
+ workflows: path.join('.forge', 'workflows'),
126
+ templates: path.join('.forge', 'templates'),
127
+ 'workflows-js': path.join('.claude', 'workflows'),
127
128
  };
128
129
 
129
130
  /**
@@ -194,6 +194,14 @@ function verifyPhase2(cwd, kbPath) {
194
194
 
195
195
  const PHASE3_DIRS = ['workflows', 'personas', 'skills', 'templates'];
196
196
 
197
+ // JS workflow files that substitute-placeholders.cjs emits from
198
+ // base-pack/workflows-js/ into .claude/workflows/ (FORGE-S28-T01).
199
+ // These are checked in addition to the .forge/ directory checks.
200
+ const PHASE3_JS_FILES = [
201
+ path.join('.claude', 'workflows', 'wfl-run-task.js'),
202
+ path.join('.claude', 'workflows', 'wfl-run-sprint.js'),
203
+ ];
204
+
197
205
  function verifyPhase3(cwd) {
198
206
  const missing = [];
199
207
  const checked = [];
@@ -213,6 +221,15 @@ function verifyPhase3(cwd) {
213
221
  }
214
222
  }
215
223
 
224
+ // Assert generated JS workflow files are present (FORGE-S28-T01)
225
+ for (const relFile of PHASE3_JS_FILES) {
226
+ checked.push(relFile);
227
+ const absFile = path.join(cwd, relFile);
228
+ if (!fs.existsSync(absFile)) {
229
+ missing.push(relFile);
230
+ }
231
+ }
232
+
216
233
  return {
217
234
  phase: 3,
218
235
  ok: missing.length === 0,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@entelligentsia/forgecli",
3
- "version": "1.0.10",
4
- "description": "Forge SDLC ported onto @earendil-works/pi-coding-agent \u2014 production launcher with three bin aliases (forge/forgecli/4ge). Bundles a curated fork of pi-coding-agent vendored under earendil-works names.",
3
+ "version": "1.0.20",
4
+ "description": "Forge SDLC ported onto @earendil-works/pi-coding-agent production launcher with three bin aliases (forge/forgecli/4ge). Bundles a curated fork of pi-coding-agent vendored under earendil-works names.",
5
5
  "license": "MIT",
6
6
  "author": "Entelligentsia",
7
7
  "homepage": "https://github.com/Entelligentsia/forge-cli#readme",
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- // Stage 1 scaffold (FORGE-S15-T01). Real CLI entry lands in FORGE-S15-T03.
3
- console.log("forgecli stub — see FORGE-S15-T03");
4
- process.exit(0);
5
- export {};
6
- //# sourceMappingURL=forgecli.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"forgecli.js","sourceRoot":"","sources":["../../src/bin/forgecli.ts"],"names":[],"mappings":";AACA,2EAA2E;AAE3E,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;AACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
@@ -1,5 +0,0 @@
1
- export { createConfigTuiComponent, type ConfigTuiComponentOptions } from "./component.js";
2
- export type { ConfigLayer } from "../config-writer.js";
3
- export type { View, ConfigBuffer, AvailableModel, InitOptions, ConfigTuiState, ConfigTuiAction, PhaseOverride, ResolvedPersonaEntry, PersonaPickerEntry, PipelineOverrideSummary, TierAssignment, } from "./state/model.js";
4
- export { CANONICAL_PHASES, initialState, reducer, getActiveView, listResolvedPersonas, listPersonaPickerEntries, uniqueProviders, listPipelineOverrideSummaries, getPhaseOverride, getTierAssignment, getAllTierAssignments, getTierForPersona, getPersonasInTier, writePersonaEntry, deletePersonaEntry, writePhaseOverride, clearPhaseOverride, writeTierAssignment, isConfigEmpty, personaSourceLabel, } from "./state.js";
5
- export type { InputResult, Screen } from "./screens/types.js";
@@ -1,5 +0,0 @@
1
- // Config-TUI public re-exports.
2
- // Phase 3: theming + width safety + data-driven menus + auth error surfacing.
3
- export { createConfigTuiComponent } from "./component.js";
4
- export { CANONICAL_PHASES, initialState, reducer, getActiveView, listResolvedPersonas, listPersonaPickerEntries, uniqueProviders, listPipelineOverrideSummaries, getPhaseOverride, getTierAssignment, getAllTierAssignments, getTierForPersona, getPersonasInTier, writePersonaEntry, deletePersonaEntry, writePhaseOverride, clearPhaseOverride, writeTierAssignment, isConfigEmpty, personaSourceLabel, } from "./state.js";
5
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/extensions/forgecli/config-tui/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,8EAA8E;AAE9E,OAAO,EAAE,wBAAwB,EAAkC,MAAM,gBAAgB,CAAC;AAkB1F,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,6BAA6B,EAC7B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
@@ -1,45 +0,0 @@
1
- import { type Static, Type } from "typebox";
2
- export declare const PersonaSchema: Type.TObject<{
3
- name: Type.TString;
4
- filePath: Type.TString;
5
- identity: Type.TString;
6
- body: Type.TString;
7
- capabilities: Type.TArray<Type.TString>;
8
- frontmatter: Type.TRecord<"^.*$", Type.TUnknown>;
9
- }>;
10
- export type Persona = Static<typeof PersonaSchema>;
11
- export declare const SkillSchema: Type.TObject<{
12
- name: Type.TString;
13
- filePath: Type.TString;
14
- body: Type.TString;
15
- capabilities: Type.TArray<Type.TString>;
16
- frontmatter: Type.TRecord<"^.*$", Type.TUnknown>;
17
- }>;
18
- export type Skill = Static<typeof SkillSchema>;
19
- export type PersonaSkillLoaderErrorCode = "missing_file" | "invalid_frontmatter" | "path_traversal" | "no_project_root" | "validation_failed";
20
- export declare class PersonaSkillLoaderError extends Error {
21
- readonly code: PersonaSkillLoaderErrorCode;
22
- constructor(code: PersonaSkillLoaderErrorCode, message: string);
23
- }
24
- export interface LoaderOptions {
25
- /** Override project root (the directory containing `.forge/`). */
26
- projectRoot?: string;
27
- /** cwd to start `.forge/config.json` discovery from. Defaults to `process.cwd()`. */
28
- cwd?: string;
29
- }
30
- /**
31
- * Load a Persona record from `<projectRoot>/.forge/personas/<name>.md`.
32
- *
33
- * `name` is the filename stem (e.g. `"engineer"` for `engineer.md`). It must
34
- * not contain path separators or traversal segments — invalid names raise
35
- * `PersonaSkillLoaderError` with `code: "path_traversal"`.
36
- */
37
- export declare function loadPersona(name: string, opts?: LoaderOptions): Persona;
38
- /**
39
- * Load a Skill record from `<projectRoot>/.forge/skills/<name>.md`.
40
- *
41
- * `name` is the filename stem. The Forge convention names skill files
42
- * `<noun>-skills.md` (e.g. `engineer-skills.md`); callers pass the full stem
43
- * including the `-skills` suffix. The loader does not auto-append it.
44
- */
45
- export declare function loadSkill(name: string, opts?: LoaderOptions): Skill;
@@ -1,227 +0,0 @@
1
- // Central persona/skill loader — FORGE-S20-T02.
2
- //
3
- // Single canonical surface for reading persona and skill markdown files from
4
- // the user's `.forge/personas/` and `.forge/skills/` directories. Future
5
- // kickoff handlers (T04 /forge:enhance, T05 /forge:plan, T06 /forge:implement)
6
- // load typed Persona/Skill records through this module instead of issuing
7
- // ad-hoc fs.readFile calls. The smoke gate locks the invariant in place.
8
- //
9
- // Path resolution anchors at the **project root** containing `.forge/`, not
10
- // at `forgeRoot` (which points at the bundled plugin source — `.forge/personas/`
11
- // is a project-local generated directory).
12
- //
13
- // Iron Law 6 (no shell-string interpolation): all I/O is via fs synchronous
14
- // APIs with absolute paths constructed by `path.join`/`path.resolve`. No
15
- // external process invocation.
16
- // Iron Law 7 (no silent continuation): every failure mode raises a typed
17
- // `PersonaSkillLoaderError` with an explicit `code`.
18
- import * as fs from "node:fs";
19
- import * as path from "node:path";
20
- import { Type } from "typebox";
21
- import { Value } from "typebox/value";
22
- import { discoverForgeConfig } from "../forge-root.js";
23
- // ── TypeBox schemas ──────────────────────────────────────────────────────
24
- export const PersonaSchema = Type.Object({
25
- name: Type.String(),
26
- filePath: Type.String(),
27
- identity: Type.String(),
28
- body: Type.String(),
29
- capabilities: Type.Array(Type.String()),
30
- frontmatter: Type.Record(Type.String(), Type.Unknown()),
31
- });
32
- export const SkillSchema = Type.Object({
33
- name: Type.String(),
34
- filePath: Type.String(),
35
- body: Type.String(),
36
- capabilities: Type.Array(Type.String()),
37
- frontmatter: Type.Record(Type.String(), Type.Unknown()),
38
- });
39
- export class PersonaSkillLoaderError extends Error {
40
- code;
41
- constructor(code, message) {
42
- super(message);
43
- this.name = "PersonaSkillLoaderError";
44
- this.code = code;
45
- }
46
- }
47
- // ── Project-root discovery ───────────────────────────────────────────────
48
- function resolveProjectRoot(opts) {
49
- if (opts?.projectRoot)
50
- return opts.projectRoot;
51
- const cwd = opts?.cwd ?? process.cwd();
52
- const cfg = discoverForgeConfig(cwd);
53
- if (!cfg) {
54
- throw new PersonaSkillLoaderError("no_project_root", `No .forge/config.json found walking up from ${cwd}. ` +
55
- "Run /forge:init to scaffold the project, or pass `projectRoot` explicitly.");
56
- }
57
- // configPath = <projectDir>/.forge/config.json → projectDir = parent of .forge/
58
- return path.dirname(path.dirname(cfg.configPath));
59
- }
60
- // ── Name validation (defence in depth) ───────────────────────────────────
61
- function validateName(name, kind) {
62
- if (typeof name !== "string" || name.length === 0) {
63
- throw new PersonaSkillLoaderError("path_traversal", `${kind} name must be a non-empty string`);
64
- }
65
- if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
66
- throw new PersonaSkillLoaderError("path_traversal", `${kind} name contains path separators: ${JSON.stringify(name)}`);
67
- }
68
- const parts = name.split(/[/\\]/);
69
- for (const p of parts) {
70
- if (p === ".." || p === "." || p === "") {
71
- throw new PersonaSkillLoaderError("path_traversal", `${kind} name contains traversal segment: ${JSON.stringify(name)}`);
72
- }
73
- }
74
- }
75
- // ── Realpath confinement check ───────────────────────────────────────────
76
- function assertWithinDir(realFile, realDir, kind) {
77
- const prefix = realDir.endsWith(path.sep) ? realDir : realDir + path.sep;
78
- if (!realFile.startsWith(prefix)) {
79
- throw new PersonaSkillLoaderError("path_traversal", `${kind} path escapes ${realDir}: resolved to ${realFile}`);
80
- }
81
- }
82
- function parseFrontmatter(content) {
83
- // Normalise CRLF for line-based parsing while preserving body intact below.
84
- const lines = content.split(/\r?\n/);
85
- if (lines.length === 0 || lines[0] !== "---") {
86
- return { frontmatter: {}, body: content };
87
- }
88
- const fm = {};
89
- let i = 1;
90
- let closed = false;
91
- for (; i < lines.length; i++) {
92
- const line = lines[i];
93
- if (line === "---") {
94
- closed = true;
95
- i++;
96
- break;
97
- }
98
- // Allow blank lines inside frontmatter.
99
- if (line.trim() === "")
100
- continue;
101
- const m = /^([A-Za-z0-9_.-]+):\s*(.*)$/.exec(line);
102
- if (!m) {
103
- throw new PersonaSkillLoaderError("invalid_frontmatter", `Malformed frontmatter line ${i + 1}: ${JSON.stringify(line)}`);
104
- }
105
- const value = m[2].trim();
106
- // Strip matching surrounding quotes if present.
107
- let parsed = value;
108
- if ((value.startsWith('"') && value.endsWith('"') && value.length >= 2) ||
109
- (value.startsWith("'") && value.endsWith("'") && value.length >= 2)) {
110
- parsed = value.slice(1, -1);
111
- }
112
- fm[m[1]] = parsed;
113
- }
114
- if (!closed) {
115
- throw new PersonaSkillLoaderError("invalid_frontmatter", "Frontmatter block opened with `---` but never closed");
116
- }
117
- const body = lines.slice(i).join("\n");
118
- return { frontmatter: fm, body };
119
- }
120
- // ── Capability + identity extraction ─────────────────────────────────────
121
- function extractCapabilities(body) {
122
- const lines = body.split(/\r?\n/);
123
- const heading = /^##\s+Capabilities\s*$/;
124
- const nextSection = /^##\s+\S/;
125
- const bullet = /^[-*]\s+(.+)$/;
126
- const out = [];
127
- let inSection = false;
128
- for (const line of lines) {
129
- if (!inSection) {
130
- if (heading.test(line))
131
- inSection = true;
132
- continue;
133
- }
134
- if (nextSection.test(line))
135
- break;
136
- const m = bullet.exec(line);
137
- if (m)
138
- out.push(m[1].trim());
139
- }
140
- return out;
141
- }
142
- function extractIdentity(body) {
143
- for (const line of body.split(/\r?\n/)) {
144
- if (line.trim().length > 0)
145
- return line.trim();
146
- }
147
- return "";
148
- }
149
- // ── Internal generic load ────────────────────────────────────────────────
150
- function loadFile(kind, subdir, name, opts) {
151
- validateName(name, kind);
152
- const projectRoot = resolveProjectRoot(opts);
153
- const baseDir = path.join(projectRoot, ".forge", subdir);
154
- const candidate = path.join(baseDir, `${name}.md`);
155
- // Realpath the *directory* first — it must exist for confinement check.
156
- let realDir;
157
- try {
158
- realDir = fs.realpathSync(baseDir);
159
- }
160
- catch {
161
- throw new PersonaSkillLoaderError("missing_file", `${kind} directory does not exist: ${baseDir}`);
162
- }
163
- let realFile;
164
- try {
165
- realFile = fs.realpathSync(candidate);
166
- }
167
- catch {
168
- throw new PersonaSkillLoaderError("missing_file", `${kind} file not found: ${candidate}`);
169
- }
170
- assertWithinDir(realFile, realDir, kind);
171
- let raw;
172
- try {
173
- raw = fs.readFileSync(realFile, "utf8");
174
- }
175
- catch (err) {
176
- const e = err;
177
- throw new PersonaSkillLoaderError("missing_file", `Failed to read ${kind} file ${realFile}: ${e.message ?? "unknown"}`);
178
- }
179
- return { filePath: realFile, doc: parseFrontmatter(raw) };
180
- }
181
- // ── Public API ───────────────────────────────────────────────────────────
182
- /**
183
- * Load a Persona record from `<projectRoot>/.forge/personas/<name>.md`.
184
- *
185
- * `name` is the filename stem (e.g. `"engineer"` for `engineer.md`). It must
186
- * not contain path separators or traversal segments — invalid names raise
187
- * `PersonaSkillLoaderError` with `code: "path_traversal"`.
188
- */
189
- export function loadPersona(name, opts) {
190
- const { filePath, doc } = loadFile("persona", "personas", name, opts);
191
- const persona = {
192
- name,
193
- filePath,
194
- identity: extractIdentity(doc.body),
195
- body: doc.body,
196
- capabilities: extractCapabilities(doc.body),
197
- frontmatter: doc.frontmatter,
198
- };
199
- if (!Value.Check(PersonaSchema, persona)) {
200
- const errs = [...Value.Errors(PersonaSchema, persona)].map((e) => e.message);
201
- throw new PersonaSkillLoaderError("validation_failed", `Persona ${name} failed schema validation: ${errs.join("; ")}`);
202
- }
203
- return persona;
204
- }
205
- /**
206
- * Load a Skill record from `<projectRoot>/.forge/skills/<name>.md`.
207
- *
208
- * `name` is the filename stem. The Forge convention names skill files
209
- * `<noun>-skills.md` (e.g. `engineer-skills.md`); callers pass the full stem
210
- * including the `-skills` suffix. The loader does not auto-append it.
211
- */
212
- export function loadSkill(name, opts) {
213
- const { filePath, doc } = loadFile("skill", "skills", name, opts);
214
- const skill = {
215
- name,
216
- filePath,
217
- body: doc.body,
218
- capabilities: extractCapabilities(doc.body),
219
- frontmatter: doc.frontmatter,
220
- };
221
- if (!Value.Check(SkillSchema, skill)) {
222
- const errs = [...Value.Errors(SkillSchema, skill)].map((e) => e.message);
223
- throw new PersonaSkillLoaderError("validation_failed", `Skill ${name} failed schema validation: ${errs.join("; ")}`);
224
- }
225
- return skill;
226
- }
227
- //# sourceMappingURL=persona-skill-loader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"persona-skill-loader.js","sourceRoot":"","sources":["../../../../src/extensions/forgecli/loaders/persona-skill-loader.ts"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- export type TemplateRenderErrorCode = "missing_template_file" | "missing_var" | "invalid_args";
2
- export declare class TemplateRenderError extends Error {
3
- readonly code: TemplateRenderErrorCode;
4
- constructor(code: TemplateRenderErrorCode, message: string);
5
- }
6
- /**
7
- * Render a template file by substituting `{NAME}` tokens with values from `vars`.
8
- *
9
- * @param templatePath Absolute or cwd-relative path to a UTF-8 template file.
10
- * @param vars Map from identifier name to substitution value (string).
11
- * @returns The rendered template as a string.
12
- *
13
- * @throws TemplateRenderError({ code: "missing_template_file" }) if the file
14
- * does not exist or cannot be read.
15
- * @throws TemplateRenderError({ code: "missing_var" }) if the template
16
- * references a token whose name is not a key in `vars`.
17
- * @throws TemplateRenderError({ code: "invalid_args" }) if `templatePath` is
18
- * not a non-empty string or any value in `vars` is not a string.
19
- */
20
- export declare function renderTemplate(templatePath: string, vars: Record<string, string>): string;
@@ -1,85 +0,0 @@
1
- // Shared template-render helper — FORGE-S20-T03.
2
- //
3
- // Tiny, dependency-free `{NAME}` substitution helper used by kickoff-message
4
- // composition in forge-cli command handlers (T05 /forge:plan, T06
5
- // /forge:implement, and any future kickoff shim that needs argv-driven seed
6
- // substitution).
7
- //
8
- // API:
9
- // renderTemplate(templatePath: string, vars: Record<string, string>): string
10
- //
11
- // Substitution rule (frozen):
12
- // - Token syntax: `{NAME}` where NAME matches /[A-Za-z_][A-Za-z0-9_]*/.
13
- // Anything else — `{}`, `{ NAME }`, `{1NAME}`, `{lowercase-dashed}` — is
14
- // a literal and passes through unchanged.
15
- // - Missing var (token in template, key absent from `vars`): throws
16
- // TemplateRenderError("missing_var", ...). Never silently renders "".
17
- // (Iron Law 7: no silent continuation.)
18
- // - Extra vars (key in `vars` not referenced in template): ignored.
19
- // - Values are inserted literally — no HTML/regex escaping. Callers handle
20
- // downstream sanitisation.
21
- // - Missing template file: throws TemplateRenderError("missing_template_file", ...).
22
- //
23
- // Iron Law 6 (no shell-string interpolation): pure fs.readFileSync, no
24
- // child_process. Iron Law 7 (no silent continuation): every failure mode
25
- // raises a typed TemplateRenderError with an explicit code.
26
- import * as fs from "node:fs";
27
- export class TemplateRenderError extends Error {
28
- code;
29
- constructor(code, message) {
30
- super(message);
31
- this.name = "TemplateRenderError";
32
- this.code = code;
33
- }
34
- }
35
- // ── Token grammar ────────────────────────────────────────────────────────
36
- //
37
- // Single regex, global, captures the bare identifier. Anchored on `{` and `}`
38
- // with no whitespace tolerance — `{ NAME }` is a literal, by design.
39
- const TOKEN_RE = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
40
- // ── Internal: substitute over an in-memory string ────────────────────────
41
- function substitute(template, vars) {
42
- return template.replace(TOKEN_RE, (_match, name) => {
43
- if (!Object.hasOwn(vars, name)) {
44
- throw new TemplateRenderError("missing_var", `template references {${name}} but no value was supplied in vars`);
45
- }
46
- const value = vars[name];
47
- if (typeof value !== "string") {
48
- throw new TemplateRenderError("invalid_args", `vars[${JSON.stringify(name)}] must be a string, got ${typeof value}`);
49
- }
50
- return value;
51
- });
52
- }
53
- // ── Public API ───────────────────────────────────────────────────────────
54
- /**
55
- * Render a template file by substituting `{NAME}` tokens with values from `vars`.
56
- *
57
- * @param templatePath Absolute or cwd-relative path to a UTF-8 template file.
58
- * @param vars Map from identifier name to substitution value (string).
59
- * @returns The rendered template as a string.
60
- *
61
- * @throws TemplateRenderError({ code: "missing_template_file" }) if the file
62
- * does not exist or cannot be read.
63
- * @throws TemplateRenderError({ code: "missing_var" }) if the template
64
- * references a token whose name is not a key in `vars`.
65
- * @throws TemplateRenderError({ code: "invalid_args" }) if `templatePath` is
66
- * not a non-empty string or any value in `vars` is not a string.
67
- */
68
- export function renderTemplate(templatePath, vars) {
69
- if (typeof templatePath !== "string" || templatePath.length === 0) {
70
- throw new TemplateRenderError("invalid_args", "templatePath must be a non-empty string");
71
- }
72
- if (vars === null || typeof vars !== "object") {
73
- throw new TemplateRenderError("invalid_args", "vars must be an object");
74
- }
75
- let raw;
76
- try {
77
- raw = fs.readFileSync(templatePath, "utf8");
78
- }
79
- catch (err) {
80
- const e = err;
81
- throw new TemplateRenderError("missing_template_file", `failed to read template at ${templatePath}: ${e.code ?? ""} ${e.message ?? "unknown error"}`.trim());
82
- }
83
- return substitute(raw, vars);
84
- }
85
- //# sourceMappingURL=template-render.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"template-render.js","sourceRoot":"","sources":["../../../../src/extensions/forgecli/loaders/template-render.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,6EAA6E;AAC7E,kEAAkE;AAClE,4EAA4E;AAC5E,iBAAiB;AACjB,EAAE;AACF,OAAO;AACP,+EAA+E;AAC/E,EAAE;AACF,8BAA8B;AAC9B,0EAA0E;AAC1E,6EAA6E;AAC7E,8CAA8C;AAC9C,sEAAsE;AACtE,0EAA0E;AAC1E,4CAA4C;AAC5C,sEAAsE;AACtE,6EAA6E;AAC7E,+BAA+B;AAC/B,uFAAuF;AACvF,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,4DAA4D;AAE5D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAM9B,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC7B,IAAI,CAA0B;IAC9C,YAAY,IAA6B,EAAE,OAAe;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED,4EAA4E;AAC5E,EAAE;AACF,8EAA8E;AAC9E,qEAAqE;AACrE,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AAEjD,4EAA4E;AAE5E,SAAS,UAAU,CAAC,QAAgB,EAAE,IAA4B;IACjE,OAAO,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,mBAAmB,CAC5B,aAAa,EACb,wBAAwB,IAAI,qCAAqC,CACjE,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,mBAAmB,CAC5B,cAAc,EACd,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,2BAA2B,OAAO,KAAK,EAAE,CACrE,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,YAAoB,EAAE,IAA4B;IAChF,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,yCAAyC,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/C,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,wBAAwB,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,GAA0C,CAAC;QACrD,MAAM,IAAI,mBAAmB,CAC5B,uBAAuB,EACvB,8BAA8B,YAAY,KAAK,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,eAAe,EAAE,CAAC,IAAI,EAAE,CACpG,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC"}
@@ -1,41 +0,0 @@
1
- import { type Static, Type } from "typebox";
2
- export declare const AUDIENCE_VALUES: readonly ["orchestrator-only", "subagent", "any"];
3
- export type AudienceValue = (typeof AUDIENCE_VALUES)[number];
4
- export declare const WorkflowFrontmatterSchema: Type.TObject<{
5
- audience: Type.TOptional<Type.TUnion<[Type.TLiteral<"orchestrator-only">, Type.TLiteral<"subagent">, Type.TLiteral<"any">]>>;
6
- deps: Type.TOptional<Type.TObject<{
7
- personas: Type.TOptional<Type.TArray<Type.TString>>;
8
- }>>;
9
- }>;
10
- export type WorkflowFrontmatter = Static<typeof WorkflowFrontmatterSchema>;
11
- export interface LoadedWorkflow {
12
- filePath: string;
13
- rawMarkdown: string;
14
- frontmatter: WorkflowFrontmatter;
15
- /** Resolved audience; defaults to "any" when the key is absent. */
16
- audience: AudienceValue;
17
- }
18
- export type WorkflowLoaderErrorCode = "missing_file" | "invalid_frontmatter" | "validation_failed";
19
- export declare class WorkflowLoaderError extends Error {
20
- readonly code: WorkflowLoaderErrorCode;
21
- constructor(code: WorkflowLoaderErrorCode, message: string);
22
- }
23
- /**
24
- * Parse the YAML-like frontmatter of a workflow markdown file.
25
- *
26
- * Returns `{}` if the file does not start with `---`.
27
- * Throws `WorkflowLoaderError("invalid_frontmatter", ...)` on malformed YAML.
28
- */
29
- export declare function parseWorkflowFrontmatter(rawMarkdown: string): WorkflowFrontmatter;
30
- /**
31
- * Extract the audience value from a parsed WorkflowFrontmatter.
32
- * Returns "any" when the key is absent or has an unrecognised value.
33
- */
34
- export declare function extractAudience(frontmatter: WorkflowFrontmatter): AudienceValue;
35
- /**
36
- * Load and parse a materialized workflow markdown file.
37
- *
38
- * Throws `WorkflowLoaderError("missing_file", ...)` if the file is absent or unreadable.
39
- * Throws `WorkflowLoaderError("invalid_frontmatter", ...)` if frontmatter is malformed.
40
- */
41
- export declare function loadWorkflow(workflowPath: string): LoadedWorkflow;