dev-flow-deepseek 0.1.0 → 0.5.2
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 +111 -113
- package/cordis.patch.yml +2 -26
- package/lib/authorization.mjs +117 -0
- package/lib/index.mjs +114 -0
- package/lib/paths.mjs +121 -0
- package/lib/runtime.mjs +103 -0
- package/lib/tool-names.mjs +43 -0
- package/package.json +40 -43
- package/runtime/darwin-arm64/dev-flow +0 -0
- package/skills/dev-flow/SKILL.md +412 -57
- package/skills/dev-flow/references/method-profiles.md +153 -0
- package/skills/dev-flow/references/node-payloads.md +305 -0
- package/lib/backend-client.js +0 -139
- package/lib/canonical-json.js +0 -61
- package/lib/compatibility.js +0 -47
- package/lib/environment.js +0 -50
- package/lib/proxy.js +0 -87
- package/lib/result-envelope.js +0 -105
- package/skills/dev-flow/references/activation-and-routing.md +0 -89
package/lib/proxy.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { APPROVED_RAW_TOOLS, MAX_ADAPTER_DIAGNOSTIC_BYTES, PACKAGE_NAME, PACKAGE_VERSION, STARTUP_EXIT_CODE } from "./compatibility.js";
|
|
3
|
-
import { BackendClient, BackendStartupError } from "./backend-client.js";
|
|
4
|
-
import { readBackendConfiguration } from "./environment.js";
|
|
5
|
-
import { createAdapterErrorResult, projectBackendResult } from "./result-envelope.js";
|
|
6
|
-
import process from "node:process";
|
|
7
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
-
|
|
11
|
-
//#region src/proxy.ts
|
|
12
|
-
function isApprovedTool(name) {
|
|
13
|
-
return APPROVED_RAW_TOOLS.includes(name);
|
|
14
|
-
}
|
|
15
|
-
function startupDiagnostic(code) {
|
|
16
|
-
const text = `dsh-dev-flow projection proxy startup failed (${code}).`;
|
|
17
|
-
if (Buffer.byteLength(text, "utf8") > 4096) return "dsh-dev-flow projection proxy startup failed.";
|
|
18
|
-
return text;
|
|
19
|
-
}
|
|
20
|
-
async function startProxy() {
|
|
21
|
-
let backend;
|
|
22
|
-
try {
|
|
23
|
-
backend = await BackendClient.connect(readBackendConfiguration());
|
|
24
|
-
} catch (error) {
|
|
25
|
-
const code = error instanceof BackendStartupError ? error.code : "BACKEND_START_FAILED";
|
|
26
|
-
process.stderr.write(`${startupDiagnostic(code)}\n`);
|
|
27
|
-
process.exitCode = 2;
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const server = new Server({
|
|
31
|
-
name: PACKAGE_NAME,
|
|
32
|
-
version: PACKAGE_VERSION
|
|
33
|
-
}, { capabilities: { tools: {} } });
|
|
34
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...backend.tools] }));
|
|
35
|
-
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
36
|
-
const name = request.params.name;
|
|
37
|
-
if (!isApprovedTool(name)) throw new TypeError("Requested tool is not exposed by this proxy.");
|
|
38
|
-
let upstream;
|
|
39
|
-
try {
|
|
40
|
-
upstream = await backend.call(name, request.params.arguments ?? {}, extra.signal);
|
|
41
|
-
} catch {
|
|
42
|
-
const code = extra.signal.aborted ? "REQUEST_CANCELLED" : "UPSTREAM_CALL_FAILED";
|
|
43
|
-
return createAdapterErrorResult(name, code);
|
|
44
|
-
}
|
|
45
|
-
try {
|
|
46
|
-
return projectBackendResult(name, upstream);
|
|
47
|
-
} catch {
|
|
48
|
-
return createAdapterErrorResult(name, "UPSTREAM_RESULT_INVALID");
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
const outwardTransport = new StdioServerTransport();
|
|
52
|
-
let shuttingDown = false;
|
|
53
|
-
const shutdown = async (exitCode) => {
|
|
54
|
-
if (shuttingDown) return;
|
|
55
|
-
shuttingDown = true;
|
|
56
|
-
if (exitCode !== 0) process.exitCode = exitCode;
|
|
57
|
-
await Promise.allSettled([server.close(), backend.close()]);
|
|
58
|
-
};
|
|
59
|
-
process.once("SIGINT", () => {
|
|
60
|
-
shutdown(0);
|
|
61
|
-
});
|
|
62
|
-
process.once("SIGTERM", () => {
|
|
63
|
-
shutdown(0);
|
|
64
|
-
});
|
|
65
|
-
backend.closed.then(() => {
|
|
66
|
-
if (!shuttingDown) shutdown(1);
|
|
67
|
-
});
|
|
68
|
-
try {
|
|
69
|
-
await server.connect(outwardTransport);
|
|
70
|
-
} catch {
|
|
71
|
-
process.stderr.write(`${startupDiagnostic("BACKEND_INITIALIZE_FAILED")}\n`);
|
|
72
|
-
await shutdown(2);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const protocolClose = outwardTransport.onclose;
|
|
76
|
-
outwardTransport.onclose = () => {
|
|
77
|
-
protocolClose?.();
|
|
78
|
-
if (!shuttingDown) shutdown(0);
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
startProxy().catch(() => {
|
|
82
|
-
process.stderr.write(`${startupDiagnostic("BACKEND_INITIALIZE_FAILED")}\n`);
|
|
83
|
-
process.exitCode = 2;
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
//#endregion
|
|
87
|
-
export { };
|
package/lib/result-envelope.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { ADAPTER_ERROR_CODES, ADAPTER_ERROR_SCHEMA, APPROVED_RAW_TOOLS, DEV_FLOW_RESULT_SCHEMA, MAX_ADAPTER_DIAGNOSTIC_BYTES } from "./compatibility.js";
|
|
2
|
-
import { canonicalJson } from "./canonical-json.js";
|
|
3
|
-
|
|
4
|
-
//#region src/result-envelope.ts
|
|
5
|
-
const REQUEST_ID_PATTERN = /^mcp-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
6
|
-
const ADAPTER_MESSAGES = Object.freeze({
|
|
7
|
-
BACKEND_START_FAILED: "The Dev Flow backend could not be started.",
|
|
8
|
-
BACKEND_INITIALIZE_FAILED: "The Dev Flow backend could not be initialized.",
|
|
9
|
-
BACKEND_CATALOG_INVALID: "The Dev Flow backend tool catalog is invalid.",
|
|
10
|
-
UPSTREAM_CALL_FAILED: "The Dev Flow backend call did not return a valid result.",
|
|
11
|
-
UPSTREAM_RESULT_INVALID: "The Dev Flow backend returned an invalid result.",
|
|
12
|
-
REQUEST_CANCELLED: "The Dev Flow backend call was cancelled before a valid result was received."
|
|
13
|
-
});
|
|
14
|
-
function isPlainObject(value) {
|
|
15
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
16
|
-
const prototype = Object.getPrototypeOf(value);
|
|
17
|
-
return prototype === Object.prototype || prototype === null;
|
|
18
|
-
}
|
|
19
|
-
function hasExactKeys(value, expected) {
|
|
20
|
-
const actual = Object.keys(value).sort();
|
|
21
|
-
const sortedExpected = [...expected].sort();
|
|
22
|
-
return actual.length === sortedExpected.length && actual.every((key, index) => key === sortedExpected[index]);
|
|
23
|
-
}
|
|
24
|
-
function invalid(reason) {
|
|
25
|
-
throw new TypeError(`Invalid Dev Flow result envelope: ${reason}.`);
|
|
26
|
-
}
|
|
27
|
-
function validateDevFlowEnvelope(value, invokedTool) {
|
|
28
|
-
if (!APPROVED_RAW_TOOLS.includes(invokedTool)) invalid("invoked tool is not approved");
|
|
29
|
-
if (!isPlainObject(value)) invalid("structuredContent is not a plain object");
|
|
30
|
-
if (!hasExactKeys(value, [
|
|
31
|
-
"schema",
|
|
32
|
-
"ok",
|
|
33
|
-
"tool",
|
|
34
|
-
"request_id",
|
|
35
|
-
"result",
|
|
36
|
-
"error"
|
|
37
|
-
])) invalid("top-level fields do not match the closed envelope");
|
|
38
|
-
if (value.schema !== "dev-flow-mcp-result/1.0.0") invalid("schema identity does not match");
|
|
39
|
-
if (value.tool !== invokedTool) invalid("tool identity does not match the invoked tool");
|
|
40
|
-
if (typeof value.request_id !== "string" || !REQUEST_ID_PATTERN.test(value.request_id)) invalid("request_id does not match the pinned grammar");
|
|
41
|
-
if (typeof value.ok !== "boolean") invalid("ok is not boolean");
|
|
42
|
-
if (value.ok) {
|
|
43
|
-
if (value.error !== null) invalid("successful envelope error is not null");
|
|
44
|
-
} else {
|
|
45
|
-
if (value.result !== null) invalid("error envelope result is not null");
|
|
46
|
-
if (!isPlainObject(value.error)) invalid("error is not a plain object");
|
|
47
|
-
if (!hasExactKeys(value.error, [
|
|
48
|
-
"code",
|
|
49
|
-
"message",
|
|
50
|
-
"details",
|
|
51
|
-
"recovery"
|
|
52
|
-
])) invalid("error fields do not match the closed union");
|
|
53
|
-
if (typeof value.error.code !== "string" || value.error.code.length === 0) invalid("error code is not a non-empty string");
|
|
54
|
-
if (typeof value.error.message !== "string" || value.error.message.length === 0) invalid("error message is not a non-empty string");
|
|
55
|
-
if (!isPlainObject(value.error.details)) invalid("error details is not a plain object");
|
|
56
|
-
if (value.error.recovery !== null && !isPlainObject(value.error.recovery)) invalid("error recovery is neither a plain object nor null");
|
|
57
|
-
}
|
|
58
|
-
canonicalJson(value);
|
|
59
|
-
return value;
|
|
60
|
-
}
|
|
61
|
-
function projectBackendResult(invokedTool, upstream) {
|
|
62
|
-
const envelope = validateDevFlowEnvelope(upstream.structuredContent, invokedTool);
|
|
63
|
-
return {
|
|
64
|
-
...upstream,
|
|
65
|
-
content: [{
|
|
66
|
-
type: "text",
|
|
67
|
-
text: canonicalJson(envelope)
|
|
68
|
-
}]
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function recoveryFor(tool) {
|
|
72
|
-
if (tool === "dev_flow_start_task") return Object.freeze(["dev_flow_find_tasks_for_path"]);
|
|
73
|
-
if (tool === "dev_flow_apply_action") return Object.freeze(["dev_flow_get_task", "dev_flow_get_next_action"]);
|
|
74
|
-
return Object.freeze([tool]);
|
|
75
|
-
}
|
|
76
|
-
function createAdapterErrorEnvelope(tool, code) {
|
|
77
|
-
if (!APPROVED_RAW_TOOLS.includes(tool)) throw new TypeError("Adapter error tool is not approved.");
|
|
78
|
-
if (!ADAPTER_ERROR_CODES.includes(code)) throw new TypeError("Adapter error code is not approved.");
|
|
79
|
-
const message = ADAPTER_MESSAGES[code];
|
|
80
|
-
if (Buffer.byteLength(message, "utf8") > 4096) throw new TypeError("Adapter diagnostic exceeds its fixed byte limit.");
|
|
81
|
-
return Object.freeze({
|
|
82
|
-
schema: ADAPTER_ERROR_SCHEMA,
|
|
83
|
-
ok: false,
|
|
84
|
-
tool,
|
|
85
|
-
error: Object.freeze({
|
|
86
|
-
code,
|
|
87
|
-
message,
|
|
88
|
-
mutation_uncertain: tool === "dev_flow_start_task" || tool === "dev_flow_apply_action",
|
|
89
|
-
recovery: recoveryFor(tool)
|
|
90
|
-
})
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
function createAdapterErrorResult(tool, code) {
|
|
94
|
-
const envelope = createAdapterErrorEnvelope(tool, code);
|
|
95
|
-
return {
|
|
96
|
-
content: [{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: canonicalJson(envelope)
|
|
99
|
-
}],
|
|
100
|
-
isError: true
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
//#endregion
|
|
105
|
-
export { createAdapterErrorEnvelope, createAdapterErrorResult, projectBackendResult, validateDevFlowEnvelope };
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# Activation and routing
|
|
2
|
-
|
|
3
|
-
These rules refine explicit `/dev-flow` task selection and recovery. They do
|
|
4
|
-
not define Controller actions, payload schemas, transitions, or terminal
|
|
5
|
-
criteria.
|
|
6
|
-
|
|
7
|
-
## Complete-result rule
|
|
8
|
-
|
|
9
|
-
Treat tool text as authority only when it is the complete canonical JSON
|
|
10
|
-
result. If any result mentions `spill`, `truncated`, `pruned`, `preview`, or
|
|
11
|
-
`Full formatted result stored at`, use a normal DSH file-reading tool to read
|
|
12
|
-
the complete saved text before parsing it. If the complete text cannot be
|
|
13
|
-
read, stop. Never infer an action, binding, payload schema, repository set,
|
|
14
|
-
recovery operation, or terminal result from a preview.
|
|
15
|
-
|
|
16
|
-
## Select or start
|
|
17
|
-
|
|
18
|
-
1. Call `mcp__dev-flow__dev_flow_find_tasks_for_path` with the exact current
|
|
19
|
-
canonical Git root. Inventory must be authoritative; stop if it is
|
|
20
|
-
unavailable or inconsistent.
|
|
21
|
-
2. Discovery does not prove complete repository membership. For every
|
|
22
|
-
candidate that might be selected, call
|
|
23
|
-
`mcp__dev-flow__dev_flow_get_next_action` and use its fresh authority.
|
|
24
|
-
3. Canonicalize every returned `repository_set.repositories[].path`. A
|
|
25
|
-
candidate is compatible only when its workflow is exactly `lite`, its
|
|
26
|
-
repository array has cardinality one, and that one canonical path equals
|
|
27
|
-
the current root. Reject subsets, supersets, different members,
|
|
28
|
-
multi-repository tasks, unrelated paths, and non-`lite` workflows.
|
|
29
|
-
4. Resume one compatible active task when the result is unambiguous. If more
|
|
30
|
-
than one is compatible, present their bounded identities and ask the user
|
|
31
|
-
to choose; never select by recency. If discovery returns only incompatible
|
|
32
|
-
tasks, report the conflict and ask the user to resolve it outside this MVP.
|
|
33
|
-
5. When authoritative discovery returns no candidate and the request is
|
|
34
|
-
substantive, call `mcp__dev-flow__dev_flow_start_task` with only the user
|
|
35
|
-
requirement, workflow `lite`, and a one-element repositories array holding
|
|
36
|
-
the current root. Omit custom task ID and contract fields.
|
|
37
|
-
|
|
38
|
-
Never merge candidates, convert workflows, cancel tasks, revise contracts, or
|
|
39
|
-
use an unexposed inventory or governance operation.
|
|
40
|
-
|
|
41
|
-
## Drive current authority
|
|
42
|
-
|
|
43
|
-
For each nonterminal result:
|
|
44
|
-
|
|
45
|
-
1. Reconfirm exact one-root repository equality.
|
|
46
|
-
2. Read the fresh action ID, objective, guidance, allowed effects, required
|
|
47
|
-
evidence, closed payload schema, and complete binding.
|
|
48
|
-
3. Perform only that action in the current repository with normal DSH tools.
|
|
49
|
-
4. Build a payload containing exactly the schema-declared fields.
|
|
50
|
-
5. Call `mcp__dev-flow__dev_flow_apply_action` with the exact task ID, action
|
|
51
|
-
ID, payload, and unmodified current binding.
|
|
52
|
-
6. Continue only from the fresh action returned by a successful mutation or a
|
|
53
|
-
new `mcp__dev-flow__dev_flow_get_next_action` read. Never reuse an old
|
|
54
|
-
binding.
|
|
55
|
-
|
|
56
|
-
When the Controller returns a terminal result, report its Delivery Dossier.
|
|
57
|
-
Do not announce completion before that authority exists.
|
|
58
|
-
|
|
59
|
-
## Errors and mutation uncertainty
|
|
60
|
-
|
|
61
|
-
A valid `dev-flow-mcp-result/1.0.0` envelope with `ok: false` remains a
|
|
62
|
-
Controller domain error. Follow its recovery only when the named operation is
|
|
63
|
-
one of the six exposed tools. If it requires a hidden governance,
|
|
64
|
-
cancellation, finding, listing, or contract operation, report that the task is
|
|
65
|
-
outside this MVP and stop.
|
|
66
|
-
|
|
67
|
-
An adapter envelope with schema `dsh-dev-flow-proxy-error/1.0.0` is not a
|
|
68
|
-
Controller result. For a failed read, wait until the six-tool surface is
|
|
69
|
-
healthy and repeat that read at most once. Never reinterpret an adapter error
|
|
70
|
-
as task state.
|
|
71
|
-
|
|
72
|
-
If `mcp__dev-flow__dev_flow_start_task` loses or corrupts its response, assume
|
|
73
|
-
neither success nor failure. Rediscover tasks for the current root and inspect
|
|
74
|
-
fresh compatible authority before considering another start. Do not create a
|
|
75
|
-
replacement for a task that may already exist.
|
|
76
|
-
|
|
77
|
-
If `mcp__dev-flow__dev_flow_apply_action` loses or corrupts its response,
|
|
78
|
-
assume neither success nor failure. First call
|
|
79
|
-
`mcp__dev-flow__dev_flow_get_task`, then
|
|
80
|
-
`mcp__dev-flow__dev_flow_get_next_action`, and compare the fresh revision,
|
|
81
|
-
action, and binding. Retry only when that authority proves the mutation did not
|
|
82
|
-
commit and the current binding still authorizes it. Never blindly replay a
|
|
83
|
-
mutation.
|
|
84
|
-
|
|
85
|
-
## Repository and product boundary
|
|
86
|
-
|
|
87
|
-
Keep the repository set fixed at the one current user-prepared worktree. Do not
|
|
88
|
-
manage Git topology or publishing. Do not install a runtime, add a UI, expose
|
|
89
|
-
another MCP tool, use remote transport, or store adapter-owned task state.
|