@united-workforce/cli 0.3.0 → 0.4.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/README.md +15 -8
- package/dist/__tests__/adapter-json-roundtrip.test.js +1 -1
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
- package/dist/__tests__/build-step-entry.test.d.ts +2 -0
- package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
- package/dist/__tests__/build-step-entry.test.js +173 -0
- package/dist/__tests__/build-step-entry.test.js.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
- package/dist/__tests__/config.test.js +26 -302
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +7 -6
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js +20 -23
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +9 -50
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/pid-recycling.test.d.ts +2 -0
- package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +271 -0
- package/dist/__tests__/pid-recycling.test.js.map +1 -0
- package/dist/__tests__/prompt.test.js +321 -0
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +4 -4
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/setup-agent-discovery.test.js +21 -30
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-complexity.test.js +2 -168
- package/dist/__tests__/setup-complexity.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
- package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
- package/dist/__tests__/setup-no-llm.test.js +52 -0
- package/dist/__tests__/setup-no-llm.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +24 -27
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.d.ts +2 -0
- package/dist/__tests__/step-ask.test.d.ts.map +1 -0
- package/dist/__tests__/step-ask.test.js +499 -0
- package/dist/__tests__/step-ask.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.js +1 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +2 -0
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/store-global-cas.test.js +2 -2
- package/dist/__tests__/store-global-cas.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +9 -9
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +6 -6
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-list-filters.test.js +344 -9
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-poke.test.d.ts +2 -0
- package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
- package/dist/__tests__/thread-poke.test.js +412 -0
- package/dist/__tests__/thread-poke.test.js.map +1 -0
- package/dist/__tests__/thread-resume.test.js +10 -14
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +17 -28
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-suspend-step.test.js +8 -14
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +10 -22
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/thread.test.js +4 -4
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +49 -21
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
- package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-list-recursive.test.js +283 -0
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +36 -21
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-show-resolution.test.js +210 -0
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
- package/dist/__tests__/workflow-validate.test.d.ts +2 -0
- package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-validate.test.js +687 -0
- package/dist/__tests__/workflow-validate.test.js.map +1 -0
- package/dist/background/background.d.ts +22 -1
- package/dist/background/background.d.ts.map +1 -1
- package/dist/background/background.js +83 -6
- package/dist/background/background.js.map +1 -1
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/types.d.ts +1 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/cli.js +66 -31
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +3 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +7 -33
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +15 -2
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +7 -39
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +27 -302
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +44 -1
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +255 -11
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +16 -3
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +379 -140
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +9 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +130 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +31 -17
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +4 -16
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/moderator/index.d.ts +1 -2
- package/dist/moderator/index.d.ts.map +1 -1
- package/dist/moderator/index.js +0 -1
- package/dist/moderator/index.js.map +1 -1
- package/dist/moderator/types.d.ts +6 -10
- package/dist/moderator/types.d.ts.map +1 -1
- package/dist/moderator/types.js +1 -3
- package/dist/moderator/types.js.map +1 -1
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +5 -3
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +28 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +75 -16
- package/dist/store.js.map +1 -1
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +83 -66
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -1
- package/package.json +8 -10
- package/src/__tests__/adapter-json-roundtrip.test.ts +1 -1
- package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
- package/src/__tests__/build-step-entry.test.ts +203 -0
- package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
- package/src/__tests__/config.test.ts +33 -321
- package/src/__tests__/current-role.test.ts +7 -6
- package/src/__tests__/e2e-mock-agent.test.ts +20 -23
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
- package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
- package/src/__tests__/moderator-evaluate.test.ts +9 -52
- package/src/__tests__/pid-recycling.test.ts +328 -0
- package/src/__tests__/prompt.test.ts +397 -0
- package/src/__tests__/resolve-head-hash.test.ts +4 -4
- package/src/__tests__/setup-agent-discovery.test.ts +26 -51
- package/src/__tests__/setup-complexity.test.ts +1 -203
- package/src/__tests__/setup-no-llm.test.ts +68 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +24 -30
- package/src/__tests__/step-ask.test.ts +670 -0
- package/src/__tests__/step-show-json.test.ts +1 -0
- package/src/__tests__/step-timing.test.ts +2 -0
- package/src/__tests__/store-global-cas.test.ts +2 -2
- package/src/__tests__/store-unified-threads.test.ts +9 -9
- package/src/__tests__/thread-cancel-status.test.ts +6 -6
- package/src/__tests__/thread-list-filters.test.ts +434 -8
- package/src/__tests__/thread-poke.test.ts +545 -0
- package/src/__tests__/thread-resume.test.ts +10 -14
- package/src/__tests__/thread-show-status.test.ts +17 -29
- package/src/__tests__/thread-suspend-step.test.ts +8 -14
- package/src/__tests__/thread-suspended-display.test.ts +10 -22
- package/src/__tests__/thread.test.ts +4 -4
- package/src/__tests__/validate-semantic.test.ts +59 -31
- package/src/__tests__/workflow-list-recursive.test.ts +370 -0
- package/src/__tests__/workflow-resolution.test.ts +39 -21
- package/src/__tests__/workflow-show-resolution.test.ts +285 -0
- package/src/__tests__/workflow-validate.test.ts +806 -0
- package/src/background/background.ts +88 -6
- package/src/background/index.ts +2 -0
- package/src/background/types.ts +1 -0
- package/src/cli.ts +97 -47
- package/src/commands/config.ts +7 -35
- package/src/commands/prompt.ts +15 -2
- package/src/commands/setup.ts +29 -357
- package/src/commands/step.ts +339 -12
- package/src/commands/thread.ts +463 -169
- package/src/commands/workflow.ts +159 -4
- package/src/moderator/__tests__/evaluate.test.ts +34 -17
- package/src/moderator/evaluate.ts +5 -17
- package/src/moderator/index.ts +1 -6
- package/src/moderator/types.ts +6 -14
- package/src/schemas.ts +13 -3
- package/src/store.ts +86 -20
- package/src/validate-semantic.ts +109 -78
- package/src/validate.ts +27 -0
- package/dist/__tests__/setup-validate.test.d.ts +0 -2
- package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
- package/dist/__tests__/setup-validate.test.js +0 -108
- package/dist/__tests__/setup-validate.test.js.map +0 -1
- package/src/__tests__/setup-validate.test.ts +0 -148
- /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { CasRef, WorkflowPayload } from "@united-workforce/protocol";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
6
|
+
import { stringify } from "yaml";
|
|
7
|
+
import { cmdWorkflowShow } from "../commands/workflow.js";
|
|
8
|
+
import type { UwfStore } from "../store.js";
|
|
9
|
+
import { createUwfStore, saveWorkflowRegistry } from "../store.js";
|
|
10
|
+
|
|
11
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
async function makeUwfStore(storageRoot: string): Promise<UwfStore> {
|
|
14
|
+
const casDir = join(storageRoot, "cas");
|
|
15
|
+
await mkdir(casDir, { recursive: true });
|
|
16
|
+
process.env.OCAS_HOME = casDir;
|
|
17
|
+
return createUwfStore(storageRoot);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeMinimalPayload(name: string, description: string): WorkflowPayload {
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
name,
|
|
24
|
+
description,
|
|
25
|
+
roles: {
|
|
26
|
+
worker: {
|
|
27
|
+
description: "worker role",
|
|
28
|
+
goal: "do work",
|
|
29
|
+
capabilities: [],
|
|
30
|
+
procedure: "",
|
|
31
|
+
output: "",
|
|
32
|
+
frontmatter: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
$status: { const: "done" },
|
|
36
|
+
},
|
|
37
|
+
required: ["$status"],
|
|
38
|
+
} as unknown as CasRef,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
graph: {
|
|
42
|
+
$START: {
|
|
43
|
+
new: { role: "worker", prompt: "start working", location: null },
|
|
44
|
+
resume: { role: "worker", prompt: "resume working", location: null },
|
|
45
|
+
},
|
|
46
|
+
worker: { done: { role: "$END", prompt: "done", location: null } },
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function storeWorkflow(uwf: UwfStore, name: string): Promise<CasRef> {
|
|
52
|
+
const payload = makeMinimalPayload(name, "Test workflow");
|
|
53
|
+
return await uwf.store.cas.put(uwf.schemas.workflow, payload);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function createWorkflowYaml(name: string, version: string | null = null): Promise<string> {
|
|
57
|
+
const payload = makeMinimalPayload(
|
|
58
|
+
name,
|
|
59
|
+
version !== null ? `Test workflow (${version})` : "Test workflow",
|
|
60
|
+
);
|
|
61
|
+
const yaml = stringify(payload);
|
|
62
|
+
return yaml;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── fixture ───────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
let tmpDir: string;
|
|
68
|
+
let storageRoot: string;
|
|
69
|
+
let projectRoot: string;
|
|
70
|
+
|
|
71
|
+
beforeEach(async () => {
|
|
72
|
+
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-wf-show-test-"));
|
|
73
|
+
storageRoot = join(tmpDir, "storage");
|
|
74
|
+
projectRoot = join(tmpDir, "project");
|
|
75
|
+
await mkdir(storageRoot, { recursive: true });
|
|
76
|
+
await mkdir(projectRoot, { recursive: true });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterEach(async () => {
|
|
80
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ── Strategy 1: CAS Hash Resolution ───────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
describe("Strategy 1: CAS Hash Resolution", () => {
|
|
86
|
+
test("should resolve valid 13-char Crockford Base32 hash", async () => {
|
|
87
|
+
const uwf = await makeUwfStore(storageRoot);
|
|
88
|
+
const hash = await storeWorkflow(uwf, "test-workflow");
|
|
89
|
+
|
|
90
|
+
const result = await cmdWorkflowShow(storageRoot, hash, projectRoot);
|
|
91
|
+
|
|
92
|
+
expect(result.hash).toBe(hash);
|
|
93
|
+
expect(result.payload.name).toBe("test-workflow");
|
|
94
|
+
expect(result.name).toBeNull(); // not in registry
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("should fail on invalid hash format (non-Crockford characters)", async () => {
|
|
98
|
+
await makeUwfStore(storageRoot);
|
|
99
|
+
|
|
100
|
+
await expect(cmdWorkflowShow(storageRoot, "123456789ABCD", projectRoot)).rejects.toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("should fail on valid-format hash not present in CAS", async () => {
|
|
104
|
+
await makeUwfStore(storageRoot);
|
|
105
|
+
const fakeHash = "0000000000000"; // valid format, doesn't exist
|
|
106
|
+
|
|
107
|
+
await expect(cmdWorkflowShow(storageRoot, fakeHash, projectRoot)).rejects.toThrow();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ── Strategy 2: File Path Resolution ──────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
describe("Strategy 2: File Path Resolution", () => {
|
|
114
|
+
test("should load workflow from absolute file path", async () => {
|
|
115
|
+
await makeUwfStore(storageRoot);
|
|
116
|
+
const yamlPath = join(tmpDir, "test-workflow.yaml");
|
|
117
|
+
await writeFile(yamlPath, await createWorkflowYaml("test-workflow"));
|
|
118
|
+
|
|
119
|
+
const result = await cmdWorkflowShow(storageRoot, yamlPath, projectRoot);
|
|
120
|
+
|
|
121
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
122
|
+
expect(result.payload.name).toBe("test-workflow");
|
|
123
|
+
expect(result.payload.description).toBe("Test workflow");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("should load workflow from relative file path", async () => {
|
|
127
|
+
await makeUwfStore(storageRoot);
|
|
128
|
+
const yamlPath = "test-workflow.yaml";
|
|
129
|
+
await writeFile(join(projectRoot, yamlPath), await createWorkflowYaml("test-workflow"));
|
|
130
|
+
|
|
131
|
+
const result = await cmdWorkflowShow(storageRoot, yamlPath, projectRoot);
|
|
132
|
+
|
|
133
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
134
|
+
expect(result.payload.name).toBe("test-workflow");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("should fail when file path does not exist", async () => {
|
|
138
|
+
await makeUwfStore(storageRoot);
|
|
139
|
+
|
|
140
|
+
await expect(cmdWorkflowShow(storageRoot, "./nonexistent.yaml", projectRoot)).rejects.toThrow();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ── Strategy 3: Local Discovery (Parent Traversal) ────────────────────────────
|
|
145
|
+
|
|
146
|
+
describe("Strategy 3: Local Discovery", () => {
|
|
147
|
+
test("should find workflow in current directory .workflows/", async () => {
|
|
148
|
+
await makeUwfStore(storageRoot);
|
|
149
|
+
const workflowDir = join(projectRoot, ".workflows");
|
|
150
|
+
await mkdir(workflowDir, { recursive: true });
|
|
151
|
+
await writeFile(join(workflowDir, "solve-issue.yaml"), await createWorkflowYaml("solve-issue"));
|
|
152
|
+
|
|
153
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", projectRoot);
|
|
154
|
+
|
|
155
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
156
|
+
expect(result.payload.name).toBe("solve-issue");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("should find workflow in parent directory .workflows/", async () => {
|
|
160
|
+
await makeUwfStore(storageRoot);
|
|
161
|
+
const workflowDir = join(projectRoot, ".workflows");
|
|
162
|
+
await mkdir(workflowDir, { recursive: true });
|
|
163
|
+
await writeFile(join(workflowDir, "solve-issue.yaml"), await createWorkflowYaml("solve-issue"));
|
|
164
|
+
|
|
165
|
+
const subdir = join(projectRoot, "packages", "cli", "src");
|
|
166
|
+
await mkdir(subdir, { recursive: true });
|
|
167
|
+
|
|
168
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", subdir);
|
|
169
|
+
|
|
170
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
171
|
+
expect(result.payload.name).toBe("solve-issue");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("should prefer .workflows/ over .workflow/ directory", async () => {
|
|
175
|
+
await makeUwfStore(storageRoot);
|
|
176
|
+
const primaryDir = join(projectRoot, ".workflows");
|
|
177
|
+
const legacyDir = join(projectRoot, ".workflow");
|
|
178
|
+
await mkdir(primaryDir, { recursive: true });
|
|
179
|
+
await mkdir(legacyDir, { recursive: true });
|
|
180
|
+
|
|
181
|
+
await writeFile(
|
|
182
|
+
join(primaryDir, "solve-issue.yaml"),
|
|
183
|
+
await createWorkflowYaml("solve-issue", "primary"),
|
|
184
|
+
);
|
|
185
|
+
await writeFile(
|
|
186
|
+
join(legacyDir, "solve-issue.yaml"),
|
|
187
|
+
await createWorkflowYaml("solve-issue", "legacy"),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", projectRoot);
|
|
191
|
+
|
|
192
|
+
expect(result.payload.description).toBe("Test workflow (primary)");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("should find workflow in folder-based layout (.workflows/<name>/index.yaml)", async () => {
|
|
196
|
+
await makeUwfStore(storageRoot);
|
|
197
|
+
const workflowDir = join(projectRoot, ".workflows", "solve-issue");
|
|
198
|
+
await mkdir(workflowDir, { recursive: true });
|
|
199
|
+
await writeFile(join(workflowDir, "index.yaml"), await createWorkflowYaml("solve-issue"));
|
|
200
|
+
|
|
201
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", projectRoot);
|
|
202
|
+
|
|
203
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
204
|
+
expect(result.payload.name).toBe("solve-issue");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("should resolve from legacy .workflow/ when .workflows/ is absent", async () => {
|
|
208
|
+
await makeUwfStore(storageRoot);
|
|
209
|
+
const legacyDir = join(projectRoot, ".workflow");
|
|
210
|
+
await mkdir(legacyDir, { recursive: true });
|
|
211
|
+
await writeFile(join(legacyDir, "solve-issue.yaml"), await createWorkflowYaml("solve-issue"));
|
|
212
|
+
|
|
213
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", projectRoot);
|
|
214
|
+
|
|
215
|
+
expect(result.hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
|
|
216
|
+
expect(result.payload.name).toBe("solve-issue");
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ── Strategy 4: Global Registry Fallback ──────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
describe("Strategy 4: Global Registry Resolution", () => {
|
|
223
|
+
test("should resolve workflow from global registry when not found locally", async () => {
|
|
224
|
+
const uwf = await makeUwfStore(storageRoot);
|
|
225
|
+
const hash = await storeWorkflow(uwf, "deploy-pipeline");
|
|
226
|
+
saveWorkflowRegistry(uwf.varStore, "deploy-pipeline", hash);
|
|
227
|
+
|
|
228
|
+
const isolatedRoot = join(tmpDir, "isolated");
|
|
229
|
+
await mkdir(isolatedRoot, { recursive: true });
|
|
230
|
+
|
|
231
|
+
const result = await cmdWorkflowShow(storageRoot, "deploy-pipeline", isolatedRoot);
|
|
232
|
+
|
|
233
|
+
expect(result.hash).toBe(hash);
|
|
234
|
+
expect(result.name).toBe("deploy-pipeline"); // found in registry
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("should fail when workflow not found in any strategy", async () => {
|
|
238
|
+
await makeUwfStore(storageRoot);
|
|
239
|
+
|
|
240
|
+
await expect(cmdWorkflowShow(storageRoot, "nonexistent", tmpDir)).rejects.toThrow();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ── Strategy Priority Order ───────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
describe("Resolution Priority", () => {
|
|
247
|
+
test("should use explicit file path over local discovery", async () => {
|
|
248
|
+
await makeUwfStore(storageRoot);
|
|
249
|
+
|
|
250
|
+
// Setup: Create workflow in .workflows/ AND as explicit file
|
|
251
|
+
const workflowDir = join(projectRoot, ".workflows");
|
|
252
|
+
await mkdir(workflowDir, { recursive: true });
|
|
253
|
+
await writeFile(
|
|
254
|
+
join(workflowDir, "solve-issue.yaml"),
|
|
255
|
+
await createWorkflowYaml("solve-issue", "discovery"),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const explicitPath = join(projectRoot, "custom-solve-issue.yaml");
|
|
259
|
+
await writeFile(explicitPath, await createWorkflowYaml("custom-solve-issue", "explicit"));
|
|
260
|
+
|
|
261
|
+
// Execute with explicit path
|
|
262
|
+
const result = await cmdWorkflowShow(storageRoot, explicitPath, projectRoot);
|
|
263
|
+
|
|
264
|
+
expect(result.payload.description).toBe("Test workflow (explicit)");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("should use local discovery over global registry", async () => {
|
|
268
|
+
const uwf = await makeUwfStore(storageRoot);
|
|
269
|
+
|
|
270
|
+
// Setup: Register globally
|
|
271
|
+
const payload = makeMinimalPayload("solve-issue", "Test workflow (global)");
|
|
272
|
+
const globalHash = await uwf.store.cas.put(uwf.schemas.workflow, payload);
|
|
273
|
+
saveWorkflowRegistry(uwf.varStore, "solve-issue", globalHash);
|
|
274
|
+
|
|
275
|
+
// Setup: Create local .workflows/
|
|
276
|
+
const workflowDir = join(projectRoot, ".workflows");
|
|
277
|
+
await mkdir(workflowDir, { recursive: true });
|
|
278
|
+
const localYaml = await createWorkflowYaml("solve-issue", "local");
|
|
279
|
+
await writeFile(join(workflowDir, "solve-issue.yaml"), localYaml);
|
|
280
|
+
|
|
281
|
+
const result = await cmdWorkflowShow(storageRoot, "solve-issue", projectRoot);
|
|
282
|
+
|
|
283
|
+
expect(result.payload.description).toBe("Test workflow (local)");
|
|
284
|
+
});
|
|
285
|
+
});
|