ts-procedures 8.1.1 → 8.2.1

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 (71) hide show
  1. package/agent_config/bin/setup.mjs +2 -2
  2. package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
  3. package/agent_config/claude-code/agents/ts-procedures-architect.md +1 -1
  4. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +348 -5
  5. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +2 -2
  6. package/agent_config/claude-code/skills/ts-procedures/patterns.md +3 -3
  7. package/agent_config/claude-code/skills/{ts-procedures-scaffold → ts-procedures}/templates/client.md +4 -2
  8. package/agent_config/copilot/copilot-instructions.md +3 -3
  9. package/agent_config/cursor/cursorrules +3 -3
  10. package/agent_config/lib/install-claude.mjs +4 -4
  11. package/build/codegen/bin/cli.js +1 -1
  12. package/build/codegen/bin/cli.js.map +1 -1
  13. package/build/codegen/bin/cli.test.js +2 -2
  14. package/build/codegen/bin/cli.test.js.map +1 -1
  15. package/build/codegen/constants.d.ts +8 -1
  16. package/build/codegen/constants.js +9 -1
  17. package/build/codegen/constants.js.map +1 -1
  18. package/build/codegen/e2e.test.js +1 -1
  19. package/build/codegen/e2e.test.js.map +1 -1
  20. package/build/codegen/emit-errors.test.js +1 -1
  21. package/build/codegen/emit-errors.test.js.map +1 -1
  22. package/build/codegen/emit-index.test.js +1 -1
  23. package/build/codegen/emit-index.test.js.map +1 -1
  24. package/build/codegen/emit-scope.test.js +1 -1
  25. package/build/codegen/emit-scope.test.js.map +1 -1
  26. package/build/codegen/pipeline.js +1 -1
  27. package/build/codegen/pipeline.js.map +1 -1
  28. package/build/codegen/pipeline.test.js +33 -10
  29. package/build/codegen/pipeline.test.js.map +1 -1
  30. package/build/codegen/targets/_shared/write-files.d.ts +11 -5
  31. package/build/codegen/targets/_shared/write-files.js +49 -7
  32. package/build/codegen/targets/_shared/write-files.js.map +1 -1
  33. package/build/codegen/targets/_shared/write-files.test.js +87 -21
  34. package/build/codegen/targets/_shared/write-files.test.js.map +1 -1
  35. package/build/codegen/targets/kotlin/emit-scope-kotlin.js +3 -2
  36. package/build/codegen/targets/kotlin/emit-scope-kotlin.js.map +1 -1
  37. package/build/codegen/targets/kotlin/format-kotlin.d.ts +6 -0
  38. package/build/codegen/targets/kotlin/format-kotlin.js +9 -0
  39. package/build/codegen/targets/kotlin/format-kotlin.js.map +1 -1
  40. package/build/codegen/targets/kotlin/format-kotlin.test.js +8 -1
  41. package/build/codegen/targets/kotlin/format-kotlin.test.js.map +1 -1
  42. package/build/codegen/targets/swift/format-swift.js +4 -1
  43. package/build/codegen/targets/swift/format-swift.js.map +1 -1
  44. package/docs/ai-agent-setup.md +5 -6
  45. package/docs/client-and-codegen.md +2 -2
  46. package/package.json +1 -1
  47. package/src/codegen/bin/cli.test.ts +2 -2
  48. package/src/codegen/bin/cli.ts +1 -1
  49. package/src/codegen/constants.ts +10 -1
  50. package/src/codegen/e2e.test.ts +1 -1
  51. package/src/codegen/emit-errors.test.ts +1 -1
  52. package/src/codegen/emit-index.test.ts +1 -1
  53. package/src/codegen/emit-scope.test.ts +1 -1
  54. package/src/codegen/pipeline.test.ts +39 -10
  55. package/src/codegen/pipeline.ts +1 -1
  56. package/src/codegen/targets/_shared/write-files.test.ts +143 -32
  57. package/src/codegen/targets/_shared/write-files.ts +53 -8
  58. package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +1 -0
  59. package/src/codegen/targets/kotlin/emit-scope-kotlin.ts +3 -2
  60. package/src/codegen/targets/kotlin/format-kotlin.test.ts +9 -0
  61. package/src/codegen/targets/kotlin/format-kotlin.ts +11 -0
  62. package/src/codegen/targets/swift/format-swift.ts +5 -1
  63. package/agent_config/claude-code/skills/ts-procedures-kotlin/SKILL.md +0 -106
  64. package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +0 -48
  65. package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +0 -50
  66. package/agent_config/claude-code/skills/ts-procedures-swift/SKILL.md +0 -119
  67. /package/agent_config/claude-code/skills/{ts-procedures-review → ts-procedures}/checklist.md +0 -0
  68. /package/agent_config/claude-code/skills/{ts-procedures-scaffold → ts-procedures}/templates/astro-catchall.md +0 -0
  69. /package/agent_config/claude-code/skills/{ts-procedures-scaffold → ts-procedures}/templates/hono.md +0 -0
  70. /package/agent_config/claude-code/skills/{ts-procedures-scaffold → ts-procedures}/templates/procedure.md +0 -0
  71. /package/agent_config/claude-code/skills/{ts-procedures-scaffold → ts-procedures}/templates/stream-procedure.md +0 -0
@@ -1,8 +1,13 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { mkdir, readFile, rm, writeFile, stat, mkdtemp } from 'node:fs/promises';
2
+ import { mkdir, readFile, rm, writeFile, stat, mkdtemp, readdir } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { writeGeneratedFiles } from './write-files.js';
6
+ import { CODEGEN_HEADER } from '../../constants.js';
7
+ /** Build file contents that carry the package signature (a generated file). */
8
+ function signed(body = 'export const x = 1\n') {
9
+ return `${CODEGEN_HEADER}\n\n${body}`;
10
+ }
6
11
  describe('writeGeneratedFiles', () => {
7
12
  describe('dryRun mode', () => {
8
13
  let logSpy;
@@ -23,20 +28,38 @@ describe('writeGeneratedFiles', () => {
23
28
  // Sanity: the dir was not created.
24
29
  await expect(stat(dir)).rejects.toBeInstanceOf(Error);
25
30
  });
26
- it('logs a clean-outDir notice when cleanOutDir is true', async () => {
27
- await writeGeneratedFiles([], '/out', { dryRun: true, cleanOutDir: true });
28
- expect(logSpy).toHaveBeenCalledWith('[dry-run] Would clean outDir: /out');
29
- });
30
- it('does not log clean-outDir when cleanOutDir is false', async () => {
31
- await writeGeneratedFiles([{ path: '/out/x', code: 'x' }], '/out', { dryRun: true });
32
- const messages = logSpy.mock.calls.map((c) => c[0]);
33
- expect(messages.find((m) => String(m).includes('Would clean'))).toBeUndefined();
34
- });
35
31
  it('counts bytes as utf-8', async () => {
36
32
  // "é" is 2 bytes in utf-8.
37
33
  await writeGeneratedFiles([{ path: '/x/e.txt', code: 'é' }], '/x', { dryRun: true });
38
34
  expect(logSpy).toHaveBeenCalledWith('[dry-run] Would write: /x/e.txt (2 bytes)');
39
35
  });
36
+ it('logs would-remove for an orphaned generated file under cleanOutDir, without deleting it', async () => {
37
+ const outDir = await mkdtemp(join(tmpdir(), 'write-files-dry-'));
38
+ try {
39
+ await writeFile(join(outDir, 'orphan.ts'), signed(), 'utf-8');
40
+ await writeGeneratedFiles([{ path: join(outDir, 'fresh.ts'), code: signed() }], outDir, { dryRun: true, cleanOutDir: true });
41
+ expect(logSpy).toHaveBeenCalledWith(`[dry-run] Would remove orphaned generated file: ${join(outDir, 'orphan.ts')}`);
42
+ // Nothing was actually removed.
43
+ expect(await readFile(join(outDir, 'orphan.ts'), 'utf-8')).toBe(signed());
44
+ }
45
+ finally {
46
+ await rm(outDir, { recursive: true, force: true });
47
+ }
48
+ });
49
+ it('does not log a would-remove notice when cleanOutDir is false', async () => {
50
+ const outDir = await mkdtemp(join(tmpdir(), 'write-files-dry-'));
51
+ try {
52
+ await writeFile(join(outDir, 'orphan.ts'), signed(), 'utf-8');
53
+ await writeGeneratedFiles([{ path: join(outDir, 'x.ts'), code: signed() }], outDir, {
54
+ dryRun: true,
55
+ });
56
+ const messages = logSpy.mock.calls.map((c) => String(c[0]));
57
+ expect(messages.find((m) => m.includes('Would remove'))).toBeUndefined();
58
+ }
59
+ finally {
60
+ await rm(outDir, { recursive: true, force: true });
61
+ }
62
+ });
40
63
  });
41
64
  describe('real write mode', () => {
42
65
  let outDir;
@@ -59,20 +82,63 @@ describe('writeGeneratedFiles', () => {
59
82
  await writeGeneratedFiles([{ path: join(nested, 'x.txt'), code: 'x' }], nested);
60
83
  expect(await readFile(join(nested, 'x.txt'), 'utf-8')).toBe('x');
61
84
  });
62
- it('removes outDir when cleanOutDir is true before writing', async () => {
63
- // Pre-populate a stale file.
64
- await mkdir(outDir, { recursive: true });
65
- await writeFile(join(outDir, 'stale.txt'), 'stale', 'utf-8');
66
- await writeGeneratedFiles([{ path: join(outDir, 'fresh.txt'), code: 'fresh' }], outDir, { cleanOutDir: true });
67
- await expect(readFile(join(outDir, 'stale.txt'), 'utf-8')).rejects.toBeInstanceOf(Error);
68
- expect(await readFile(join(outDir, 'fresh.txt'), 'utf-8')).toBe('fresh');
85
+ describe('cleanOutDir (marker-based prune)', () => {
86
+ it('removes an orphaned generated file that is no longer emitted', async () => {
87
+ // A previously-generated scope file that this run does not re-emit.
88
+ await writeFile(join(outDir, 'orphan-scope.ts'), signed(), 'utf-8');
89
+ await writeGeneratedFiles([{ path: join(outDir, 'fresh.ts'), code: signed() }], outDir, { cleanOutDir: true });
90
+ await expect(readFile(join(outDir, 'orphan-scope.ts'), 'utf-8')).rejects.toBeInstanceOf(Error);
91
+ expect(await readFile(join(outDir, 'fresh.ts'), 'utf-8')).toBe(signed());
92
+ });
93
+ it('never deletes a file that lacks the package signature', async () => {
94
+ // Hand-written file the developer co-located in the output dir.
95
+ await writeFile(join(outDir, 'hand-written.ts'), 'export const mine = 1\n', 'utf-8');
96
+ // Even a file that merely imports from ts-procedures (no -codegen token).
97
+ await writeFile(join(outDir, 'helper.ts'), "import { createClient } from 'ts-procedures/client'\n", 'utf-8');
98
+ await writeGeneratedFiles([{ path: join(outDir, 'fresh.ts'), code: signed() }], outDir, { cleanOutDir: true });
99
+ expect(await readFile(join(outDir, 'hand-written.ts'), 'utf-8')).toBe('export const mine = 1\n');
100
+ expect(await readFile(join(outDir, 'helper.ts'), 'utf-8')).toBe("import { createClient } from 'ts-procedures/client'\n");
101
+ });
102
+ it('does not delete a signed file that is part of the current output', async () => {
103
+ // Pre-existing signed file that IS re-emitted this run — it should be
104
+ // overwritten, not removed-then-rewritten in a way that loses it.
105
+ await writeFile(join(outDir, 'users.ts'), signed('old'), 'utf-8');
106
+ await writeGeneratedFiles([{ path: join(outDir, 'users.ts'), code: signed('new') }], outDir, { cleanOutDir: true });
107
+ expect(await readFile(join(outDir, 'users.ts'), 'utf-8')).toBe(signed('new'));
108
+ });
109
+ it('prunes orphans left by a different package version (version-agnostic)', async () => {
110
+ // A file generated by an older release: different version in the header.
111
+ const oldHeader = '// Auto-generated by ts-procedures-codegen (v0.0.1) — do not edit';
112
+ await writeFile(join(outDir, 'legacy.ts'), `${oldHeader}\n\nexport const y = 2\n`, 'utf-8');
113
+ await writeGeneratedFiles([{ path: join(outDir, 'fresh.ts'), code: signed() }], outDir, { cleanOutDir: true });
114
+ await expect(readFile(join(outDir, 'legacy.ts'), 'utf-8')).rejects.toBeInstanceOf(Error);
115
+ });
116
+ it('skips subdirectories entirely', async () => {
117
+ const sub = join(outDir, 'nested');
118
+ await mkdir(sub, { recursive: true });
119
+ await writeFile(join(sub, 'inner.ts'), signed(), 'utf-8');
120
+ await writeGeneratedFiles([{ path: join(outDir, 'fresh.ts'), code: signed() }], outDir, { cleanOutDir: true });
121
+ // The nested dir and its signed file are untouched.
122
+ expect(await readFile(join(sub, 'inner.ts'), 'utf-8')).toBe(signed());
123
+ const entries = await readdir(outDir);
124
+ expect(entries).toContain('nested');
125
+ });
126
+ it('does not throw when outDir does not exist yet', async () => {
127
+ const fresh = join(outDir, 'brand-new');
128
+ await expect(writeGeneratedFiles([{ path: join(fresh, 'a.ts'), code: signed() }], fresh, {
129
+ cleanOutDir: true,
130
+ })).resolves.toBeUndefined();
131
+ expect(await readFile(join(fresh, 'a.ts'), 'utf-8')).toBe(signed());
132
+ });
69
133
  });
70
- it('does not remove outDir when cleanOutDir is false', async () => {
71
- await mkdir(outDir, { recursive: true });
134
+ it('does not remove anything when cleanOutDir is false', async () => {
135
+ await writeFile(join(outDir, 'orphan.ts'), signed(), 'utf-8');
72
136
  await writeFile(join(outDir, 'keep.txt'), 'keep', 'utf-8');
73
- await writeGeneratedFiles([{ path: join(outDir, 'new.txt'), code: 'new' }], outDir);
137
+ await writeGeneratedFiles([{ path: join(outDir, 'new.ts'), code: signed() }], outDir);
138
+ // Even a signed orphan stays put when cleanOutDir is not requested.
139
+ expect(await readFile(join(outDir, 'orphan.ts'), 'utf-8')).toBe(signed());
74
140
  expect(await readFile(join(outDir, 'keep.txt'), 'utf-8')).toBe('keep');
75
- expect(await readFile(join(outDir, 'new.txt'), 'utf-8')).toBe('new');
141
+ expect(await readFile(join(outDir, 'new.ts'), 'utf-8')).toBe(signed());
76
142
  });
77
143
  });
78
144
  });
@@ -1 +1 @@
1
- {"version":3,"file":"write-files.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/write-files.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-files.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/_shared/write-files.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { emitKotlinRoute } from './emit-route-kotlin.js';
2
- import { kotlinPackageDecl, kotlinSourceHashHeader, kotlinImports } from './format-kotlin.js';
2
+ import { kotlinPackageDecl, kotlinSignatureHeader, kotlinSourceHashHeader, kotlinImports } from './format-kotlin.js';
3
3
  import { indent } from '../_shared/indent.js';
4
4
  import { pickDefined } from '../_shared/pick-defined.js';
5
5
  import { pascalCase } from '../_shared/pascal-case.js';
@@ -25,9 +25,10 @@ export function emitKotlinScope(group, opts, emitter, errorSchemas) {
25
25
  ? `object ${scopeName} {\n}`
26
26
  : `object ${scopeName} {\n${innerScope}\n}`;
27
27
  const importsBlock = kotlinImports(allImports);
28
+ const headerBlock = [kotlinSignatureHeader(), kotlinSourceHashHeader(opts.sourceHash)].join('\n');
28
29
  const parts = [
29
30
  kotlinPackageDecl(opts.kotlinPackage),
30
- kotlinSourceHashHeader(opts.sourceHash),
31
+ headerBlock,
31
32
  importsBlock,
32
33
  scopeBlock,
33
34
  ].filter((p) => p.length > 0);
@@ -1 +1 @@
1
- {"version":3,"file":"emit-scope-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/emit-scope-kotlin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAsB,MAAM,wBAAwB,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7F,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AActD,MAAM,UAAU,eAAe,CAC7B,KAAiB,EACjB,IAAsB,EACtB,OAAsB,EACtB,YAAkC;IAElC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5C,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,WAAW,GAAa,EAAE,CAAA;IAChC,MAAM,cAAc,GAAa,EAAE,CAAA;IAEnC,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,CAAU,CAAA;IAC3H,MAAM,SAAS,GAAkB,WAAW,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAA;IAEpE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,CAAA;QAClE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YAChC,SAAQ;QACV,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAA;QAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,SAAS,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAA;QAClE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3B,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IACtF,MAAM,UAAU,GAAG,UAAU,KAAK,EAAE;QAClC,CAAC,CAAC,UAAU,SAAS,OAAO;QAC5B,CAAC,CAAC,UAAU,SAAS,OAAO,UAAU,KAAK,CAAA;IAE7C,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IAC9C,MAAM,KAAK,GAAG;QACZ,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC;QACrC,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC;QACvC,YAAY;QACZ,UAAU;KACX,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAE7B,OAAO,EAAE,QAAQ,EAAE,GAAG,SAAS,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,cAAc,EAAE,CAAA;AACzF,CAAC"}
1
+ {"version":3,"file":"emit-scope-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/emit-scope-kotlin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAsB,MAAM,wBAAwB,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACpH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AActD,MAAM,UAAU,eAAe,CAC7B,KAAiB,EACjB,IAAsB,EACtB,OAAsB,EACtB,YAAkC;IAElC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5C,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,WAAW,GAAa,EAAE,CAAA;IAChC,MAAM,cAAc,GAAa,EAAE,CAAA;IAEnC,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,CAAU,CAAA;IAC3H,MAAM,SAAS,GAAkB,WAAW,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAA;IAEpE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,CAAA;QAClE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YAChC,SAAQ;QACV,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAA;QAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,SAAS,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAA;QAClE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3B,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IACtF,MAAM,UAAU,GAAG,UAAU,KAAK,EAAE;QAClC,CAAC,CAAC,UAAU,SAAS,OAAO;QAC5B,CAAC,CAAC,UAAU,SAAS,OAAO,UAAU,KAAK,CAAA;IAE7C,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IAC9C,MAAM,WAAW,GAAG,CAAC,qBAAqB,EAAE,EAAE,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjG,MAAM,KAAK,GAAG;QACZ,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC;QACrC,WAAW;QACX,YAAY;QACZ,UAAU;KACX,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAE7B,OAAO,EAAE,QAAQ,EAAE,GAAG,SAAS,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,cAAc,EAAE,CAAA;AACzF,CAAC"}
@@ -1,3 +1,9 @@
1
1
  export declare function kotlinPackageDecl(pkg: string): string;
2
+ /**
3
+ * Stable signature comment embedded at the top of every generated Kotlin file.
4
+ * Carries the version-agnostic `ts-procedures-codegen` token so the marker-based
5
+ * prune in `writeGeneratedFiles` can recognise generated Kotlin files as owned.
6
+ */
7
+ export declare function kotlinSignatureHeader(): string;
2
8
  export declare function kotlinSourceHashHeader(hash: string): string;
3
9
  export declare function kotlinImports(imports: string[]): string;
@@ -1,6 +1,15 @@
1
+ import { CODEGEN_SIGNATURE } from '../../constants.js';
1
2
  export function kotlinPackageDecl(pkg) {
2
3
  return `package ${pkg}`;
3
4
  }
5
+ /**
6
+ * Stable signature comment embedded at the top of every generated Kotlin file.
7
+ * Carries the version-agnostic `ts-procedures-codegen` token so the marker-based
8
+ * prune in `writeGeneratedFiles` can recognise generated Kotlin files as owned.
9
+ */
10
+ export function kotlinSignatureHeader() {
11
+ return `// ${CODEGEN_SIGNATURE} — do not edit`;
12
+ }
4
13
  export function kotlinSourceHashHeader(hash) {
5
14
  return `// Source hash: ${hash}`;
6
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"format-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/format-kotlin.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,WAAW,GAAG,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,mBAAmB,IAAI,EAAE,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAiB;IAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpD,CAAC"}
1
+ {"version":3,"file":"format-kotlin.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/format-kotlin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,WAAW,GAAG,EAAE,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,mBAAmB,IAAI,EAAE,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAiB;IAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpD,CAAC"}
@@ -1,9 +1,16 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { kotlinPackageDecl, kotlinSourceHashHeader, kotlinImports, } from './format-kotlin.js';
2
+ import { kotlinPackageDecl, kotlinSignatureHeader, kotlinSourceHashHeader, kotlinImports, } from './format-kotlin.js';
3
+ import { CODEGEN_SIGNATURE } from '../../constants.js';
3
4
  describe('format-kotlin', () => {
4
5
  it('emits a package declaration', () => {
5
6
  expect(kotlinPackageDecl('com.example.api')).toBe('package com.example.api');
6
7
  });
8
+ it('emits a signature header line carrying the package token', () => {
9
+ const header = kotlinSignatureHeader();
10
+ expect(header).toBe(`// ${CODEGEN_SIGNATURE} — do not edit`);
11
+ // The marker-based prune depends on this token being present.
12
+ expect(header).toContain(CODEGEN_SIGNATURE);
13
+ });
7
14
  it('emits a source-hash header line', () => {
8
15
  expect(kotlinSourceHashHeader('abc123')).toBe('// Source hash: abc123');
9
16
  });
@@ -1 +1 @@
1
- {"version":3,"file":"format-kotlin.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/format-kotlin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAE3B,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;IACzE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,aAAa,CAAC,CAAC,oCAAoC,EAAE,kCAAkC,EAAE,oCAAoC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC1I,oFAAoF,CACrF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"format-kotlin.test.js","sourceRoot":"","sources":["../../../../src/codegen/targets/kotlin/format-kotlin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,iBAAiB,gBAAgB,CAAC,CAAA;QAC5D,8DAA8D;QAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;IACzE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,aAAa,CAAC,CAAC,oCAAoC,EAAE,kCAAkC,EAAE,oCAAoC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC1I,oFAAoF,CACrF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,5 +1,8 @@
1
+ import { CODEGEN_SIGNATURE } from '../../constants.js';
1
2
  export function swiftHeader(sourceHash) {
2
- return `// Generated by ts-procedures-codegen do not edit.\n// Source hash: ${sourceHash}`;
3
+ // The marker-based prune in `writeGeneratedFiles` relies on CODEGEN_SIGNATURE
4
+ // appearing in this header.
5
+ return `// Generated by ${CODEGEN_SIGNATURE} — do not edit.\n// Source hash: ${sourceHash}`;
3
6
  }
4
7
  export function swiftImports(imports) {
5
8
  if (imports.length === 0)
@@ -1 +1 @@
1
- {"version":3,"file":"format-swift.js","sourceRoot":"","sources":["../../../../src/codegen/targets/swift/format-swift.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,yEAAyE,UAAU,EAAE,CAAA;AAC9F,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAiB;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpD,CAAC"}
1
+ {"version":3,"file":"format-swift.js","sourceRoot":"","sources":["../../../../src/codegen/targets/swift/format-swift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,8EAA8E;IAC9E,4BAA4B;IAC5B,OAAO,mBAAmB,iBAAiB,oCAAoC,UAAU,EAAE,CAAA;AAC7F,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAiB;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpD,CAAC"}
@@ -22,7 +22,7 @@ npx ts-procedures-setup copilot # GitHub Copilot only
22
22
 
23
23
  | Tool | Files | Auto-updates? |
24
24
  |------|-------|---------------|
25
- | **Claude Code** | `.claude/skills/ts-procedures/`, `.claude/skills/ts-procedures-scaffold/`, `.claude/skills/ts-procedures-review/`, `.claude/agents/ts-procedures-architect.md` | Yes |
25
+ | **Claude Code** | `.claude/skills/ts-procedures/` (single skill — reference + `review`/`scaffold` modes + Kotlin/Swift codegen), `.claude/agents/ts-procedures-architect.md` | Yes |
26
26
  | **Cursor** | `.cursorrules` (marker-based section) | Yes |
27
27
  | **GitHub Copilot** | `.github/copilot-instructions.md` (marker-based section) | Yes |
28
28
 
@@ -34,9 +34,10 @@ After initial setup, rules are automatically refreshed on every `npm install` or
34
34
 
35
35
  Once installed, Claude Code gets:
36
36
 
37
- - **Framework reference skill** — `ts-procedures` with core API, schema system, error handling, and decision framework (auto-discovered by Claude Code)
38
- - **Scaffold skill**`/ts-procedures-scaffold <type> <Name>` generates procedures, streams, and HTTP setups with correct patterns
39
- - **Review skill** — `/ts-procedures-review <path>` checks code against a 60+ item checklist
37
+ - **Unified `ts-procedures` skill** one skill with three modes selected by the first word of its arguments:
38
+ - *Reference* (no argument) core API, schema system, error handling, decision framework, plus Kotlin/Swift client-codegen reference (auto-discovered by Claude Code)
39
+ - *Scaffold* — `/ts-procedures scaffold <type> <Name>` generates procedures, streams, and HTTP setups with correct patterns
40
+ - *Review* — `/ts-procedures review <path>` checks code against a 60+ item checklist
40
41
  - **Architecture agent** — `ts-procedures-architect` helps plan procedure structure, schema design, and HTTP implementation choices
41
42
 
42
43
  ## CLI Options
@@ -54,8 +55,6 @@ The `.claude/` files are auto-generated and regenerated on `npm install`. You ca
54
55
  ```gitignore
55
56
  # Auto-generated AI agent rules (regenerated on npm install)
56
57
  .claude/skills/ts-procedures/
57
- .claude/skills/ts-procedures-scaffold/
58
- .claude/skills/ts-procedures-review/
59
58
  .claude/agents/ts-procedures-*.md
60
59
  ```
61
60
 
@@ -99,7 +99,7 @@ generated/
99
99
  | `--array-item-naming <value>` | Postfix for array item type names (ignored with `--no-namespace-types`) | Off |
100
100
  | `--uncountable-words <list>` | Comma-separated words to skip singularization (ignored with `--no-namespace-types`) | Off |
101
101
  | `--service-name <name>` | Names the service namespace and binding factory (e.g. `Auth` → `export namespace Auth` + `createAuthBindings`). Also prefixes `${Name}Errors` and `${Name}ProcedureErrorUnion` in `_errors.ts`. | `Api` |
102
- | `--clean-out-dir` / `--no-clean-out-dir` | Recursively remove `--out <dir>` before writing so stale scope files are pruned. Skipped under `--dry-run`. | Off |
102
+ | `--clean-out-dir` / `--no-clean-out-dir` | Prune orphaned generated files from `--out <dir>` before writing files carrying the `ts-procedures-codegen` signature that this run no longer emits (e.g. a deleted scope). Hand-written files (no signature) are never removed, and subdirectories are left untouched. Skipped under `--dry-run`. Pass `--no-clean-out-dir` to opt out. | **On** |
103
103
 
104
104
  > **Note:** ajsc formatting options (`--enum-style`, `--depluralize`, `--array-item-naming`, `--uncountable-words`) only take effect in namespace mode (the default). With `--no-namespace-types`, all types are inlined and these options have no effect.
105
105
  >
@@ -407,7 +407,7 @@ await generateClient({
407
407
  namespaceTypes: true, // CLI defaults to true; programmatic API requires explicit opt-in
408
408
  selfContained: true, // CLI defaults to true; programmatic API requires explicit opt-in
409
409
  dryRun: false, // optional
410
- cleanOutDir: false, // optional — when true, recursively removes outDir before writing
410
+ cleanOutDir: true, // optional — DEFAULT; prunes orphaned generated files (signed by ts-procedures-codegen) that this run no longer emits; never touches hand-written files. Set false to opt out.
411
411
  ajsc: { // optional — ajsc TypescriptConverter options
412
412
  jsdoc: true, // CLI defaults to true; programmatic API requires explicit opt-in
413
413
  enumStyle: 'union',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-procedures",
3
- "version": "8.1.1",
3
+ "version": "8.2.1",
4
4
  "description": "A TypeScript RPC framework that creates type-safe, schema-validated procedure calls with a single function definition. Define your procedures once and get full type inference, runtime validation, and framework integration hooks.",
5
5
  "main": "build/exports.js",
6
6
  "types": "build/exports.d.ts",
@@ -203,9 +203,9 @@ describe('parseArgs', () => {
203
203
  expect(result.cleanOutDir).toBe(true)
204
204
  })
205
205
 
206
- it('defaults cleanOutDir to false when not provided', () => {
206
+ it('defaults cleanOutDir to true when not provided', () => {
207
207
  const result = parseArgs(['--url', 'http://x.com/docs', '--out', './out'])
208
- expect(result.cleanOutDir).toBe(false)
208
+ expect(result.cleanOutDir).toBe(true)
209
209
  })
210
210
 
211
211
  it('parses --no-clean-out-dir flag', () => {
@@ -176,7 +176,7 @@ export function parseArgs(argv: string[], config?: CodegenConfig): ParsedArgs {
176
176
  let namespaceTypes = config?.namespaceTypes ?? true
177
177
  let selfContained = config?.selfContained ?? true
178
178
  let serviceName: string | undefined = config?.serviceName
179
- let cleanOutDir = config?.cleanOutDir ?? false
179
+ let cleanOutDir = config?.cleanOutDir ?? true
180
180
  let target: 'ts' | 'kotlin' | 'swift' | undefined = config?.target
181
181
  let kotlinPackage: string | undefined = config?.kotlin?.package
182
182
  let kotlinSerializer: 'kotlinx' | 'none' | undefined = config?.kotlin?.serializer
@@ -1 +1,10 @@
1
- export const CODEGEN_HEADER = '// Auto-generated by ts-procedures-codegen do not edit'
1
+ import pkg from '../../package.json' with { type: 'json' }
2
+
3
+ /**
4
+ * Stable, version-agnostic token embedded in every generated file (across all
5
+ * targets). The marker-based prune in `writeGeneratedFiles` uses this to tell
6
+ * generator-owned files apart from hand-written ones — so it must NOT include
7
+ * the package version (an orphan left by an older release must still match).
8
+ */
9
+ export const CODEGEN_SIGNATURE = 'ts-procedures-codegen'
10
+ export const CODEGEN_HEADER = `// Auto-generated by ${CODEGEN_SIGNATURE} (v${pkg.version}) — do not edit`
@@ -476,7 +476,7 @@ describe('E2E: generateClient full pipeline', () => {
476
476
 
477
477
  for (const file of ['users.ts', 'events.ts', 'index.ts', '_errors.ts']) {
478
478
  const content = readFileSync(join(tmpDir, file), 'utf-8')
479
- expect(content).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
479
+ expect(content).toContain('// Auto-generated by ts-procedures-codegen')
480
480
  }
481
481
  })
482
482
 
@@ -109,7 +109,7 @@ describe('emitErrorsFile', () => {
109
109
 
110
110
  it('includes the auto-generated header comment', async () => {
111
111
  const result = await emitErrorsFile([procedureErrorDoc])
112
- expect(result).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
112
+ expect(result).toContain('// Auto-generated by ts-procedures-codegen')
113
113
  })
114
114
 
115
115
  it('returns undefined when no errors have schemas', async () => {
@@ -31,7 +31,7 @@ const adminUsersGroup: ScopeGroup = {
31
31
  describe('emitIndexFile', () => {
32
32
  it('includes the auto-generated header comment', () => {
33
33
  const output = emitIndexFile([usersGroup])
34
- expect(output).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
34
+ expect(output).toContain('// Auto-generated by ts-procedures-codegen')
35
35
  })
36
36
 
37
37
  it('imports createClient as a value and client types for the factory', () => {
@@ -271,7 +271,7 @@ describe('emitScopeFile', () => {
271
271
  describe('RPC scope', () => {
272
272
  it('includes the auto-generated header comment', async () => {
273
273
  const output = await emitScopeFile(rpcGroup)
274
- expect(output).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
274
+ expect(output).toContain('// Auto-generated by ts-procedures-codegen')
275
275
  })
276
276
 
277
277
  it('imports ClientInstance and ProcedureCallOptions but not TypedStream', async () => {
@@ -7,6 +7,7 @@ import { mkdirSync, rmSync, readFileSync, existsSync, writeFileSync } from 'node
7
7
  import { join } from 'node:path'
8
8
  import { tmpdir } from 'node:os'
9
9
  import type { DocEnvelope } from '../implementations/types.js'
10
+ import { CODEGEN_HEADER } from './constants.js'
10
11
 
11
12
  // ---------------------------------------------------------------------------
12
13
  // Fixtures
@@ -140,7 +141,7 @@ describe('generateClient pipeline', () => {
140
141
 
141
142
  for (const file of ['users.ts', 'billing.ts', 'index.ts']) {
142
143
  const content = readFileSync(join(tmpDir, file), 'utf-8')
143
- expect(content).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
144
+ expect(content).toContain('// Auto-generated by ts-procedures-codegen')
144
145
  }
145
146
  })
146
147
 
@@ -172,7 +173,7 @@ describe('generateClient pipeline', () => {
172
173
  expect(paths).toContain(join(dryDir, 'billing.ts'))
173
174
  expect(paths).toContain(join(dryDir, 'index.ts'))
174
175
  for (const file of files) {
175
- expect(file.code).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
176
+ expect(file.code).toContain('// Auto-generated by ts-procedures-codegen')
176
177
  }
177
178
  })
178
179
 
@@ -186,8 +187,8 @@ describe('generateClient pipeline', () => {
186
187
  // Verify both files have auto-generated header and source hash
187
188
  const typesFile = files.find((f) => f.path.endsWith('_types.ts'))!
188
189
  const clientFile = files.find((f) => f.path.endsWith('_client.ts'))!
189
- expect(typesFile.code).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
190
- expect(clientFile.code).toContain('// Auto-generated by ts-procedures-codegen — do not edit')
190
+ expect(typesFile.code).toContain('// Auto-generated by ts-procedures-codegen')
191
+ expect(clientFile.code).toContain('// Auto-generated by ts-procedures-codegen')
191
192
  const typesLines = typesFile.code.split('\n')
192
193
  expect(typesLines[1]).toMatch(/^\/\/ Source hash: [a-f0-9]{32}$/)
193
194
  const clientLines = clientFile.code.split('\n')
@@ -290,30 +291,58 @@ describe('generateClient pipeline', () => {
290
291
  expect(content).not.toContain('createScopeBindings')
291
292
  })
292
293
 
293
- it('cleanOutDir: true removes stale files before writing', async () => {
294
+ it('cleanOutDir: true removes orphaned generated files before writing', async () => {
294
295
  tmpDir = makeTmpDir()
295
- const stale = join(tmpDir, 'stale.ts')
296
- writeFileSync(stale, '// stale', 'utf-8')
297
- expect(existsSync(stale)).toBe(true)
296
+ // A previously-generated scope file (carries the package signature) that
297
+ // this envelope no longer emits.
298
+ const orphan = join(tmpDir, 'old-scope.ts')
299
+ writeFileSync(orphan, `${CODEGEN_HEADER}\n\nexport const gone = 1\n`, 'utf-8')
300
+ expect(existsSync(orphan)).toBe(true)
298
301
 
299
302
  await generateClient({ envelope, outDir: tmpDir, cleanOutDir: true })
300
303
 
301
- expect(existsSync(stale)).toBe(false)
304
+ expect(existsSync(orphan)).toBe(false)
302
305
  expect(existsSync(join(tmpDir, 'index.ts'))).toBe(true)
303
306
  expect(existsSync(join(tmpDir, 'users.ts'))).toBe(true)
304
307
  })
305
308
 
306
- it('cleanOutDir: false (default) leaves unrelated files in place', async () => {
309
+ it('cleanOutDir: true never removes hand-written files lacking the signature', async () => {
307
310
  tmpDir = makeTmpDir()
311
+ const handWritten = join(tmpDir, 'hand-written.ts')
312
+ writeFileSync(handWritten, 'export const mine = 1\n', 'utf-8')
313
+
314
+ await generateClient({ envelope, outDir: tmpDir, cleanOutDir: true })
315
+
316
+ expect(existsSync(handWritten)).toBe(true)
317
+ expect(existsSync(join(tmpDir, 'index.ts'))).toBe(true)
318
+ })
319
+
320
+ it('prunes orphaned generated files by default (cleanOutDir defaults to true)', async () => {
321
+ tmpDir = makeTmpDir()
322
+ const orphan = join(tmpDir, 'old-scope.ts')
323
+ writeFileSync(orphan, `${CODEGEN_HEADER}\n\nexport const gone = 1\n`, 'utf-8')
324
+ // Hand-written file is still safe even with pruning on by default.
308
325
  const keep = join(tmpDir, 'keep.ts')
309
326
  writeFileSync(keep, '// keep', 'utf-8')
310
327
 
311
328
  await generateClient({ envelope, outDir: tmpDir })
312
329
 
330
+ expect(existsSync(orphan)).toBe(false)
313
331
  expect(existsSync(keep)).toBe(true)
314
332
  expect(existsSync(join(tmpDir, 'index.ts'))).toBe(true)
315
333
  })
316
334
 
335
+ it('cleanOutDir: false opts out of pruning, leaving orphaned generated files', async () => {
336
+ tmpDir = makeTmpDir()
337
+ const orphan = join(tmpDir, 'old-scope.ts')
338
+ writeFileSync(orphan, `${CODEGEN_HEADER}\n\nexport const gone = 1\n`, 'utf-8')
339
+
340
+ await generateClient({ envelope, outDir: tmpDir, cleanOutDir: false })
341
+
342
+ expect(existsSync(orphan)).toBe(true)
343
+ expect(existsSync(join(tmpDir, 'index.ts'))).toBe(true)
344
+ })
345
+
317
346
  it('cleanOutDir works when outDir does not exist', async () => {
318
347
  tmpDir = makeTmpDir()
319
348
  const nested = join(tmpDir, 'nested', 'missing')
@@ -43,7 +43,7 @@ export type { GeneratedFile } from './targets/_shared/write-files.js'
43
43
  * `_shared/write-files.ts`).
44
44
  */
45
45
  export async function runPipeline(options: PipelineOptions): Promise<GeneratedFile[]> {
46
- const { envelope, outDir, ajsc: ajscOpts, dryRun = false, namespaceTypes = false, selfContained = false, cleanOutDir = false } = options
46
+ const { envelope, outDir, ajsc: ajscOpts, dryRun = false, namespaceTypes = false, selfContained = false, cleanOutDir = true } = options
47
47
  const serviceName = options.serviceName ?? 'Api'
48
48
  validateServiceName(serviceName)
49
49