@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.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +155 -0
- package/dist/cli.js.map +1 -0
- package/dist/defineOgmaReview.d.ts +3 -0
- package/dist/defineOgmaReview.d.ts.map +1 -0
- package/dist/defineOgmaReview.js +4 -0
- package/dist/defineOgmaReview.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/node/project.d.ts +18 -0
- package/dist/node/project.d.ts.map +1 -0
- package/dist/node/project.js +104 -0
- package/dist/node/project.js.map +1 -0
- package/dist/node/server.d.ts +21 -0
- package/dist/node/server.d.ts.map +1 -0
- package/dist/node/server.js +476 -0
- package/dist/node/server.js.map +1 -0
- package/dist/node/templates.d.ts +5 -0
- package/dist/node/templates.d.ts.map +1 -0
- package/dist/node/templates.js +145 -0
- package/dist/node/templates.js.map +1 -0
- package/dist/types.d.ts +100 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/viewer/OgmaReviewApp.d.ts +7 -0
- package/dist/viewer/OgmaReviewApp.d.ts.map +1 -0
- package/dist/viewer/OgmaReviewApp.js +387 -0
- package/dist/viewer/OgmaReviewApp.js.map +1 -0
- package/dist/viewer/client.d.ts +2 -0
- package/dist/viewer/client.d.ts.map +1 -0
- package/dist/viewer/client.js +13 -0
- package/dist/viewer/client.js.map +1 -0
- package/dist/viewer/defaultReview.d.ts +4 -0
- package/dist/viewer/defaultReview.d.ts.map +1 -0
- package/dist/viewer/defaultReview.js +55 -0
- package/dist/viewer/defaultReview.js.map +1 -0
- package/dist/viewer/normalizeReviewModule.d.ts +3 -0
- package/dist/viewer/normalizeReviewModule.d.ts.map +1 -0
- package/dist/viewer/normalizeReviewModule.js +58 -0
- package/dist/viewer/normalizeReviewModule.js.map +1 -0
- package/package.json +47 -0
- package/src/cli.ts +194 -0
- package/src/defineOgmaReview.ts +5 -0
- package/src/index.ts +17 -0
- package/src/node/project.ts +143 -0
- package/src/node/server.ts +598 -0
- package/src/node/templates.ts +148 -0
- package/src/types.ts +111 -0
- package/src/viewer/OgmaReviewApp.tsx +1099 -0
- package/src/viewer/client.tsx +18 -0
- package/src/viewer/defaultReview.tsx +168 -0
- package/src/viewer/normalizeReviewModule.ts +87 -0
- package/src/viewer/styles.css +1140 -0
- 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 @@
|
|
|
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
|
+
});
|
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
|
+
}
|