import { useRegisterState } from 'cedar-os';
import { Node } from 'reactflow';
import { z } from 'zod';
// Register nodes state with state setters
useRegisterState({
key: 'nodes',
value: nodes,
setValue: setNodes,
description: 'Product roadmap nodes',
stateSetters: {
addNode: {
name: 'addNode',
description: 'Add a new node to the roadmap',
argsSchema: z.object({
node: z.object({
data: z.object({
title: z.string(),
description: z.string(),
status: z.enum(['done', 'planned', 'backlog', 'in progress']),
nodeType: z.literal('feature').default('feature'),
}),
}),
}),
const newNode = {
...node,
id: node.id || uuidv4(),
type: 'featureNode',
position: { x: Math.random() * 400, y: Math.random() * 400 },
data: {
...node.data,
nodeType: node.data.nodeType || 'feature',
status: node.data.status || 'planned',
upvotes: node.data.upvotes || 0,
comments: node.data.comments || [],
diff: 'added' as const,
},
};
setNodes([...currentNodes, newNode]);
},
},
removeNode: {
name: 'removeNode',
description: 'Remove a node from the roadmap',
argsSchema: z.object({
id: z.string().describe('The ID of the node to remove'),
}),
// Mark as removed with diff instead of immediate deletion
setNodes(
currentNodes.map((node) =>
node.id === id
? { ...node, data: { ...node.data, diff: 'removed' } }
: node
)
);
},
},
acceptAllDiffs: {
name: 'acceptAllDiffs',
description: 'Accept all pending diffs',
argsSchema: z.object({}),
const nodesWithDiffs = currentNodes.filter((n) => n.data.diff);
// Process removals
const removedNodeIds = nodesWithDiffs
.filter((n) => n.data.diff === 'removed')
.map((n) => n.id);
for (const nodeId of removedNodeIds) {
await deleteNode(nodeId);
}
// Update remaining nodes
const remainingNodes = currentNodes.filter(
(n) => !removedNodeIds.includes(n.id)
);
setNodes(
remainingNodes.map((n) => ({
...n,
data: { ...n.data, diff: undefined },
}))
);
},
},
},
});