@braidhq/server 0.0.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app.d.ts +7 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +71 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/composeFs.d.ts +20 -0
  7. package/dist/composeFs.d.ts.map +1 -0
  8. package/dist/composeFs.js +134 -0
  9. package/dist/composeFs.js.map +1 -0
  10. package/dist/composition.d.ts +49 -0
  11. package/dist/composition.d.ts.map +1 -0
  12. package/dist/composition.js +43 -0
  13. package/dist/composition.js.map +1 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +5 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts +8 -0
  19. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts.map +1 -0
  20. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js +62 -0
  21. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js.map +1 -0
  22. package/dist/infrastructure/agent/SubprocessSkillRunner.d.ts +54 -0
  23. package/dist/infrastructure/agent/SubprocessSkillRunner.d.ts.map +1 -0
  24. package/dist/infrastructure/agent/SubprocessSkillRunner.js +240 -0
  25. package/dist/infrastructure/agent/SubprocessSkillRunner.js.map +1 -0
  26. package/dist/infrastructure/agent/asyncQueue.d.ts +22 -0
  27. package/dist/infrastructure/agent/asyncQueue.d.ts.map +1 -0
  28. package/dist/infrastructure/agent/asyncQueue.js +43 -0
  29. package/dist/infrastructure/agent/asyncQueue.js.map +1 -0
  30. package/dist/infrastructure/agent/mcpConfig.d.ts +18 -0
  31. package/dist/infrastructure/agent/mcpConfig.d.ts.map +1 -0
  32. package/dist/infrastructure/agent/mcpConfig.js +47 -0
  33. package/dist/infrastructure/agent/mcpConfig.js.map +1 -0
  34. package/dist/infrastructure/agent/streamJsonParser.d.ts +20 -0
  35. package/dist/infrastructure/agent/streamJsonParser.d.ts.map +1 -0
  36. package/dist/infrastructure/agent/streamJsonParser.js +44 -0
  37. package/dist/infrastructure/agent/streamJsonParser.js.map +1 -0
  38. package/dist/infrastructure/agent/subprocessEventStream.d.ts +26 -0
  39. package/dist/infrastructure/agent/subprocessEventStream.d.ts.map +1 -0
  40. package/dist/infrastructure/agent/subprocessEventStream.js +112 -0
  41. package/dist/infrastructure/agent/subprocessEventStream.js.map +1 -0
  42. package/dist/infrastructure/fs/FsClarifyTicketRepository.d.ts +13 -0
  43. package/dist/infrastructure/fs/FsClarifyTicketRepository.d.ts.map +1 -0
  44. package/dist/infrastructure/fs/FsClarifyTicketRepository.js +33 -0
  45. package/dist/infrastructure/fs/FsClarifyTicketRepository.js.map +1 -0
  46. package/dist/infrastructure/fs/FsDecisionRepository.d.ts +13 -0
  47. package/dist/infrastructure/fs/FsDecisionRepository.d.ts.map +1 -0
  48. package/dist/infrastructure/fs/FsDecisionRepository.js +56 -0
  49. package/dist/infrastructure/fs/FsDecisionRepository.js.map +1 -0
  50. package/dist/infrastructure/fs/FsProposalRepository.d.ts +14 -0
  51. package/dist/infrastructure/fs/FsProposalRepository.d.ts.map +1 -0
  52. package/dist/infrastructure/fs/FsProposalRepository.js +40 -0
  53. package/dist/infrastructure/fs/FsProposalRepository.js.map +1 -0
  54. package/dist/infrastructure/fs/FsRunRepository.d.ts +21 -0
  55. package/dist/infrastructure/fs/FsRunRepository.d.ts.map +1 -0
  56. package/dist/infrastructure/fs/FsRunRepository.js +88 -0
  57. package/dist/infrastructure/fs/FsRunRepository.js.map +1 -0
  58. package/dist/infrastructure/fs/FsSkillRegistry.d.ts +34 -0
  59. package/dist/infrastructure/fs/FsSkillRegistry.d.ts.map +1 -0
  60. package/dist/infrastructure/fs/FsSkillRegistry.js +143 -0
  61. package/dist/infrastructure/fs/FsSkillRegistry.js.map +1 -0
  62. package/dist/infrastructure/fs/FsStatusedJsonRepository.d.ts +35 -0
  63. package/dist/infrastructure/fs/FsStatusedJsonRepository.d.ts.map +1 -0
  64. package/dist/infrastructure/fs/FsStatusedJsonRepository.js +79 -0
  65. package/dist/infrastructure/fs/FsStatusedJsonRepository.js.map +1 -0
  66. package/dist/infrastructure/fs/FsWorkspaceRepository.d.ts +21 -0
  67. package/dist/infrastructure/fs/FsWorkspaceRepository.d.ts.map +1 -0
  68. package/dist/infrastructure/fs/FsWorkspaceRepository.js +87 -0
  69. package/dist/infrastructure/fs/FsWorkspaceRepository.js.map +1 -0
  70. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts +17 -0
  71. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts.map +1 -0
  72. package/dist/infrastructure/fs/WorkspaceRegistryFile.js +58 -0
  73. package/dist/infrastructure/fs/WorkspaceRegistryFile.js.map +1 -0
  74. package/dist/infrastructure/fs/frontmatter.d.ts +13 -0
  75. package/dist/infrastructure/fs/frontmatter.d.ts.map +1 -0
  76. package/dist/infrastructure/fs/frontmatter.js +46 -0
  77. package/dist/infrastructure/fs/frontmatter.js.map +1 -0
  78. package/dist/infrastructure/fs/jsonFileStore.d.ts +5 -0
  79. package/dist/infrastructure/fs/jsonFileStore.d.ts.map +1 -0
  80. package/dist/infrastructure/fs/jsonFileStore.js +29 -0
  81. package/dist/infrastructure/fs/jsonFileStore.js.map +1 -0
  82. package/dist/infrastructure/fs/paths.d.ts +22 -0
  83. package/dist/infrastructure/fs/paths.d.ts.map +1 -0
  84. package/dist/infrastructure/fs/paths.js +46 -0
  85. package/dist/infrastructure/fs/paths.js.map +1 -0
  86. package/dist/infrastructure/fs/productManifestWriter.d.ts +22 -0
  87. package/dist/infrastructure/fs/productManifestWriter.d.ts.map +1 -0
  88. package/dist/infrastructure/fs/productManifestWriter.js +75 -0
  89. package/dist/infrastructure/fs/productManifestWriter.js.map +1 -0
  90. package/dist/infrastructure/oauth/GoogleOAuth.d.ts +60 -0
  91. package/dist/infrastructure/oauth/GoogleOAuth.d.ts.map +1 -0
  92. package/dist/infrastructure/oauth/GoogleOAuth.js +114 -0
  93. package/dist/infrastructure/oauth/GoogleOAuth.js.map +1 -0
  94. package/dist/infrastructure/orphanReaper.d.ts +18 -0
  95. package/dist/infrastructure/orphanReaper.d.ts.map +1 -0
  96. package/dist/infrastructure/orphanReaper.js +42 -0
  97. package/dist/infrastructure/orphanReaper.js.map +1 -0
  98. package/dist/infrastructure/secrets/SecretStore.d.ts +36 -0
  99. package/dist/infrastructure/secrets/SecretStore.d.ts.map +1 -0
  100. package/dist/infrastructure/secrets/SecretStore.js +44 -0
  101. package/dist/infrastructure/secrets/SecretStore.js.map +1 -0
  102. package/dist/middleware/cors.d.ts +11 -0
  103. package/dist/middleware/cors.d.ts.map +1 -0
  104. package/dist/middleware/cors.js +30 -0
  105. package/dist/middleware/cors.js.map +1 -0
  106. package/dist/middleware/error.d.ts +3 -0
  107. package/dist/middleware/error.d.ts.map +1 -0
  108. package/dist/middleware/error.js +52 -0
  109. package/dist/middleware/error.js.map +1 -0
  110. package/dist/middleware/workspaceId.d.ts +10 -0
  111. package/dist/middleware/workspaceId.d.ts.map +1 -0
  112. package/dist/middleware/workspaceId.js +9 -0
  113. package/dist/middleware/workspaceId.js.map +1 -0
  114. package/dist/routes/clarify.d.ts +8 -0
  115. package/dist/routes/clarify.d.ts.map +1 -0
  116. package/dist/routes/clarify.js +84 -0
  117. package/dist/routes/clarify.js.map +1 -0
  118. package/dist/routes/decisions.d.ts +7 -0
  119. package/dist/routes/decisions.d.ts.map +1 -0
  120. package/dist/routes/decisions.js +30 -0
  121. package/dist/routes/decisions.js.map +1 -0
  122. package/dist/routes/edges.d.ts +7 -0
  123. package/dist/routes/edges.d.ts.map +1 -0
  124. package/dist/routes/edges.js +30 -0
  125. package/dist/routes/edges.js.map +1 -0
  126. package/dist/routes/health.d.ts +3 -0
  127. package/dist/routes/health.d.ts.map +1 -0
  128. package/dist/routes/health.js +10 -0
  129. package/dist/routes/health.js.map +1 -0
  130. package/dist/routes/helpers.d.ts +5 -0
  131. package/dist/routes/helpers.d.ts.map +1 -0
  132. package/dist/routes/helpers.js +14 -0
  133. package/dist/routes/helpers.js.map +1 -0
  134. package/dist/routes/model.d.ts +7 -0
  135. package/dist/routes/model.d.ts.map +1 -0
  136. package/dist/routes/model.js +12 -0
  137. package/dist/routes/model.js.map +1 -0
  138. package/dist/routes/nodes.d.ts +7 -0
  139. package/dist/routes/nodes.d.ts.map +1 -0
  140. package/dist/routes/nodes.js +47 -0
  141. package/dist/routes/nodes.js.map +1 -0
  142. package/dist/routes/oauth.d.ts +20 -0
  143. package/dist/routes/oauth.d.ts.map +1 -0
  144. package/dist/routes/oauth.js +107 -0
  145. package/dist/routes/oauth.js.map +1 -0
  146. package/dist/routes/ontology.d.ts +8 -0
  147. package/dist/routes/ontology.d.ts.map +1 -0
  148. package/dist/routes/ontology.js +21 -0
  149. package/dist/routes/ontology.js.map +1 -0
  150. package/dist/routes/proposals.d.ts +10 -0
  151. package/dist/routes/proposals.d.ts.map +1 -0
  152. package/dist/routes/proposals.js +80 -0
  153. package/dist/routes/proposals.js.map +1 -0
  154. package/dist/routes/runs.d.ts +9 -0
  155. package/dist/routes/runs.d.ts.map +1 -0
  156. package/dist/routes/runs.js +73 -0
  157. package/dist/routes/runs.js.map +1 -0
  158. package/dist/routes/skills.d.ts +9 -0
  159. package/dist/routes/skills.d.ts.map +1 -0
  160. package/dist/routes/skills.js +42 -0
  161. package/dist/routes/skills.js.map +1 -0
  162. package/dist/routes/workspaceEvents.d.ts +14 -0
  163. package/dist/routes/workspaceEvents.d.ts.map +1 -0
  164. package/dist/routes/workspaceEvents.js +37 -0
  165. package/dist/routes/workspaceEvents.js.map +1 -0
  166. package/dist/routes/workspaces.d.ts +8 -0
  167. package/dist/routes/workspaces.d.ts.map +1 -0
  168. package/dist/routes/workspaces.js +167 -0
  169. package/dist/routes/workspaces.js.map +1 -0
  170. package/dist/server.d.ts +2 -0
  171. package/dist/server.d.ts.map +1 -0
  172. package/dist/server.js +33 -0
  173. package/dist/server.js.map +1 -0
  174. package/package.json +49 -0
@@ -0,0 +1,79 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { NotFoundError } from '@braidhq/core';
4
+ import { listJsonFiles, moveFile, readJsonFile, writeJsonFile } from './jsonFileStore.js';
5
+ export class FsStatusedJsonRepository {
6
+ config;
7
+ workspaceRoots;
8
+ constructor(config, workspaceRoots) {
9
+ this.config = config;
10
+ this.workspaceRoots = workspaceRoots;
11
+ }
12
+ async list(filter) {
13
+ const roots = await this.candidateRoots(filter?.workspaceId);
14
+ const statuses = filter?.statuses?.length ? filter.statuses : this.config.statuses;
15
+ const entities = [];
16
+ for (const root of roots.values()) {
17
+ for (const status of statuses) {
18
+ const files = await listJsonFiles(this.config.dirFor(root, status));
19
+ for (const file of files) {
20
+ const data = await readJsonFile(file);
21
+ entities.push(this.config.parse(data));
22
+ }
23
+ }
24
+ }
25
+ return entities;
26
+ }
27
+ async load(id) {
28
+ const found = await this.locate(id);
29
+ if (!found)
30
+ throw new NotFoundError(`${this.config.entityName} "${id}" not found`);
31
+ const data = await readJsonFile(found.path);
32
+ return this.config.parse(data);
33
+ }
34
+ async save(entity) {
35
+ const roots = await this.workspaceRoots();
36
+ const workspaceId = this.config.workspaceIdOf(entity);
37
+ const root = roots.get(workspaceId);
38
+ if (!root)
39
+ throw new NotFoundError(`Workspace "${workspaceId}" not registered`);
40
+ const id = this.config.idOf(entity);
41
+ const status = this.config.statusOf(entity);
42
+ const targetPath = join(this.config.dirFor(root, status), `${id}.json`);
43
+ const existing = await this.locate(id);
44
+ if (existing && existing.path !== targetPath)
45
+ await moveFile(existing.path, targetPath);
46
+ await writeJsonFile(targetPath, this.config.serialize(entity));
47
+ }
48
+ async remove(id) {
49
+ const found = await this.locate(id);
50
+ if (!found)
51
+ throw new NotFoundError(`${this.config.entityName} "${id}" not found`);
52
+ await rm(found.path);
53
+ }
54
+ async locate(id) {
55
+ const roots = await this.workspaceRoots();
56
+ for (const root of roots.values()) {
57
+ for (const status of this.config.statuses) {
58
+ const candidatePath = join(this.config.dirFor(root, status), `${id}.json`);
59
+ try {
60
+ await readJsonFile(candidatePath);
61
+ return { path: candidatePath, status };
62
+ }
63
+ catch (error) {
64
+ if (error.code !== 'ENOENT')
65
+ throw error;
66
+ }
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ async candidateRoots(workspaceId) {
72
+ const roots = await this.workspaceRoots();
73
+ if (!workspaceId)
74
+ return roots;
75
+ const root = roots.get(workspaceId);
76
+ return root ? new Map([[workspaceId, root]]) : new Map();
77
+ }
78
+ }
79
+ //# sourceMappingURL=FsStatusedJsonRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FsStatusedJsonRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/FsStatusedJsonRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { WorkspaceRegistryFile } from './WorkspaceRegistryFile.js';
2
+ import { Workspace, type WorkspaceRepository } from '@braidhq/core';
3
+ import { AbsolutePath } from '@braidhq/schema';
4
+ export interface FsWorkspaceRepositoryDeps {
5
+ readonly registry: WorkspaceRegistryFile;
6
+ }
7
+ export declare class FsWorkspaceRepository implements WorkspaceRepository {
8
+ private readonly deps;
9
+ private readonly cache;
10
+ constructor(deps: FsWorkspaceRepositoryDeps);
11
+ list(): Promise<Workspace[]>;
12
+ load(rootPath: AbsolutePath): Promise<Workspace>;
13
+ save(workspace: Workspace): Promise<void>;
14
+ remove(rootPath: AbsolutePath): Promise<void>;
15
+ invalidate(rootPath: AbsolutePath): void;
16
+ private readFromDisk;
17
+ private assertDirectoryExists;
18
+ private readManifest;
19
+ private parseManifest;
20
+ }
21
+ //# sourceMappingURL=FsWorkspaceRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FsWorkspaceRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/FsWorkspaceRepository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAkC,SAAS,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnG,OAAO,EAAE,YAAY,EAAgC,MAAM,iBAAiB,CAAA;AAI5E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAA;CACzC;AAED,qBAAa,qBAAsB,YAAW,mBAAmB;IAGnD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;gBAE9B,IAAI,EAAE,yBAAyB;IAEtD,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAS5B,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAShD,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC,MAAM,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,UAAU,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;YAI1B,YAAY;YAeZ,qBAAqB;YAerB,YAAY;IAY1B,OAAO,CAAC,aAAa;CAOtB"}
@@ -0,0 +1,87 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { NotFoundError, ValidationError, Workspace } from '@braidhq/core';
4
+ import { AbsolutePath, ProductManifest, WorkspaceId } from '@braidhq/schema';
5
+ import { parseMarkdownFrontmatter } from './frontmatter.js';
6
+ import { workspaceProductManifestPath } from './paths.js';
7
+ export class FsWorkspaceRepository {
8
+ deps;
9
+ cache = new Map();
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ }
13
+ async list() {
14
+ const rootPaths = await this.deps.registry.list();
15
+ const workspaces = [];
16
+ for (const rootPath of rootPaths) {
17
+ workspaces.push(await this.load(rootPath));
18
+ }
19
+ return workspaces;
20
+ }
21
+ async load(rootPath) {
22
+ const cached = this.cache.get(rootPath);
23
+ if (cached)
24
+ return cached;
25
+ const workspace = await this.readFromDisk(rootPath);
26
+ this.cache.set(rootPath, workspace);
27
+ return workspace;
28
+ }
29
+ async save(workspace) {
30
+ await this.deps.registry.add(workspace.rootPath);
31
+ this.cache.set(workspace.rootPath, workspace);
32
+ }
33
+ async remove(rootPath) {
34
+ await this.deps.registry.remove(rootPath);
35
+ this.cache.delete(rootPath);
36
+ }
37
+ invalidate(rootPath) {
38
+ this.cache.delete(rootPath);
39
+ }
40
+ async readFromDisk(rootPath) {
41
+ await this.assertDirectoryExists(rootPath);
42
+ const manifestPath = workspaceProductManifestPath(rootPath);
43
+ const raw = await this.readManifest(manifestPath);
44
+ const { frontmatter } = parseMarkdownFrontmatter(raw);
45
+ const productManifest = this.parseManifest(frontmatter, manifestPath);
46
+ const data = {
47
+ id: WorkspaceId.parse(productManifest.name),
48
+ rootPath: AbsolutePath.parse(resolve(rootPath)),
49
+ productManifest,
50
+ pluginConfig: { plugins: [] },
51
+ };
52
+ return new Workspace(data);
53
+ }
54
+ async assertDirectoryExists(rootPath) {
55
+ try {
56
+ const stats = await stat(rootPath);
57
+ if (!stats.isDirectory()) {
58
+ throw new ValidationError(`Workspace path "${rootPath}" is not a directory`);
59
+ }
60
+ }
61
+ catch (error) {
62
+ if (error.code === 'ENOENT') {
63
+ throw new NotFoundError(`Workspace directory "${rootPath}" not found`);
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+ async readManifest(manifestPath) {
69
+ try {
70
+ return await readFile(manifestPath, 'utf-8');
71
+ }
72
+ catch (error) {
73
+ if (error.code === 'ENOENT') {
74
+ throw new NotFoundError(`PRODUCT.md not found at "${manifestPath}"`);
75
+ }
76
+ throw error;
77
+ }
78
+ }
79
+ parseManifest(raw, manifestPath) {
80
+ const parsed = ProductManifest.safeParse(raw);
81
+ if (!parsed.success) {
82
+ throw new ValidationError(`Invalid PRODUCT.md at "${manifestPath}": ${parsed.error.message}`);
83
+ }
84
+ return parsed.data;
85
+ }
86
+ }
87
+ //# sourceMappingURL=FsWorkspaceRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FsWorkspaceRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/FsWorkspaceRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AbsolutePath } from '@braidhq/schema';
2
+ /**
3
+ * Persists the list of registered workspace rootPaths to a JSON file.
4
+ * Default location is `${BRAID_HOME}/workspaces.json` (set by the caller).
5
+ * Acts as the source of truth across server restarts — the in-memory
6
+ * `FsWorkspaceRepository` cache is rebuilt from this file on cold start.
7
+ */
8
+ export declare class WorkspaceRegistryFile {
9
+ private readonly filePath;
10
+ constructor(filePath: string);
11
+ list(): Promise<AbsolutePath[]>;
12
+ add(rootPath: AbsolutePath): Promise<void>;
13
+ remove(rootPath: AbsolutePath): Promise<void>;
14
+ private read;
15
+ private write;
16
+ }
17
+ //# sourceMappingURL=WorkspaceRegistryFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceRegistryFile.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/WorkspaceRegistryFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAanD;;;;;GAKG;AACH,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAEvC,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAK/B,GAAG,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1C,MAAM,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAQrC,IAAI;YAgBJ,KAAK;CAIpB"}
@@ -0,0 +1,58 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { ValidationError } from '@braidhq/core';
4
+ import { AbsolutePath as AbsolutePathSchema } from '@braidhq/schema';
5
+ import { z } from 'zod';
6
+ const RegistryContent = z.object({
7
+ workspaces: z.array(z.object({ rootPath: AbsolutePathSchema })).default([]),
8
+ });
9
+ /**
10
+ * Persists the list of registered workspace rootPaths to a JSON file.
11
+ * Default location is `${BRAID_HOME}/workspaces.json` (set by the caller).
12
+ * Acts as the source of truth across server restarts — the in-memory
13
+ * `FsWorkspaceRepository` cache is rebuilt from this file on cold start.
14
+ */
15
+ export class WorkspaceRegistryFile {
16
+ filePath;
17
+ constructor(filePath) {
18
+ this.filePath = filePath;
19
+ }
20
+ async list() {
21
+ const content = await this.read();
22
+ return content.workspaces.map(entry => entry.rootPath);
23
+ }
24
+ async add(rootPath) {
25
+ const content = await this.read();
26
+ if (content.workspaces.some(entry => entry.rootPath === rootPath))
27
+ return;
28
+ content.workspaces.push({ rootPath });
29
+ await this.write(content);
30
+ }
31
+ async remove(rootPath) {
32
+ const content = await this.read();
33
+ const filtered = content.workspaces.filter(entry => entry.rootPath !== rootPath);
34
+ if (filtered.length === content.workspaces.length)
35
+ return;
36
+ await this.write({ workspaces: filtered });
37
+ }
38
+ async read() {
39
+ try {
40
+ const raw = await readFile(this.filePath, 'utf-8');
41
+ const parsed = RegistryContent.safeParse(JSON.parse(raw));
42
+ if (!parsed.success) {
43
+ throw new ValidationError(`Invalid workspace registry at "${this.filePath}": ${parsed.error.message}`);
44
+ }
45
+ return parsed.data;
46
+ }
47
+ catch (error) {
48
+ if (error.code === 'ENOENT')
49
+ return { workspaces: [] };
50
+ throw error;
51
+ }
52
+ }
53
+ async write(content) {
54
+ await mkdir(dirname(this.filePath), { recursive: true });
55
+ await writeFile(this.filePath, `${JSON.stringify(content, null, 2)}\n`, 'utf-8');
56
+ }
57
+ }
58
+ //# sourceMappingURL=WorkspaceRegistryFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceRegistryFile.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/WorkspaceRegistryFile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface MarkdownDocument<T> {
2
+ readonly frontmatter: T;
3
+ readonly body: string;
4
+ }
5
+ /**
6
+ * Splits a markdown file into YAML frontmatter + body. Wraps the YAML
7
+ * between `---` delimiters on their own lines at the top of the file.
8
+ * Keys are normalised from kebab-case (YAML convention, used by Claude
9
+ * Code itself) to camelCase (TS convention) recursively so Zod schemas
10
+ * can declare fields naturally in TS.
11
+ */
12
+ export declare function parseMarkdownFrontmatter<T>(content: string): MarkdownDocument<T>;
13
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/frontmatter.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAmBhF"}
@@ -0,0 +1,46 @@
1
+ import { parse as parseYaml } from 'yaml';
2
+ const FRONTMATTER_DELIMITER = '---';
3
+ /**
4
+ * Splits a markdown file into YAML frontmatter + body. Wraps the YAML
5
+ * between `---` delimiters on their own lines at the top of the file.
6
+ * Keys are normalised from kebab-case (YAML convention, used by Claude
7
+ * Code itself) to camelCase (TS convention) recursively so Zod schemas
8
+ * can declare fields naturally in TS.
9
+ */
10
+ export function parseMarkdownFrontmatter(content) {
11
+ const lines = content.split('\n');
12
+ if (lines[0]?.trim() !== FRONTMATTER_DELIMITER) {
13
+ throw new Error('Frontmatter must start with "---" on the first line');
14
+ }
15
+ let closingIndex = -1;
16
+ for (let index = 1; index < lines.length; index += 1) {
17
+ if (lines[index]?.trim() === FRONTMATTER_DELIMITER) {
18
+ closingIndex = index;
19
+ break;
20
+ }
21
+ }
22
+ if (closingIndex === -1) {
23
+ throw new Error('Unterminated YAML frontmatter (missing closing "---")');
24
+ }
25
+ const yamlSource = lines.slice(1, closingIndex).join('\n');
26
+ const body = lines.slice(closingIndex + 1).join('\n');
27
+ const raw = parseYaml(yamlSource);
28
+ return { frontmatter: normaliseKeys(raw), body };
29
+ }
30
+ function kebabToCamelCase(key) {
31
+ return key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
32
+ }
33
+ function normaliseKeys(value) {
34
+ if (Array.isArray(value)) {
35
+ return value.map(normaliseKeys);
36
+ }
37
+ if (value !== null && typeof value === 'object') {
38
+ const result = {};
39
+ for (const [key, nested] of Object.entries(value)) {
40
+ result[kebabToCamelCase(key)] = normaliseKeys(nested);
41
+ }
42
+ return result;
43
+ }
44
+ return value;
45
+ }
46
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAA;AAEzC,MAAM,qBAAqB,GAAG,KAAK,CAAA;AAOnC;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAI,OAAe;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,qBAAqB,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IACD,IAAI,YAAY,GAAG,CAAC,CAAC,CAAA;IACrB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,qBAAqB,EAAE,CAAC;YACnD,YAAY,GAAG,KAAK,CAAA;YACpB,MAAK;QACP,CAAC;IACH,CAAC;IACD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAA;IAC1E,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAY,CAAA;IAC5C,OAAO,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,CAAM,EAAE,IAAI,EAAE,CAAA;AACvD,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;AAC1E,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IACjC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAA;QAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;QACvD,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function readJsonFile<T>(filePath: string): Promise<T>;
2
+ export declare function writeJsonFile<T>(filePath: string, content: T): Promise<void>;
3
+ export declare function moveFile(fromPath: string, toPath: string): Promise<void>;
4
+ export declare function listJsonFiles(directory: string): Promise<string[]>;
5
+ //# sourceMappingURL=jsonFileStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonFileStore.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/jsonFileStore.ts"],"names":[],"mappings":"AAGA,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGlE;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlF;AAED,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9E;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAaxE"}
@@ -0,0 +1,29 @@
1
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ export async function readJsonFile(filePath) {
4
+ const raw = await readFile(filePath, 'utf-8');
5
+ return JSON.parse(raw);
6
+ }
7
+ export async function writeJsonFile(filePath, content) {
8
+ await mkdir(dirname(filePath), { recursive: true });
9
+ await writeFile(filePath, `${JSON.stringify(content, null, 2)}\n`, 'utf-8');
10
+ }
11
+ export async function moveFile(fromPath, toPath) {
12
+ await mkdir(dirname(toPath), { recursive: true });
13
+ await rename(fromPath, toPath);
14
+ }
15
+ export async function listJsonFiles(directory) {
16
+ const { readdir } = await import('node:fs/promises');
17
+ try {
18
+ const entries = await readdir(directory, { withFileTypes: true });
19
+ return entries
20
+ .filter(entry => entry.isFile() && entry.name.endsWith('.json'))
21
+ .map(entry => join(directory, entry.name));
22
+ }
23
+ catch (error) {
24
+ if (error.code === 'ENOENT')
25
+ return [];
26
+ throw error;
27
+ }
28
+ }
29
+ //# sourceMappingURL=jsonFileStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonFileStore.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/jsonFileStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,QAAgB;IACpD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAA;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,QAAgB,EAAE,OAAU;IACjE,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACnD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC7E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB,EAAE,MAAc;IAC7D,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACjD,MAAM,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IACpD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACjE,OAAO,OAAO;aACX,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAC/D,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,KAAK,EAAE,CAAC;QACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YACpD,OAAO,EAAE,CAAA;QACX,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { AbsolutePath, ClarifyStatus, ProposalStatus, WorkspaceId } from '@braidhq/schema';
2
+ export declare const PROPOSAL_STATUSES: readonly ProposalStatus[];
3
+ export declare const CLARIFY_STATUSES: readonly ClarifyStatus[];
4
+ export declare function workspaceArtifactsDir(workspaceRoot: AbsolutePath): string;
5
+ export declare function proposalsDir(workspaceRoot: AbsolutePath, status: ProposalStatus): string;
6
+ export declare function clarifyDir(workspaceRoot: AbsolutePath, status: ClarifyStatus): string;
7
+ export declare function decisionsDir(workspaceRoot: AbsolutePath): string;
8
+ export declare function viewsDir(workspaceRoot: AbsolutePath, kind: string): string;
9
+ export declare function runsDir(workspaceRoot: AbsolutePath): string;
10
+ export declare function runIndexPath(workspaceRoot: AbsolutePath): string;
11
+ export declare function runEventsPath(workspaceRoot: AbsolutePath, runId: string): string;
12
+ export declare function sessionsDir(workspaceRoot: AbsolutePath): string;
13
+ export declare function sessionDirPath(workspaceRoot: AbsolutePath, runId: string): string;
14
+ export declare function workspaceSkillsDir(workspaceRoot: AbsolutePath): string;
15
+ export declare function workspaceSkillExtensionsDir(workspaceRoot: AbsolutePath): string;
16
+ export declare function workspaceProductManifestPath(workspaceRoot: AbsolutePath): string;
17
+ export interface WorkspaceLayout {
18
+ readonly root: AbsolutePath;
19
+ readonly workspaceId: WorkspaceId;
20
+ readonly manifestPath: string;
21
+ }
22
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG/F,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAAuC,CAAA;AAC9F,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAAkD,CAAA;AAEvG,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEzE;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAExF;AAED,wBAAgB,UAAU,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEhE;AAED,wBAAgB,QAAQ,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,wBAAgB,OAAO,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEhE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAKD,wBAAgB,WAAW,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEtE;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE/E;AAED,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEhF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B"}
@@ -0,0 +1,46 @@
1
+ import { join } from 'node:path';
2
+ export const PROPOSAL_STATUSES = ['pending', 'applied', 'rejected'];
3
+ export const CLARIFY_STATUSES = ['pending', 'answered', 'applied', 'skipped'];
4
+ export function workspaceArtifactsDir(workspaceRoot) {
5
+ return join(workspaceRoot, 'artifacts');
6
+ }
7
+ export function proposalsDir(workspaceRoot, status) {
8
+ return join(workspaceArtifactsDir(workspaceRoot), 'proposals', status);
9
+ }
10
+ export function clarifyDir(workspaceRoot, status) {
11
+ return join(workspaceArtifactsDir(workspaceRoot), 'clarify', status);
12
+ }
13
+ export function decisionsDir(workspaceRoot) {
14
+ return join(workspaceArtifactsDir(workspaceRoot), 'decisions');
15
+ }
16
+ export function viewsDir(workspaceRoot, kind) {
17
+ return join(workspaceArtifactsDir(workspaceRoot), 'views', kind);
18
+ }
19
+ export function runsDir(workspaceRoot) {
20
+ return join(workspaceArtifactsDir(workspaceRoot), 'runs');
21
+ }
22
+ export function runIndexPath(workspaceRoot) {
23
+ return join(runsDir(workspaceRoot), 'index.jsonl');
24
+ }
25
+ export function runEventsPath(workspaceRoot, runId) {
26
+ return join(runsDir(workspaceRoot), `${runId}.jsonl`);
27
+ }
28
+ // claude stores conversation memory keyed by cwd, so resume must spawn from
29
+ // the same dir as the first turn. Putting it inside the workspace (not /tmp)
30
+ // keeps the path derivable from runId and lets it survive a server restart.
31
+ export function sessionsDir(workspaceRoot) {
32
+ return join(workspaceRoot, '.braid-sessions');
33
+ }
34
+ export function sessionDirPath(workspaceRoot, runId) {
35
+ return join(sessionsDir(workspaceRoot), runId);
36
+ }
37
+ export function workspaceSkillsDir(workspaceRoot) {
38
+ return join(workspaceRoot, 'skills');
39
+ }
40
+ export function workspaceSkillExtensionsDir(workspaceRoot) {
41
+ return join(workspaceRoot, 'skill-extensions');
42
+ }
43
+ export function workspaceProductManifestPath(workspaceRoot) {
44
+ return join(workspaceRoot, 'PRODUCT.md');
45
+ }
46
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/paths.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,MAAM,iBAAiB,GAA8B,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAA6B,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;AAEvG,MAAM,UAAU,qBAAqB,CAAC,aAA2B;IAC/D,OAAO,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,aAA2B,EAAE,MAAsB;IAC9E,OAAO,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;AACxE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,aAA2B,EAAE,MAAqB;IAC3E,OAAO,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AACtE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,aAA2B;IACtD,OAAO,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC,CAAA;AAChE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,aAA2B,EAAE,IAAY;IAChE,OAAO,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,aAA2B;IACjD,OAAO,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,aAA2B;IACtD,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,CAAA;AACpD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,aAA2B,EAAE,KAAa;IACtE,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAA;AACvD,CAAC;AAED,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,aAA2B;IACrD,OAAO,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,aAA2B,EAAE,KAAa;IACvE,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,aAA2B;IAC5D,OAAO,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,aAA2B;IACrE,OAAO,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,aAA2B;IACtE,OAAO,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ProductManifest, ProductManifestDraft } from '@braidhq/schema';
2
+ /**
3
+ * Take a user-provided `ProductManifestDraft` and fill in the structural
4
+ * blocks (agent / storage / channels) so the result is a valid, complete
5
+ * `ProductManifest` Zod will accept. Throws if the user's input itself is
6
+ * inconsistent.
7
+ */
8
+ export declare function fillManifestDefaults(draft: ProductManifestDraft): ProductManifest;
9
+ /**
10
+ * Render a `ProductManifest` as YAML-frontmatter markdown and write it
11
+ * to `<workspaceRoot>/PRODUCT.md`. The parent directory is created if
12
+ * missing; an existing PRODUCT.md is overwritten.
13
+ */
14
+ export declare function writeProductManifest(workspaceRoot: string, manifest: ProductManifest, bodyTitle?: string): Promise<string>;
15
+ /**
16
+ * Replace the YAML frontmatter of an existing PRODUCT.md while preserving the
17
+ * markdown body underneath. Used by workspace mutation endpoints so user-written
18
+ * prose (e.g. project notes added below the frontmatter) survives a manifest
19
+ * update. The file must exist; throws otherwise.
20
+ */
21
+ export declare function updateProductManifest(workspaceRoot: string, manifest: ProductManifest): Promise<string>;
22
+ //# sourceMappingURL=productManifestWriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productManifestWriter.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/fs/productManifestWriter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAMV,eAAe,EACf,oBAAoB,EAGrB,MAAM,iBAAiB,CAAA;AA8BxB;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CAcjF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAShI;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ7G"}
@@ -0,0 +1,75 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { OntologyId, ProductManifest as ProductManifestSchema } from '@braidhq/schema';
4
+ import { stringify as stringifyYaml } from 'yaml';
5
+ import { parseMarkdownFrontmatter } from './frontmatter.js';
6
+ const FRONTMATTER_DELIMITER = '---';
7
+ const DEFAULT_AGENT_BINDING = {
8
+ id: 'claude-default',
9
+ kind: 'claude-code',
10
+ model: 'opus',
11
+ effort: 'high',
12
+ extraArgs: [],
13
+ env: {},
14
+ };
15
+ const DEFAULT_AGENTS = {
16
+ default: 'claude-default',
17
+ tasks: {},
18
+ };
19
+ const DEFAULT_STORAGE = {
20
+ kind: 'kuzu',
21
+ config: {},
22
+ };
23
+ const DEFAULT_CHANNELS = [];
24
+ /**
25
+ * Take a user-provided `ProductManifestDraft` and fill in the structural
26
+ * blocks (agent / storage / channels) so the result is a valid, complete
27
+ * `ProductManifest` Zod will accept. Throws if the user's input itself is
28
+ * inconsistent.
29
+ */
30
+ export function fillManifestDefaults(draft) {
31
+ const manifest = {
32
+ name: draft.name,
33
+ version: draft.version ?? '0.1.0',
34
+ ...(draft.description ? { description: draft.description } : {}),
35
+ ontologyId: draft.ontologyId ?? OntologyId.parse('ddd'),
36
+ sources: draft.sources,
37
+ mcpServers: draft.mcpServers,
38
+ agents: draft.agents ?? DEFAULT_AGENTS,
39
+ agentBindings: draft.agentBindings ?? [DEFAULT_AGENT_BINDING],
40
+ storage: draft.storage ?? DEFAULT_STORAGE,
41
+ channels: draft.channels ?? [...DEFAULT_CHANNELS],
42
+ };
43
+ return ProductManifestSchema.parse(manifest);
44
+ }
45
+ /**
46
+ * Render a `ProductManifest` as YAML-frontmatter markdown and write it
47
+ * to `<workspaceRoot>/PRODUCT.md`. The parent directory is created if
48
+ * missing; an existing PRODUCT.md is overwritten.
49
+ */
50
+ export async function writeProductManifest(workspaceRoot, manifest, bodyTitle) {
51
+ await mkdir(workspaceRoot, { recursive: true });
52
+ const yaml = stringifyYaml(manifest, { sortMapEntries: false }).trimEnd();
53
+ const heading = bodyTitle ?? manifest.name;
54
+ const body = `\n# ${heading}\n`;
55
+ const content = `${FRONTMATTER_DELIMITER}\n${yaml}\n${FRONTMATTER_DELIMITER}\n${body}`;
56
+ const path = join(workspaceRoot, 'PRODUCT.md');
57
+ await writeFile(path, content, 'utf-8');
58
+ return path;
59
+ }
60
+ /**
61
+ * Replace the YAML frontmatter of an existing PRODUCT.md while preserving the
62
+ * markdown body underneath. Used by workspace mutation endpoints so user-written
63
+ * prose (e.g. project notes added below the frontmatter) survives a manifest
64
+ * update. The file must exist; throws otherwise.
65
+ */
66
+ export async function updateProductManifest(workspaceRoot, manifest) {
67
+ const path = join(workspaceRoot, 'PRODUCT.md');
68
+ const existing = await readFile(path, 'utf-8');
69
+ const { body } = parseMarkdownFrontmatter(existing);
70
+ const yaml = stringifyYaml(manifest, { sortMapEntries: false }).trimEnd();
71
+ const content = `${FRONTMATTER_DELIMITER}\n${yaml}\n${FRONTMATTER_DELIMITER}\n${body}`;
72
+ await writeFile(path, content, 'utf-8');
73
+ return path;
74
+ }
75
+ //# sourceMappingURL=productManifestWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productManifestWriter.js","sourceRoot":"","sources":["../../../src/infrastructure/fs/productManifestWriter.ts"],"names":[],"mappings":"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"}