@shrkcrft/templates 0.1.0-alpha.30 → 0.1.0-alpha.31

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './template-definition.js';
2
+ export * from './template-remainder.js';
2
3
  export * from './template-variable.js';
3
4
  export * from './template-registry.js';
4
5
  export * from './template-renderer.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./template-definition.js";
2
+ export * from "./template-remainder.js";
2
3
  export * from "./template-variable.js";
3
4
  export * from "./template-registry.js";
4
5
  export * from "./template-renderer.js";
@@ -1,4 +1,5 @@
1
1
  import type { ITemplateVariable, TemplateVariableValues } from './template-variable.js';
2
+ import type { TemplateRemainder } from './template-remainder.js';
2
3
  /**
3
4
  * Subset of @shrkcrft/generator's `IPlannedChange` re-declared here to
4
5
  * avoid a generator → templates → generator cycle. The two shapes are kept
@@ -144,6 +145,22 @@ export interface ITemplateDefinition {
144
145
  changes?: ChangesResolver;
145
146
  /** Post-generation notes shown to the user. */
146
147
  postGenerationNotes?: readonly string[];
148
+ /**
149
+ * Work this template deliberately does NOT perform (build config, a path
150
+ * alias, workspace registration, …). Machine-readable, so a generator whose
151
+ * output needs a follow-up reads as a documented remainder, not a bug.
152
+ * Printed after generation; checked by template lint.
153
+ */
154
+ notScaffolded?: readonly (TemplateRemainder | `${TemplateRemainder}`)[];
155
+ /**
156
+ * Manual steps the human performs after generation. `covers` names the
157
+ * remainder(s) a step handles, so lint can tell a covered remainder from an
158
+ * undeclared one.
159
+ */
160
+ manualSteps?: readonly {
161
+ readonly description: string;
162
+ readonly covers?: readonly (TemplateRemainder | `${TemplateRemainder}`)[];
163
+ }[];
147
164
  /** Related knowledge entry IDs. */
148
165
  related?: readonly string[];
149
166
  /**
@@ -155,7 +172,12 @@ export interface ITemplateDefinition {
155
172
  metadata?: {
156
173
  /** Files that must NOT be produced by this template (regex fragments). */
157
174
  forbiddenPathFragments?: readonly string[];
158
- /** Profile ids this template depends on. */
175
+ /**
176
+ * WorkspaceProfile ids this template depends on (`has-typescript`,
177
+ * `is-library`, … — `shrk profiles list --kind workspace`). Resolved by the
178
+ * self-config doctor (`template-profile-missing`); not evaluated as a
179
+ * filter by the renderer.
180
+ */
159
181
  requiredProfileIds?: readonly string[];
160
182
  /** Convention ids this template's outputs are expected to satisfy. */
161
183
  requiredConventionIds?: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"template-definition.d.ts","sourceRoot":"","sources":["../src/template-definition.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template-definition.d.ts","sourceRoot":"","sources":["../src/template-definition.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,10 @@ export function defineTemplate(input) {
15
15
  postGenerationNotes: input.postGenerationNotes
16
16
  ? Object.freeze([...input.postGenerationNotes])
17
17
  : undefined,
18
+ notScaffolded: input.notScaffolded ? Object.freeze([...input.notScaffolded]) : undefined,
19
+ manualSteps: input.manualSteps
20
+ ? Object.freeze(input.manualSteps.map((s) => Object.freeze({ ...s, ...(s.covers ? { covers: Object.freeze([...s.covers]) } : {}) })))
21
+ : undefined,
18
22
  related: input.related ? Object.freeze([...input.related]) : undefined,
19
23
  };
20
24
  }
@@ -1,9 +1,25 @@
1
- import { type IImportContext } from '@shrkcrft/core';
1
+ import { type IImportContext, type IRejectedEntry } from '@shrkcrft/core';
2
2
  import type { ITemplateDefinition } from './template-definition.js';
3
+ /**
4
+ * THE template-shape predicate the loader registers by. Exported so the
5
+ * unregistered-export check asks exactly the question the loader asks.
6
+ */
7
+ export declare function isTemplate(value: unknown): value is ITemplateDefinition;
8
+ /**
9
+ * Why `value` is not a template: one `<field>: <message>` per field
10
+ * {@link isTemplate} requires and `value` lacks — `[]` when it is one.
11
+ */
12
+ export declare function templateRejectionReasons(value: unknown): readonly string[];
3
13
  export interface ILoadedTemplates {
4
14
  templates: ITemplateDefinition[];
5
15
  warnings: string[];
6
16
  sourceFiles: string[];
17
+ /**
18
+ * Every template the file declared that the loader refused (round 12,
19
+ * 12.1): a list member (or the `default` object) meant as a template that
20
+ * fails {@link isTemplate}, and a different object reusing an id.
21
+ */
22
+ rejected: IRejectedEntry[];
7
23
  }
8
24
  export interface ILoadTemplatesOptions {
9
25
  importContext?: IImportContext;
@@ -1 +1 @@
1
- {"version":3,"file":"template-loader.d.ts","sourceRoot":"","sources":["../src/template-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAQpE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,gBAAgB,CAAC,CAuC3B"}
1
+ {"version":3,"file":"template-loader.d.ts","sourceRoot":"","sources":["../src/template-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAA8B,MAAM,gBAAgB,CAAC;AACtG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAIvE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAO1E;AAmCD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,gBAAgB,CAAC,CAiF3B"}
@@ -1,18 +1,71 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { safeImport } from '@shrkcrft/core';
3
- function isTemplate(value) {
2
+ import { RejectionCause, safeImport } from '@shrkcrft/core';
3
+ /**
4
+ * THE template-shape predicate the loader registers by. Exported so the
5
+ * unregistered-export check asks exactly the question the loader asks.
6
+ */
7
+ export function isTemplate(value) {
4
8
  if (!value || typeof value !== 'object')
5
9
  return false;
6
10
  const v = value;
7
11
  return typeof v.id === 'string' && typeof v.name === 'string';
8
12
  }
13
+ /**
14
+ * Why `value` is not a template: one `<field>: <message>` per field
15
+ * {@link isTemplate} requires and `value` lacks — `[]` when it is one.
16
+ */
17
+ export function templateRejectionReasons(value) {
18
+ if (!value || typeof value !== 'object' || Array.isArray(value))
19
+ return ['(entry): must be an object'];
20
+ const v = value;
21
+ const out = [];
22
+ if (typeof v.id !== 'string')
23
+ out.push('id: must be a string');
24
+ if (typeof v.name !== 'string')
25
+ out.push('name: must be a string');
26
+ return out;
27
+ }
28
+ /** An object plainly meant as a template: it carries a string `id`, `name` or `title`. */
29
+ function isTemplateCandidate(value) {
30
+ if (!value || typeof value !== 'object' || Array.isArray(value))
31
+ return false;
32
+ const v = value;
33
+ return typeof v.id === 'string' || typeof v.name === 'string' || typeof v.title === 'string';
34
+ }
35
+ /**
36
+ * The list fields every consumer reads as arrays (`t.tags.length` in
37
+ * `templates list`). `ITemplateDefinition` declares them, but THE predicate
38
+ * accepts a template by id + name only — so, exactly as the knowledge loader
39
+ * does for its list fields, a missing one is normalised to a frozen `[]`
40
+ * rather than reaching a consumer as `undefined` (round 12, 12.1d: `templates
41
+ * list` crashed on a pack template with no `tags`).
42
+ */
43
+ const TEMPLATE_LIST_FIELDS = ['tags', 'scope', 'appliesWhen', 'variables'];
44
+ function normalizeTemplateListFields(t, file, warnings) {
45
+ const rec = t;
46
+ for (const field of TEMPLATE_LIST_FIELDS) {
47
+ const value = rec[field];
48
+ if (Array.isArray(value))
49
+ continue;
50
+ if (value !== undefined && value !== null) {
51
+ warnings.push(`${file}: template "${t.id}" has a non-list \`${field}\` (${typeof value}) — treated as []`);
52
+ }
53
+ try {
54
+ rec[field] = Object.freeze([]);
55
+ }
56
+ catch {
57
+ warnings.push(`${file}: template "${t.id}" is frozen without a \`${field}\` list — read as []`);
58
+ }
59
+ }
60
+ }
9
61
  export async function loadTemplatesFromFile(filePath, options = {}) {
10
62
  const warnings = [];
11
63
  const templates = [];
12
64
  const sourceFiles = [];
65
+ const rejected = [];
13
66
  if (!existsSync(filePath)) {
14
67
  warnings.push(`Template file not found: ${filePath}`);
15
- return { templates, warnings, sourceFiles };
68
+ return { templates, warnings, sourceFiles, rejected };
16
69
  }
17
70
  sourceFiles.push(filePath);
18
71
  const result = options.importContext
@@ -21,28 +74,60 @@ export async function loadTemplatesFromFile(filePath, options = {}) {
21
74
  if (!result.ok) {
22
75
  const label = result.timedOut ? 'timed out importing' : 'Failed to import';
23
76
  warnings.push(`${label} ${filePath}: ${result.error.message}`);
24
- return { templates, warnings, sourceFiles };
77
+ return { templates, warnings, sourceFiles, rejected };
25
78
  }
26
- const seen = new Set();
27
- const tryPush = (v) => {
28
- if (!isTemplate(v))
79
+ // First OBJECT per id, and the export it came from. The same object exported
80
+ // twice (`export const t` + `export default [t]`) is one template; a
81
+ // DIFFERENT object reusing an id is silently shadowed — so say so.
82
+ const firstById = new Map();
83
+ const tryPush = (v, key, at, candidate) => {
84
+ if (!isTemplate(v)) {
85
+ if (candidate && isTemplateCandidate(v)) {
86
+ const id = v.id;
87
+ rejected.push({
88
+ file: filePath,
89
+ index: at.index,
90
+ exportName: at.exportName,
91
+ ...(typeof id === 'string' ? { entryId: id } : {}),
92
+ reasons: templateRejectionReasons(v),
93
+ cause: RejectionCause.Invalid,
94
+ });
95
+ }
29
96
  return;
30
- if (seen.has(v.id))
97
+ }
98
+ const first = firstById.get(v.id);
99
+ if (first) {
100
+ if (first.value !== v) {
101
+ warnings.push(`duplicate id "${v.id}" in ${filePath} (export "${key}") shadowed by an earlier export "${first.key}" — only the first is registered`);
102
+ rejected.push({
103
+ file: filePath,
104
+ index: at.index,
105
+ exportName: at.exportName,
106
+ entryId: v.id,
107
+ reasons: [`id: "${v.id}" is already declared by export "${first.key}" — only the first is registered`],
108
+ cause: RejectionCause.DuplicateId,
109
+ });
110
+ }
31
111
  return;
32
- seen.add(v.id);
112
+ }
113
+ firstById.set(v.id, { value: v, key });
114
+ normalizeTemplateListFields(v, filePath, warnings);
33
115
  templates.push(v);
34
116
  };
35
117
  for (const key of Object.keys(result.module)) {
36
118
  const v = result.module[key];
37
119
  if (isTemplate(v)) {
38
- tryPush(v);
120
+ tryPush(v, key, { exportName: key, index: -1 }, key === 'default');
39
121
  }
40
122
  else if (Array.isArray(v)) {
41
- for (const item of v)
42
- tryPush(item);
123
+ // `default[3]`: the index tells two members of one array export apart.
124
+ v.forEach((item, i) => tryPush(item, `${key}[${i}]`, { exportName: key, index: i }, true));
125
+ }
126
+ else if (key === 'default') {
127
+ tryPush(v, key, { exportName: 'default', index: -1 }, true);
43
128
  }
44
129
  }
45
130
  if (templates.length === 0)
46
131
  warnings.push(`No templates exported by ${filePath}`);
47
- return { templates, warnings, sourceFiles };
132
+ return { templates, warnings, sourceFiles, rejected };
48
133
  }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The closed vocabulary of work a template deliberately does NOT perform.
3
+ *
4
+ * Two sibling templates used to differ on whether they emitted build config or
5
+ * patched the workspace path map, and nothing machine-readable said so — a
6
+ * generator whose output did not build read as a generator bug instead of a
7
+ * documented remainder. `ITemplateDefinition.notScaffolded` / `manualSteps`
8
+ * name the remainder with these values; template lint checks the shape, and
9
+ * `gen` / `templates preview` / `templates get` print it.
10
+ */
11
+ export declare enum TemplateRemainder {
12
+ BuildConfig = "build-config",
13
+ PathAlias = "path-alias",
14
+ WorkspaceRegistration = "workspace-registration",
15
+ Tests = "tests",
16
+ Docs = "docs",
17
+ Other = "other"
18
+ }
19
+ /** Every remainder value, for validation and error messages. */
20
+ export declare const TEMPLATE_REMAINDERS: readonly string[];
21
+ export declare function isTemplateRemainder(value: unknown): value is TemplateRemainder;
22
+ /**
23
+ * The remainder as structured fields — the ONE machine shape: `templates get
24
+ * --json` and MCP `get_template` both spread it (round 11 review R11-GAP-7:
25
+ * MCP carried neither field, so an agent never learned what a template leaves
26
+ * undone). Always present, `[]` when undeclared.
27
+ */
28
+ export declare function templateRemainderFields(template: {
29
+ readonly notScaffolded?: readonly string[];
30
+ readonly manualSteps?: readonly {
31
+ readonly description: string;
32
+ readonly covers?: readonly string[];
33
+ }[];
34
+ }): {
35
+ readonly notScaffolded: readonly string[];
36
+ readonly manualSteps: readonly {
37
+ readonly description: string;
38
+ readonly covers?: readonly string[];
39
+ }[];
40
+ };
41
+ /**
42
+ * {@link templateRemainderFields} with the empty fields left out — the compact
43
+ * form a task packet's template rows carry (`task --json`, MCP
44
+ * `get_task_packet`), so an undeclared remainder adds no bytes.
45
+ */
46
+ export declare function templateRemainderSummary(template: {
47
+ readonly notScaffolded?: readonly string[];
48
+ readonly manualSteps?: readonly {
49
+ readonly description: string;
50
+ readonly covers?: readonly string[];
51
+ }[];
52
+ }): {
53
+ readonly notScaffolded?: readonly string[];
54
+ readonly manualSteps?: readonly {
55
+ readonly description: string;
56
+ readonly covers?: readonly string[];
57
+ }[];
58
+ };
59
+ /**
60
+ * The printable remainder block for a template — the ONE wording every surface
61
+ * (gen, templates preview/get) prints after `postGenerationNotes`. Empty when
62
+ * the template declares neither field.
63
+ */
64
+ export declare function templateRemainderLines(template: {
65
+ readonly notScaffolded?: readonly string[];
66
+ readonly manualSteps?: readonly {
67
+ readonly description: string;
68
+ readonly covers?: readonly string[];
69
+ }[];
70
+ }): readonly string[];
71
+ //# sourceMappingURL=template-remainder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-remainder.d.ts","sourceRoot":"","sources":["../src/template-remainder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,oBAAY,iBAAiB;IAC3B,WAAW,iBAAiB;IAC5B,SAAS,eAAe;IACxB,qBAAqB,2BAA2B;IAChD,KAAK,UAAU;IACf,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAoD,CAAC;AAEtG,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACzG,GAAG;IACF,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACxG,CAEA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE;IACjD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACzG,GAAG;IACF,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACzG,CAKA;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACzG,GAAG,SAAS,MAAM,EAAE,CAapB"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The closed vocabulary of work a template deliberately does NOT perform.
3
+ *
4
+ * Two sibling templates used to differ on whether they emitted build config or
5
+ * patched the workspace path map, and nothing machine-readable said so — a
6
+ * generator whose output did not build read as a generator bug instead of a
7
+ * documented remainder. `ITemplateDefinition.notScaffolded` / `manualSteps`
8
+ * name the remainder with these values; template lint checks the shape, and
9
+ * `gen` / `templates preview` / `templates get` print it.
10
+ */
11
+ export var TemplateRemainder;
12
+ (function (TemplateRemainder) {
13
+ TemplateRemainder["BuildConfig"] = "build-config";
14
+ TemplateRemainder["PathAlias"] = "path-alias";
15
+ TemplateRemainder["WorkspaceRegistration"] = "workspace-registration";
16
+ TemplateRemainder["Tests"] = "tests";
17
+ TemplateRemainder["Docs"] = "docs";
18
+ TemplateRemainder["Other"] = "other";
19
+ })(TemplateRemainder || (TemplateRemainder = {}));
20
+ /** Every remainder value, for validation and error messages. */
21
+ export const TEMPLATE_REMAINDERS = Object.freeze(Object.values(TemplateRemainder));
22
+ export function isTemplateRemainder(value) {
23
+ return typeof value === 'string' && TEMPLATE_REMAINDERS.includes(value);
24
+ }
25
+ /**
26
+ * The remainder as structured fields — the ONE machine shape: `templates get
27
+ * --json` and MCP `get_template` both spread it (round 11 review R11-GAP-7:
28
+ * MCP carried neither field, so an agent never learned what a template leaves
29
+ * undone). Always present, `[]` when undeclared.
30
+ */
31
+ export function templateRemainderFields(template) {
32
+ return { notScaffolded: template.notScaffolded ?? [], manualSteps: template.manualSteps ?? [] };
33
+ }
34
+ /**
35
+ * {@link templateRemainderFields} with the empty fields left out — the compact
36
+ * form a task packet's template rows carry (`task --json`, MCP
37
+ * `get_task_packet`), so an undeclared remainder adds no bytes.
38
+ */
39
+ export function templateRemainderSummary(template) {
40
+ return {
41
+ ...(template.notScaffolded && template.notScaffolded.length > 0 ? { notScaffolded: template.notScaffolded } : {}),
42
+ ...(template.manualSteps && template.manualSteps.length > 0 ? { manualSteps: template.manualSteps } : {}),
43
+ };
44
+ }
45
+ /**
46
+ * The printable remainder block for a template — the ONE wording every surface
47
+ * (gen, templates preview/get) prints after `postGenerationNotes`. Empty when
48
+ * the template declares neither field.
49
+ */
50
+ export function templateRemainderLines(template) {
51
+ const lines = [];
52
+ if (template.notScaffolded && template.notScaffolded.length > 0) {
53
+ lines.push(`Not scaffolded by this template: ${template.notScaffolded.join(', ')}`);
54
+ }
55
+ if (template.manualSteps && template.manualSteps.length > 0) {
56
+ lines.push('Manual steps:');
57
+ for (const step of template.manualSteps) {
58
+ const covers = step.covers && step.covers.length > 0 ? ` (covers: ${step.covers.join(', ')})` : '';
59
+ lines.push(` • ${step.description}${covers}`);
60
+ }
61
+ }
62
+ return lines;
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/templates",
3
- "version": "0.1.0-alpha.30",
3
+ "version": "0.1.0-alpha.31",
4
4
  "description": "SharkCraft templates: typed template definitions, registry, variable validation, rendering.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -43,7 +43,7 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.30"
46
+ "@shrkcrft/core": "^0.1.0-alpha.31"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"