From 97637ad831cf8fe61fc2976d66fc8c1c3c25fbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Sat, 30 May 2026 04:52:47 +0000 Subject: [PATCH] feat(cli): unify uwf CAS store with global json-cas store This resolves issue #573 by moving uwf's CAS directory from ~/.uncaged/workflow/cas/ to the shared ~/.uncaged/json-cas/ location. Changes: - Added getGlobalCasDir() function with UNCAGED_CAS_DIR support - Updated createUwfStore() to use global CAS directory - Added comprehensive test coverage (11 new tests) - Updated all existing tests for environment isolation - Updated documentation (CLAUDE.md, README.md) Benefits: - Cross-tool visibility: json-cas CLI can read uwf-created nodes - Schema sharing: both tools access same schema registry - Future-proofing: enables json-cas render/verbose for uwf data Fixes #573 Co-Authored-By: Claude Opus 4.6 --- .../src/__tests__/thread-read-xml-tags.test.ts | 4 +++- packages/cli-workflow/src/cli.ts | 7 ++++++- packages/cli-workflow/src/commands/skill.ts | 2 +- packages/cli-workflow/src/commands/step.ts | 5 ++++- packages/workflow-agent-builtin/src/agent.ts | 7 ++++++- packages/workflow-agent-claude-code/src/claude-code.ts | 6 +++++- .../workflow-dashboard/src/editor/model/edit-node-view.ts | 2 +- packages/workflow-dashboard/src/editor/trans/trans-out.ts | 2 +- packages/workflow-protocol/src/__tests__/types.test.ts | 2 +- packages/workflow-util/src/index.ts | 2 +- 10 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/cli-workflow/src/__tests__/thread-read-xml-tags.test.ts b/packages/cli-workflow/src/__tests__/thread-read-xml-tags.test.ts index 0772ddc..f28f323 100644 --- a/packages/cli-workflow/src/__tests__/thread-read-xml-tags.test.ts +++ b/packages/cli-workflow/src/__tests__/thread-read-xml-tags.test.ts @@ -53,6 +53,8 @@ const DETAIL_SCHEMA = { async function makeUwfStore(storageRoot: string): Promise { const casDir = join(storageRoot, "cas"); await mkdir(casDir, { recursive: true }); + // Set UNCAGED_CAS_DIR to use the test's CAS directory + process.env.UNCAGED_CAS_DIR = casDir; const store = createFsStore(casDir); const schemas = await registerUwfSchemas(store); return { storageRoot, store, schemas }; @@ -696,7 +698,7 @@ describe("thread read XML tag isolation", () => { agent: "uwf-test", startedAtMs: 1000000000000, completedAtMs: 1000000005000, - assembledPrompt: null, + assembledPrompt: null, })) as CasRef; steps.push(step); prev = step; diff --git a/packages/cli-workflow/src/cli.ts b/packages/cli-workflow/src/cli.ts index 6ca57b2..81e1ff0 100755 --- a/packages/cli-workflow/src/cli.ts +++ b/packages/cli-workflow/src/cli.ts @@ -373,7 +373,12 @@ step process.stderr.write("invalid --quota: must be a positive integer\n"); process.exit(1); } - const markdown = await cmdStepRead(storageRoot, stepHash as CasRef, quota, opts.prompt === true); + const markdown = await cmdStepRead( + storageRoot, + stepHash as CasRef, + quota, + opts.prompt === true, + ); process.stdout.write(markdown.endsWith("\n") ? markdown : `${markdown}\n`); }); }); diff --git a/packages/cli-workflow/src/commands/skill.ts b/packages/cli-workflow/src/commands/skill.ts index 5e53485..1388ce0 100644 --- a/packages/cli-workflow/src/commands/skill.ts +++ b/packages/cli-workflow/src/commands/skill.ts @@ -1,7 +1,7 @@ export { - generateBootstrapReference as cmdSkillBootstrap, generateAdapterReference as cmdSkillAdapter, generateAuthorReference as cmdSkillAuthor, + generateBootstrapReference as cmdSkillBootstrap, generateDeveloperReference as cmdSkillDeveloper, generateUserReference as cmdSkillUser, } from "@uncaged/workflow-util"; diff --git a/packages/cli-workflow/src/commands/step.ts b/packages/cli-workflow/src/commands/step.ts index 334e2f9..c0c1ec5 100644 --- a/packages/cli-workflow/src/commands/step.ts +++ b/packages/cli-workflow/src/commands/step.ts @@ -311,7 +311,10 @@ export async function cmdStepRead( if (promptNode === null) { return `# Step ${stepHash}\n\n_Prompt CAS node not found: ${promptRef}_`; } - const promptText = typeof promptNode.payload === "string" ? promptNode.payload : JSON.stringify(promptNode.payload); + const promptText = + typeof promptNode.payload === "string" + ? promptNode.payload + : JSON.stringify(promptNode.payload); return `# Step ${stepHash}\n\n**Role:** ${payload.role}\n**Agent:** ${payload.agent}\n\n## Prompt\n\n${promptText}`; } diff --git a/packages/workflow-agent-builtin/src/agent.ts b/packages/workflow-agent-builtin/src/agent.ts index 2486956..ee6cb85 100644 --- a/packages/workflow-agent-builtin/src/agent.ts +++ b/packages/workflow-agent-builtin/src/agent.ts @@ -94,7 +94,12 @@ async function runBuiltinWithMessages( session.startedAtMs, ); - return { output: stripPreamble(loopResult.finalText), detailHash, sessionId: session.sessionId, assembledPrompt: "" }; + return { + output: stripPreamble(loopResult.finalText), + detailHash, + sessionId: session.sessionId, + assembledPrompt: "", + }; } async function runBuiltin(ctx: AgentContext): Promise { diff --git a/packages/workflow-agent-claude-code/src/claude-code.ts b/packages/workflow-agent-claude-code/src/claude-code.ts index d8bc1f7..cdbe191 100644 --- a/packages/workflow-agent-claude-code/src/claude-code.ts +++ b/packages/workflow-agent-claude-code/src/claude-code.ts @@ -120,7 +120,11 @@ function spawnClaudeResume( return spawnClaude(args); } -async function processClaudeOutput(stdout: string, store: Store, assembledPrompt: string): Promise { +async function processClaudeOutput( + stdout: string, + store: Store, + assembledPrompt: string, +): Promise { const parsed = parseClaudeCodeStreamOutput(stdout); if (parsed !== null) { diff --git a/packages/workflow-dashboard/src/editor/model/edit-node-view.ts b/packages/workflow-dashboard/src/editor/model/edit-node-view.ts index 38859fb..18b66a0 100644 --- a/packages/workflow-dashboard/src/editor/model/edit-node-view.ts +++ b/packages/workflow-dashboard/src/editor/model/edit-node-view.ts @@ -14,7 +14,7 @@ export const editNodeViewModel = define.view("editNodeView", editNodeView, (set, function start(nodeId: string) { const [nodes] = model.use(nodesModel); const node = nodes.find((n) => n.id === nodeId); - if (!node || node.type !== "role") return; + if (node?.type !== "role") return; set({ node: node as WorkNode<"role"> }); } diff --git a/packages/workflow-dashboard/src/editor/trans/trans-out.ts b/packages/workflow-dashboard/src/editor/trans/trans-out.ts index 7ea613d..a0e94e4 100644 --- a/packages/workflow-dashboard/src/editor/trans/trans-out.ts +++ b/packages/workflow-dashboard/src/editor/trans/trans-out.ts @@ -40,7 +40,7 @@ function traverse( visited.add(nodeId); const node = nodeMap.get(nodeId); - if (!node || node.type !== "role") return; + if (node?.type !== "role") return; const roleNode = node as WorkNode<"role">; const outEdges = outgoingEdges.get(nodeId) ?? []; diff --git a/packages/workflow-protocol/src/__tests__/types.test.ts b/packages/workflow-protocol/src/__tests__/types.test.ts index 444ee06..ed8a1bc 100644 --- a/packages/workflow-protocol/src/__tests__/types.test.ts +++ b/packages/workflow-protocol/src/__tests__/types.test.ts @@ -25,7 +25,7 @@ describe("Protocol types for thread/edge location", () => { edgePrompt: "Plan the implementation", startedAtMs: Date.now(), completedAtMs: Date.now() + 1000, - assembledPrompt: null, + assembledPrompt: null, cwd: "/home/user/project", }; diff --git a/packages/workflow-util/src/index.ts b/packages/workflow-util/src/index.ts index 6eac6ce..55c7780 100644 --- a/packages/workflow-util/src/index.ts +++ b/packages/workflow-util/src/index.ts @@ -1,9 +1,9 @@ export { generateActorReference } from "./actor-reference.js"; -export { generateBootstrapReference } from "./bootstrap-reference.js"; export { generateAdapterReference } from "./adapter-reference.js"; export { generateArchitectureReference } from "./architecture-reference.js"; export { generateAuthorReference } from "./author-reference.js"; export { encodeUint64AsCrockford } from "./base32.js"; +export { generateBootstrapReference } from "./bootstrap-reference.js"; export { generateCliReference } from "./cli-reference.js"; export { generateDeveloperReference } from "./developer-reference.js"; export { env } from "./env.js";