@hcgstudio/ogma 0.0.0

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +155 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/defineOgmaReview.d.ts +3 -0
  8. package/dist/defineOgmaReview.d.ts.map +1 -0
  9. package/dist/defineOgmaReview.js +4 -0
  10. package/dist/defineOgmaReview.js.map +1 -0
  11. package/dist/index.d.ts +5 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +4 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/node/project.d.ts +18 -0
  16. package/dist/node/project.d.ts.map +1 -0
  17. package/dist/node/project.js +104 -0
  18. package/dist/node/project.js.map +1 -0
  19. package/dist/node/server.d.ts +21 -0
  20. package/dist/node/server.d.ts.map +1 -0
  21. package/dist/node/server.js +476 -0
  22. package/dist/node/server.js.map +1 -0
  23. package/dist/node/templates.d.ts +5 -0
  24. package/dist/node/templates.d.ts.map +1 -0
  25. package/dist/node/templates.js +145 -0
  26. package/dist/node/templates.js.map +1 -0
  27. package/dist/types.d.ts +100 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/viewer/OgmaReviewApp.d.ts +7 -0
  32. package/dist/viewer/OgmaReviewApp.d.ts.map +1 -0
  33. package/dist/viewer/OgmaReviewApp.js +387 -0
  34. package/dist/viewer/OgmaReviewApp.js.map +1 -0
  35. package/dist/viewer/client.d.ts +2 -0
  36. package/dist/viewer/client.d.ts.map +1 -0
  37. package/dist/viewer/client.js +13 -0
  38. package/dist/viewer/client.js.map +1 -0
  39. package/dist/viewer/defaultReview.d.ts +4 -0
  40. package/dist/viewer/defaultReview.d.ts.map +1 -0
  41. package/dist/viewer/defaultReview.js +55 -0
  42. package/dist/viewer/defaultReview.js.map +1 -0
  43. package/dist/viewer/normalizeReviewModule.d.ts +3 -0
  44. package/dist/viewer/normalizeReviewModule.d.ts.map +1 -0
  45. package/dist/viewer/normalizeReviewModule.js +58 -0
  46. package/dist/viewer/normalizeReviewModule.js.map +1 -0
  47. package/package.json +47 -0
  48. package/src/cli.ts +194 -0
  49. package/src/defineOgmaReview.ts +5 -0
  50. package/src/index.ts +17 -0
  51. package/src/node/project.ts +143 -0
  52. package/src/node/server.ts +598 -0
  53. package/src/node/templates.ts +148 -0
  54. package/src/types.ts +111 -0
  55. package/src/viewer/OgmaReviewApp.tsx +1099 -0
  56. package/src/viewer/client.tsx +18 -0
  57. package/src/viewer/defaultReview.tsx +168 -0
  58. package/src/viewer/normalizeReviewModule.ts +87 -0
  59. package/src/viewer/styles.css +1140 -0
  60. package/src/viewer/virtual.d.ts +11 -0
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function DashboardScreen() {
3
+ return (_jsxs("div", { className: "ogma-demo-screen", children: [_jsxs("aside", { className: "ogma-demo-sidebar", children: [_jsx("div", { className: "ogma-demo-brand", children: "O" }), _jsx("span", { className: "ogma-demo-nav is-active", children: "Overview" }), _jsx("span", { className: "ogma-demo-nav", children: "Signals" }), _jsx("span", { className: "ogma-demo-nav", children: "Queue" }), _jsx("span", { className: "ogma-demo-nav", children: "Settings" })] }), _jsxs("main", { className: "ogma-demo-content", children: [_jsxs("div", { className: "ogma-demo-header", children: [_jsxs("div", { children: [_jsx("p", { children: "Dashboard" }), _jsx("h2", { children: "Atlas Brief" })] }), _jsx("button", { type: "button", children: "Review ready" })] }), _jsxs("div", { className: "ogma-demo-grid", children: [_jsxs("section", { className: "ogma-demo-card is-wide", children: [_jsx("div", { className: "ogma-demo-card-title" }), _jsx("div", { className: "ogma-demo-chart", children: [42, 68, 53, 81, 61, 75, 88].map((value) => (_jsx("span", { style: { height: `${value}%` } }, value))) })] }), _jsxs("section", { className: "ogma-demo-card is-accent", children: [_jsx("strong", { children: "91%" }), _jsx("p", { children: "Review confidence" })] }), _jsxs("section", { className: "ogma-demo-card", children: [_jsx("div", { className: "ogma-demo-list-row" }), _jsx("div", { className: "ogma-demo-list-row is-medium" }), _jsx("div", { className: "ogma-demo-list-row is-short" })] }), _jsxs("section", { className: "ogma-demo-card is-action", children: [_jsx("strong", { children: "2" }), _jsx("p", { children: "Copy edits before approval" })] })] })] })] }));
4
+ }
5
+ function DesignSpecScreen() {
6
+ return (_jsxs("div", { className: "ogma-spec-screen", children: [_jsxs("header", { children: [_jsx("p", { children: "Design spec" }), _jsx("h2", { children: "Decision System" })] }), _jsx("div", { className: "ogma-spec-grid", children: ['Information density', 'Review confidence', 'Agent handoff', 'Mobile parity'].map((item, index) => (_jsxs("section", { children: [_jsx("span", { children: `0${index + 1}` }), _jsx("h3", { children: item }), _jsx("p", { children: "Prototype state prepared for pinned reviewer feedback and agent follow-up." })] }, item))) })] }));
7
+ }
8
+ function CheckoutFlowScreen() {
9
+ return (_jsxs("div", { className: "ogma-flow-screen", children: [_jsxs("header", { children: [_jsx("p", { children: "Checkout flow" }), _jsx("h2", { children: "Approve Plan" })] }), _jsx("div", { className: "ogma-flow-columns", children: ['Cart', 'Review', 'Confirm'].map((step, index) => (_jsxs("section", { children: [_jsx("span", { children: index + 1 }), _jsx("h3", { children: step }), _jsx("div", { className: "ogma-flow-line" }), _jsx("button", { type: "button", children: index === 2 ? 'Approve' : 'Continue' })] }, step))) })] }));
10
+ }
11
+ function MobileNavigationScreen() {
12
+ return (_jsx("div", { className: "ogma-mobile-screen", children: _jsxs("div", { className: "ogma-phone", children: [_jsxs("header", { children: [_jsx("span", { children: "Ogma" }), _jsx("button", { type: "button", children: "New" })] }), _jsxs("main", { children: [_jsxs("section", { children: [_jsx("p", { children: "Mobile nav" }), _jsx("h2", { children: "Design queue" })] }), _jsxs("div", { className: "ogma-phone-list", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] })] }), _jsxs("nav", { children: [_jsx("span", { className: "is-active" }), _jsx("span", {}), _jsx("span", {})] })] }) }));
13
+ }
14
+ export function createDefaultOgmaReview() {
15
+ return {
16
+ title: 'Ogma starter review',
17
+ description: 'A local starter review used until an agent writes project-specific JSX screens.',
18
+ notes: 'Use this starter review to confirm Ogma is running, then replace it with product-specific JSX screens and product notes in designs/ogma.',
19
+ metadata: {
20
+ source: 'ogma-default',
21
+ updatedAt: new Date().toISOString()
22
+ },
23
+ screens: [
24
+ {
25
+ id: 'dashboard',
26
+ title: 'Dashboard',
27
+ description: 'Main review workspace for desktop inspection.',
28
+ component: DashboardScreen
29
+ },
30
+ {
31
+ id: 'design-spec',
32
+ title: 'Design spec',
33
+ description: 'Product decisions and design rationale.',
34
+ component: DesignSpecScreen
35
+ },
36
+ {
37
+ id: 'checkout-flow',
38
+ title: 'Checkout flow',
39
+ description: 'Interaction state for a step-based product path.',
40
+ component: CheckoutFlowScreen
41
+ },
42
+ {
43
+ id: 'mobile-navigation',
44
+ title: 'Mobile nav',
45
+ description: 'Compact viewport state for mobile review.',
46
+ component: MobileNavigationScreen,
47
+ width: 390
48
+ }
49
+ ]
50
+ };
51
+ }
52
+ export function MissingScreen({ screen }) {
53
+ return (_jsxs("div", { className: "ogma-empty-screen", children: [_jsx("h2", { children: screen.title }), _jsx("p", { children: "This screen is missing a React component export." })] }));
54
+ }
55
+ //# sourceMappingURL=defaultReview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultReview.js","sourceRoot":"","sources":["../../src/viewer/defaultReview.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { OgmaReviewDefinition } from '../types.js';
2
+ export declare function normalizeReviewModule(moduleValue: unknown, productNotes?: string): OgmaReviewDefinition;
3
+ //# sourceMappingURL=normalizeReviewModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeReviewModule.d.ts","sourceRoot":"","sources":["../../src/viewer/normalizeReviewModule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiD,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAwDvG,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,OAAO,EAAE,YAAY,SAAK,GAAG,oBAAoB,CA6BnG"}
@@ -0,0 +1,58 @@
1
+ import { createDefaultOgmaReview, MissingScreen } from './defaultReview.js';
2
+ function slugify(value, fallback) {
3
+ const slug = value
4
+ .trim()
5
+ .toLowerCase()
6
+ .replace(/[^a-z0-9]+/g, '-')
7
+ .replace(/^-+|-+$/g, '');
8
+ return slug || fallback;
9
+ }
10
+ function isObject(value) {
11
+ return typeof value === 'object' && value !== null;
12
+ }
13
+ function normalizeScreen(value, index) {
14
+ if (!isObject(value)) {
15
+ return null;
16
+ }
17
+ const title = typeof value.title === 'string' ? value.title : `Screen ${index + 1}`;
18
+ const component = (typeof value.component === 'function'
19
+ ? value.component
20
+ : typeof value.Component === 'function'
21
+ ? value.Component
22
+ : MissingScreen);
23
+ return {
24
+ id: typeof value.id === 'string' ? slugify(value.id, `screen-${index + 1}`) : slugify(title, `screen-${index + 1}`),
25
+ title,
26
+ description: typeof value.description === 'string' ? value.description : undefined,
27
+ width: typeof value.width === 'number' ? value.width : undefined,
28
+ height: typeof value.height === 'number' ? value.height : undefined,
29
+ component
30
+ };
31
+ }
32
+ function pickReviewCandidate(moduleValue) {
33
+ return moduleValue.default ?? moduleValue.review ?? moduleValue.ogmaReview ?? moduleValue;
34
+ }
35
+ export function normalizeReviewModule(moduleValue, productNotes = '') {
36
+ const fallback = createDefaultOgmaReview();
37
+ if (!isObject(moduleValue)) {
38
+ return { ...fallback, notes: productNotes || fallback.notes };
39
+ }
40
+ const candidate = pickReviewCandidate(moduleValue);
41
+ if (!isObject(candidate) || !Array.isArray(candidate.screens)) {
42
+ return { ...fallback, notes: productNotes || fallback.notes };
43
+ }
44
+ const screens = candidate.screens
45
+ .map((screen, index) => normalizeScreen(screen, index))
46
+ .filter((screen) => screen !== null);
47
+ if (screens.length === 0) {
48
+ return { ...fallback, notes: productNotes || fallback.notes };
49
+ }
50
+ return {
51
+ title: typeof candidate.title === 'string' ? candidate.title : fallback.title,
52
+ description: typeof candidate.description === 'string' ? candidate.description : fallback.description,
53
+ screens,
54
+ notes: productNotes || (typeof candidate.notes === 'string' ? candidate.notes : fallback.notes),
55
+ metadata: isObject(candidate.metadata) ? candidate.metadata : fallback.metadata
56
+ };
57
+ }
58
+ //# sourceMappingURL=normalizeReviewModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeReviewModule.js","sourceRoot":"","sources":["../../src/viewer/normalizeReviewModule.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@hcgstudio/ogma",
3
+ "version": "0.0.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/hcgstudio/ogma.git",
8
+ "directory": "packages/ogma"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "bin": "./dist/cli.js",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./styles.css": "./src/viewer/styles.css"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src",
25
+ "LICENSE",
26
+ "README.md"
27
+ ],
28
+ "scripts": {
29
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
30
+ "dev": "yarn build && node ./dist/cli.js start --no-open",
31
+ "ogma": "yarn build && node ./dist/cli.js",
32
+ "start": "yarn build && node ./dist/cli.js start --no-open",
33
+ "typecheck": "tsc --noEmit"
34
+ },
35
+ "dependencies": {
36
+ "lucide-react": "^1.18.0",
37
+ "react": "^19.2.7",
38
+ "react-dom": "^19.2.7",
39
+ "vite": "^8.0.16"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.9.3",
43
+ "@types/react": "^19",
44
+ "@types/react-dom": "^19",
45
+ "typescript": "^6.0.3"
46
+ }
47
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { fileURLToPath } from 'node:url';
5
+ import type { OgmaResolvedProject } from './node/project.js';
6
+ import { DEFAULT_DESIGN_DIR, DEFAULT_SKILL_URL } from './node/templates.js';
7
+
8
+ interface ParsedArgs {
9
+ command: string;
10
+ flags: Map<string, string | boolean>;
11
+ }
12
+
13
+ function parseArgs(argv: string[]): ParsedArgs {
14
+ const args = [...argv];
15
+ const command = args[0] && !args[0].startsWith('-') ? args.shift() ?? 'start' : 'start';
16
+ const flags = new Map<string, string | boolean>();
17
+
18
+ for (let index = 0; index < args.length; index += 1) {
19
+ const item = args[index];
20
+
21
+ if (!item?.startsWith('--')) {
22
+ continue;
23
+ }
24
+
25
+ const [rawName, rawValue] = item.slice(2).split('=', 2);
26
+ const name = rawName ?? '';
27
+
28
+ if (name.startsWith('no-')) {
29
+ flags.set(name.slice(3), false);
30
+ continue;
31
+ }
32
+
33
+ if (rawValue !== undefined) {
34
+ flags.set(name, rawValue);
35
+ continue;
36
+ }
37
+
38
+ const next = args[index + 1];
39
+
40
+ if (next && !next.startsWith('--')) {
41
+ flags.set(name, next);
42
+ index += 1;
43
+ } else {
44
+ flags.set(name, true);
45
+ }
46
+ }
47
+
48
+ return { command, flags };
49
+ }
50
+
51
+ function stringFlag(flags: Map<string, string | boolean>, name: string, fallback?: string) {
52
+ const value = flags.get(name);
53
+ return typeof value === 'string' ? value : fallback;
54
+ }
55
+
56
+ function numberFlag(flags: Map<string, string | boolean>, name: string, fallback: number) {
57
+ const value = Number(stringFlag(flags, name, String(fallback)));
58
+ return Number.isFinite(value) ? value : fallback;
59
+ }
60
+
61
+ function booleanFlag(flags: Map<string, string | boolean>, name: string, fallback: boolean) {
62
+ const value = flags.get(name);
63
+ return typeof value === 'boolean' ? value : fallback;
64
+ }
65
+
66
+ function displayPath(cwd: string, targetPath: string) {
67
+ const relativePath = path.relative(cwd, targetPath);
68
+
69
+ return relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)
70
+ ? relativePath
71
+ : targetPath;
72
+ }
73
+
74
+ function printScaffoldResult(project: OgmaResolvedProject) {
75
+ console.log('Ogma scaffold ready');
76
+
77
+ if (project.createdPaths.length > 0) {
78
+ console.log(' Created:');
79
+
80
+ for (const createdPath of project.createdPaths) {
81
+ console.log(` - ${displayPath(project.cwd, createdPath)}`);
82
+ }
83
+ } else {
84
+ console.log(' Created: none');
85
+ }
86
+
87
+ console.log(` Designs: ${displayPath(project.cwd, project.designEntry)}`);
88
+ console.log(` Notes: ${displayPath(project.cwd, project.notesPath)}`);
89
+ console.log(` Feedback: ${displayPath(project.cwd, project.sessionPath)}`);
90
+ }
91
+
92
+ function printHelp() {
93
+ console.log(`Ogma local design review
94
+
95
+ Usage:
96
+ ogma scaffold [--review designs/ogma]
97
+ ogma start [--review designs/ogma] [--port 4317] [--host localhost] [--no-open]
98
+ ogma init [--review designs/ogma]
99
+ ogma skill-url
100
+
101
+ Commands:
102
+ scaffold Create the JSX review scaffold and local Ogma data files.
103
+ start Create missing design files and launch the local review server.
104
+ init Alias for scaffold.
105
+ skill-url Print the public Ogma skill URL.
106
+
107
+ Options:
108
+ --review <path> Design directory or review entry file. Default: ${DEFAULT_DESIGN_DIR}
109
+ --port <number> Preferred local port. Default: 4317
110
+ --host <host> Host for the local server. Default: localhost
111
+ --skill-url <url> Override the URL shown to agents.
112
+ --open / --no-open Open the browser after startup. Default: --open
113
+ `);
114
+ }
115
+
116
+ async function checkRuntimeDependencies() {
117
+ const major = Number(process.versions.node.split('.')[0] ?? '0');
118
+
119
+ if (major < 18) {
120
+ throw new Error('Ogma requires Node.js 18 or newer.');
121
+ }
122
+
123
+ const require = createRequire(path.join(packageRoot(), 'package.json'));
124
+ const missing: string[] = [];
125
+
126
+ for (const specifier of ['react', 'react-dom/client', 'vite']) {
127
+ try {
128
+ require.resolve(specifier);
129
+ } catch {
130
+ missing.push(specifier);
131
+ }
132
+ }
133
+
134
+ if (missing.length > 0) {
135
+ throw new Error(
136
+ `Missing runtime dependencies: ${missing.join(', ')}. Run npm install -D @hcgstudio/ogma react react-dom vite.`
137
+ );
138
+ }
139
+ }
140
+
141
+ function packageRoot() {
142
+ return path.dirname(path.dirname(fileURLToPath(import.meta.url)));
143
+ }
144
+
145
+ async function main() {
146
+ const { command, flags } = parseArgs(process.argv.slice(2));
147
+
148
+ if (flags.has('help') || command === 'help') {
149
+ printHelp();
150
+ return;
151
+ }
152
+
153
+ if (command === 'skill-url') {
154
+ console.log(stringFlag(flags, 'skill-url', DEFAULT_SKILL_URL));
155
+ return;
156
+ }
157
+
158
+ await checkRuntimeDependencies();
159
+
160
+ const cwd = process.cwd();
161
+ const review = stringFlag(flags, 'review');
162
+
163
+ if (command === 'init' || command === 'scaffold') {
164
+ const { ensureOgmaProject } = await import('./node/project.js');
165
+ const project = await ensureOgmaProject({ cwd, review });
166
+ printScaffoldResult(project);
167
+ return;
168
+ }
169
+
170
+ if (command !== 'start') {
171
+ printHelp();
172
+ process.exitCode = 1;
173
+ return;
174
+ }
175
+
176
+ const { startOgmaServer } = await import('./node/server.js');
177
+
178
+ await startOgmaServer({
179
+ cwd,
180
+ host: stringFlag(flags, 'host', 'localhost') ?? 'localhost',
181
+ open: booleanFlag(flags, 'open', true),
182
+ packageRoot: packageRoot(),
183
+ port: numberFlag(flags, 'port', 4317),
184
+ review,
185
+ skillUrl: stringFlag(flags, 'skill-url', DEFAULT_SKILL_URL)
186
+ });
187
+ }
188
+
189
+ main().catch((error: unknown) => {
190
+ console.error('');
191
+ console.error(`[ogma] ${error instanceof Error ? error.message : String(error)}`);
192
+ console.error('');
193
+ process.exitCode = 1;
194
+ });
@@ -0,0 +1,5 @@
1
+ import type { OgmaReviewDefinition } from './types.js';
2
+
3
+ export function defineOgmaReview(review: OgmaReviewDefinition): OgmaReviewDefinition {
4
+ return review;
5
+ }
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ export { defineOgmaReview } from './defineOgmaReview.js';
2
+ export { OgmaReviewApp } from './viewer/OgmaReviewApp.js';
3
+ export { createDefaultOgmaReview } from './viewer/defaultReview.js';
4
+ export type {
5
+ OgmaAnnotation,
6
+ OgmaAnnotationStatus,
7
+ OgmaClientConfig,
8
+ OgmaFeedbackExport,
9
+ OgmaPrototypeScreen,
10
+ OgmaPrototypeScreenProps,
11
+ OgmaReviewDefinition,
12
+ OgmaReviewMetadata,
13
+ OgmaReviewSession,
14
+ OgmaServerStatus,
15
+ OgmaSessionHistoryEntry,
16
+ OgmaViewportSnapshot
17
+ } from './types.js';
@@ -0,0 +1,143 @@
1
+ import { mkdir, stat, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import type { OgmaReviewSession } from '../types.js';
4
+ import { DEFAULT_DESIGN_DIR, DEFAULT_PRODUCT_NOTES, DEFAULT_REVIEW_SOURCE } from './templates.js';
5
+
6
+ export interface OgmaResolvedProject {
7
+ cwd: string;
8
+ createdPaths: string[];
9
+ dataDir: string;
10
+ defaultDesignDir: string;
11
+ designEntry: string;
12
+ existingPaths: string[];
13
+ feedbackPath: string;
14
+ historyPath: string;
15
+ notesPath: string;
16
+ sessionPath: string;
17
+ snapshotsDir: string;
18
+ }
19
+
20
+ const reviewEntryNames = ['review.tsx', 'review.jsx', 'ogma.review.tsx', 'ogma.review.jsx'];
21
+
22
+ function nowIso() {
23
+ return new Date().toISOString();
24
+ }
25
+
26
+ function emptySession(): OgmaReviewSession {
27
+ return {
28
+ reviewId: 'ogma-review',
29
+ annotations: [],
30
+ updatedAt: nowIso()
31
+ };
32
+ }
33
+
34
+ async function fileExists(filePath: string) {
35
+ try {
36
+ await stat(filePath);
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ async function pathIsFile(filePath: string) {
44
+ try {
45
+ return (await stat(filePath)).isFile();
46
+ } catch {
47
+ return Boolean(path.extname(filePath));
48
+ }
49
+ }
50
+
51
+ async function ensureDirectory(dirPath: string, project: Pick<OgmaResolvedProject, 'createdPaths' | 'existingPaths'>) {
52
+ if (await fileExists(dirPath)) {
53
+ await mkdir(dirPath, { recursive: true });
54
+ project.existingPaths.push(dirPath);
55
+ return;
56
+ }
57
+
58
+ await mkdir(dirPath, { recursive: true });
59
+ project.createdPaths.push(dirPath);
60
+ }
61
+
62
+ async function ensureFile(
63
+ filePath: string,
64
+ contents: string,
65
+ project: Pick<OgmaResolvedProject, 'createdPaths' | 'existingPaths'>
66
+ ) {
67
+ if (await fileExists(filePath)) {
68
+ project.existingPaths.push(filePath);
69
+ return;
70
+ }
71
+
72
+ await writeFile(filePath, contents, 'utf8');
73
+ project.createdPaths.push(filePath);
74
+ }
75
+
76
+ async function resolveReviewTarget(cwd: string, target = DEFAULT_DESIGN_DIR) {
77
+ const absoluteTarget = path.resolve(cwd, target);
78
+
79
+ if (await pathIsFile(absoluteTarget)) {
80
+ return {
81
+ designDir: path.dirname(absoluteTarget),
82
+ designEntry: absoluteTarget
83
+ };
84
+ }
85
+
86
+ for (const entryName of reviewEntryNames) {
87
+ const candidate = path.join(absoluteTarget, entryName);
88
+
89
+ if (await fileExists(candidate)) {
90
+ return {
91
+ designDir: absoluteTarget,
92
+ designEntry: candidate
93
+ };
94
+ }
95
+ }
96
+
97
+ return {
98
+ designDir: absoluteTarget,
99
+ designEntry: path.join(absoluteTarget, reviewEntryNames[0] ?? 'review.tsx')
100
+ };
101
+ }
102
+
103
+ export async function ensureOgmaProject({
104
+ cwd,
105
+ review
106
+ }: {
107
+ cwd: string;
108
+ review?: string;
109
+ }): Promise<OgmaResolvedProject> {
110
+ const { designDir, designEntry } = await resolveReviewTarget(cwd, review);
111
+ const dataDir = path.join(cwd, '.ogma');
112
+ const notesPath = path.join(designDir, 'product-notes.md');
113
+ const sessionPath = path.join(dataDir, 'session.json');
114
+ const feedbackPath = path.join(dataDir, 'feedback.json');
115
+ const historyPath = path.join(dataDir, 'history.json');
116
+ const snapshotsDir = path.join(dataDir, 'snapshots');
117
+ const project = {
118
+ createdPaths: [],
119
+ existingPaths: []
120
+ };
121
+
122
+ await ensureDirectory(designDir, project);
123
+ await ensureDirectory(dataDir, project);
124
+ await ensureDirectory(snapshotsDir, project);
125
+ await ensureFile(designEntry, DEFAULT_REVIEW_SOURCE, project);
126
+ await ensureFile(notesPath, DEFAULT_PRODUCT_NOTES, project);
127
+ await ensureFile(sessionPath, `${JSON.stringify(emptySession(), null, 2)}\n`, project);
128
+ await ensureFile(historyPath, '[]\n', project);
129
+
130
+ return {
131
+ cwd,
132
+ createdPaths: project.createdPaths,
133
+ dataDir,
134
+ defaultDesignDir: path.relative(cwd, designDir) || '.',
135
+ designEntry,
136
+ existingPaths: project.existingPaths,
137
+ feedbackPath,
138
+ historyPath,
139
+ notesPath,
140
+ sessionPath,
141
+ snapshotsDir
142
+ };
143
+ }