@mrtdown/core 2.0.0-alpha.3 → 2.0.0-alpha.4

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 (109) hide show
  1. package/dist/cli/commands/create.d.ts +30 -0
  2. package/dist/cli/commands/create.js +189 -0
  3. package/dist/cli/commands/create.js.map +1 -0
  4. package/dist/cli/commands/list.d.ts +6 -0
  5. package/dist/cli/commands/list.js +106 -0
  6. package/dist/cli/commands/list.js.map +1 -0
  7. package/dist/cli/commands/show.d.ts +6 -0
  8. package/dist/cli/commands/show.js +156 -0
  9. package/dist/cli/commands/show.js.map +1 -0
  10. package/dist/cli/commands/validate.d.ts +6 -0
  11. package/dist/cli/commands/validate.js +19 -0
  12. package/dist/cli/commands/validate.js.map +1 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +162 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/llm/client.d.ts +2 -0
  17. package/dist/llm/client.js +5 -0
  18. package/dist/llm/client.js.map +1 -0
  19. package/dist/llm/common/MemoryStore.d.ts +21 -0
  20. package/dist/llm/common/MemoryStore.js +100 -0
  21. package/dist/llm/common/MemoryStore.js.map +1 -0
  22. package/dist/llm/common/MemoryStore.test.d.ts +1 -0
  23. package/dist/llm/common/MemoryStore.test.js +225 -0
  24. package/dist/llm/common/MemoryStore.test.js.map +1 -0
  25. package/dist/llm/common/formatCurrentState.d.ts +10 -0
  26. package/dist/llm/common/formatCurrentState.js +342 -0
  27. package/dist/llm/common/formatCurrentState.js.map +1 -0
  28. package/dist/llm/common/tool.d.ts +32 -0
  29. package/dist/llm/common/tool.js +6 -0
  30. package/dist/llm/common/tool.js.map +1 -0
  31. package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.d.ts +1 -0
  32. package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js +433 -0
  33. package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js.map +1 -0
  34. package/dist/llm/functions/extractClaimsFromNewEvidence/index.d.ts +18 -0
  35. package/dist/llm/functions/extractClaimsFromNewEvidence/index.js +153 -0
  36. package/dist/llm/functions/extractClaimsFromNewEvidence/index.js.map +1 -0
  37. package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.d.ts +1 -0
  38. package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.js +168 -0
  39. package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.js.map +1 -0
  40. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.d.ts +19 -0
  41. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.js +65 -0
  42. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.js.map +1 -0
  43. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.d.ts +21 -0
  44. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js +115 -0
  45. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js.map +1 -0
  46. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.d.ts +24 -0
  47. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js +110 -0
  48. package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js.map +1 -0
  49. package/dist/llm/functions/generateIssueTitleAndSlug/index.d.ts +14 -0
  50. package/dist/llm/functions/generateIssueTitleAndSlug/index.js +38 -0
  51. package/dist/llm/functions/generateIssueTitleAndSlug/index.js.map +1 -0
  52. package/dist/llm/functions/generateIssueTitleAndSlug/prompt.d.ts +1 -0
  53. package/dist/llm/functions/generateIssueTitleAndSlug/prompt.js +23 -0
  54. package/dist/llm/functions/generateIssueTitleAndSlug/prompt.js.map +1 -0
  55. package/dist/llm/functions/translate/index.d.ts +1 -0
  56. package/dist/llm/functions/translate/index.js +59 -0
  57. package/dist/llm/functions/translate/index.js.map +1 -0
  58. package/dist/llm/functions/triageNewEvidence/eval.test.d.ts +1 -0
  59. package/dist/llm/functions/triageNewEvidence/eval.test.js +139 -0
  60. package/dist/llm/functions/triageNewEvidence/eval.test.js.map +1 -0
  61. package/dist/llm/functions/triageNewEvidence/index.d.ts +37 -0
  62. package/dist/llm/functions/triageNewEvidence/index.js +121 -0
  63. package/dist/llm/functions/triageNewEvidence/index.js.map +1 -0
  64. package/dist/llm/functions/triageNewEvidence/prompt.d.ts +1 -0
  65. package/dist/llm/functions/triageNewEvidence/prompt.js +60 -0
  66. package/dist/llm/functions/triageNewEvidence/prompt.js.map +1 -0
  67. package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.d.ts +19 -0
  68. package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js +65 -0
  69. package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js.map +1 -0
  70. package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.d.ts +19 -0
  71. package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js +37 -0
  72. package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js.map +1 -0
  73. package/dist/scripts/ingestViaWebhook.d.ts +1 -0
  74. package/dist/scripts/ingestViaWebhook.js +9 -0
  75. package/dist/scripts/ingestViaWebhook.js.map +1 -0
  76. package/dist/validators/buildContext.d.ts +7 -0
  77. package/dist/validators/buildContext.js +164 -0
  78. package/dist/validators/buildContext.js.map +1 -0
  79. package/dist/validators/index.d.ts +17 -0
  80. package/dist/validators/index.js +58 -0
  81. package/dist/validators/index.js.map +1 -0
  82. package/dist/validators/issue.d.ts +13 -0
  83. package/dist/validators/issue.js +220 -0
  84. package/dist/validators/issue.js.map +1 -0
  85. package/dist/validators/landmark.d.ts +7 -0
  86. package/dist/validators/landmark.js +43 -0
  87. package/dist/validators/landmark.js.map +1 -0
  88. package/dist/validators/line.d.ts +8 -0
  89. package/dist/validators/line.js +87 -0
  90. package/dist/validators/line.js.map +1 -0
  91. package/dist/validators/operator.d.ts +7 -0
  92. package/dist/validators/operator.js +43 -0
  93. package/dist/validators/operator.js.map +1 -0
  94. package/dist/validators/service.d.ts +8 -0
  95. package/dist/validators/service.js +87 -0
  96. package/dist/validators/service.js.map +1 -0
  97. package/dist/validators/station.d.ts +8 -0
  98. package/dist/validators/station.js +93 -0
  99. package/dist/validators/station.js.map +1 -0
  100. package/dist/validators/town.d.ts +7 -0
  101. package/dist/validators/town.js +43 -0
  102. package/dist/validators/town.js.map +1 -0
  103. package/dist/validators/types.d.ts +19 -0
  104. package/dist/validators/types.js +2 -0
  105. package/dist/validators/types.js.map +1 -0
  106. package/dist/validators/utils.d.ts +2 -0
  107. package/dist/validators/utils.js +9 -0
  108. package/dist/validators/utils.js.map +1 -0
  109. package/package.json +11 -7
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+ import { join } from 'node:path';
3
+ import { Command } from 'commander';
4
+ import { runCreateIssue, runCreateLandmark, runCreateLine, runCreateOperator, runCreateService, runCreateStation, runCreateTown, } from './commands/create.js';
5
+ import { runList } from './commands/list.js';
6
+ import { runShowIssue } from './commands/show.js';
7
+ import { runValidate } from './commands/validate.js';
8
+ const program = new Command();
9
+ program
10
+ .name('mrtdown-cli')
11
+ .description('CLI for mrtdown-data: create entities and validate data')
12
+ .option('-d, --data-dir <path>', 'Data directory', join(process.cwd(), 'data'));
13
+ program
14
+ .command('validate')
15
+ .description('Validate all data files against schemas')
16
+ .option('--scope <scope>', 'Only validate these entity types (repeatable): town, landmark, operator, station, line, service, issue', (val, prev) => (prev ?? []).concat(val))
17
+ .action((opts) => {
18
+ const dataDir = program.opts().dataDir;
19
+ const scope = opts.scope;
20
+ const code = runValidate({
21
+ dataDir,
22
+ scope: scope?.length
23
+ ? scope
24
+ : undefined,
25
+ });
26
+ process.exit(code);
27
+ });
28
+ program
29
+ .command('show')
30
+ .description('Display the current state of an issue')
31
+ .argument('<issue-id>', 'Issue ID (e.g. 2011-09-20-faulty-cable-led-to-circle-line-disruption)')
32
+ .option('--json', 'Output as JSON')
33
+ .action((issueId, opts) => {
34
+ const dataDir = program.opts().dataDir;
35
+ const code = runShowIssue({
36
+ dataDir,
37
+ issueId,
38
+ json: opts.json,
39
+ });
40
+ process.exit(code);
41
+ });
42
+ const list = program.command('list').description('List entities');
43
+ const listEntities = [
44
+ 'issue',
45
+ 'town',
46
+ 'landmark',
47
+ 'operator',
48
+ 'station',
49
+ 'line',
50
+ 'service',
51
+ ];
52
+ for (const entity of listEntities) {
53
+ list
54
+ .command(entity)
55
+ .description(`List ${entity}s`)
56
+ .option('--json', 'Output as JSON')
57
+ .action((opts) => {
58
+ const dataDir = program.opts().dataDir;
59
+ const code = runList({
60
+ dataDir,
61
+ entity,
62
+ json: opts.json,
63
+ });
64
+ process.exit(code);
65
+ });
66
+ }
67
+ const create = program.command('create').description('Create a new entity');
68
+ create
69
+ .command('issue')
70
+ .description('Create a new issue')
71
+ .requiredOption('--date <YYYY-MM-DD>', 'Issue date')
72
+ .requiredOption('--slug <slug>', 'URL-safe slug for the issue')
73
+ .requiredOption('--title <title>', 'English title')
74
+ .option('--type <type>', 'Issue type: disruption, maintenance, infra', 'disruption')
75
+ .option('--source <source>', 'Title source', 'cli')
76
+ .option('--dry-run', 'Print what would be created without writing')
77
+ .action(async (opts) => {
78
+ const dataDir = program.opts().dataDir;
79
+ const code = await runCreateIssue({ dataDir, dryRun: opts.dryRun }, {
80
+ date: opts.date,
81
+ slug: opts.slug,
82
+ title: opts.title,
83
+ type: opts.type,
84
+ source: opts.source,
85
+ });
86
+ process.exit(code);
87
+ });
88
+ create
89
+ .command('town')
90
+ .description('Create a new town')
91
+ .requiredOption('--id <id>', 'Town ID (e.g. yishun)')
92
+ .requiredOption('--name <name>', 'English name')
93
+ .option('--dry-run', 'Print what would be created without writing')
94
+ .action(async (opts) => {
95
+ const dataDir = program.opts().dataDir;
96
+ const code = await runCreateTown({ dataDir, dryRun: opts.dryRun }, { id: opts.id, name: opts.name });
97
+ process.exit(code);
98
+ });
99
+ create
100
+ .command('landmark')
101
+ .description('Create a new landmark')
102
+ .requiredOption('--id <id>', 'Landmark ID (e.g. northpoint-city)')
103
+ .requiredOption('--name <name>', 'English name')
104
+ .option('--dry-run', 'Print what would be created without writing')
105
+ .action(async (opts) => {
106
+ const dataDir = program.opts().dataDir;
107
+ const code = await runCreateLandmark({ dataDir, dryRun: opts.dryRun }, { id: opts.id, name: opts.name });
108
+ process.exit(code);
109
+ });
110
+ create
111
+ .command('operator')
112
+ .description('Create a new operator')
113
+ .requiredOption('--id <id>', 'Operator ID (e.g. SMRT_TRAINS)')
114
+ .requiredOption('--name <name>', 'English name')
115
+ .requiredOption('--founded-at <date>', 'Founded date (YYYY-MM-DD)')
116
+ .option('--url <url>', 'Operator website URL')
117
+ .option('--dry-run', 'Print what would be created without writing')
118
+ .action(async (opts) => {
119
+ const dataDir = program.opts().dataDir;
120
+ const code = await runCreateOperator({ dataDir, dryRun: opts.dryRun }, {
121
+ id: opts.id,
122
+ name: opts.name,
123
+ foundedAt: opts.foundedAt,
124
+ url: opts.url,
125
+ });
126
+ process.exit(code);
127
+ });
128
+ create
129
+ .command('station')
130
+ .description('Create a station from JSON (--stdin or --file)')
131
+ .option('--stdin', 'Read JSON from stdin')
132
+ .option('--file <path>', 'Read JSON from file')
133
+ .option('--dry-run', 'Print what would be created without writing')
134
+ .action(async (opts) => {
135
+ const dataDir = program.opts().dataDir;
136
+ const code = await runCreateStation({ dataDir, dryRun: opts.dryRun, stdin: opts.stdin }, { file: opts.file });
137
+ process.exit(code);
138
+ });
139
+ create
140
+ .command('line')
141
+ .description('Create a line from JSON (--stdin or --file)')
142
+ .option('--stdin', 'Read JSON from stdin')
143
+ .option('--file <path>', 'Read JSON from file')
144
+ .option('--dry-run', 'Print what would be created without writing')
145
+ .action(async (opts) => {
146
+ const dataDir = program.opts().dataDir;
147
+ const code = await runCreateLine({ dataDir, dryRun: opts.dryRun, stdin: opts.stdin }, { file: opts.file });
148
+ process.exit(code);
149
+ });
150
+ create
151
+ .command('service')
152
+ .description('Create a service from JSON (--stdin or --file)')
153
+ .option('--stdin', 'Read JSON from stdin')
154
+ .option('--file <path>', 'Read JSON from file')
155
+ .option('--dry-run', 'Print what would be created without writing')
156
+ .action(async (opts) => {
157
+ const dataDir = program.opts().dataDir;
158
+ const code = await runCreateService({ dataDir, dryRun: opts.dryRun, stdin: opts.stdin }, { file: opts.file });
159
+ process.exit(code);
160
+ });
161
+ program.parse();
162
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { join } from 'node:path';\nimport { Command } from 'commander';\nimport {\n runCreateIssue,\n runCreateLandmark,\n runCreateLine,\n runCreateOperator,\n runCreateService,\n runCreateStation,\n runCreateTown,\n} from './commands/create.js';\nimport { runList } from './commands/list.js';\nimport { runShowIssue } from './commands/show.js';\nimport { runValidate } from './commands/validate.js';\n\nconst program = new Command();\n\nprogram\n .name('mrtdown-cli')\n .description('CLI for mrtdown-data: create entities and validate data')\n .option(\n '-d, --data-dir <path>',\n 'Data directory',\n join(process.cwd(), 'data'),\n );\n\nprogram\n .command('validate')\n .description('Validate all data files against schemas')\n .option(\n '--scope <scope>',\n 'Only validate these entity types (repeatable): town, landmark, operator, station, line, service, issue',\n (val: string, prev: string[] | undefined) => (prev ?? []).concat(val),\n )\n .action((opts) => {\n const dataDir = program.opts().dataDir;\n const scope = opts.scope as string[] | undefined;\n const code = runValidate({\n dataDir,\n scope: scope?.length\n ? (scope as import('../../src/validators/index.js').ValidationScope[])\n : undefined,\n });\n process.exit(code);\n });\n\nprogram\n .command('show')\n .description('Display the current state of an issue')\n .argument('<issue-id>', 'Issue ID (e.g. 2011-09-20-faulty-cable-led-to-circle-line-disruption)')\n .option('--json', 'Output as JSON')\n .action((issueId, opts) => {\n const dataDir = program.opts().dataDir;\n const code = runShowIssue({\n dataDir,\n issueId,\n json: opts.json,\n });\n process.exit(code);\n });\n\nconst list = program.command('list').description('List entities');\n\nconst listEntities = [\n 'issue',\n 'town',\n 'landmark',\n 'operator',\n 'station',\n 'line',\n 'service',\n] as const;\n\nfor (const entity of listEntities) {\n list\n .command(entity)\n .description(`List ${entity}s`)\n .option('--json', 'Output as JSON')\n .action((opts) => {\n const dataDir = program.opts().dataDir;\n const code = runList({\n dataDir,\n entity,\n json: opts.json,\n });\n process.exit(code);\n });\n}\n\nconst create = program.command('create').description('Create a new entity');\n\ncreate\n .command('issue')\n .description('Create a new issue')\n .requiredOption('--date <YYYY-MM-DD>', 'Issue date')\n .requiredOption('--slug <slug>', 'URL-safe slug for the issue')\n .requiredOption('--title <title>', 'English title')\n .option(\n '--type <type>',\n 'Issue type: disruption, maintenance, infra',\n 'disruption',\n )\n .option('--source <source>', 'Title source', 'cli')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateIssue(\n { dataDir, dryRun: opts.dryRun },\n {\n date: opts.date,\n slug: opts.slug,\n title: opts.title,\n type: opts.type,\n source: opts.source,\n },\n );\n process.exit(code);\n });\n\ncreate\n .command('town')\n .description('Create a new town')\n .requiredOption('--id <id>', 'Town ID (e.g. yishun)')\n .requiredOption('--name <name>', 'English name')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateTown(\n { dataDir, dryRun: opts.dryRun },\n { id: opts.id, name: opts.name },\n );\n process.exit(code);\n });\n\ncreate\n .command('landmark')\n .description('Create a new landmark')\n .requiredOption('--id <id>', 'Landmark ID (e.g. northpoint-city)')\n .requiredOption('--name <name>', 'English name')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateLandmark(\n { dataDir, dryRun: opts.dryRun },\n { id: opts.id, name: opts.name },\n );\n process.exit(code);\n });\n\ncreate\n .command('operator')\n .description('Create a new operator')\n .requiredOption('--id <id>', 'Operator ID (e.g. SMRT_TRAINS)')\n .requiredOption('--name <name>', 'English name')\n .requiredOption('--founded-at <date>', 'Founded date (YYYY-MM-DD)')\n .option('--url <url>', 'Operator website URL')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateOperator(\n { dataDir, dryRun: opts.dryRun },\n {\n id: opts.id,\n name: opts.name,\n foundedAt: opts.foundedAt,\n url: opts.url,\n },\n );\n process.exit(code);\n });\n\ncreate\n .command('station')\n .description('Create a station from JSON (--stdin or --file)')\n .option('--stdin', 'Read JSON from stdin')\n .option('--file <path>', 'Read JSON from file')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateStation(\n { dataDir, dryRun: opts.dryRun, stdin: opts.stdin },\n { file: opts.file },\n );\n process.exit(code);\n });\n\ncreate\n .command('line')\n .description('Create a line from JSON (--stdin or --file)')\n .option('--stdin', 'Read JSON from stdin')\n .option('--file <path>', 'Read JSON from file')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateLine(\n { dataDir, dryRun: opts.dryRun, stdin: opts.stdin },\n { file: opts.file },\n );\n process.exit(code);\n });\n\ncreate\n .command('service')\n .description('Create a service from JSON (--stdin or --file)')\n .option('--stdin', 'Read JSON from stdin')\n .option('--file <path>', 'Read JSON from file')\n .option('--dry-run', 'Print what would be created without writing')\n .action(async (opts) => {\n const dataDir = program.opts().dataDir;\n const code = await runCreateService(\n { dataDir, dryRun: opts.dryRun, stdin: opts.stdin },\n { file: opts.file },\n );\n process.exit(code);\n });\n\nprogram.parse();\n"]}
@@ -0,0 +1,2 @@
1
+ import OpenAI from 'openai';
2
+ export declare const openAiClient: OpenAI;
@@ -0,0 +1,5 @@
1
+ import OpenAI from 'openai';
2
+ export const openAiClient = new OpenAI({
3
+ apiKey: process.env.OPENAI_AI_KEY,
4
+ });
5
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"/","sources":["llm/client.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;CAClC,CAAC,CAAC","sourcesContent":["import OpenAI from 'openai';\n\nexport const openAiClient = new OpenAI({\n apiKey: process.env.OPENAI_AI_KEY,\n});\n"]}
@@ -0,0 +1,21 @@
1
+ import type { IStore } from '#repo/common/store.js';
2
+ import type { IWriteStore } from '#write/common/store.js';
3
+ export declare class MemoryStore implements IStore, IWriteStore {
4
+ private readonly files;
5
+ private readonly dirs;
6
+ constructor(seed?: {
7
+ files?: Record<string, string>;
8
+ });
9
+ private toStorePath;
10
+ exists(path: string): boolean;
11
+ readText(path: string): string;
12
+ readJson<T>(path: string): T;
13
+ listDir(path: string): string[];
14
+ ensureDir(path: string): void;
15
+ private addChild;
16
+ writeText(path: string, text: string): void;
17
+ writeJson(path: string, json: unknown): void;
18
+ appendText(path: string, text: string): void;
19
+ delete(path: string): void;
20
+ dumpFiles(): Record<string, string>;
21
+ }
@@ -0,0 +1,100 @@
1
+ import { basename, dirname, normalize } from 'node:path';
2
+ export class MemoryStore {
3
+ files = new Map();
4
+ dirs = new Map();
5
+ constructor(seed) {
6
+ // Root dir
7
+ this.dirs.set('', new Set());
8
+ if (seed?.files) {
9
+ for (const [path, text] of Object.entries(seed.files)) {
10
+ this.writeText(path, text);
11
+ }
12
+ }
13
+ }
14
+ // --------- IStore ---------
15
+ toStorePath(path) {
16
+ const p = normalize(path);
17
+ return p === '.' ? '' : p;
18
+ }
19
+ exists(path) {
20
+ const p = this.toStorePath(path);
21
+ return this.files.has(p) || this.dirs.has(p);
22
+ }
23
+ readText(path) {
24
+ const p = this.toStorePath(path);
25
+ const v = this.files.get(p);
26
+ if (v == null) {
27
+ throw new Error(`MemoryStore: File not found: ${path}`);
28
+ }
29
+ return v;
30
+ }
31
+ readJson(path) {
32
+ return JSON.parse(this.readText(path));
33
+ }
34
+ listDir(path) {
35
+ const p = this.toStorePath(path);
36
+ const children = this.dirs.get(p);
37
+ if (children == null) {
38
+ throw new Error(`MemoryStore: Directory not found: ${path}`);
39
+ }
40
+ return Array.from(children.values()).sort();
41
+ }
42
+ // --------- IWriteStore ---------
43
+ ensureDir(path) {
44
+ const p = this.toStorePath(path);
45
+ if (this.dirs.has(p)) {
46
+ return;
47
+ }
48
+ // Ensure parents (dirname returns '.' for root, treat as '')
49
+ const rawParent = dirname(p === '' ? '.' : p);
50
+ const parent = rawParent === '.' ? '' : rawParent;
51
+ if (parent !== '') {
52
+ this.ensureDir(parent);
53
+ }
54
+ // Create this directory
55
+ this.dirs.set(p, new Set());
56
+ // Register in parent (including root when parent is '')
57
+ this.addChild(parent, basename(p));
58
+ }
59
+ addChild(dir, child) {
60
+ const d = this.toStorePath(dir);
61
+ const set = this.dirs.get(d);
62
+ if (set == null) {
63
+ this.ensureDir(d);
64
+ this.addChild(d, child);
65
+ return;
66
+ }
67
+ set.add(child);
68
+ }
69
+ writeText(path, text) {
70
+ const p = this.toStorePath(path);
71
+ this.ensureDir(dirname(p));
72
+ this.addChild(dirname(p), basename(p));
73
+ this.files.set(p, text);
74
+ }
75
+ writeJson(path, json) {
76
+ this.writeText(path, JSON.stringify(json));
77
+ }
78
+ appendText(path, text) {
79
+ const p = this.toStorePath(path);
80
+ this.ensureDir(dirname(p));
81
+ this.addChild(dirname(p), basename(p));
82
+ const prevContent = this.files.get(p) ?? '';
83
+ this.files.set(p, prevContent + text);
84
+ }
85
+ delete(path) {
86
+ const p = this.toStorePath(path);
87
+ this.files.delete(p);
88
+ this.dirs.delete(p);
89
+ this.dirs.delete(path);
90
+ }
91
+ // Debug helpers
92
+ dumpFiles() {
93
+ const result = {};
94
+ for (const [path, text] of this.files.entries()) {
95
+ result[path] = text;
96
+ }
97
+ return result;
98
+ }
99
+ }
100
+ //# sourceMappingURL=MemoryStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryStore.js","sourceRoot":"/","sources":["llm/common/MemoryStore.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, dirname, normalize } from 'node:path';\nimport type { IStore } from '#repo/common/store.js';\nimport type { IWriteStore } from '#write/common/store.js';\n\nexport class MemoryStore implements IStore, IWriteStore {\n private readonly files = new Map<string, string>();\n private readonly dirs = new Map<string, Set<string>>();\n\n constructor(seed?: { files?: Record<string, string> }) {\n // Root dir\n this.dirs.set('', new Set());\n\n if (seed?.files) {\n for (const [path, text] of Object.entries(seed.files)) {\n this.writeText(path, text);\n }\n }\n }\n\n // --------- IStore ---------\n private toStorePath(path: string): string {\n const p = normalize(path);\n return p === '.' ? '' : p;\n }\n\n exists(path: string): boolean {\n const p = this.toStorePath(path);\n return this.files.has(p) || this.dirs.has(p);\n }\n\n readText(path: string): string {\n const p = this.toStorePath(path);\n const v = this.files.get(p);\n if (v == null) {\n throw new Error(`MemoryStore: File not found: ${path}`);\n }\n return v;\n }\n\n readJson<T>(path: string): T {\n return JSON.parse(this.readText(path));\n }\n\n listDir(path: string): string[] {\n const p = this.toStorePath(path);\n const children = this.dirs.get(p);\n if (children == null) {\n throw new Error(`MemoryStore: Directory not found: ${path}`);\n }\n return Array.from(children.values()).sort();\n }\n\n // --------- IWriteStore ---------\n ensureDir(path: string): void {\n const p = this.toStorePath(path);\n if (this.dirs.has(p)) {\n return;\n }\n\n // Ensure parents (dirname returns '.' for root, treat as '')\n const rawParent = dirname(p === '' ? '.' : p);\n const parent = rawParent === '.' ? '' : rawParent;\n if (parent !== '') {\n this.ensureDir(parent);\n }\n\n // Create this directory\n this.dirs.set(p, new Set());\n\n // Register in parent (including root when parent is '')\n this.addChild(parent, basename(p));\n }\n\n private addChild(dir: string, child: string): void {\n const d = this.toStorePath(dir);\n const set = this.dirs.get(d);\n if (set == null) {\n this.ensureDir(d);\n this.addChild(d, child);\n return;\n }\n set.add(child);\n }\n\n writeText(path: string, text: string): void {\n const p = this.toStorePath(path);\n this.ensureDir(dirname(p));\n this.addChild(dirname(p), basename(p));\n this.files.set(p, text);\n }\n\n writeJson(path: string, json: unknown): void {\n this.writeText(path, JSON.stringify(json));\n }\n\n appendText(path: string, text: string): void {\n const p = this.toStorePath(path);\n this.ensureDir(dirname(p));\n this.addChild(dirname(p), basename(p));\n const prevContent = this.files.get(p) ?? '';\n this.files.set(p, prevContent + text);\n }\n\n delete(path: string): void {\n const p = this.toStorePath(path);\n this.files.delete(p);\n this.dirs.delete(p);\n this.dirs.delete(path);\n }\n\n // Debug helpers\n dumpFiles(): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [path, text] of this.files.entries()) {\n result[path] = text;\n }\n return result;\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,225 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { MemoryStore } from './MemoryStore.js';
3
+ describe('MemoryStore', () => {
4
+ describe('constructor', () => {
5
+ test('creates empty store by default', () => {
6
+ const store = new MemoryStore();
7
+ expect(store.dumpFiles()).toEqual({});
8
+ expect(store.listDir('')).toEqual([]);
9
+ });
10
+ test('seeds store with files from seed object', () => {
11
+ const store = new MemoryStore({
12
+ files: {
13
+ 'foo.txt': 'hello',
14
+ 'bar/baz.json': '{"x":1}',
15
+ },
16
+ });
17
+ expect(store.readText('foo.txt')).toBe('hello');
18
+ expect(store.readText('bar/baz.json')).toBe('{"x":1}');
19
+ expect(store.listDir('')).toEqual(['bar', 'foo.txt']);
20
+ expect(store.listDir('bar')).toEqual(['baz.json']);
21
+ });
22
+ });
23
+ describe('exists', () => {
24
+ test('returns false for non-existent path', () => {
25
+ const store = new MemoryStore();
26
+ expect(store.exists('missing')).toBe(false);
27
+ expect(store.exists('a/b/c')).toBe(false);
28
+ });
29
+ test('returns true for root directory', () => {
30
+ const store = new MemoryStore();
31
+ expect(store.exists('')).toBe(true);
32
+ });
33
+ test('returns true for existing file', () => {
34
+ const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });
35
+ expect(store.exists('foo.txt')).toBe(true);
36
+ });
37
+ test('returns true for existing directory', () => {
38
+ const store = new MemoryStore({ files: { 'dir/file.txt': 'hi' } });
39
+ expect(store.exists('dir')).toBe(true);
40
+ });
41
+ test('normalizes path when checking', () => {
42
+ const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });
43
+ expect(store.exists('foo.txt')).toBe(true);
44
+ expect(store.exists('./foo.txt')).toBe(true);
45
+ });
46
+ });
47
+ describe('readText', () => {
48
+ test('returns content of existing file', () => {
49
+ const store = new MemoryStore({ files: { 'foo.txt': 'hello world' } });
50
+ expect(store.readText('foo.txt')).toBe('hello world');
51
+ });
52
+ test('throws for non-existent file', () => {
53
+ const store = new MemoryStore();
54
+ expect(() => store.readText('missing.txt')).toThrow('MemoryStore: File not found: missing.txt');
55
+ });
56
+ test('normalizes path when reading', () => {
57
+ const store = new MemoryStore({ files: { 'a/b.txt': 'content' } });
58
+ expect(store.readText('./a/b.txt')).toBe('content');
59
+ });
60
+ });
61
+ describe('readJson', () => {
62
+ test('parses and returns JSON', () => {
63
+ const store = new MemoryStore({
64
+ files: { 'data.json': '{"name":"test","count":42}' },
65
+ });
66
+ const data = store.readJson('data.json');
67
+ expect(data).toEqual({ name: 'test', count: 42 });
68
+ });
69
+ test('throws for invalid JSON', () => {
70
+ const store = new MemoryStore({ files: { 'bad.json': 'not json' } });
71
+ expect(() => store.readJson('bad.json')).toThrow();
72
+ });
73
+ });
74
+ describe('listDir', () => {
75
+ test('returns sorted list of children for root', () => {
76
+ const store = new MemoryStore({
77
+ files: {
78
+ 'a.txt': 'a',
79
+ 'z.txt': 'z',
80
+ 'm.txt': 'm',
81
+ 'dir/file.txt': 'f',
82
+ },
83
+ });
84
+ expect(store.listDir('')).toEqual(['a.txt', 'dir', 'm.txt', 'z.txt']);
85
+ });
86
+ test('returns sorted list of children for nested dir', () => {
87
+ const store = new MemoryStore({
88
+ files: {
89
+ 'parent/child1.txt': '1',
90
+ 'parent/child2.txt': '2',
91
+ 'parent/subdir/file.txt': '3',
92
+ },
93
+ });
94
+ expect(store.listDir('parent')).toEqual(['child1.txt', 'child2.txt', 'subdir']);
95
+ });
96
+ test('returns empty array for empty directory', () => {
97
+ const store = new MemoryStore();
98
+ expect(store.listDir('')).toEqual([]);
99
+ });
100
+ test('throws for non-existent directory', () => {
101
+ const store = new MemoryStore();
102
+ expect(() => store.listDir('missing')).toThrow('MemoryStore: Directory not found: missing');
103
+ });
104
+ });
105
+ describe('ensureDir', () => {
106
+ test('creates nested directories recursively', () => {
107
+ const store = new MemoryStore();
108
+ store.ensureDir('a/b/c');
109
+ expect(store.exists('a')).toBe(true);
110
+ expect(store.exists('a/b')).toBe(true);
111
+ expect(store.exists('a/b/c')).toBe(true);
112
+ expect(store.listDir('a')).toEqual(['b']);
113
+ expect(store.listDir('a/b')).toEqual(['c']);
114
+ expect(store.listDir('a/b/c')).toEqual([]);
115
+ });
116
+ test('is idempotent for existing directory', () => {
117
+ const store = new MemoryStore();
118
+ store.ensureDir('x');
119
+ store.ensureDir('x');
120
+ expect(store.listDir('x')).toEqual([]);
121
+ });
122
+ });
123
+ describe('writeText', () => {
124
+ test('writes file and creates parent directories', () => {
125
+ const store = new MemoryStore();
126
+ store.writeText('deep/path/file.txt', 'content');
127
+ expect(store.readText('deep/path/file.txt')).toBe('content');
128
+ expect(store.listDir('deep')).toEqual(['path']);
129
+ expect(store.listDir('deep/path')).toEqual(['file.txt']);
130
+ });
131
+ test('overwrites existing file', () => {
132
+ const store = new MemoryStore({ files: { 'foo.txt': 'old' } });
133
+ store.writeText('foo.txt', 'new');
134
+ expect(store.readText('foo.txt')).toBe('new');
135
+ });
136
+ });
137
+ describe('writeJson', () => {
138
+ test('writes JSON-serialized data', () => {
139
+ const store = new MemoryStore();
140
+ store.writeJson('data.json', { foo: 'bar', num: 123 });
141
+ expect(store.readText('data.json')).toBe('{"foo":"bar","num":123}');
142
+ expect(store.readJson('data.json')).toEqual({ foo: 'bar', num: 123 });
143
+ });
144
+ });
145
+ describe('appendText', () => {
146
+ test('creates new file when path does not exist', () => {
147
+ const store = new MemoryStore();
148
+ store.appendText('log.txt', 'line1\n');
149
+ expect(store.readText('log.txt')).toBe('line1\n');
150
+ });
151
+ test('appends to existing file', () => {
152
+ const store = new MemoryStore({ files: { 'log.txt': 'line1\n' } });
153
+ store.appendText('log.txt', 'line2\n');
154
+ store.appendText('log.txt', 'line3\n');
155
+ expect(store.readText('log.txt')).toBe('line1\nline2\nline3\n');
156
+ });
157
+ test('creates parent directories when needed', () => {
158
+ const store = new MemoryStore();
159
+ store.appendText('logs/2025/app.log', 'entry\n');
160
+ expect(store.readText('logs/2025/app.log')).toBe('entry\n');
161
+ });
162
+ });
163
+ describe('delete', () => {
164
+ test('removes file', () => {
165
+ const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });
166
+ store.delete('foo.txt');
167
+ expect(store.exists('foo.txt')).toBe(false);
168
+ expect(() => store.readText('foo.txt')).toThrow();
169
+ });
170
+ test('removes directory', () => {
171
+ const store = new MemoryStore({ files: { 'dir/file.txt': 'hi' } });
172
+ store.delete('dir');
173
+ expect(store.exists('dir')).toBe(false);
174
+ expect(() => store.listDir('dir')).toThrow();
175
+ });
176
+ test('removes both normalized and original path', () => {
177
+ const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });
178
+ store.delete('./foo.txt');
179
+ expect(store.exists('foo.txt')).toBe(false);
180
+ });
181
+ });
182
+ describe('dumpFiles', () => {
183
+ test('returns copy of all files', () => {
184
+ const store = new MemoryStore({
185
+ files: {
186
+ 'a.txt': 'a',
187
+ 'b/c.txt': 'c',
188
+ },
189
+ });
190
+ const dumped = store.dumpFiles();
191
+ expect(dumped).toEqual({
192
+ 'a.txt': 'a',
193
+ 'b/c.txt': 'c',
194
+ });
195
+ });
196
+ test('returns empty object for empty store', () => {
197
+ const store = new MemoryStore();
198
+ expect(store.dumpFiles()).toEqual({});
199
+ });
200
+ });
201
+ describe('full workflow', () => {
202
+ test('write → read → append → read → delete cycle', () => {
203
+ const store = new MemoryStore();
204
+ store.writeText('notes.txt', 'First note\n');
205
+ expect(store.readText('notes.txt')).toBe('First note\n');
206
+ store.appendText('notes.txt', 'Second note\n');
207
+ expect(store.readText('notes.txt')).toBe('First note\nSecond note\n');
208
+ store.delete('notes.txt');
209
+ expect(store.exists('notes.txt')).toBe(false);
210
+ });
211
+ test('nested structure with multiple operations', () => {
212
+ const store = new MemoryStore();
213
+ store.writeText('issues/2025-01-01-disruption.json', '{}');
214
+ store.writeText('issues/2025-01-02-maintenance.json', '{}');
215
+ store.writeJson('config.json', { version: 1 });
216
+ expect(store.listDir('')).toEqual(['config.json', 'issues']);
217
+ expect(store.listDir('issues')).toEqual([
218
+ '2025-01-01-disruption.json',
219
+ '2025-01-02-maintenance.json',
220
+ ]);
221
+ expect(store.readJson('config.json')).toEqual({ version: 1 });
222
+ });
223
+ });
224
+ });
225
+ //# sourceMappingURL=MemoryStore.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryStore.test.js","sourceRoot":"/","sources":["llm/common/MemoryStore.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, test } from 'vitest';\nimport { MemoryStore } from './MemoryStore.js';\n\ndescribe('MemoryStore', () => {\n describe('constructor', () => {\n test('creates empty store by default', () => {\n const store = new MemoryStore();\n expect(store.dumpFiles()).toEqual({});\n expect(store.listDir('')).toEqual([]);\n });\n\n test('seeds store with files from seed object', () => {\n const store = new MemoryStore({\n files: {\n 'foo.txt': 'hello',\n 'bar/baz.json': '{\"x\":1}',\n },\n });\n expect(store.readText('foo.txt')).toBe('hello');\n expect(store.readText('bar/baz.json')).toBe('{\"x\":1}');\n expect(store.listDir('')).toEqual(['bar', 'foo.txt']);\n expect(store.listDir('bar')).toEqual(['baz.json']);\n });\n });\n\n describe('exists', () => {\n test('returns false for non-existent path', () => {\n const store = new MemoryStore();\n expect(store.exists('missing')).toBe(false);\n expect(store.exists('a/b/c')).toBe(false);\n });\n\n test('returns true for root directory', () => {\n const store = new MemoryStore();\n expect(store.exists('')).toBe(true);\n });\n\n test('returns true for existing file', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });\n expect(store.exists('foo.txt')).toBe(true);\n });\n\n test('returns true for existing directory', () => {\n const store = new MemoryStore({ files: { 'dir/file.txt': 'hi' } });\n expect(store.exists('dir')).toBe(true);\n });\n\n test('normalizes path when checking', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });\n expect(store.exists('foo.txt')).toBe(true);\n expect(store.exists('./foo.txt')).toBe(true);\n });\n });\n\n describe('readText', () => {\n test('returns content of existing file', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'hello world' } });\n expect(store.readText('foo.txt')).toBe('hello world');\n });\n\n test('throws for non-existent file', () => {\n const store = new MemoryStore();\n expect(() => store.readText('missing.txt')).toThrow(\n 'MemoryStore: File not found: missing.txt'\n );\n });\n\n test('normalizes path when reading', () => {\n const store = new MemoryStore({ files: { 'a/b.txt': 'content' } });\n expect(store.readText('./a/b.txt')).toBe('content');\n });\n });\n\n describe('readJson', () => {\n test('parses and returns JSON', () => {\n const store = new MemoryStore({\n files: { 'data.json': '{\"name\":\"test\",\"count\":42}' },\n });\n const data = store.readJson<{ name: string; count: number }>('data.json');\n expect(data).toEqual({ name: 'test', count: 42 });\n });\n\n test('throws for invalid JSON', () => {\n const store = new MemoryStore({ files: { 'bad.json': 'not json' } });\n expect(() => store.readJson('bad.json')).toThrow();\n });\n });\n\n describe('listDir', () => {\n test('returns sorted list of children for root', () => {\n const store = new MemoryStore({\n files: {\n 'a.txt': 'a',\n 'z.txt': 'z',\n 'm.txt': 'm',\n 'dir/file.txt': 'f',\n },\n });\n expect(store.listDir('')).toEqual(['a.txt', 'dir', 'm.txt', 'z.txt']);\n });\n\n test('returns sorted list of children for nested dir', () => {\n const store = new MemoryStore({\n files: {\n 'parent/child1.txt': '1',\n 'parent/child2.txt': '2',\n 'parent/subdir/file.txt': '3',\n },\n });\n expect(store.listDir('parent')).toEqual(['child1.txt', 'child2.txt', 'subdir']);\n });\n\n test('returns empty array for empty directory', () => {\n const store = new MemoryStore();\n expect(store.listDir('')).toEqual([]);\n });\n\n test('throws for non-existent directory', () => {\n const store = new MemoryStore();\n expect(() => store.listDir('missing')).toThrow(\n 'MemoryStore: Directory not found: missing'\n );\n });\n });\n\n describe('ensureDir', () => {\n test('creates nested directories recursively', () => {\n const store = new MemoryStore();\n store.ensureDir('a/b/c');\n expect(store.exists('a')).toBe(true);\n expect(store.exists('a/b')).toBe(true);\n expect(store.exists('a/b/c')).toBe(true);\n expect(store.listDir('a')).toEqual(['b']);\n expect(store.listDir('a/b')).toEqual(['c']);\n expect(store.listDir('a/b/c')).toEqual([]);\n });\n\n test('is idempotent for existing directory', () => {\n const store = new MemoryStore();\n store.ensureDir('x');\n store.ensureDir('x');\n expect(store.listDir('x')).toEqual([]);\n });\n });\n\n describe('writeText', () => {\n test('writes file and creates parent directories', () => {\n const store = new MemoryStore();\n store.writeText('deep/path/file.txt', 'content');\n expect(store.readText('deep/path/file.txt')).toBe('content');\n expect(store.listDir('deep')).toEqual(['path']);\n expect(store.listDir('deep/path')).toEqual(['file.txt']);\n });\n\n test('overwrites existing file', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'old' } });\n store.writeText('foo.txt', 'new');\n expect(store.readText('foo.txt')).toBe('new');\n });\n });\n\n describe('writeJson', () => {\n test('writes JSON-serialized data', () => {\n const store = new MemoryStore();\n store.writeJson('data.json', { foo: 'bar', num: 123 });\n expect(store.readText('data.json')).toBe('{\"foo\":\"bar\",\"num\":123}');\n expect(store.readJson('data.json')).toEqual({ foo: 'bar', num: 123 });\n });\n });\n\n describe('appendText', () => {\n test('creates new file when path does not exist', () => {\n const store = new MemoryStore();\n store.appendText('log.txt', 'line1\\n');\n expect(store.readText('log.txt')).toBe('line1\\n');\n });\n\n test('appends to existing file', () => {\n const store = new MemoryStore({ files: { 'log.txt': 'line1\\n' } });\n store.appendText('log.txt', 'line2\\n');\n store.appendText('log.txt', 'line3\\n');\n expect(store.readText('log.txt')).toBe('line1\\nline2\\nline3\\n');\n });\n\n test('creates parent directories when needed', () => {\n const store = new MemoryStore();\n store.appendText('logs/2025/app.log', 'entry\\n');\n expect(store.readText('logs/2025/app.log')).toBe('entry\\n');\n });\n });\n\n describe('delete', () => {\n test('removes file', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });\n store.delete('foo.txt');\n expect(store.exists('foo.txt')).toBe(false);\n expect(() => store.readText('foo.txt')).toThrow();\n });\n\n test('removes directory', () => {\n const store = new MemoryStore({ files: { 'dir/file.txt': 'hi' } });\n store.delete('dir');\n expect(store.exists('dir')).toBe(false);\n expect(() => store.listDir('dir')).toThrow();\n });\n\n test('removes both normalized and original path', () => {\n const store = new MemoryStore({ files: { 'foo.txt': 'hi' } });\n store.delete('./foo.txt');\n expect(store.exists('foo.txt')).toBe(false);\n });\n });\n\n describe('dumpFiles', () => {\n test('returns copy of all files', () => {\n const store = new MemoryStore({\n files: {\n 'a.txt': 'a',\n 'b/c.txt': 'c',\n },\n });\n const dumped = store.dumpFiles();\n expect(dumped).toEqual({\n 'a.txt': 'a',\n 'b/c.txt': 'c',\n });\n });\n\n test('returns empty object for empty store', () => {\n const store = new MemoryStore();\n expect(store.dumpFiles()).toEqual({});\n });\n });\n\n describe('full workflow', () => {\n test('write → read → append → read → delete cycle', () => {\n const store = new MemoryStore();\n store.writeText('notes.txt', 'First note\\n');\n expect(store.readText('notes.txt')).toBe('First note\\n');\n\n store.appendText('notes.txt', 'Second note\\n');\n expect(store.readText('notes.txt')).toBe('First note\\nSecond note\\n');\n\n store.delete('notes.txt');\n expect(store.exists('notes.txt')).toBe(false);\n });\n\n test('nested structure with multiple operations', () => {\n const store = new MemoryStore();\n store.writeText('issues/2025-01-01-disruption.json', '{}');\n store.writeText('issues/2025-01-02-maintenance.json', '{}');\n store.writeJson('config.json', { version: 1 });\n\n expect(store.listDir('')).toEqual(['config.json', 'issues']);\n expect(store.listDir('issues')).toEqual([\n '2025-01-01-disruption.json',\n '2025-01-02-maintenance.json',\n ]);\n expect(store.readJson('config.json')).toEqual({ version: 1 });\n });\n });\n});\n"]}
@@ -0,0 +1,10 @@
1
+ import type { IssueBundleState } from '#repo/issue/helpers/deriveCurrentState.js';
2
+ import type { Evidence } from '#schema/issue/evidence.js';
3
+ export type FormatCurrentStateOptions = {
4
+ state: IssueBundleState | null;
5
+ evidence?: Evidence[];
6
+ };
7
+ /**
8
+ * Format the current state as markdown for better LLM readability
9
+ */
10
+ export declare function formatCurrentState(stateOrOptions: IssueBundleState | null | FormatCurrentStateOptions): string;