@open-agent-toolkit/cli 0.3.10 → 0.3.12

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 (163) hide show
  1. package/README.md +2 -1
  2. package/assets/bundle-metadata.json +1 -1
  3. package/assets/docs/cli-utilities/meta.json +13 -0
  4. package/assets/docs/cli-utilities/tool-packs.md +8 -2
  5. package/assets/docs/cli-utilities/workflow-gates.md +13 -0
  6. package/assets/docs/contributing/code.md +8 -2
  7. package/assets/docs/contributing/documentation.md +7 -1
  8. package/assets/docs/contributing/meta.json +15 -0
  9. package/assets/docs/docs-tooling/add-docs-to-a-repo.md +14 -5
  10. package/assets/docs/docs-tooling/commands.md +26 -8
  11. package/assets/docs/docs-tooling/meta.json +4 -0
  12. package/assets/docs/docs-tooling/workflows.md +2 -2
  13. package/assets/docs/guide/meta.json +4 -0
  14. package/assets/docs/meta.json +14 -0
  15. package/assets/docs/provider-sync/instruction-sync.md +8 -0
  16. package/assets/docs/provider-sync/meta.json +11 -0
  17. package/assets/docs/provider-sync/scope-and-surface.md +3 -4
  18. package/assets/docs/reference/cli-reference.md +5 -2
  19. package/assets/docs/reference/docs-index-contract.md +23 -6
  20. package/assets/docs/reference/file-locations.md +7 -2
  21. package/assets/docs/reference/index.md +1 -1
  22. package/assets/docs/reference/meta.json +10 -0
  23. package/assets/docs/reference/troubleshooting.md +41 -6
  24. package/assets/docs/workflows/ideas/meta.json +4 -0
  25. package/assets/docs/workflows/meta.json +4 -0
  26. package/assets/docs/workflows/projects/cursor-cloud.md +8 -3
  27. package/assets/docs/workflows/projects/evidence-layers.md +6 -8
  28. package/assets/docs/workflows/projects/implementation-execution.md +30 -4
  29. package/assets/docs/workflows/projects/lifecycle.md +7 -1
  30. package/assets/docs/workflows/projects/meta.json +25 -0
  31. package/assets/docs/workflows/projects/orchestration-model.md +5 -6
  32. package/assets/docs/workflows/skills/meta.json +10 -0
  33. package/assets/docs/workflows/skills/recon.md +31 -3
  34. package/assets/public-package-versions.json +4 -4
  35. package/assets/skills/oat-cursor-cloud-projects/SKILL.md +30 -16
  36. package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -12
  37. package/assets/skills/oat-dispatch-subagents/references/managed-claude-example.json +1 -1
  38. package/assets/skills/oat-dispatch-subagents/references/provider-codex.md +51 -0
  39. package/assets/skills/oat-dispatch-subagents/references/record-schema.md +8 -7
  40. package/assets/skills/oat-docs-analyze/SKILL.md +28 -3
  41. package/assets/skills/oat-docs-apply/SKILL.md +3 -3
  42. package/assets/skills/oat-docs-authoring/SKILL.md +1 -1
  43. package/assets/skills/oat-docs-authoring/references/oat-fumadocs-contract.md +5 -2
  44. package/assets/skills/oat-docs-bootstrap/SKILL.md +4 -3
  45. package/assets/skills/oat-docs-bootstrap/assets/AGENTS.md.template +3 -3
  46. package/assets/skills/oat-pjm-decision/SKILL.md +3 -2
  47. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +4 -4
  48. package/assets/skills/oat-project-complete/SKILL.md +47 -2
  49. package/assets/skills/oat-project-design/SKILL.md +10 -6
  50. package/assets/skills/oat-project-discover/SKILL.md +3 -3
  51. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +3 -10
  52. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +4 -4
  53. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  54. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +135 -3
  55. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +7 -9
  56. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +4 -4
  57. package/assets/skills/oat-project-implement/references/plan-and-resume.md +1 -1
  58. package/assets/skills/oat-project-import-plan/SKILL.md +3 -3
  59. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +4 -4
  60. package/assets/skills/oat-project-new/SKILL.md +3 -3
  61. package/assets/skills/oat-project-next/SKILL.md +50 -7
  62. package/assets/skills/oat-project-plan/SKILL.md +4 -4
  63. package/assets/skills/oat-project-plan-writing/SKILL.md +2 -2
  64. package/assets/skills/oat-project-pr-final/SKILL.md +11 -1
  65. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +4 -4
  66. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -2
  67. package/assets/skills/oat-project-quick-start/SKILL.md +12 -8
  68. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +4 -4
  69. package/assets/skills/oat-project-retro/SKILL.md +6 -3
  70. package/assets/skills/oat-project-review-provide/SKILL.md +2 -2
  71. package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -2
  72. package/assets/skills/oat-project-spec/SKILL.md +3 -3
  73. package/assets/skills/oat-project-summary/SKILL.md +22 -3
  74. package/assets/skills/oat-repo-improve/SKILL.md +2 -2
  75. package/assets/skills/recon/SKILL.md +24 -3
  76. package/assets/skills/recon/references/packet-contract.md +36 -6
  77. package/assets/skills/recon/references/profiles.md +3 -0
  78. package/assets/skills/recon/references/worker-contract.md +16 -3
  79. package/assets/skills/recon/scripts/create-review-brief.mjs +17 -86
  80. package/assets/skills/recon/scripts/lib/contracts.mjs +158 -41
  81. package/assets/skills/recon/scripts/lib/review-binding.mjs +123 -0
  82. package/assets/skills/recon/scripts/lib/routing.mjs +5 -2
  83. package/assets/skills/recon/scripts/reconcile-ledger.mjs +18 -7
  84. package/assets/skills/recon/scripts/render-packet.mjs +120 -1
  85. package/assets/skills/recon/scripts/validate-packet.mjs +119 -88
  86. package/assets/templates/state.md +1 -0
  87. package/dist/commands/backlog/new.d.ts +2 -2
  88. package/dist/commands/backlog/new.d.ts.map +1 -1
  89. package/dist/commands/backlog/new.js +2 -2
  90. package/dist/commands/decision/new.d.ts +2 -2
  91. package/dist/commands/decision/new.d.ts.map +1 -1
  92. package/dist/commands/decision/new.js +2 -2
  93. package/dist/commands/docs/nav/contents.d.ts +9 -0
  94. package/dist/commands/docs/nav/contents.d.ts.map +1 -1
  95. package/dist/commands/docs/nav/contents.js +7 -2
  96. package/dist/commands/docs/nav/fumadocs.d.ts +58 -0
  97. package/dist/commands/docs/nav/fumadocs.d.ts.map +1 -0
  98. package/dist/commands/docs/nav/fumadocs.js +326 -0
  99. package/dist/commands/docs/nav/sync.d.ts +31 -1
  100. package/dist/commands/docs/nav/sync.d.ts.map +1 -1
  101. package/dist/commands/docs/nav/sync.js +163 -6
  102. package/dist/commands/index.d.ts.map +1 -1
  103. package/dist/commands/index.js +2 -0
  104. package/dist/commands/instructions/instructions.utils.d.ts +19 -0
  105. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  106. package/dist/commands/instructions/instructions.utils.js +89 -17
  107. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  108. package/dist/commands/instructions/sync/sync.js +49 -2
  109. package/dist/commands/pjm/init.d.ts.map +1 -1
  110. package/dist/commands/pjm/init.js +2 -2
  111. package/dist/commands/project/closeout-check/closeout-invariant.d.ts +97 -0
  112. package/dist/commands/project/closeout-check/closeout-invariant.d.ts.map +1 -0
  113. package/dist/commands/project/closeout-check/closeout-invariant.js +301 -0
  114. package/dist/commands/project/closeout-check/index.d.ts +38 -0
  115. package/dist/commands/project/closeout-check/index.d.ts.map +1 -0
  116. package/dist/commands/project/closeout-check/index.js +117 -0
  117. package/dist/commands/project/complete-state/index.d.ts +4 -0
  118. package/dist/commands/project/complete-state/index.d.ts.map +1 -1
  119. package/dist/commands/project/complete-state/index.js +11 -13
  120. package/dist/commands/project/dispatch/index.d.ts.map +1 -1
  121. package/dist/commands/project/dispatch/index.js +6 -30
  122. package/dist/commands/project/dispatch/record.d.ts +9 -18
  123. package/dist/commands/project/dispatch/record.d.ts.map +1 -1
  124. package/dist/commands/project/dispatch/record.js +15 -212
  125. package/dist/commands/project/index.d.ts.map +1 -1
  126. package/dist/commands/project/index.js +2 -0
  127. package/dist/commands/project/new/scaffold.d.ts +0 -1
  128. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  129. package/dist/commands/project/new/scaffold.js +19 -20
  130. package/dist/commands/project/promote/promote.d.ts +2 -2
  131. package/dist/commands/project/promote/promote.d.ts.map +1 -1
  132. package/dist/commands/project/promote/promote.js +13 -11
  133. package/dist/commands/shared/frontmatter.d.ts +21 -0
  134. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  135. package/dist/commands/shared/frontmatter.js +70 -4
  136. package/dist/commands/shared/template-source.d.ts +29 -0
  137. package/dist/commands/shared/template-source.d.ts.map +1 -0
  138. package/dist/commands/{pjm → shared}/template-source.js +24 -7
  139. package/dist/commands/state/generate.js +2 -2
  140. package/dist/commands/template/index.d.ts +10 -0
  141. package/dist/commands/template/index.d.ts.map +1 -0
  142. package/dist/commands/template/index.js +87 -0
  143. package/dist/commands/template/resolve.d.ts +31 -0
  144. package/dist/commands/template/resolve.d.ts.map +1 -0
  145. package/dist/commands/template/resolve.js +86 -0
  146. package/dist/fs/io.d.ts +0 -41
  147. package/dist/fs/io.d.ts.map +1 -1
  148. package/dist/fs/io.js +2 -414
  149. package/dist/providers/identity/absolute-paths.d.ts +4 -13
  150. package/dist/providers/identity/absolute-paths.d.ts.map +1 -1
  151. package/dist/providers/identity/absolute-paths.js +4 -17
  152. package/dist/providers/identity/generic-dispatch-record.d.ts +1 -1
  153. package/dist/providers/identity/generic-dispatch-record.js +2 -2
  154. package/dist/providers/identity/oat-dispatch-record.d.ts +4 -357
  155. package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -1
  156. package/dist/providers/identity/oat-dispatch-record.js +4 -236
  157. package/dist/release/public-package-contract.d.ts.map +1 -1
  158. package/dist/release/public-package-contract.js +12 -4
  159. package/dist/validation/skills.d.ts.map +1 -1
  160. package/dist/validation/skills.js +32 -6
  161. package/package.json +2 -2
  162. package/dist/commands/pjm/template-source.d.ts +0 -14
  163. package/dist/commands/pjm/template-source.d.ts.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import YAML, { isMap, isScalar } from 'yaml';
3
+ import YAML, { isMap, isScalar, LineCounter, } from 'yaml';
4
4
  export const PROJECT_STATE_KINDS = ['implementation', 'coordination'];
5
5
  export const PROJECT_STATE_PHASES = [
6
6
  'discovery',
@@ -201,6 +201,20 @@ export async function parseFrontmatterField(filePath, field) {
201
201
  return '';
202
202
  }
203
203
  }
204
+ /** The parser's message without its trailing position and code frame. */
205
+ function yamlErrorMessage(message) {
206
+ return (message.split('\n')[0] ?? message)
207
+ .replace(/\s+at line \d+, column \d+:?\s*$/, '')
208
+ .trim();
209
+ }
210
+ function problemAt(node, lineCounter, message) {
211
+ const offset = node?.range?.[0];
212
+ if (offset === undefined) {
213
+ return { line: null, column: null, message };
214
+ }
215
+ const position = lineCounter.linePos(offset);
216
+ return { line: position.line, column: position.col, message };
217
+ }
204
218
  /**
205
219
  * Read a frontmatter value that is a plain, undecorated string.
206
220
  *
@@ -239,9 +253,20 @@ function uniqueMapValue(map, key) {
239
253
  * indentation-based reader cannot tell a nested key from a top-level one.
240
254
  */
241
255
  export function parseSkillFrontmatter(block) {
242
- const document = YAML.parseDocument(block, { uniqueKeys: true });
243
- if (document.errors.length > 0) {
244
- return { malformed: true, unusableVersionDeclaration: false };
256
+ const lineCounter = new LineCounter();
257
+ const document = YAML.parseDocument(block, { uniqueKeys: true, lineCounter });
258
+ const error = document.errors[0];
259
+ if (error !== undefined) {
260
+ const position = error.linePos?.[0];
261
+ return {
262
+ malformed: true,
263
+ unusableVersionDeclaration: false,
264
+ problem: {
265
+ line: position?.line ?? null,
266
+ column: position?.col ?? null,
267
+ message: yamlErrorMessage(error.message),
268
+ },
269
+ };
245
270
  }
246
271
  if (!isMap(document.contents)) {
247
272
  return { malformed: false, unusableVersionDeclaration: false };
@@ -260,6 +285,7 @@ export function parseSkillFrontmatter(block) {
260
285
  return {
261
286
  malformed: true,
262
287
  unusableVersionDeclaration: parsed.unusableVersionDeclaration,
288
+ problem: problemAt(metadata, lineCounter, 'metadata must be a mapping'),
263
289
  };
264
290
  }
265
291
  const metadataVersion = scalarStringValue(uniqueMapValue(metadata, 'version'));
@@ -271,6 +297,46 @@ export function parseSkillFrontmatter(block) {
271
297
  }
272
298
  return parsed;
273
299
  }
300
+ /**
301
+ * The YAML type each top-level skill frontmatter key must have when present.
302
+ * `metadata` is not listed: a non-mapping `metadata` already makes the block
303
+ * malformed in `parseSkillFrontmatter`, which reports it with its location.
304
+ */
305
+ const SKILL_FRONTMATTER_KEY_TYPES = [
306
+ ['name', 'string'],
307
+ ['description', 'string'],
308
+ ['disable-model-invocation', 'boolean'],
309
+ ['user-invocable', 'boolean'],
310
+ ['allowed-tools', 'string'],
311
+ ];
312
+ /**
313
+ * Keys whose parsed YAML value has the wrong type, such as `name: 123` or
314
+ * `user-invocable: "true"`, each located within the block. A block that does
315
+ * not parse as a mapping yields nothing here; `parseSkillFrontmatter` reports
316
+ * it.
317
+ */
318
+ export function findSkillFrontmatterTypeProblems(block) {
319
+ const lineCounter = new LineCounter();
320
+ const document = YAML.parseDocument(block, { uniqueKeys: true, lineCounter });
321
+ if (document.errors.length > 0 || !isMap(document.contents)) {
322
+ return [];
323
+ }
324
+ const problems = [];
325
+ for (const [key, expected] of SKILL_FRONTMATTER_KEY_TYPES) {
326
+ if (!hasKey(document.contents, key)) {
327
+ continue;
328
+ }
329
+ const value = uniqueMapValue(document.contents, key);
330
+ const actual = isScalar(value) ? typeof value.value : 'collection';
331
+ if (actual !== expected) {
332
+ problems.push({
333
+ key,
334
+ ...problemAt(value, lineCounter, `${key} must be a ${expected}`),
335
+ });
336
+ }
337
+ }
338
+ return problems;
339
+ }
274
340
  /**
275
341
  * Resolve the canonical version with `metadata.version` taking precedence over
276
342
  * the deprecated top-level `version` alias.
@@ -0,0 +1,29 @@
1
+ export type TemplateTier = 'repository' | 'user' | 'bundle';
2
+ export interface ResolveTemplateOptions {
3
+ name: string;
4
+ /**
5
+ * Bundled assets root, or a function that returns it. A function is only
6
+ * called when the repository and user tiers both miss, so a caller whose
7
+ * template usually comes from an earlier tier does not need a valid bundle.
8
+ */
9
+ assetsRoot: string | (() => Promise<string>);
10
+ /** Repository template directory, normally `<repo>/.oat/templates`. */
11
+ templatesRoot?: string;
12
+ home?: string;
13
+ }
14
+ export interface ResolvedTemplate {
15
+ content: string;
16
+ path: string;
17
+ tier: TemplateTier;
18
+ }
19
+ /** Raised when no tier supplies the requested template. */
20
+ export declare class TemplateNotFoundError extends Error {
21
+ readonly templateName: string;
22
+ constructor(templateName: string);
23
+ }
24
+ /**
25
+ * Resolve a template in repository, user, then bundle order
26
+ * (`DR-260927-templates-resolve-repository`).
27
+ */
28
+ export declare function resolveTemplate(options: ResolveTemplateOptions): Promise<ResolvedTemplate>;
29
+ //# sourceMappingURL=template-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-source.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/template-source.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,2DAA2D;AAC3D,qBAAa,qBAAsB,SAAQ,KAAK;aAClB,YAAY,EAAE,MAAM;IAAhD,YAA4B,YAAY,EAAE,MAAM,EAK/C;CACF;AAkBD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CA+C3B"}
@@ -1,6 +1,15 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, join } from 'node:path';
4
+ /** Raised when no tier supplies the requested template. */
5
+ export class TemplateNotFoundError extends Error {
6
+ templateName;
7
+ constructor(templateName) {
8
+ super(`Template ${templateName} was not found in repository, user, or bundled templates.`);
9
+ this.templateName = templateName;
10
+ this.name = 'TemplateNotFoundError';
11
+ }
12
+ }
4
13
  async function readIfExists(path) {
5
14
  try {
6
15
  return await readFile(path, 'utf8');
@@ -15,9 +24,13 @@ async function readIfExists(path) {
15
24
  throw error;
16
25
  }
17
26
  }
18
- export async function resolvePjmTemplate(options) {
27
+ /**
28
+ * Resolve a template in repository, user, then bundle order
29
+ * (`DR-260927-templates-resolve-repository`).
30
+ */
31
+ export async function resolveTemplate(options) {
19
32
  if (basename(options.name) !== options.name) {
20
- throw new Error(`Invalid PJM template name: ${options.name}`);
33
+ throw new Error(`Invalid template name: ${options.name}`);
21
34
  }
22
35
  // Resolve the same home the installer writes to. `CommandContext.home` is
23
36
  // `os.homedir()`, so falling back to `process.env.HOME` would skip the user
@@ -41,10 +54,6 @@ export async function resolvePjmTemplate(options) {
41
54
  },
42
55
  ]
43
56
  : []),
44
- {
45
- path: join(options.assetsRoot, 'templates', options.name),
46
- tier: 'bundle',
47
- },
48
57
  ];
49
58
  for (const candidate of candidates) {
50
59
  const content = await readIfExists(candidate.path);
@@ -52,5 +61,13 @@ export async function resolvePjmTemplate(options) {
52
61
  return { ...candidate, content };
53
62
  }
54
63
  }
55
- throw new Error(`Template ${options.name} was not found in repository, user, or bundled PJM templates.`);
64
+ const assetsRoot = typeof options.assetsRoot === 'function'
65
+ ? await options.assetsRoot()
66
+ : options.assetsRoot;
67
+ const bundlePath = join(assetsRoot, 'templates', options.name);
68
+ const bundleContent = await readIfExists(bundlePath);
69
+ if (bundleContent !== null) {
70
+ return { content: bundleContent, path: bundlePath, tier: 'bundle' };
71
+ }
72
+ throw new TemplateNotFoundError(options.name);
56
73
  }
@@ -276,8 +276,8 @@ function computeNextStep(project, hasProjects, state) {
276
276
  reason: 'Continue quick discovery phase',
277
277
  },
278
278
  'quick:discovery:complete': {
279
- step: 'oat-project-plan',
280
- reason: 'Generate plan directly for quick workflow',
279
+ step: 'oat-project-quick-start',
280
+ reason: 'Generate the plan with the quick workflow',
281
281
  },
282
282
  'import:plan:in_progress': {
283
283
  step: 'oat-project-import-plan',
@@ -0,0 +1,10 @@
1
+ import { buildCommandContext } from '../../app/command-context.js';
2
+ import { Command } from 'commander';
3
+ interface TemplateCommandDependencies {
4
+ buildCommandContext: typeof buildCommandContext;
5
+ resolveProjectRoot: (cwd: string) => Promise<string>;
6
+ resolveAssetsRoot: () => Promise<string>;
7
+ }
8
+ export declare function createTemplateCommand(overrides?: Partial<TemplateCommandDependencies>): Command;
9
+ export {};
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/template/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAMhF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AA6DD,wBAAgB,qBAAqB,CACnC,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAsCT"}
@@ -0,0 +1,87 @@
1
+ import { buildCommandContext } from '../../app/command-context.js';
2
+ import { readGlobalOptions } from '../shared/shared.utils.js';
3
+ import { TemplateNotFoundError } from '../shared/template-source.js';
4
+ import { CliError } from '../../errors/index.js';
5
+ import { resolveAssetsRoot } from '../../fs/assets.js';
6
+ import { resolveProjectRoot } from '../../fs/paths.js';
7
+ import { Command } from 'commander';
8
+ import { runTemplateResolve } from './resolve.js';
9
+ const DEFAULT_DEPENDENCIES = {
10
+ buildCommandContext,
11
+ resolveProjectRoot,
12
+ resolveAssetsRoot: () => resolveAssetsRoot(),
13
+ };
14
+ function reportResult(context, result) {
15
+ if (!result.found) {
16
+ const message = new TemplateNotFoundError(result.name).message;
17
+ if (context.json) {
18
+ context.logger.json({ status: 'error', ...result, message });
19
+ }
20
+ else {
21
+ context.logger.error(message);
22
+ }
23
+ process.exitCode = 1;
24
+ return;
25
+ }
26
+ if (context.json) {
27
+ context.logger.json({ status: 'ok', ...result });
28
+ }
29
+ else {
30
+ context.logger.info(`Template: ${result.name}`);
31
+ context.logger.info('Found: yes');
32
+ context.logger.info(`Tier: ${result.tier}`);
33
+ context.logger.info(`Path: ${result.path ?? 'none (bundled with the CLI)'}`);
34
+ if (result.output) {
35
+ context.logger.info(`Wrote: ${result.output}`);
36
+ }
37
+ }
38
+ process.exitCode = 0;
39
+ }
40
+ function reportError(context, name, error) {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ if (context.json) {
43
+ context.logger.json({
44
+ status: 'error',
45
+ name,
46
+ found: false,
47
+ tier: null,
48
+ path: null,
49
+ output: null,
50
+ message,
51
+ });
52
+ }
53
+ else {
54
+ context.logger.error(message);
55
+ }
56
+ process.exitCode = error instanceof CliError ? error.exitCode : 1;
57
+ }
58
+ export function createTemplateCommand(overrides = {}) {
59
+ const dependencies = {
60
+ ...DEFAULT_DEPENDENCIES,
61
+ ...overrides,
62
+ };
63
+ const cmd = new Command('template').description('Resolve OAT templates (repository, user, bundle)');
64
+ cmd
65
+ .command('resolve')
66
+ .description('Report which tier supplies a template and optionally copy it')
67
+ .argument('<name>', 'Template name, such as plan or plan.md')
68
+ .option('--output <path>', 'Copy the resolved template to this file, replacing it')
69
+ .action(async (name, options, command) => {
70
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
71
+ try {
72
+ const result = await runTemplateResolve({
73
+ name,
74
+ cwd: context.cwd,
75
+ home: context.home,
76
+ output: options.output,
77
+ resolveProjectRoot: dependencies.resolveProjectRoot,
78
+ resolveAssetsRoot: dependencies.resolveAssetsRoot,
79
+ });
80
+ reportResult(context, result);
81
+ }
82
+ catch (error) {
83
+ reportError(context, name, error);
84
+ }
85
+ });
86
+ return cmd;
87
+ }
@@ -0,0 +1,31 @@
1
+ import { type TemplateTier } from '../shared/template-source.js';
2
+ /**
3
+ * Normalize a template name to its file name. `plan` and `plan.md` both map to
4
+ * `plan.md`; anything that could address a path outside the template
5
+ * directories (separators, `..`) is rejected.
6
+ */
7
+ export declare function normalizeTemplateName(raw: string): string;
8
+ export interface TemplateResolveResult {
9
+ name: string;
10
+ found: boolean;
11
+ tier: TemplateTier | null;
12
+ /** Set only for the repository and user tiers. */
13
+ path: string | null;
14
+ /** Absolute path written by `--output`, or `null` when nothing was written. */
15
+ output: string | null;
16
+ }
17
+ export interface RunTemplateResolveOptions {
18
+ name: string;
19
+ cwd: string;
20
+ home: string;
21
+ output?: string;
22
+ resolveProjectRoot: (cwd: string) => Promise<string>;
23
+ resolveAssetsRoot: () => Promise<string>;
24
+ }
25
+ /**
26
+ * Resolve a template in repository, user, then bundle order and optionally
27
+ * copy its content to `output`. A missing template returns `found: false`;
28
+ * invalid names and write failures throw.
29
+ */
30
+ export declare function runTemplateResolve(options: RunTemplateResolveOptions): Promise<TemplateResolveResult>;
31
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/commands/template/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAC;AAK1C;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAuCD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAqChC"}
@@ -0,0 +1,86 @@
1
+ import { stat, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { resolveTemplate, TemplateNotFoundError, } from '../shared/template-source.js';
4
+ import { CliError } from '../../errors/index.js';
5
+ const TEMPLATE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
6
+ /**
7
+ * Normalize a template name to its file name. `plan` and `plan.md` both map to
8
+ * `plan.md`; anything that could address a path outside the template
9
+ * directories (separators, `..`) is rejected.
10
+ */
11
+ export function normalizeTemplateName(raw) {
12
+ if (!TEMPLATE_NAME_PATTERN.test(raw) || raw.includes('..')) {
13
+ throw new CliError(`Invalid template name: ${raw}. Use a bare template name such as plan or plan.md.`, 1);
14
+ }
15
+ return raw.endsWith('.md') ? raw : `${raw}.md`;
16
+ }
17
+ async function resolveTemplatesRoot(cwd, resolveProjectRoot) {
18
+ try {
19
+ return resolve(await resolveProjectRoot(cwd), '.oat', 'templates');
20
+ }
21
+ catch (error) {
22
+ // Outside a repository there is no repository tier; user and bundle
23
+ // tiers still apply.
24
+ if (error instanceof CliError) {
25
+ return undefined;
26
+ }
27
+ throw error;
28
+ }
29
+ }
30
+ async function writeOutput(dest, content) {
31
+ const parent = dirname(dest);
32
+ const parentStat = await stat(parent).catch(() => null);
33
+ if (!parentStat?.isDirectory()) {
34
+ throw new CliError(`Output directory does not exist: ${parent}`, 1);
35
+ }
36
+ try {
37
+ await writeFile(dest, content, 'utf8');
38
+ }
39
+ catch (error) {
40
+ if (error &&
41
+ typeof error === 'object' &&
42
+ 'code' in error &&
43
+ error.code === 'EISDIR') {
44
+ throw new CliError(`Output path is a directory: ${dest}`, 1);
45
+ }
46
+ throw error;
47
+ }
48
+ }
49
+ /**
50
+ * Resolve a template in repository, user, then bundle order and optionally
51
+ * copy its content to `output`. A missing template returns `found: false`;
52
+ * invalid names and write failures throw.
53
+ */
54
+ export async function runTemplateResolve(options) {
55
+ const name = normalizeTemplateName(options.name);
56
+ const templatesRoot = await resolveTemplatesRoot(options.cwd, options.resolveProjectRoot);
57
+ let resolved;
58
+ try {
59
+ resolved = await resolveTemplate({
60
+ name,
61
+ templatesRoot,
62
+ home: options.home,
63
+ assetsRoot: options.resolveAssetsRoot,
64
+ });
65
+ }
66
+ catch (error) {
67
+ if (error instanceof TemplateNotFoundError) {
68
+ return { name, found: false, tier: null, path: null, output: null };
69
+ }
70
+ throw error;
71
+ }
72
+ let output = null;
73
+ if (options.output !== undefined) {
74
+ output = resolve(options.cwd, options.output);
75
+ await writeOutput(output, resolved.content);
76
+ }
77
+ return {
78
+ name,
79
+ found: true,
80
+ tier: resolved.tier,
81
+ // The bundle lives inside the installed package; skills copy it through
82
+ // `--output` instead of reading a package-manager path.
83
+ path: resolved.tier === 'bundle' ? null : resolved.path,
84
+ output,
85
+ };
86
+ }
package/dist/fs/io.d.ts CHANGED
@@ -33,45 +33,4 @@ export declare function removeCollectionSymlinkIfUnchanged(linkPath: string, cre
33
33
  */
34
34
  export declare function serializeJsonDocument(data: unknown): string;
35
35
  export declare function atomicWriteJson(filePath: string, data: unknown): Promise<void>;
36
- export interface ContainedWriterLockOptions {
37
- timeoutMs?: number;
38
- minReclaimMs?: number;
39
- hardStaleMs?: number;
40
- }
41
- /**
42
- * Exclusive contained writer lock. `mkdir` without `recursive` is the atomic
43
- * primitive: exactly one caller can create the directory, and every other
44
- * caller observes `EEXIST`. The lock is validated as a real directory so a
45
- * planted symlink fails closed instead of redirecting the guarded section, it
46
- * records holder evidence so a crashed writer can be reclaimed rather than
47
- * wedging the project forever, and no message it raises contains an absolute
48
- * path.
49
- */
50
- export declare function withContainedWriterLock<T>(lockPath: string, scopeRoot: string, run: () => Promise<T>, options?: ContainedWriterLockOptions): Promise<T>;
51
- export declare function journalErrorCode(error: unknown): string | null;
52
- /**
53
- * Re-raise a filesystem error without Node's absolute-path message text while
54
- * preserving its `code` so callers can still branch on `EEXIST`/`ENOENT`.
55
- * Exported so every journal producer redacts identically instead of each one
56
- * re-deriving the rule.
57
- */
58
- export declare function redactedFsError(error: unknown, operation: string, relativeTarget: string): Error;
59
- /**
60
- * Publish one immutable JSON journal revision.
61
- *
62
- * Guarantees, stated as what the code does:
63
- * - The destination is created with `link` only. An occupied destination raises
64
- * `EEXIST`; the destination is never renamed onto, removed, or truncated.
65
- * - After publication the destination is proven to be the exact staged inode,
66
- * inside the same directory inode validated before staging, still resolving
67
- * inside `scopeRoot`. Failure raises and removes nothing.
68
- * - Staging cleanup removes only the inode this call created.
69
- * - No raised message contains an absolute path.
70
- *
71
- * Residual, unchanged by this design: a privileged concurrent process that
72
- * swaps the validated directory pathname can cause the `link` to create an
73
- * unreferenced file under a directory it controls, and can strand the staged
74
- * file. Neither outcome replaces or deletes pre-existing content.
75
- */
76
- export declare function publishContainedJsonRevision(filePath: string, data: unknown, scopeRoot: string): Promise<void>;
77
36
  //# sourceMappingURL=io.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/fs/io.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/fs/io.ts"],"names":[],"mappings":"AAeA,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAC9C,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAGD,MAAM,MAAM,mBAAmB,GAAG,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,mBAAmB,EAC5B,UAAU,SAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACrC,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,SAAS,CAAC,CAOpB;AAED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,wBAAwB,CAAC,CA0FnC;AAED,wBAAsB,kCAAkC,CACtD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE3D;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf"}