studiograph 1.2.0-beta.9 → 1.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 (226) hide show
  1. package/README.md +13 -18
  2. package/dist/agent/orchestrator.d.ts +56 -0
  3. package/dist/agent/orchestrator.js +167 -22
  4. package/dist/agent/orchestrator.js.map +1 -1
  5. package/dist/agent/prompts/system.md +51 -63
  6. package/dist/agent/skill-loader.js +8 -0
  7. package/dist/agent/skill-loader.js.map +1 -1
  8. package/dist/agent/skills/entity-schema.md +4 -19
  9. package/dist/agent/skills/gather-context.md +1 -1
  10. package/dist/agent/tools/graph-tools.d.ts +41 -1
  11. package/dist/agent/tools/graph-tools.js +133 -57
  12. package/dist/agent/tools/graph-tools.js.map +1 -1
  13. package/dist/agent/tools/ops-tools.js +82 -0
  14. package/dist/agent/tools/ops-tools.js.map +1 -1
  15. package/dist/agent/tools/permission-tools.d.ts +90 -0
  16. package/dist/agent/tools/permission-tools.js +207 -0
  17. package/dist/agent/tools/permission-tools.js.map +1 -0
  18. package/dist/agent/tools/sync-tools.js +2 -2
  19. package/dist/agent/tools/sync-tools.js.map +1 -1
  20. package/dist/auth/github.d.ts +1 -1
  21. package/dist/auth/github.js +21 -13
  22. package/dist/auth/github.js.map +1 -1
  23. package/dist/cli/commands/access.d.ts +11 -0
  24. package/dist/cli/commands/access.js +156 -0
  25. package/dist/cli/commands/access.js.map +1 -0
  26. package/dist/cli/commands/app.js +25 -2
  27. package/dist/cli/commands/app.js.map +1 -1
  28. package/dist/cli/commands/clear.d.ts +5 -0
  29. package/dist/cli/commands/clear.js +36 -0
  30. package/dist/cli/commands/clear.js.map +1 -0
  31. package/dist/cli/commands/clone.js +3 -3
  32. package/dist/cli/commands/clone.js.map +1 -1
  33. package/dist/cli/commands/collection.d.ts +10 -0
  34. package/dist/cli/commands/collection.js +187 -0
  35. package/dist/cli/commands/collection.js.map +1 -0
  36. package/dist/cli/commands/commit.js +2 -1
  37. package/dist/cli/commands/commit.js.map +1 -1
  38. package/dist/cli/commands/config.d.ts +100 -0
  39. package/dist/cli/commands/config.js +1 -1
  40. package/dist/cli/commands/config.js.map +1 -1
  41. package/dist/cli/commands/deploy.js +10 -1
  42. package/dist/cli/commands/deploy.js.map +1 -1
  43. package/dist/cli/commands/index.js +1 -1
  44. package/dist/cli/commands/index.js.map +1 -1
  45. package/dist/cli/commands/init.js +32 -40
  46. package/dist/cli/commands/init.js.map +1 -1
  47. package/dist/cli/commands/join.d.ts +0 -6
  48. package/dist/cli/commands/join.js +26 -111
  49. package/dist/cli/commands/join.js.map +1 -1
  50. package/dist/cli/commands/lint.js +1 -2
  51. package/dist/cli/commands/lint.js.map +1 -1
  52. package/dist/cli/commands/mcp.js +6 -5
  53. package/dist/cli/commands/mcp.js.map +1 -1
  54. package/dist/cli/commands/org.d.ts +10 -0
  55. package/dist/cli/commands/org.js +132 -0
  56. package/dist/cli/commands/org.js.map +1 -0
  57. package/dist/cli/commands/orphans.js +1 -2
  58. package/dist/cli/commands/orphans.js.map +1 -1
  59. package/dist/cli/commands/provision.js +3 -3
  60. package/dist/cli/commands/provision.js.map +1 -1
  61. package/dist/cli/commands/resolve.d.ts +8 -0
  62. package/dist/cli/commands/resolve.js +85 -0
  63. package/dist/cli/commands/resolve.js.map +1 -0
  64. package/dist/cli/commands/serve.js +55 -25
  65. package/dist/cli/commands/serve.js.map +1 -1
  66. package/dist/cli/commands/start.js +22 -205
  67. package/dist/cli/commands/start.js.map +1 -1
  68. package/dist/cli/commands/sync.js +53 -8
  69. package/dist/cli/commands/sync.js.map +1 -1
  70. package/dist/cli/commands/team.d.ts +12 -0
  71. package/dist/cli/commands/team.js +185 -0
  72. package/dist/cli/commands/team.js.map +1 -0
  73. package/dist/cli/index.js +41 -34
  74. package/dist/cli/index.js.map +1 -1
  75. package/dist/cli/scaffolding.d.ts +5 -3
  76. package/dist/cli/scaffolding.js +191 -97
  77. package/dist/cli/scaffolding.js.map +1 -1
  78. package/dist/cli/setup-wizard.js +20 -10
  79. package/dist/cli/setup-wizard.js.map +1 -1
  80. package/dist/cli/sync-review-interactive.js +1 -1
  81. package/dist/cli/sync-review-interactive.js.map +1 -1
  82. package/dist/core/graph.d.ts +5 -10
  83. package/dist/core/graph.js +84 -85
  84. package/dist/core/graph.js.map +1 -1
  85. package/dist/core/migration-runner.d.ts +42 -0
  86. package/dist/core/migration-runner.js +232 -0
  87. package/dist/core/migration-runner.js.map +1 -0
  88. package/dist/core/migration-types.d.ts +101 -0
  89. package/dist/core/migration-types.js +21 -0
  90. package/dist/core/migration-types.js.map +1 -0
  91. package/dist/core/migrations/20260219-formalize-memory-location.d.ts +2 -0
  92. package/dist/core/migrations/20260219-formalize-memory-location.js +35 -0
  93. package/dist/core/migrations/20260219-formalize-memory-location.js.map +1 -0
  94. package/dist/core/migrations/20260220-add-workspace-metadata.d.ts +12 -0
  95. package/dist/core/migrations/20260220-add-workspace-metadata.js +65 -0
  96. package/dist/core/migrations/20260220-add-workspace-metadata.js.map +1 -0
  97. package/dist/core/migrations/20260220-add-workspace-readme.d.ts +11 -0
  98. package/dist/core/migrations/20260220-add-workspace-readme.js +82 -0
  99. package/dist/core/migrations/20260220-add-workspace-readme.js.map +1 -0
  100. package/dist/core/migrations/20260220-migrate-yaml-to-json.d.ts +9 -0
  101. package/dist/core/migrations/20260220-migrate-yaml-to-json.js +64 -0
  102. package/dist/core/migrations/20260220-migrate-yaml-to-json.js.map +1 -0
  103. package/dist/core/migrations/index.d.ts +11 -0
  104. package/dist/core/migrations/index.js +23 -0
  105. package/dist/core/migrations/index.js.map +1 -0
  106. package/dist/core/types.d.ts +19 -34
  107. package/dist/core/types.js +3 -21
  108. package/dist/core/types.js.map +1 -1
  109. package/dist/core/user-config.d.ts +27 -0
  110. package/dist/core/user-config.js +51 -0
  111. package/dist/core/user-config.js.map +1 -1
  112. package/dist/core/validation.d.ts +978 -2182
  113. package/dist/core/validation.js +242 -397
  114. package/dist/core/validation.js.map +1 -1
  115. package/dist/core/workspace-manager.d.ts +32 -31
  116. package/dist/core/workspace-manager.js +171 -186
  117. package/dist/core/workspace-manager.js.map +1 -1
  118. package/dist/core/workspace.d.ts +0 -5
  119. package/dist/core/workspace.js +33 -101
  120. package/dist/core/workspace.js.map +1 -1
  121. package/dist/lib/lib/utils.d.ts +2 -0
  122. package/dist/lib/lib/utils.js +6 -0
  123. package/dist/lib/lib/utils.js.map +1 -0
  124. package/dist/mcp/tools.js +13 -13
  125. package/dist/mcp/tools.js.map +1 -1
  126. package/dist/server/chrome/chrome.css +562 -0
  127. package/dist/server/chrome/chrome.js +540 -0
  128. package/dist/server/index.js +163 -10
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/server/plugin-loader.d.ts +7 -0
  131. package/dist/server/plugin-loader.js +9 -1
  132. package/dist/server/plugin-loader.js.map +1 -1
  133. package/dist/server/routes/chat.d.ts +3 -2
  134. package/dist/server/routes/chat.js +67 -16
  135. package/dist/server/routes/chat.js.map +1 -1
  136. package/dist/server/routes/git-api.d.ts +9 -0
  137. package/dist/server/routes/git-api.js +82 -0
  138. package/dist/server/routes/git-api.js.map +1 -0
  139. package/dist/server/routes/graph-api.d.ts +2 -2
  140. package/dist/server/routes/graph-api.js +267 -3
  141. package/dist/server/routes/graph-api.js.map +1 -1
  142. package/dist/server/routes/permissions-api.d.ts +9 -0
  143. package/dist/server/routes/permissions-api.js +176 -0
  144. package/dist/server/routes/permissions-api.js.map +1 -0
  145. package/dist/server/routes/workspace-api.d.ts +9 -0
  146. package/dist/server/routes/workspace-api.js +283 -0
  147. package/dist/server/routes/workspace-api.js.map +1 -0
  148. package/dist/services/assets/base.d.ts +2 -2
  149. package/dist/services/assets/base.js +4 -4
  150. package/dist/services/assets/base.js.map +1 -1
  151. package/dist/services/assets/index.d.ts +6 -6
  152. package/dist/services/assets/index.js +12 -12
  153. package/dist/services/assets/index.js.map +1 -1
  154. package/dist/services/git.d.ts +35 -0
  155. package/dist/services/git.js +93 -0
  156. package/dist/services/git.js.map +1 -1
  157. package/dist/services/github-provisioner.js +1 -2
  158. package/dist/services/github-provisioner.js.map +1 -1
  159. package/dist/services/github-service.d.ts +99 -0
  160. package/dist/services/github-service.js +310 -0
  161. package/dist/services/github-service.js.map +1 -0
  162. package/dist/services/markdown.js +4 -9
  163. package/dist/services/markdown.js.map +1 -1
  164. package/dist/services/memory-service.d.ts +4 -0
  165. package/dist/services/memory-service.js +13 -1
  166. package/dist/services/memory-service.js.map +1 -1
  167. package/dist/services/sync/commit.d.ts +2 -0
  168. package/dist/services/sync/commit.js +21 -6
  169. package/dist/services/sync/commit.js.map +1 -1
  170. package/dist/services/sync/graphrag-indexer.js +2 -2
  171. package/dist/services/sync/graphrag-indexer.js.map +1 -1
  172. package/dist/utils/git.d.ts +5 -0
  173. package/dist/utils/git.js +10 -0
  174. package/dist/utils/git.js.map +1 -1
  175. package/dist/utils/merge-resolver.d.ts +33 -8
  176. package/dist/utils/merge-resolver.js +157 -55
  177. package/dist/utils/merge-resolver.js.map +1 -1
  178. package/dist/utils/preflight.d.ts +1 -1
  179. package/dist/utils/preflight.js +1 -1
  180. package/dist/web/_app/env.js +1 -0
  181. package/dist/web/_app/immutable/assets/0.CDbX4Cwz.css +1 -0
  182. package/dist/web/_app/immutable/assets/2.DRHi7ABa.css +1 -0
  183. package/dist/web/_app/immutable/assets/3.BJy7pVXi.css +1 -0
  184. package/dist/web/_app/immutable/assets/4.Ad16uh9o.css +1 -0
  185. package/dist/web/_app/immutable/assets/5.BhKgiXd2.css +1 -0
  186. package/dist/web/_app/immutable/assets/6.CeHKR5ZY.css +1 -0
  187. package/dist/web/_app/immutable/assets/AppShell.CJtUfb0N.css +1 -0
  188. package/dist/web/_app/immutable/assets/ChatPanel.RFD5GGYI.css +1 -0
  189. package/dist/web/_app/immutable/assets/editor.CPAf2SRV.css +1 -0
  190. package/dist/web/_app/immutable/chunks/3_5VIr68.js +1 -0
  191. package/dist/web/_app/immutable/chunks/4QY4j-jX.js +1 -0
  192. package/dist/web/_app/immutable/chunks/BB_5th5W.js +3383 -0
  193. package/dist/web/_app/immutable/chunks/BButMJ6M.js +1 -0
  194. package/dist/web/_app/immutable/chunks/BiubvAUI.js +2 -0
  195. package/dist/web/_app/immutable/chunks/CAXuTUkp.js +1 -0
  196. package/dist/web/_app/immutable/chunks/CQENNNNl.js +6 -0
  197. package/dist/web/_app/immutable/chunks/CUzqHQY_.js +1 -0
  198. package/dist/web/_app/immutable/chunks/CV8ganSj.js +23 -0
  199. package/dist/web/_app/immutable/chunks/CYrVHOHA.js +1 -0
  200. package/dist/web/_app/immutable/chunks/CqkleIqs.js +1 -0
  201. package/dist/web/_app/immutable/chunks/DKyztuK9.js +1 -0
  202. package/dist/web/_app/immutable/chunks/DdNsM6oV.js +64 -0
  203. package/dist/web/_app/immutable/chunks/Dh_H7Owr.js +18 -0
  204. package/dist/web/_app/immutable/chunks/DnlgZ_Tk.js +5 -0
  205. package/dist/web/_app/immutable/chunks/DtVH--hH.js +6 -0
  206. package/dist/web/_app/immutable/chunks/F20aIBpJ.js +1 -0
  207. package/dist/web/_app/immutable/chunks/PPVm8Dsz.js +1 -0
  208. package/dist/web/_app/immutable/chunks/WSUKABI_.js +1 -0
  209. package/dist/web/_app/immutable/chunks/YFT1281h.js +2 -0
  210. package/dist/web/_app/immutable/chunks/aosHekRC.js +1 -0
  211. package/dist/web/_app/immutable/chunks/fwnBoL5x.js +1 -0
  212. package/dist/web/_app/immutable/chunks/hHxe9oXh.js +1 -0
  213. package/dist/web/_app/immutable/entry/app.BFVUP2fV.js +2 -0
  214. package/dist/web/_app/immutable/entry/start.BqsXHYP6.js +1 -0
  215. package/dist/web/_app/immutable/nodes/0.DQ5KdeNU.js +1 -0
  216. package/dist/web/_app/immutable/nodes/1.DwnzyOvm.js +1 -0
  217. package/dist/web/_app/immutable/nodes/2.FZxrWJeh.js +1 -0
  218. package/dist/web/_app/immutable/nodes/3.CQVWlbKr.js +1 -0
  219. package/dist/web/_app/immutable/nodes/4.CEh40Z_h.js +16 -0
  220. package/dist/web/_app/immutable/nodes/5.D_QsCLEf.js +4 -0
  221. package/dist/web/_app/immutable/nodes/6.CndEQ0o5.js +2 -0
  222. package/dist/web/_app/version.json +1 -0
  223. package/dist/web/index.html +43 -0
  224. package/package.json +21 -3
  225. package/dist/agent/skills/bundled/enrich-entities.md +0 -124
  226. package/dist/agent/skills/bundled/gather-context.md +0 -46
@@ -1,8 +1,13 @@
1
1
  /**
2
2
  * Entity validation schemas for Studiograph
3
3
  *
4
- * Based on real-world templates from Schema Design's Obsidian vault,
5
- * generalized for use by any creative service firm.
4
+ * 17 built-in entity types organized into three categories:
5
+ * - Identity (4): person, organization, role, practice-area
6
+ * - Universal (10): project, meeting, decision, task, document, dataset, reference, note, deliverable, publication
7
+ * - Knowledge (3): process, standard, case-study
8
+ *
9
+ * App plugins register additional types at runtime via SchemaRegistry.
10
+ * Teams can define custom types via schema_extensions in workspace config.
6
11
  */
7
12
  import { z } from 'zod';
8
13
  /**
@@ -17,305 +22,255 @@ export const EntityBaseSchema = z.object({
17
22
  updated_at: z.string().datetime('Invalid datetime format'),
18
23
  created_by: z.string().min(1, 'Created by is required'),
19
24
  updated_by: z.string().min(1, 'Updated by is required'),
20
- visibility: z.enum(['public', 'team', 'admin', 'restricted'])
21
- .default('team')
22
- .describe('Access level for this entity'),
23
- restricted_to: z.array(z.string())
24
- .optional()
25
- .describe('User IDs with access (if visibility=restricted)'),
26
- sensitive_fields: z.array(z.string())
27
- .optional()
28
- .describe('Fields that contain sensitive data'),
29
25
  tags: z.array(z.string())
30
26
  .optional()
31
27
  .describe('Tags for categorization'),
32
28
  }).passthrough(); // Allow custom fields beyond the base schema
29
+ // ══════════════════════════════════════════════════════════════════════════════
30
+ // IDENTITY ENTITIES — people, organizations, roles, practice areas
31
+ // ══════════════════════════════════════════════════════════════════════════════
32
+ /**
33
+ * Person — team members, client contacts, any individual
34
+ */
35
+ export const PersonSchema = EntityBaseSchema.extend({
36
+ entity_type: z.literal('person'),
37
+ name: z.string().min(1, 'Person name is required'),
38
+ role: z.string().optional().describe('Role or title'),
39
+ status: z.enum(['current', 'former', 'contractor', 'client', 'partner'])
40
+ .optional()
41
+ .describe('Relationship status'),
42
+ email: z.string().optional().describe('Email address'),
43
+ location: z.string().optional().describe('Location'),
44
+ organization: z.string().optional().describe('Organization wikilink'),
45
+ bio: z.string().optional().describe('Biography (markdown)'),
46
+ projects: z.array(z.string()).optional().describe('Related project wikilinks'),
47
+ reports_to: z.string().optional().describe('Manager or role wikilink'),
48
+ department: z.string().optional().describe('Department'),
49
+ });
50
+ /**
51
+ * Organization — clients, vendors, partners, prospects, industry bodies
52
+ * Replaces the former separate client and vendor types.
53
+ */
54
+ export const OrganizationSchema = EntityBaseSchema.extend({
55
+ entity_type: z.literal('organization'),
56
+ name: z.string().min(1, 'Organization name is required'),
57
+ type: z.enum(['client', 'vendor', 'partner', 'prospect', 'industry-body', 'other'])
58
+ .optional()
59
+ .describe('Relationship type'),
60
+ status: z.enum(['active', 'inactive', 'prospect', 'former'])
61
+ .default('active')
62
+ .describe('Organization status'),
63
+ industry: z.string().optional().describe('Industry sector'),
64
+ website: z.string().url().optional().describe('Website URL'),
65
+ location: z.string().optional().describe('Primary location'),
66
+ size: z.enum(['small', 'medium', 'large', 'enterprise'])
67
+ .optional()
68
+ .describe('Organization size'),
69
+ contacts: z.array(z.string()).optional().describe('Contact person wikilinks'),
70
+ projects: z.array(z.string()).optional().describe('Related project wikilinks'),
71
+ relationship_start: z.string().optional().describe('When the relationship started'),
72
+ });
33
73
  /**
34
- * PROJECT REPO ENTITIES
35
- * Used in project-specific repositories (one per client/project)
74
+ * Role role definitions from the firm's leveling framework
36
75
  */
76
+ export const RoleSchema = EntityBaseSchema.extend({
77
+ entity_type: z.literal('role'),
78
+ title: z.string().min(1, 'Role title is required'),
79
+ department: z.string().optional().describe('Department'),
80
+ level: z.number().optional().describe('Level 1–5 from leveling framework'),
81
+ type: z.enum(['staff', 'contractor', 'intern'])
82
+ .optional()
83
+ .describe('Employment type'),
84
+ status: z.enum(['active', 'archived'])
85
+ .default('active')
86
+ .describe('Role status'),
87
+ location: z.string().optional().describe('Location'),
88
+ reports_to: z.string().optional().describe('Parent role or person wikilink'),
89
+ });
37
90
  /**
38
- * Project entity - represents a client engagement or internal initiative
91
+ * Practice Area firm capabilities and disciplines
92
+ */
93
+ export const PracticeAreaSchema = EntityBaseSchema.extend({
94
+ entity_type: z.literal('practice-area'),
95
+ name: z.string().min(1, 'Practice area name is required'),
96
+ description: z.string().optional().describe('Overview of the discipline'),
97
+ capabilities: z.array(z.string()).optional().describe('Specific service offerings'),
98
+ related_case_studies: z.array(z.string()).optional().describe('Case study wikilinks'),
99
+ related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
100
+ });
101
+ // ══════════════════════════════════════════════════════════════════════════════
102
+ // UNIVERSAL ENTITIES — available in any repo
103
+ // ══════════════════════════════════════════════════════════════════════════════
104
+ /**
105
+ * Project — a client engagement or internal initiative
106
+ * Subtypes via `type` field: client-project, internal-initiative, event, research
39
107
  */
40
108
  export const ProjectSchema = EntityBaseSchema.extend({
41
109
  entity_type: z.literal('project'),
42
110
  name: z.string().min(1, 'Project name is required'),
43
- client: z.string().optional().describe('Client name or wikilink'),
111
+ type: z.string().optional().describe('Project subtype (e.g., client-project, internal-initiative, event, research)'),
112
+ client: z.string().optional().describe('Client organization wikilink'),
44
113
  website_url: z.string().url().optional().describe('Project website URL'),
45
- start_date: z.string().optional().describe('Project start date (ISO 8601 or human-readable)'),
114
+ start_date: z.string().optional().describe('Project start date'),
46
115
  end_date: z.string().optional().describe('Project end date'),
47
- target_completion: z.string().optional().describe('Target completion date'),
48
- year_released: z.number().optional().describe('Year the project was released'),
49
116
  status: z.enum(['planning', 'scoping', 'active', 'on-hold', 'completed', 'archived'])
50
117
  .optional()
51
118
  .describe('Current project status'),
52
119
  related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
53
- related_areas: z.array(z.string()).optional().describe('Practice areas (e.g., [[Data Visualization]])'),
120
+ related_areas: z.array(z.string()).optional().describe('Practice area wikilinks'),
54
121
  team: z.array(z.string()).optional().describe('Team member wikilinks'),
55
- deliverables: z.array(z.string()).optional().describe('Project deliverables'),
122
+ deliverables: z.array(z.string()).optional().describe('Deliverable wikilinks'),
56
123
  deal: z.string().optional().describe('Originating deal wikilink'),
57
124
  });
58
125
  /**
59
- * Meeting entity - captures meeting notes, decisions, and action items
126
+ * Meeting any scheduled gathering
127
+ * Subtypes via `type` field: internal, client, workshop, conference, industry-event
60
128
  */
61
129
  export const MeetingSchema = EntityBaseSchema.extend({
62
130
  entity_type: z.literal('meeting'),
63
131
  name: z.string().min(1, 'Meeting name/subject is required'),
64
- date: z.string().optional().describe('Meeting date (ISO 8601 or human-readable)'),
65
- attendees: z.array(z.string()).optional().describe('Attendee names or wikilinks'),
132
+ type: z.string().optional().describe('Meeting subtype (e.g., internal, client, workshop, conference, industry-event)'),
133
+ date: z.string().optional().describe('Meeting date'),
134
+ attendees: z.array(z.string()).optional().describe('Attendee wikilinks'),
66
135
  related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
67
136
  related_organizations: z.array(z.string()).optional().describe('Related organization wikilinks'),
68
137
  related_areas: z.array(z.string()).optional().describe('Practice area wikilinks'),
69
138
  agenda: z.string().optional().describe('Meeting agenda (markdown)'),
70
- notes: z.string().optional().describe('Meeting notes (markdown)'),
71
139
  action_items: z.array(z.string()).optional().describe('Action items from the meeting'),
72
140
  summary: z.string().optional().describe('Meeting summary'),
73
141
  });
74
142
  /**
75
- * Decision entity - structured decision records (ADR-style)
76
- * Format: DEC-YYYY-NNN
143
+ * Decision structured decision records (ADR-style)
77
144
  */
78
145
  export const DecisionSchema = EntityBaseSchema.extend({
79
146
  entity_type: z.literal('decision'),
80
- id: z.string()
81
- .regex(/^DEC-\d{4}-\d{3}$/, 'Decision ID must be in format DEC-YYYY-NNN')
82
- .describe('Unique decision identifier'),
83
147
  title: z.string().min(1, 'Decision title is required'),
84
- date: z.string().describe('Decision date (ISO 8601 or human-readable)'),
148
+ date: z.string().optional().describe('Decision date'),
85
149
  status: z.enum(['proposed', 'accepted', 'rejected', 'deprecated', 'superseded'])
86
150
  .default('proposed')
87
- .describe('Current status of the decision'),
88
- decision_makers: z.array(z.string())
89
- .optional()
90
- .describe('People who made the decision'),
91
- stakeholders: z.array(z.string())
92
- .optional()
93
- .describe('People affected by the decision'),
94
- related_decisions: z.array(z.string())
95
- .optional()
96
- .describe('Related decision wikilinks'),
97
- supersedes: z.string()
98
- .optional()
99
- .describe('Decision this one replaces (wikilink)'),
100
- superseded_by: z.string()
101
- .optional()
102
- .describe('Decision that replaces this one (wikilink)'),
103
- context: z.string()
104
- .optional()
105
- .describe('Background and why this decision was needed'),
106
- alternatives: z.string()
107
- .optional()
108
- .describe('Alternatives considered (markdown)'),
109
- decision: z.string()
110
- .optional()
111
- .describe('The actual decision made'),
112
- rationale: z.string()
113
- .optional()
114
- .describe('Why this decision was chosen'),
115
- consequences: z.string()
116
- .optional()
117
- .describe('Positive/negative outcomes and risks'),
118
- enables: z.array(z.string())
119
- .optional()
120
- .describe('Decisions this one enables'),
151
+ .describe('Current status'),
152
+ decision_makers: z.array(z.string()).optional().describe('People who made the decision'),
153
+ stakeholders: z.array(z.string()).optional().describe('People affected'),
154
+ context: z.string().optional().describe('Background and why this decision was needed'),
155
+ alternatives: z.string().optional().describe('Alternatives considered (markdown)'),
156
+ decision: z.string().optional().describe('The actual decision made'),
157
+ rationale: z.string().optional().describe('Why this decision was chosen'),
158
+ consequences: z.string().optional().describe('Outcomes and risks'),
159
+ supersedes: z.string().optional().describe('Decision this one replaces (wikilink)'),
160
+ superseded_by: z.string().optional().describe('Decision that replaces this one (wikilink)'),
121
161
  });
122
162
  /**
123
- * Task entity - lightweight action items and to-dos
163
+ * Task lightweight action items and to-dos
124
164
  */
125
165
  export const TaskSchema = EntityBaseSchema.extend({
126
166
  entity_type: z.literal('task'),
127
167
  name: z.string().min(1, 'Task name is required'),
128
- assignee: z.string()
129
- .optional()
130
- .describe('Person assigned to this task (wikilink)'),
131
- due_date: z.string()
132
- .optional()
133
- .describe('Due date (ISO 8601 or human-readable)'),
168
+ assignee: z.string().optional().describe('Person assigned (wikilink)'),
169
+ due_date: z.string().optional().describe('Due date'),
134
170
  status: z.enum(['open', 'in-progress', 'done', 'cancelled'])
135
171
  .default('open')
136
- .describe('Current status of the task'),
172
+ .describe('Task status'),
137
173
  priority: z.enum(['low', 'medium', 'high', 'urgent'])
138
174
  .optional()
139
- .describe('Task priority level'),
140
- project: z.string()
141
- .optional()
142
- .describe('Related project (wikilink)'),
143
- deliverable: z.string()
144
- .optional()
145
- .describe('Related deliverable (wikilink)'),
146
- related_meeting: z.string()
147
- .optional()
148
- .describe('Meeting this task came from (wikilink)'),
149
- description: z.string()
150
- .optional()
151
- .describe('Detailed description of the task'),
175
+ .describe('Priority level'),
176
+ project: z.string().optional().describe('Related project (wikilink)'),
177
+ deliverable: z.string().optional().describe('Related deliverable (wikilink)'),
178
+ related_meeting: z.string().optional().describe('Meeting this task came from (wikilink)'),
179
+ description: z.string().optional().describe('Detailed description'),
152
180
  });
153
181
  /**
154
- * Brief entity - project briefs and requirements
182
+ * Document any authored document
183
+ * Subtypes via `type` field: proposal, contract, brief, memo, financial-plan, template, report
184
+ * Absorbs the former separate proposal, contract, brief, financial-plan, and template types.
155
185
  */
156
- export const BriefSchema = EntityBaseSchema.extend({
157
- entity_type: z.literal('brief'),
158
- project_name: z.string().min(1, 'Project name is required'),
159
- client: z.string().optional().describe('Client name or wikilink'),
160
- start_date: z.string().optional().describe('Project start date'),
161
- target_completion: z.string().optional().describe('Target completion date'),
162
- status: z.enum(['planning', 'approved', 'in-progress', 'completed'])
163
- .default('planning')
164
- .describe('Brief status'),
165
- objectives: z.array(z.string()).optional().describe('Project objectives'),
166
- target_users: z.string().optional().describe('Target users/audience'),
167
- success_criteria: z.array(z.string()).optional().describe('Success criteria'),
168
- constraints: z.string().optional().describe('Constraints (timeline, budget, technical)'),
169
- stakeholders: z.array(z.string()).optional().describe('Key stakeholder wikilinks'),
170
- deliverables: z.array(z.string()).optional().describe('Expected deliverables'),
186
+ export const DocumentSchema = EntityBaseSchema.extend({
187
+ entity_type: z.literal('document'),
188
+ name: z.string().min(1, 'Document name is required'),
189
+ type: z.string().optional().describe('Document subtype (e.g., proposal, contract, brief, memo, financial-plan, template, report)'),
190
+ client: z.string().optional().describe('Client organization wikilink'),
191
+ project: z.string().optional().describe('Related project wikilink'),
192
+ date: z.string().optional().describe('Document date'),
193
+ version: z.string().optional().describe('Version (e.g., v1, v2)'),
194
+ status: z.enum(['draft', 'sent', 'accepted', 'rejected', 'negotiating', 'signed', 'completed', 'terminated'])
195
+ .default('draft')
196
+ .describe('Document status'),
197
+ description: z.string().optional().describe('Description or summary'),
198
+ });
199
+ /**
200
+ * Dataset — structured tabular data with typed columns and CSV body
201
+ */
202
+ export const ColumnDefinitionSchema = z.object({
203
+ name: z.string().min(1),
204
+ type: z.enum(['string', 'number', 'boolean', 'date']),
205
+ required: z.boolean().optional(),
206
+ description: z.string().optional(),
207
+ });
208
+ export const DatasetSchema = EntityBaseSchema.extend({
209
+ entity_type: z.literal('dataset'),
210
+ name: z.string().min(1, 'Dataset name is required'),
211
+ schema: z.array(ColumnDefinitionSchema).min(1, 'At least one column required'),
212
+ });
213
+ /**
214
+ * Reference — external resources and inspiration
215
+ * Subtypes via `type` field: article, video, tool, technique, framework, website
216
+ * Absorbs the former separate technique type.
217
+ */
218
+ export const ReferenceSchema = EntityBaseSchema.extend({
219
+ entity_type: z.literal('reference'),
220
+ name: z.string().min(1, 'Reference name is required'),
221
+ type: z.string().optional().describe('Reference subtype (e.g., article, video, tool, technique, framework, website)'),
222
+ date: z.string().optional().describe('Publication date'),
223
+ source: z.string().optional().describe('Source/author'),
224
+ link: z.string().optional().describe('URL'),
171
225
  related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
226
+ related_areas: z.array(z.string()).optional().describe('Related practice areas'),
227
+ description: z.string().optional().describe('Description'),
172
228
  });
173
229
  /**
174
- * Artifact entity - deliverables and project outputs
230
+ * Note freeform thinking, research, scratch, daily notes
231
+ * Lightweight entity with minimal schema. Participates in the graph like any other entity.
175
232
  */
176
- export const ArtifactSchema = EntityBaseSchema.extend({
177
- entity_type: z.literal('artifact'),
178
- name: z.string().min(1, 'Artifact name is required'),
179
- type: z.string()
180
- .optional()
181
- .describe('Artifact type (e.g., presentation, prototype, report, design-file)'),
182
- project: z.string().optional().describe('Related project wikilink'),
183
- deliverable: z.string().optional().describe('Parent deliverable wikilink'),
184
- date_created: z.string().optional().describe('Creation date'),
185
- description: z.string().optional().describe('Artifact description'),
186
- url: z.string().optional().describe('URL or file path'),
187
- version: z.string().optional().describe('Version number'),
233
+ export const NoteSchema = EntityBaseSchema.extend({
234
+ entity_type: z.literal('note'),
235
+ title: z.string().optional().describe('Note title'),
236
+ type: z.string().optional().describe('Note subtype (e.g., research, scratch, daily, brainstorm, draft)'),
237
+ related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
238
+ related_organizations: z.array(z.string()).optional().describe('Related organization wikilinks'),
188
239
  });
189
240
  /**
190
- * Deliverable entity - a scoped, contractual output promised to a client
191
- *
192
- * Operates at a higher level than individual artifacts: a deliverable is the
193
- * "big ticket item" committed to in a proposal or contract (e.g. "Brand
194
- * Identity System"), while artifacts are the individual produced items within
195
- * it (logo SVG, style guide PDF, brand guidelines deck).
196
- *
197
- * Bridges the commercial side (proposal, contract) to the execution side
198
- * (artifact): proposal.deliverables[] → deliverable → artifact[]
241
+ * Deliverable a scoped contractual output promised to a client
242
+ * Bridges the commercial side (proposal, contract) to execution (artifacts, tasks).
199
243
  */
200
244
  export const DeliverableSchema = EntityBaseSchema.extend({
201
245
  entity_type: z.literal('deliverable'),
202
246
  name: z.string().min(1, 'Deliverable name is required'),
203
247
  project: z.string().optional().describe('Related project wikilink'),
204
- proposal: z.string().optional().describe('Originating proposal wikilink'),
205
- contract: z.string().optional().describe('Formalizing contract wikilink'),
206
- artifacts: z.array(z.string()).optional().describe('Component artifact wikilinks'),
207
248
  status: z.enum(['planned', 'in-progress', 'delivered', 'accepted', 'rejected'])
208
249
  .default('planned')
209
250
  .describe('Delivery status'),
210
- due_date: z.string().optional().describe('Due date (ISO 8601 or human-readable)'),
251
+ due_date: z.string().optional().describe('Due date'),
211
252
  delivered_date: z.string().optional().describe('Actual delivery date'),
212
253
  description: z.string().optional().describe('What this deliverable comprises'),
213
254
  });
214
255
  /**
215
- * FUNCTION REPO ENTITIES
216
- * Used in function-specific repositories (business operations)
217
- */
218
- /**
219
- * Proposal entity - client proposals and SOWs
256
+ * Publication authored content published externally
257
+ * Subtypes via `type` field: article, social-post, case-study-writeup, talk, podcast, award-submission
220
258
  */
221
- export const ProposalSchema = EntityBaseSchema.extend({
222
- entity_type: z.literal('proposal'),
223
- name: z.string().min(1, 'Proposal name is required'),
224
- client: z.string().optional().describe('Client name or wikilink'),
225
- project_name: z.string().optional().describe('Project name'),
226
- date: z.string().optional().describe('Proposal date'),
227
- version: z.string().optional().describe('Version (e.g., v1, v2)'),
228
- status: z.enum(['draft', 'sent', 'accepted', 'rejected', 'negotiating'])
259
+ export const PublicationSchema = EntityBaseSchema.extend({
260
+ entity_type: z.literal('publication'),
261
+ title: z.string().min(1, 'Publication title is required'),
262
+ type: z.string().optional().describe('Publication subtype (e.g., article, social-post, talk, podcast, award-submission)'),
263
+ platform: z.string().optional().describe('Platform (e.g., linkedin, medium, behance, conference)'),
264
+ status: z.enum(['draft', 'review', 'scheduled', 'published'])
229
265
  .default('draft')
230
- .describe('Proposal status'),
231
- deliverables: z.array(z.string()).optional().describe('Proposed deliverables'),
232
- phases: z.string().optional().describe('Project phases (markdown)'),
233
- team: z.array(z.string()).optional().describe('Proposed team members'),
234
- schedule: z.string().optional().describe('Proposed schedule'),
235
- fees: z.string().optional().describe('Fee structure'),
236
- payment_terms: z.string().optional().describe('Payment terms and schedule'),
237
- });
238
- /**
239
- * Contract entity - SOWs, MSAs, NDAs
240
- */
241
- export const ContractSchema = EntityBaseSchema.extend({
242
- entity_type: z.literal('contract'),
243
- name: z.string().min(1, 'Contract name is required'),
244
- type: z.enum(['sow', 'msa', 'nda', 'other'])
245
- .optional()
246
- .describe('Contract type'),
247
- client: z.string().optional().describe('Client name or wikilink'),
248
- project: z.string().optional().describe('Related project wikilink'),
249
- date_signed: z.string().optional().describe('Signature date'),
250
- start_date: z.string().optional().describe('Contract start date'),
251
- end_date: z.string().optional().describe('Contract end date'),
252
- status: z.enum(['draft', 'pending', 'signed', 'completed', 'terminated'])
253
- .default('draft')
254
- .describe('Contract status'),
255
- value: z.string().optional().describe('Contract value'),
256
- payment_terms: z.string().optional().describe('Payment terms'),
257
- });
258
- /**
259
- * Person entity - team members, clients, contacts
260
- */
261
- export const PersonSchema = EntityBaseSchema.extend({
262
- entity_type: z.literal('person'),
263
- name: z.string().min(1, 'Person name is required'),
264
- role: z.string().optional().describe('Role or title'),
265
- status: z.enum(['current', 'former', 'contractor', 'client', 'partner'])
266
- .optional()
267
- .describe('Relationship status'),
268
- email: z.string().optional().describe('Email address'),
269
- location: z.string().optional().describe('Location'),
270
- organization: z.string().optional().describe('Organization wikilink'),
271
- years_experience: z.number().optional().describe('Years of experience'),
272
- bio: z.string().optional().describe('Biography (markdown)'),
273
- projects: z.array(z.string()).optional().describe('Related project wikilinks'),
274
- reports_to: z.string().optional().describe('Manager or role wikilink'),
275
- department: z.string().optional().describe('Department (e.g. Creative Technology, Design)'),
276
- });
277
- /**
278
- * Client entity - client organizations and companies
279
- */
280
- export const ClientSchema = EntityBaseSchema.extend({
281
- entity_type: z.literal('client'),
282
- name: z.string().min(1, 'Client name is required'),
283
- type: z.enum(['corporate', 'nonprofit', 'government', 'startup', 'agency', 'other'])
284
- .optional()
285
- .describe('Organization type'),
286
- status: z.enum(['active', 'inactive', 'prospect', 'former'])
287
- .default('active')
288
- .describe('Client status'),
289
- industry: z.string().optional().describe('Industry sector'),
290
- website: z.string().url().optional().describe('Website URL'),
291
- location: z.string().optional().describe('Primary location'),
292
- size: z.enum(['small', 'medium', 'large', 'enterprise'])
293
- .optional()
294
- .describe('Organization size'),
295
- contacts: z.array(z.string()).optional().describe('Related person wikilinks'),
296
- projects: z.array(z.string()).optional().describe('Related project wikilinks'),
297
- relationship_start: z.string().optional().describe('When the relationship started'),
298
- notes: z.string().optional().describe('Additional notes (markdown)'),
299
- });
300
- /**
301
- * Financial Plan entity - budgets, rates, forecasts
302
- */
303
- export const FinancialPlanSchema = EntityBaseSchema.extend({
304
- entity_type: z.literal('financial-plan'),
305
- name: z.string().min(1, 'Financial plan name is required'),
306
- type: z.enum(['budget', 'forecast', 'rates', 'compensation'])
307
- .optional()
308
- .describe('Plan type'),
309
- period: z.string().optional().describe('Time period (e.g., Q1 2026, FY2026)'),
310
- project: z.string().optional().describe('Related project wikilink'),
311
- amounts: z.string().optional().describe('Financial amounts (markdown table)'),
266
+ .describe('Publication status'),
267
+ published_date: z.string().optional().describe('Date published'),
268
+ url: z.string().optional().describe('URL of published content'),
269
+ related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
270
+ related_areas: z.array(z.string()).optional().describe('Related practice area wikilinks'),
312
271
  });
313
272
  /**
314
- * Deal entity - pre-project opportunity moving through the sales pipeline
315
- *
316
- * Distinct from a project (which is post-signing execution) and from a proposal
317
- * (which is a document artifact). Multiple proposals can belong to one deal.
318
- * When a deal is won, a project is created and linked via project.deal.
273
+ * Deal pre-project opportunity in the sales pipeline
319
274
  */
320
275
  export const DealSchema = EntityBaseSchema.extend({
321
276
  entity_type: z.literal('deal'),
@@ -324,77 +279,22 @@ export const DealSchema = EntityBaseSchema.extend({
324
279
  contacts: z.array(z.string()).optional().describe('Contact person wikilinks'),
325
280
  stage: z.enum(['scoping', 'proposal', 'negotiating', 'won', 'lost'])
326
281
  .default('scoping')
327
- .describe('Current pipeline stage'),
328
- deal_value: z.number().optional().describe('Estimated deal value (USD)'),
282
+ .describe('Pipeline stage'),
283
+ deal_value: z.number().optional().describe('Estimated deal value'),
329
284
  probability: z.number().min(0).max(100).optional().describe('Win probability 0–100'),
330
- expected_close: z.string().optional().describe('Expected close date (ISO 8601 or human-readable)'),
285
+ expected_close: z.string().optional().describe('Expected close date'),
331
286
  source: z.enum(['referral', 'inbound', 'outbound', 'repeat', 'partner', 'other'])
332
287
  .optional()
333
288
  .describe('How the opportunity originated'),
334
- proposals: z.array(z.string()).optional().describe('Related proposal wikilinks'),
289
+ proposals: z.array(z.string()).optional().describe('Related proposal document wikilinks'),
335
290
  project: z.string().optional().describe('Resulting project wikilink (once won)'),
336
291
  lost_reason: z.string().optional().describe('Why the deal was lost'),
337
292
  });
293
+ // ══════════════════════════════════════════════════════════════════════════════
294
+ // KNOWLEDGE ENTITIES — typically in resource repos
295
+ // ══════════════════════════════════════════════════════════════════════════════
338
296
  /**
339
- * Vendor entity - external suppliers, freelancers, and partners
340
- */
341
- export const VendorSchema = EntityBaseSchema.extend({
342
- entity_type: z.literal('vendor'),
343
- name: z.string().min(1, 'Vendor name is required'),
344
- type: z.enum(['freelancer', 'agency', 'supplier', 'platform', 'partner', 'other'])
345
- .optional()
346
- .describe('Vendor type'),
347
- status: z.enum(['active', 'inactive', 'preferred'])
348
- .default('active')
349
- .describe('Vendor status'),
350
- contact_name: z.string().optional().describe('Primary contact name'),
351
- email: z.string().optional().describe('Contact email'),
352
- website: z.string().url().optional().describe('Vendor website URL'),
353
- services: z.array(z.string()).optional().describe('Services provided'),
354
- projects: z.array(z.string()).optional().describe('Related project wikilinks'),
355
- notes: z.string().optional().describe('Additional notes (markdown)'),
356
- });
357
- /**
358
- * Role entity - generalized role definitions from the firm's leveling framework
359
- *
360
- * Body markdown holds: Role Overview, Key Responsibilities,
361
- * Leveling Criteria (pillars), Required Skills.
362
- */
363
- export const RoleSchema = EntityBaseSchema.extend({
364
- entity_type: z.literal('role'),
365
- title: z.string().min(1, 'Role title is required'),
366
- department: z.string().optional().describe('Department (e.g. Creative Technology, Design)'),
367
- level: z.number().optional().describe('Level 1–5 from leveling framework'),
368
- type: z.enum(['staff', 'contractor', 'intern'])
369
- .optional()
370
- .describe('Employment type'),
371
- status: z.enum(['active', 'archived'])
372
- .default('active')
373
- .describe('Role status'),
374
- location: z.string().optional().describe('Location'),
375
- reports_to: z.string().optional().describe('Parent role or person wikilink'),
376
- });
377
- /**
378
- * SHARED RESOURCE REPO ENTITIES
379
- * Used in shared knowledge repositories
380
- */
381
- /**
382
- * Template entity - document templates and boilerplates
383
- */
384
- export const TemplateSchema = EntityBaseSchema.extend({
385
- entity_type: z.literal('template'),
386
- name: z.string().min(1, 'Template name is required'),
387
- category: z.string()
388
- .optional()
389
- .describe('Template category (e.g., proposal, brief, sow, meeting-notes)'),
390
- description: z.string().optional().describe('Template description'),
391
- template_content: z.string().optional().describe('Template content (markdown)'),
392
- variables: z.array(z.string())
393
- .optional()
394
- .describe('Template variables (e.g., {{project_name}})'),
395
- });
396
- /**
397
- * Process entity - workflows and methodologies
297
+ * Process workflows, SOPs, and methodologies
398
298
  */
399
299
  export const ProcessSchema = EntityBaseSchema.extend({
400
300
  entity_type: z.literal('process'),
@@ -402,146 +302,91 @@ export const ProcessSchema = EntityBaseSchema.extend({
402
302
  description: z.string().optional().describe('Process description'),
403
303
  steps: z.string().optional().describe('Process steps (markdown)'),
404
304
  related_areas: z.array(z.string()).optional().describe('Related practice areas'),
405
- related_templates: z.array(z.string()).optional().describe('Related template wikilinks'),
406
305
  owner: z.string().optional().describe('Person or team responsible (wikilink)'),
407
- review_cycle: z.enum(['monthly', 'quarterly', 'annual', 'as-needed']).optional().describe('How often this process is reviewed'),
306
+ review_cycle: z.enum(['monthly', 'quarterly', 'annual', 'as-needed']).optional().describe('Review frequency'),
408
307
  });
409
308
  /**
410
- * Standard entity - guidelines and best practices
309
+ * Standard guidelines and best practices
411
310
  */
412
311
  export const StandardSchema = EntityBaseSchema.extend({
413
312
  entity_type: z.literal('standard'),
414
313
  name: z.string().min(1, 'Standard name is required'),
415
- category: z.string()
416
- .optional()
417
- .describe('Standard category (e.g., accessibility, design-principles, code-standards)'),
314
+ category: z.string().optional().describe('Category (e.g., accessibility, design-principles, code-standards)'),
418
315
  description: z.string().optional().describe('Standard description'),
419
316
  guidelines: z.string().optional().describe('Guidelines (markdown)'),
420
317
  examples: z.array(z.string()).optional().describe('Example wikilinks'),
421
318
  });
422
319
  /**
423
- * Reference entity - external references and inspirations
424
- */
425
- export const ReferenceSchema = EntityBaseSchema.extend({
426
- entity_type: z.literal('reference'),
427
- name: z.string().min(1, 'Reference name is required'),
428
- type: z.enum(['article', 'video', 'visualization', 'website', 'research', 'podcast', 'project', 'tool'])
429
- .optional()
430
- .describe('Reference type'),
431
- date: z.string().optional().describe('Publication or creation date'),
432
- source: z.string().optional().describe('Source/author'),
433
- link: z.string().optional().describe('URL'),
434
- related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
435
- related_people: z.array(z.string()).optional().describe('Related people wikilinks'),
436
- related_areas: z.array(z.string()).optional().describe('Related practice areas'),
437
- description: z.string().optional().describe('Reference description'),
438
- });
439
- /**
440
- * Technique entity - methods and patterns
441
- */
442
- export const TechniqueSchema = EntityBaseSchema.extend({
443
- entity_type: z.literal('technique'),
444
- name: z.string().min(1, 'Technique name is required'),
445
- category: z.string()
446
- .optional()
447
- .describe('Technique category (e.g., visualization, interaction-pattern, data-structure)'),
448
- description: z.string().optional().describe('Technique description'),
449
- difficulty: z.enum(['beginner', 'intermediate', 'advanced'])
450
- .optional()
451
- .describe('Difficulty level'),
452
- related_frameworks: z.array(z.string()).optional().describe('Related framework wikilinks'),
453
- examples: z.array(z.string()).optional().describe('Example wikilinks'),
454
- });
455
- /**
456
- * Practice Area entity - firm capabilities and disciplines
457
- */
458
- export const PracticeAreaSchema = EntityBaseSchema.extend({
459
- entity_type: z.literal('practice-area'),
460
- name: z.string().min(1, 'Practice area name is required'),
461
- description: z.string().optional().describe('Overview of the discipline'),
462
- capabilities: z.array(z.string()).optional().describe('Specific service offerings'),
463
- related_case_studies: z.array(z.string()).optional().describe('Case study wikilinks'),
464
- related_projects: z.array(z.string()).optional().describe('Related project wikilinks'),
465
- });
466
- /**
467
- * Case Study entity - portfolio narratives for completed projects
468
- *
469
- * Body markdown holds: Objective, Solution, Outcome, Team, Press, Awards.
320
+ * Case Study portfolio narratives for completed projects
470
321
  */
471
322
  export const CaseStudySchema = EntityBaseSchema.extend({
472
323
  entity_type: z.literal('case-study'),
473
324
  title: z.string().min(1, 'Case study title is required'),
474
- client: z.string().optional().describe('Client wikilink'),
325
+ client: z.string().optional().describe('Client organization wikilink'),
475
326
  year: z.number().optional().describe('Year of the project'),
476
327
  practice_areas: z.array(z.string()).optional().describe('Practice area wikilinks'),
477
328
  sectors: z.array(z.string()).optional().describe('Industry tags'),
478
329
  website_url: z.string().url().optional().describe('Project website URL'),
479
- description: z.string().optional().describe('Short description / meta'),
330
+ description: z.string().optional().describe('Short description'),
480
331
  });
332
+ // ══════════════════════════════════════════════════════════════════════════════
333
+ // ══════════════════════════════════════════════════════════════════════════════
334
+ // SCHEMA REGISTRY
335
+ // ══════════════════════════════════════════════════════════════════════════════
481
336
  /**
482
- * Dataset entity - structured tabular data with CSV body
483
- */
484
- export const ColumnDefinitionSchema = z.object({
485
- name: z.string().min(1),
486
- type: z.enum(['string', 'number', 'boolean', 'date']),
487
- required: z.boolean().optional(),
488
- description: z.string().optional(),
489
- });
490
- export const DatasetSchema = EntityBaseSchema.extend({
491
- entity_type: z.literal('dataset'),
492
- name: z.string().min(1, 'Dataset name is required'),
493
- schema: z.array(ColumnDefinitionSchema).min(1, 'At least one column required'),
494
- });
495
- // ── App entity types ────────────────────────────────────────────────────────
496
- /**
497
- * Deck — presentation deck managed by the Schema Slides app plugin.
498
- * The document body is the full slide markdown (HTML comment directives + slide content).
499
- */
500
- export const DeckSchema = EntityBaseSchema.extend({
501
- entity_type: z.literal('deck'),
502
- title: z.string().min(1, 'Deck title is required'),
503
- client: z.string().optional(),
504
- date: z.string().min(1, 'Date is required'),
505
- phase: z.string().optional(),
506
- theme: z.enum(['dark', 'light']).default('dark'),
507
- status: z.enum(['draft', 'review', 'approved']).default('draft'),
508
- related_project: z.string().optional(),
509
- related_client: z.string().optional(),
510
- });
511
- /**
512
- * Schema registry - maps entity types to their schemas
337
+ * Maps entity types to their Zod schemas.
338
+ * 17 built-in types. App plugins register additional types at runtime via SchemaRegistry.
513
339
  */
514
340
  export const ENTITY_SCHEMAS = {
515
- // Project repos
341
+ // Identity (4)
342
+ person: PersonSchema,
343
+ organization: OrganizationSchema,
344
+ role: RoleSchema,
345
+ 'practice-area': PracticeAreaSchema,
346
+ // Universal (10)
516
347
  project: ProjectSchema,
517
348
  meeting: MeetingSchema,
518
349
  decision: DecisionSchema,
519
350
  task: TaskSchema,
520
- brief: BriefSchema,
521
- artifact: ArtifactSchema,
351
+ document: DocumentSchema,
352
+ dataset: DatasetSchema,
353
+ reference: ReferenceSchema,
354
+ note: NoteSchema,
522
355
  deliverable: DeliverableSchema,
523
- // Function repos
524
- proposal: ProposalSchema,
525
- contract: ContractSchema,
526
- person: PersonSchema,
527
- client: ClientSchema,
528
- 'financial-plan': FinancialPlanSchema,
356
+ publication: PublicationSchema,
529
357
  deal: DealSchema,
530
- vendor: VendorSchema,
531
- role: RoleSchema,
532
- // Shared resource repos
533
- template: TemplateSchema,
358
+ // Knowledge (3)
534
359
  process: ProcessSchema,
535
360
  standard: StandardSchema,
536
- reference: ReferenceSchema,
537
- technique: TechniqueSchema,
538
- 'practice-area': PracticeAreaSchema,
539
361
  'case-study': CaseStudySchema,
540
- // Dataset
541
- dataset: DatasetSchema,
542
- // App entity types (registered by app plugins)
543
- deck: DeckSchema,
544
362
  };
363
+ /**
364
+ * Plural subfolder name → singular entity_type mapping
365
+ */
366
+ export const PLURAL_TO_SINGULAR = {
367
+ 'people': 'person',
368
+ 'organizations': 'organization',
369
+ 'roles': 'role',
370
+ 'practice-areas': 'practice-area',
371
+ 'projects': 'project',
372
+ 'meetings': 'meeting',
373
+ 'decisions': 'decision',
374
+ 'tasks': 'task',
375
+ 'documents': 'document',
376
+ 'datasets': 'dataset',
377
+ 'references': 'reference',
378
+ 'notes': 'note',
379
+ 'deliverables': 'deliverable',
380
+ 'publications': 'publication',
381
+ 'deals': 'deal',
382
+ 'processes': 'process',
383
+ 'standards': 'standard',
384
+ 'case-studies': 'case-study',
385
+ };
386
+ /**
387
+ * Singular entity_type → plural subfolder name mapping
388
+ */
389
+ export const SINGULAR_TO_PLURAL = Object.fromEntries(Object.entries(PLURAL_TO_SINGULAR).map(([plural, singular]) => [singular, plural]));
545
390
  /**
546
391
  * Validation helper with helpful error messages
547
392
  */
@@ -583,7 +428,7 @@ export function validateCustomEntity(data) {
583
428
  }
584
429
  }
585
430
  /**
586
- * Check if entity type is a known type
431
+ * Check if a type is a known built-in entity type
587
432
  */
588
433
  export function isKnownEntityType(type) {
589
434
  return type in ENTITY_SCHEMAS;