@cleocode/contracts 2026.5.111 → 2026.5.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts +21 -0
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts.map +1 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js +70 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js.map +1 -0
- package/dist/__tests__/evidence-atom-schema.test.d.ts +16 -0
- package/dist/__tests__/evidence-atom-schema.test.d.ts.map +1 -0
- package/dist/__tests__/evidence-atom-schema.test.js +394 -0
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -0
- package/dist/config/__tests__/manifest.test.d.ts +13 -0
- package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/config/__tests__/manifest.test.js +143 -0
- package/dist/config/__tests__/manifest.test.js.map +1 -0
- package/dist/config/manifest.d.ts +158 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +123 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/docs/__tests__/provenance.test.d.ts +14 -0
- package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
- package/dist/docs/__tests__/provenance.test.js +579 -0
- package/dist/docs/__tests__/provenance.test.js.map +1 -0
- package/dist/docs/provenance.d.ts +731 -0
- package/dist/docs/provenance.d.ts.map +1 -0
- package/dist/docs/provenance.js +258 -0
- package/dist/docs/provenance.js.map +1 -0
- package/dist/docs-taxonomy.d.ts +24 -0
- package/dist/docs-taxonomy.d.ts.map +1 -1
- package/dist/docs-taxonomy.js +27 -0
- package/dist/docs-taxonomy.js.map +1 -1
- package/dist/doctor.d.ts +101 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/evidence-atom-schema.d.ts +407 -0
- package/dist/evidence-atom-schema.d.ts.map +1 -0
- package/dist/evidence-atom-schema.js +505 -0
- package/dist/evidence-atom-schema.js.map +1 -0
- package/dist/exit-codes.d.ts +37 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +37 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
- package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
- package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
- package/dist/invariants/__tests__/registry.test.d.ts +14 -0
- package/dist/invariants/__tests__/registry.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/registry.test.js +113 -0
- package/dist/invariants/__tests__/registry.test.js.map +1 -0
- package/dist/invariants/adr-056-release.d.ts +42 -0
- package/dist/invariants/adr-056-release.d.ts.map +1 -0
- package/dist/invariants/adr-056-release.js +143 -0
- package/dist/invariants/adr-056-release.js.map +1 -0
- package/dist/invariants/adr-070-orchestration.d.ts +73 -0
- package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
- package/dist/invariants/adr-070-orchestration.js +290 -0
- package/dist/invariants/adr-070-orchestration.js.map +1 -0
- package/dist/invariants/adr-073-saga.d.ts +32 -0
- package/dist/invariants/adr-073-saga.d.ts.map +1 -0
- package/dist/invariants/adr-073-saga.js +155 -0
- package/dist/invariants/adr-073-saga.js.map +1 -0
- package/dist/invariants/index.d.ts +162 -0
- package/dist/invariants/index.d.ts.map +1 -0
- package/dist/invariants/index.js +118 -0
- package/dist/invariants/index.js.map +1 -0
- package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
- package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
- package/dist/operations/__tests__/input-contract.test.js +158 -0
- package/dist/operations/__tests__/input-contract.test.js.map +1 -0
- package/dist/operations/docs.d.ts +16 -0
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/input-contract.d.ts +267 -0
- package/dist/operations/input-contract.d.ts.map +1 -0
- package/dist/operations/input-contract.js +29 -0
- package/dist/operations/input-contract.js.map +1 -0
- package/dist/templates/__tests__/manifest.test.d.ts +18 -0
- package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/templates/__tests__/manifest.test.js +123 -0
- package/dist/templates/__tests__/manifest.test.js.map +1 -0
- package/dist/templates/manifest.d.ts +186 -0
- package/dist/templates/manifest.d.ts.map +1 -0
- package/dist/templates/manifest.js +165 -0
- package/dist/templates/manifest.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
- package/src/__tests__/evidence-atom-schema.test.ts +499 -0
- package/src/config/__tests__/manifest.test.ts +176 -0
- package/src/config/manifest.ts +218 -0
- package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
- package/src/docs/__tests__/provenance.test.ts +648 -0
- package/src/docs/provenance.ts +601 -0
- package/src/docs-taxonomy.ts +55 -0
- package/src/doctor.ts +111 -0
- package/src/errors.ts +4 -0
- package/src/evidence-atom-schema.ts +628 -0
- package/src/exit-codes.ts +39 -0
- package/src/index.ts +129 -0
- package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
- package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
- package/src/invariants/__tests__/registry.test.ts +136 -0
- package/src/invariants/adr-056-release.ts +154 -0
- package/src/invariants/adr-070-orchestration.ts +317 -0
- package/src/invariants/adr-073-saga.ts +167 -0
- package/src/invariants/index.ts +222 -0
- package/src/operations/__tests__/input-contract.test.ts +188 -0
- package/src/operations/docs.ts +16 -0
- package/src/operations/input-contract.ts +301 -0
- package/src/templates/__tests__/manifest.test.ts +140 -0
- package/src/templates/manifest.ts +208 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract tests for the TemplateManifest schema.
|
|
3
|
+
*
|
|
4
|
+
* Asserts:
|
|
5
|
+
*
|
|
6
|
+
* 1. A valid entry for every {@link TemplateKind} parses successfully.
|
|
7
|
+
* 2. An entry with an unknown `kind` is rejected.
|
|
8
|
+
* 3. An entry missing `installPath` is rejected.
|
|
9
|
+
* 4. The {@link PlaceholderSpec} `source` field is exhaustively typed —
|
|
10
|
+
* every member of {@link PLACEHOLDER_SOURCES} parses, and a bogus
|
|
11
|
+
* source value is rejected.
|
|
12
|
+
*
|
|
13
|
+
* @task T9875
|
|
14
|
+
* @epic T9874
|
|
15
|
+
* @saga T9855
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=manifest.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.d.ts","sourceRoot":"","sources":["../../../src/templates/__tests__/manifest.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract tests for the TemplateManifest schema.
|
|
3
|
+
*
|
|
4
|
+
* Asserts:
|
|
5
|
+
*
|
|
6
|
+
* 1. A valid entry for every {@link TemplateKind} parses successfully.
|
|
7
|
+
* 2. An entry with an unknown `kind` is rejected.
|
|
8
|
+
* 3. An entry missing `installPath` is rejected.
|
|
9
|
+
* 4. The {@link PlaceholderSpec} `source` field is exhaustively typed —
|
|
10
|
+
* every member of {@link PLACEHOLDER_SOURCES} parses, and a bogus
|
|
11
|
+
* source value is rejected.
|
|
12
|
+
*
|
|
13
|
+
* @task T9875
|
|
14
|
+
* @epic T9874
|
|
15
|
+
* @saga T9855
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, it } from 'vitest';
|
|
18
|
+
import { PLACEHOLDER_SOURCES, PlaceholderSpecSchema, TEMPLATE_KINDS, TemplateManifestEntrySchema, } from '../manifest.js';
|
|
19
|
+
/**
|
|
20
|
+
* Build a valid baseline entry parameterized by `kind` so each
|
|
21
|
+
* assertion below stays minimal.
|
|
22
|
+
*/
|
|
23
|
+
function baseEntry(kind) {
|
|
24
|
+
return {
|
|
25
|
+
id: `sample-${kind}`,
|
|
26
|
+
kind,
|
|
27
|
+
sourcePath: `packages/core/templates/${kind}s/sample.tmpl`,
|
|
28
|
+
installPath: `.cleo/${kind}s/sample`,
|
|
29
|
+
substitution: 'regex-tmpl',
|
|
30
|
+
placeholders: [
|
|
31
|
+
{
|
|
32
|
+
name: 'NODE_VERSION',
|
|
33
|
+
source: 'project-context',
|
|
34
|
+
sourcePath: 'engines.node',
|
|
35
|
+
defaultValue: 24,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
updateStrategy: 'overwrite-on-bump',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
describe('TemplateManifestEntrySchema (T9875)', () => {
|
|
42
|
+
it('accepts a valid entry for every TemplateKind', () => {
|
|
43
|
+
for (const kind of TEMPLATE_KINDS) {
|
|
44
|
+
const entry = baseEntry(kind);
|
|
45
|
+
const parsed = TemplateManifestEntrySchema.parse(entry);
|
|
46
|
+
expect(parsed.kind, `kind '${kind}' must round-trip`).toBe(kind);
|
|
47
|
+
expect(parsed.installPath).toBe(entry.installPath);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
it('rejects an entry with an unknown kind', () => {
|
|
51
|
+
const bogus = {
|
|
52
|
+
...baseEntry('workflow'),
|
|
53
|
+
kind: 'not-a-real-kind',
|
|
54
|
+
};
|
|
55
|
+
const result = TemplateManifestEntrySchema.safeParse(bogus);
|
|
56
|
+
expect(result.success).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
it('rejects an entry missing installPath', () => {
|
|
59
|
+
const { installPath: _drop, ...rest } = baseEntry('config');
|
|
60
|
+
const result = TemplateManifestEntrySchema.safeParse(rest);
|
|
61
|
+
expect(result.success).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('accepts an entry whose placeholders array is empty', () => {
|
|
64
|
+
const entry = { ...baseEntry('doc'), placeholders: [] };
|
|
65
|
+
const result = TemplateManifestEntrySchema.safeParse(entry);
|
|
66
|
+
expect(result.success).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it('accepts a defaultValue of every supported literal type and null', () => {
|
|
69
|
+
const types = [
|
|
70
|
+
{ defaultValue: 'string-default' },
|
|
71
|
+
{ defaultValue: 42 },
|
|
72
|
+
{ defaultValue: true },
|
|
73
|
+
{ defaultValue: null },
|
|
74
|
+
{},
|
|
75
|
+
];
|
|
76
|
+
for (const extra of types) {
|
|
77
|
+
const result = PlaceholderSpecSchema.safeParse({
|
|
78
|
+
name: 'X',
|
|
79
|
+
source: 'literal',
|
|
80
|
+
sourcePath: 'X',
|
|
81
|
+
...extra,
|
|
82
|
+
});
|
|
83
|
+
expect(result.success, `defaultValue case ${JSON.stringify(extra)} must parse`).toBe(true);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('PlaceholderSpecSchema source enum (T9875)', () => {
|
|
88
|
+
it('exhaustively accepts every member of PLACEHOLDER_SOURCES', () => {
|
|
89
|
+
for (const source of PLACEHOLDER_SOURCES) {
|
|
90
|
+
const result = PlaceholderSpecSchema.safeParse({
|
|
91
|
+
name: 'PLACEHOLDER',
|
|
92
|
+
source,
|
|
93
|
+
sourcePath: 'some.path',
|
|
94
|
+
});
|
|
95
|
+
expect(result.success, `source '${source}' must parse`).toBe(true);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it('rejects an unknown source value', () => {
|
|
99
|
+
const result = PlaceholderSpecSchema.safeParse({
|
|
100
|
+
name: 'PLACEHOLDER',
|
|
101
|
+
source: 'not-a-source',
|
|
102
|
+
sourcePath: 'some.path',
|
|
103
|
+
});
|
|
104
|
+
expect(result.success).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('rejects an empty name', () => {
|
|
107
|
+
const result = PlaceholderSpecSchema.safeParse({
|
|
108
|
+
name: '',
|
|
109
|
+
source: 'project-context',
|
|
110
|
+
sourcePath: 'some.path',
|
|
111
|
+
});
|
|
112
|
+
expect(result.success).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
it('rejects an empty sourcePath', () => {
|
|
115
|
+
const result = PlaceholderSpecSchema.safeParse({
|
|
116
|
+
name: 'X',
|
|
117
|
+
source: 'project-context',
|
|
118
|
+
sourcePath: '',
|
|
119
|
+
});
|
|
120
|
+
expect(result.success).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=manifest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.js","sourceRoot":"","sources":["../../../src/templates/__tests__/manifest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateManifest contract — SSoT for every template CLEO ships, installs,
|
|
3
|
+
* or rewrites during `cleo init` / `cleo upgrade` / `cleo scaffold-*`.
|
|
4
|
+
*
|
|
5
|
+
* A {@link TemplateManifestEntry} declares one template file: where its
|
|
6
|
+
* source lives in the monorepo, where it installs into a consumer project,
|
|
7
|
+
* how its placeholders are substituted, which placeholders it accepts, and
|
|
8
|
+
* how it MUST be reconciled when the shipped version moves ahead of the
|
|
9
|
+
* deployed copy.
|
|
10
|
+
*
|
|
11
|
+
* This contract is the leaf type. The CORE-side template registry
|
|
12
|
+
* (T9877) consumes it; the CLI verbs (`cleo init --workflows`,
|
|
13
|
+
* `cleo upgrade`, the scaffold sweeper) read the manifest at runtime to
|
|
14
|
+
* decide what to write, what to skip, and what to diff-prompt the user on.
|
|
15
|
+
*
|
|
16
|
+
* @example minimal workflow template entry
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { TemplateManifestEntrySchema, type TemplateManifestEntry } from '@cleocode/contracts';
|
|
19
|
+
*
|
|
20
|
+
* const entry: TemplateManifestEntry = TemplateManifestEntrySchema.parse({
|
|
21
|
+
* id: 'ci.yml',
|
|
22
|
+
* kind: 'workflow',
|
|
23
|
+
* sourcePath: 'packages/core/templates/workflows/ci.yml.tmpl',
|
|
24
|
+
* installPath: '.github/workflows/ci.yml',
|
|
25
|
+
* substitution: 'regex-tmpl',
|
|
26
|
+
* placeholders: [
|
|
27
|
+
* {
|
|
28
|
+
* name: 'NODE_VERSION',
|
|
29
|
+
* source: 'project-context',
|
|
30
|
+
* sourcePath: 'engines.node',
|
|
31
|
+
* defaultValue: 24,
|
|
32
|
+
* },
|
|
33
|
+
* ],
|
|
34
|
+
* updateStrategy: 'overwrite-on-bump',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @task T9875
|
|
39
|
+
* @epic T9874
|
|
40
|
+
* @saga T9855
|
|
41
|
+
* @see ADR-076 (canonical doc routing — same boundary discipline applies here)
|
|
42
|
+
*/
|
|
43
|
+
import { z } from 'zod';
|
|
44
|
+
/**
|
|
45
|
+
* Allowed categories of template the manifest can describe.
|
|
46
|
+
*
|
|
47
|
+
* - `workflow` — GitHub Actions / CI workflow files.
|
|
48
|
+
* - `config` — Configuration files (e.g. `.editorconfig`, `tsconfig.json`).
|
|
49
|
+
* - `agent` — Agent definition files shipped to `.claude/agents/` etc.
|
|
50
|
+
* - `skill` — Skill markdown shipped to `.claude/skills/` etc.
|
|
51
|
+
* - `provider` — Provider-specific bootstrap files (Anthropic, OpenAI, …).
|
|
52
|
+
* - `doc` — Documentation scaffolds (e.g. README.md, AGENTS.md).
|
|
53
|
+
*/
|
|
54
|
+
export declare const TEMPLATE_KINDS: readonly ["workflow", "config", "agent", "skill", "provider", "doc"];
|
|
55
|
+
/** Discriminator for the category of file a template represents. */
|
|
56
|
+
export type TemplateKind = (typeof TEMPLATE_KINDS)[number];
|
|
57
|
+
/**
|
|
58
|
+
* Allowed substitution strategies a template can request.
|
|
59
|
+
*
|
|
60
|
+
* - `regex-tmpl` — `{{KEY}}` placeholder substitution via regex.
|
|
61
|
+
* - `static` — No substitution; the file is copied byte-for-byte.
|
|
62
|
+
* - `json-merge` — The template is parsed as JSON and merged into any
|
|
63
|
+
* existing JSON file at `installPath`; placeholders inside
|
|
64
|
+
* string values still go through `regex-tmpl`.
|
|
65
|
+
*/
|
|
66
|
+
export declare const TEMPLATE_SUBSTITUTIONS: readonly ["regex-tmpl", "static", "json-merge"];
|
|
67
|
+
/** Strategy the installer uses to materialize a template's `sourcePath`. */
|
|
68
|
+
export type TemplateSubstitution = (typeof TEMPLATE_SUBSTITUTIONS)[number];
|
|
69
|
+
/**
|
|
70
|
+
* How an installed template is reconciled with the shipped copy on
|
|
71
|
+
* `cleo upgrade` / `cleo init --force`.
|
|
72
|
+
*
|
|
73
|
+
* - `overwrite-on-bump` — Always overwrite when the manifest version is
|
|
74
|
+
* newer than the deployed copy.
|
|
75
|
+
* - `diff-prompt` — Show a diff and ask the user before overwriting.
|
|
76
|
+
* - `immutable` — Never overwrite once installed.
|
|
77
|
+
* - `manifest-merge` — Apply structural merge (e.g. JSON deep-merge,
|
|
78
|
+
* respecting existing user keys).
|
|
79
|
+
*/
|
|
80
|
+
export declare const TEMPLATE_UPDATE_STRATEGIES: readonly ["overwrite-on-bump", "diff-prompt", "immutable", "manifest-merge"];
|
|
81
|
+
/** Reconciliation policy for an installed template on upgrade. */
|
|
82
|
+
export type UpdateStrategy = (typeof TEMPLATE_UPDATE_STRATEGIES)[number];
|
|
83
|
+
/**
|
|
84
|
+
* Allowed resolver sources a {@link PlaceholderSpec} can reference.
|
|
85
|
+
*
|
|
86
|
+
* - `project-context` — `.cleo/project-context.json` (detected ecosystem hints).
|
|
87
|
+
* - `project-info` — `.cleo/project-info.json` (per-project metadata).
|
|
88
|
+
* - `.cleo/config` — Per-project config (`<projectRoot>/.cleo/config.json`).
|
|
89
|
+
* - `~/.cleo/config` — User-global config (`$XDG_CONFIG_HOME/cleo/config.json`).
|
|
90
|
+
* - `tool-resolver` — Resolved via the per-language tool resolver
|
|
91
|
+
* (e.g. `pnpm` vs `npm` chosen by `primaryType`).
|
|
92
|
+
* - `literal` — Hard-coded value baked into the manifest entry.
|
|
93
|
+
*/
|
|
94
|
+
export declare const PLACEHOLDER_SOURCES: readonly ["project-context", "project-info", ".cleo/config", "~/.cleo/config", "tool-resolver", "literal"];
|
|
95
|
+
/** Resolver source enum for {@link PlaceholderSpec.source}. */
|
|
96
|
+
export type PlaceholderSource = (typeof PLACEHOLDER_SOURCES)[number];
|
|
97
|
+
/**
|
|
98
|
+
* Zod schema for a single placeholder declaration on a template entry.
|
|
99
|
+
*
|
|
100
|
+
* Each placeholder is resolved at install time by reading `sourcePath` from
|
|
101
|
+
* the named `source`. If resolution returns `undefined`, the installer
|
|
102
|
+
* falls back to `defaultValue`; if neither yields a value, installation
|
|
103
|
+
* fails with `E_PLACEHOLDER_UNRESOLVED`.
|
|
104
|
+
*/
|
|
105
|
+
export declare const PlaceholderSpecSchema: z.ZodObject<{
|
|
106
|
+
name: z.ZodString;
|
|
107
|
+
source: z.ZodEnum<{
|
|
108
|
+
literal: "literal";
|
|
109
|
+
"project-info": "project-info";
|
|
110
|
+
"project-context": "project-context";
|
|
111
|
+
".cleo/config": ".cleo/config";
|
|
112
|
+
"~/.cleo/config": "~/.cleo/config";
|
|
113
|
+
"tool-resolver": "tool-resolver";
|
|
114
|
+
}>;
|
|
115
|
+
sourcePath: z.ZodString;
|
|
116
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
117
|
+
}, z.core.$strip>;
|
|
118
|
+
/**
|
|
119
|
+
* Zod-inferred input shape for a placeholder declaration.
|
|
120
|
+
*
|
|
121
|
+
* Identical to {@link PlaceholderSpec} — there are no schema transforms.
|
|
122
|
+
*/
|
|
123
|
+
export type PlaceholderSpecInput = z.infer<typeof PlaceholderSpecSchema>;
|
|
124
|
+
/** Declared placeholder on a {@link TemplateManifestEntry}. */
|
|
125
|
+
export type PlaceholderSpec = PlaceholderSpecInput;
|
|
126
|
+
/**
|
|
127
|
+
* Zod schema for a single template manifest entry.
|
|
128
|
+
*
|
|
129
|
+
* Validates the full descriptor a template registry consumer needs to
|
|
130
|
+
* install a template file:
|
|
131
|
+
*
|
|
132
|
+
* - `id` — Stable identifier (kebab-case, matches install basename
|
|
133
|
+
* or a domain-unique slug).
|
|
134
|
+
* - `kind` — One of {@link TEMPLATE_KINDS}.
|
|
135
|
+
* - `sourcePath` — Repo-relative path of the template source.
|
|
136
|
+
* - `installPath` — Project-relative path where the rendered file lands.
|
|
137
|
+
* - `substitution` — One of {@link TEMPLATE_SUBSTITUTIONS}.
|
|
138
|
+
* - `placeholders` — Declared placeholders the installer must resolve.
|
|
139
|
+
* - `updateStrategy` — Reconciliation policy on upgrade.
|
|
140
|
+
*/
|
|
141
|
+
export declare const TemplateManifestEntrySchema: z.ZodObject<{
|
|
142
|
+
id: z.ZodString;
|
|
143
|
+
kind: z.ZodEnum<{
|
|
144
|
+
agent: "agent";
|
|
145
|
+
config: "config";
|
|
146
|
+
provider: "provider";
|
|
147
|
+
workflow: "workflow";
|
|
148
|
+
doc: "doc";
|
|
149
|
+
skill: "skill";
|
|
150
|
+
}>;
|
|
151
|
+
sourcePath: z.ZodString;
|
|
152
|
+
installPath: z.ZodString;
|
|
153
|
+
substitution: z.ZodEnum<{
|
|
154
|
+
static: "static";
|
|
155
|
+
"regex-tmpl": "regex-tmpl";
|
|
156
|
+
"json-merge": "json-merge";
|
|
157
|
+
}>;
|
|
158
|
+
placeholders: z.ZodArray<z.ZodObject<{
|
|
159
|
+
name: z.ZodString;
|
|
160
|
+
source: z.ZodEnum<{
|
|
161
|
+
literal: "literal";
|
|
162
|
+
"project-info": "project-info";
|
|
163
|
+
"project-context": "project-context";
|
|
164
|
+
".cleo/config": ".cleo/config";
|
|
165
|
+
"~/.cleo/config": "~/.cleo/config";
|
|
166
|
+
"tool-resolver": "tool-resolver";
|
|
167
|
+
}>;
|
|
168
|
+
sourcePath: z.ZodString;
|
|
169
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
170
|
+
}, z.core.$strip>>;
|
|
171
|
+
updateStrategy: z.ZodEnum<{
|
|
172
|
+
"overwrite-on-bump": "overwrite-on-bump";
|
|
173
|
+
"diff-prompt": "diff-prompt";
|
|
174
|
+
immutable: "immutable";
|
|
175
|
+
"manifest-merge": "manifest-merge";
|
|
176
|
+
}>;
|
|
177
|
+
}, z.core.$strip>;
|
|
178
|
+
/**
|
|
179
|
+
* Zod-inferred input shape for a template manifest entry.
|
|
180
|
+
*
|
|
181
|
+
* Identical to {@link TemplateManifestEntry} — there are no schema transforms.
|
|
182
|
+
*/
|
|
183
|
+
export type TemplateManifestEntryInput = z.infer<typeof TemplateManifestEntrySchema>;
|
|
184
|
+
/** Public type for a fully-validated template manifest entry. */
|
|
185
|
+
export type TemplateManifestEntry = TemplateManifestEntryInput;
|
|
186
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/templates/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,sEAAuE,CAAC;AAEnG,oEAAoE;AACpE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAI3D;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,iDAAkD,CAAC;AAEtF,4EAA4E;AAC5E,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAI3E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,8EAK7B,CAAC;AAEX,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAIzE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,4GAOtB,CAAC;AAEX,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;iBAmBhC,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEzE,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAInD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAetC,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAErF,iEAAiE;AACjE,MAAM,MAAM,qBAAqB,GAAG,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateManifest contract — SSoT for every template CLEO ships, installs,
|
|
3
|
+
* or rewrites during `cleo init` / `cleo upgrade` / `cleo scaffold-*`.
|
|
4
|
+
*
|
|
5
|
+
* A {@link TemplateManifestEntry} declares one template file: where its
|
|
6
|
+
* source lives in the monorepo, where it installs into a consumer project,
|
|
7
|
+
* how its placeholders are substituted, which placeholders it accepts, and
|
|
8
|
+
* how it MUST be reconciled when the shipped version moves ahead of the
|
|
9
|
+
* deployed copy.
|
|
10
|
+
*
|
|
11
|
+
* This contract is the leaf type. The CORE-side template registry
|
|
12
|
+
* (T9877) consumes it; the CLI verbs (`cleo init --workflows`,
|
|
13
|
+
* `cleo upgrade`, the scaffold sweeper) read the manifest at runtime to
|
|
14
|
+
* decide what to write, what to skip, and what to diff-prompt the user on.
|
|
15
|
+
*
|
|
16
|
+
* @example minimal workflow template entry
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { TemplateManifestEntrySchema, type TemplateManifestEntry } from '@cleocode/contracts';
|
|
19
|
+
*
|
|
20
|
+
* const entry: TemplateManifestEntry = TemplateManifestEntrySchema.parse({
|
|
21
|
+
* id: 'ci.yml',
|
|
22
|
+
* kind: 'workflow',
|
|
23
|
+
* sourcePath: 'packages/core/templates/workflows/ci.yml.tmpl',
|
|
24
|
+
* installPath: '.github/workflows/ci.yml',
|
|
25
|
+
* substitution: 'regex-tmpl',
|
|
26
|
+
* placeholders: [
|
|
27
|
+
* {
|
|
28
|
+
* name: 'NODE_VERSION',
|
|
29
|
+
* source: 'project-context',
|
|
30
|
+
* sourcePath: 'engines.node',
|
|
31
|
+
* defaultValue: 24,
|
|
32
|
+
* },
|
|
33
|
+
* ],
|
|
34
|
+
* updateStrategy: 'overwrite-on-bump',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @task T9875
|
|
39
|
+
* @epic T9874
|
|
40
|
+
* @saga T9855
|
|
41
|
+
* @see ADR-076 (canonical doc routing — same boundary discipline applies here)
|
|
42
|
+
*/
|
|
43
|
+
import { z } from 'zod';
|
|
44
|
+
// ─── TemplateKind ─────────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Allowed categories of template the manifest can describe.
|
|
47
|
+
*
|
|
48
|
+
* - `workflow` — GitHub Actions / CI workflow files.
|
|
49
|
+
* - `config` — Configuration files (e.g. `.editorconfig`, `tsconfig.json`).
|
|
50
|
+
* - `agent` — Agent definition files shipped to `.claude/agents/` etc.
|
|
51
|
+
* - `skill` — Skill markdown shipped to `.claude/skills/` etc.
|
|
52
|
+
* - `provider` — Provider-specific bootstrap files (Anthropic, OpenAI, …).
|
|
53
|
+
* - `doc` — Documentation scaffolds (e.g. README.md, AGENTS.md).
|
|
54
|
+
*/
|
|
55
|
+
export const TEMPLATE_KINDS = ['workflow', 'config', 'agent', 'skill', 'provider', 'doc'];
|
|
56
|
+
// ─── TemplateSubstitution ─────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Allowed substitution strategies a template can request.
|
|
59
|
+
*
|
|
60
|
+
* - `regex-tmpl` — `{{KEY}}` placeholder substitution via regex.
|
|
61
|
+
* - `static` — No substitution; the file is copied byte-for-byte.
|
|
62
|
+
* - `json-merge` — The template is parsed as JSON and merged into any
|
|
63
|
+
* existing JSON file at `installPath`; placeholders inside
|
|
64
|
+
* string values still go through `regex-tmpl`.
|
|
65
|
+
*/
|
|
66
|
+
export const TEMPLATE_SUBSTITUTIONS = ['regex-tmpl', 'static', 'json-merge'];
|
|
67
|
+
// ─── UpdateStrategy ───────────────────────────────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* How an installed template is reconciled with the shipped copy on
|
|
70
|
+
* `cleo upgrade` / `cleo init --force`.
|
|
71
|
+
*
|
|
72
|
+
* - `overwrite-on-bump` — Always overwrite when the manifest version is
|
|
73
|
+
* newer than the deployed copy.
|
|
74
|
+
* - `diff-prompt` — Show a diff and ask the user before overwriting.
|
|
75
|
+
* - `immutable` — Never overwrite once installed.
|
|
76
|
+
* - `manifest-merge` — Apply structural merge (e.g. JSON deep-merge,
|
|
77
|
+
* respecting existing user keys).
|
|
78
|
+
*/
|
|
79
|
+
export const TEMPLATE_UPDATE_STRATEGIES = [
|
|
80
|
+
'overwrite-on-bump',
|
|
81
|
+
'diff-prompt',
|
|
82
|
+
'immutable',
|
|
83
|
+
'manifest-merge',
|
|
84
|
+
];
|
|
85
|
+
// ─── PlaceholderSpec ──────────────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Allowed resolver sources a {@link PlaceholderSpec} can reference.
|
|
88
|
+
*
|
|
89
|
+
* - `project-context` — `.cleo/project-context.json` (detected ecosystem hints).
|
|
90
|
+
* - `project-info` — `.cleo/project-info.json` (per-project metadata).
|
|
91
|
+
* - `.cleo/config` — Per-project config (`<projectRoot>/.cleo/config.json`).
|
|
92
|
+
* - `~/.cleo/config` — User-global config (`$XDG_CONFIG_HOME/cleo/config.json`).
|
|
93
|
+
* - `tool-resolver` — Resolved via the per-language tool resolver
|
|
94
|
+
* (e.g. `pnpm` vs `npm` chosen by `primaryType`).
|
|
95
|
+
* - `literal` — Hard-coded value baked into the manifest entry.
|
|
96
|
+
*/
|
|
97
|
+
export const PLACEHOLDER_SOURCES = [
|
|
98
|
+
'project-context',
|
|
99
|
+
'project-info',
|
|
100
|
+
'.cleo/config',
|
|
101
|
+
'~/.cleo/config',
|
|
102
|
+
'tool-resolver',
|
|
103
|
+
'literal',
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* Zod schema for a single placeholder declaration on a template entry.
|
|
107
|
+
*
|
|
108
|
+
* Each placeholder is resolved at install time by reading `sourcePath` from
|
|
109
|
+
* the named `source`. If resolution returns `undefined`, the installer
|
|
110
|
+
* falls back to `defaultValue`; if neither yields a value, installation
|
|
111
|
+
* fails with `E_PLACEHOLDER_UNRESOLVED`.
|
|
112
|
+
*/
|
|
113
|
+
export const PlaceholderSpecSchema = z.object({
|
|
114
|
+
/**
|
|
115
|
+
* Placeholder identifier as it appears in the template body
|
|
116
|
+
* (e.g. `NODE_VERSION` matches `{{NODE_VERSION}}`).
|
|
117
|
+
*/
|
|
118
|
+
name: z.string().min(1, 'placeholder name must be non-empty'),
|
|
119
|
+
/** Resolver source the installer consults for this placeholder. */
|
|
120
|
+
source: z.enum(PLACEHOLDER_SOURCES),
|
|
121
|
+
/**
|
|
122
|
+
* Path expression evaluated against `source` (e.g. `engines.node` against
|
|
123
|
+
* `project-context`, `defaults.branchModel` against `.cleo/config`).
|
|
124
|
+
* For `literal` source, this MAY be the literal value's identifier.
|
|
125
|
+
*/
|
|
126
|
+
sourcePath: z.string().min(1, 'placeholder sourcePath must be non-empty'),
|
|
127
|
+
/**
|
|
128
|
+
* Fallback value used when `source[sourcePath]` resolves to `undefined`.
|
|
129
|
+
* `null` is permitted to explicitly mark "no default — failure required".
|
|
130
|
+
*/
|
|
131
|
+
defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
132
|
+
});
|
|
133
|
+
// ─── TemplateManifestEntry ────────────────────────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Zod schema for a single template manifest entry.
|
|
136
|
+
*
|
|
137
|
+
* Validates the full descriptor a template registry consumer needs to
|
|
138
|
+
* install a template file:
|
|
139
|
+
*
|
|
140
|
+
* - `id` — Stable identifier (kebab-case, matches install basename
|
|
141
|
+
* or a domain-unique slug).
|
|
142
|
+
* - `kind` — One of {@link TEMPLATE_KINDS}.
|
|
143
|
+
* - `sourcePath` — Repo-relative path of the template source.
|
|
144
|
+
* - `installPath` — Project-relative path where the rendered file lands.
|
|
145
|
+
* - `substitution` — One of {@link TEMPLATE_SUBSTITUTIONS}.
|
|
146
|
+
* - `placeholders` — Declared placeholders the installer must resolve.
|
|
147
|
+
* - `updateStrategy` — Reconciliation policy on upgrade.
|
|
148
|
+
*/
|
|
149
|
+
export const TemplateManifestEntrySchema = z.object({
|
|
150
|
+
/** Stable identifier for this template entry. */
|
|
151
|
+
id: z.string().min(1, 'id must be non-empty'),
|
|
152
|
+
/** Category of file this template represents. */
|
|
153
|
+
kind: z.enum(TEMPLATE_KINDS),
|
|
154
|
+
/** Repo-relative path of the template source file. */
|
|
155
|
+
sourcePath: z.string().min(1, 'sourcePath must be non-empty'),
|
|
156
|
+
/** Project-relative path where the rendered template installs. */
|
|
157
|
+
installPath: z.string().min(1, 'installPath must be non-empty'),
|
|
158
|
+
/** Substitution strategy the installer applies to `sourcePath`. */
|
|
159
|
+
substitution: z.enum(TEMPLATE_SUBSTITUTIONS),
|
|
160
|
+
/** Declared placeholders this template requires. May be empty. */
|
|
161
|
+
placeholders: z.array(PlaceholderSpecSchema),
|
|
162
|
+
/** Reconciliation policy on upgrade. */
|
|
163
|
+
updateStrategy: z.enum(TEMPLATE_UPDATE_STRATEGIES),
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/templates/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iFAAiF;AAEjF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,CAAU,CAAC;AAKnG,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAC;AAKtF,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,mBAAmB;IACnB,aAAa;IACb,WAAW;IACX,gBAAgB;CACR,CAAC;AAKX,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,iBAAiB;IACjB,cAAc;IACd,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,SAAS;CACD,CAAC;AAKX;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C;;;OAGG;IACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;IAC7D,mEAAmE;IACnE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;IACnC;;;;OAIG;IACH,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,0CAA0C,CAAC;IACzE;;;OAGG;IACH,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;CAClF,CAAC,CAAC;AAYH,iFAAiF;AAEjF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,iDAAiD;IACjD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;IAC7C,iDAAiD;IACjD,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;IAC5B,sDAAsD;IACtD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,8BAA8B,CAAC;IAC7D,kEAAkE;IAClE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,+BAA+B,CAAC;IAC/D,mEAAmE;IACnE,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC;IAC5C,kEAAkE;IAClE,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC;IAC5C,wCAAwC;IACxC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC;CACnD,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/contracts",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.113",
|
|
4
4
|
"description": "Domain types, interfaces, and contracts for the CLEO ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"dependencies": {
|
|
139
139
|
"zod": "^4.3.6",
|
|
140
140
|
"zod-to-json-schema": "^3.25.2",
|
|
141
|
-
"@cleocode/lafs": "2026.5.
|
|
141
|
+
"@cleocode/lafs": "2026.5.113"
|
|
142
142
|
},
|
|
143
143
|
"scripts": {
|
|
144
144
|
"build": "tsc -b --force && node scripts/copy-assets.mjs && node scripts/emit-schemas.mjs",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-shape contract test for the envelope-wide `meta.suggestedNext`
|
|
3
|
+
* field promoted in T9920 (Saga T9855 / E8.1).
|
|
4
|
+
*
|
|
5
|
+
* `CliMeta.suggestedNext` is the canonical LLM-next-action hint
|
|
6
|
+
* surfaced on every CLI envelope. This test asserts the type allows
|
|
7
|
+
* the optional `ReadonlyArray<string>` shape and never widens to
|
|
8
|
+
* `string | null` or to nested objects (which would re-introduce the
|
|
9
|
+
* pre-T9920 nexus-internal-only structured form at the envelope tier).
|
|
10
|
+
*
|
|
11
|
+
* The runtime `attachSuggestedNext` helper lives in
|
|
12
|
+
* `packages/core/src/dispatch/suggested-next.ts` and is exercised by
|
|
13
|
+
* `packages/core/src/dispatch/__tests__/suggested-next.test.ts` —
|
|
14
|
+
* contracts cannot import core without breaking the layering contract.
|
|
15
|
+
*
|
|
16
|
+
* @epic T9919
|
|
17
|
+
* @task T9920
|
|
18
|
+
* @saga T9855
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { CliEnvelope, CliMeta } from '@cleocode/lafs';
|
|
22
|
+
import { describe, expect, expectTypeOf, it } from 'vitest';
|
|
23
|
+
|
|
24
|
+
describe('CliMeta.suggestedNext type shape (T9920)', () => {
|
|
25
|
+
it('allows ReadonlyArray<string>', () => {
|
|
26
|
+
const meta: CliMeta = {
|
|
27
|
+
operation: 'tasks.show',
|
|
28
|
+
requestId: '00000000-0000-0000-0000-000000000000',
|
|
29
|
+
duration_ms: 0,
|
|
30
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
31
|
+
suggestedNext: ['cleo focus T1234', 'cleo verify T1234'],
|
|
32
|
+
};
|
|
33
|
+
expect(meta.suggestedNext).toEqual(['cleo focus T1234', 'cleo verify T1234']);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('is optional — omitting it leaves a valid CliMeta', () => {
|
|
37
|
+
const meta: CliMeta = {
|
|
38
|
+
operation: 'tasks.list',
|
|
39
|
+
requestId: '00000000-0000-0000-0000-000000000001',
|
|
40
|
+
duration_ms: 0,
|
|
41
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
42
|
+
};
|
|
43
|
+
expect(meta.suggestedNext).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('accepts an empty array (explicit "no follow-up suggested")', () => {
|
|
47
|
+
const meta: CliMeta = {
|
|
48
|
+
operation: 'tasks.show',
|
|
49
|
+
requestId: '00000000-0000-0000-0000-000000000002',
|
|
50
|
+
duration_ms: 0,
|
|
51
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
52
|
+
suggestedNext: [],
|
|
53
|
+
};
|
|
54
|
+
expect(meta.suggestedNext).toEqual([]);
|
|
55
|
+
expect(Array.isArray(meta.suggestedNext)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('typechecks as ReadonlyArray<string> — never `null`, never nested objects', () => {
|
|
59
|
+
expectTypeOf<CliMeta['suggestedNext']>().toEqualTypeOf<ReadonlyArray<string> | undefined>();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('flows through CliEnvelope<T>.meta verbatim', () => {
|
|
63
|
+
const envelope: CliEnvelope<{ taskId: string }> = {
|
|
64
|
+
success: true,
|
|
65
|
+
data: { taskId: 'T1234' },
|
|
66
|
+
meta: {
|
|
67
|
+
operation: 'tasks.show',
|
|
68
|
+
requestId: '00000000-0000-0000-0000-000000000003',
|
|
69
|
+
duration_ms: 1,
|
|
70
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
71
|
+
suggestedNext: ['cleo focus T1234'],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
expect(envelope.meta.suggestedNext).toEqual(['cleo focus T1234']);
|
|
75
|
+
});
|
|
76
|
+
});
|