@lmzhen/dsh-evolution-merge 0.30.18 → 0.30.20
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 +21 -4
- package/data/minhash_params.json +1063 -0
- package/lib/index.js +7313 -3272
- package/lib/types/adapters/channel.d.ts +1 -0
- package/lib/types/adapters/channel.js +15 -1
- package/lib/types/adapters/engine-screen.d.ts +1 -1
- package/lib/types/adapters/engine-screen.js +5 -2
- package/lib/types/adapters/model-runner.d.ts +0 -5
- package/lib/types/adapters/model-runner.js +8 -2
- package/lib/types/adapters/model.js +2 -1
- package/lib/types/adapters/select.js +12 -8
- package/lib/types/approve-command.d.ts +12 -1
- package/lib/types/approve-command.js +39 -6
- package/lib/types/authoring-flow.d.ts +16 -0
- package/lib/types/authoring-flow.js +37 -13
- package/lib/types/channel-port.d.ts +3 -1
- package/lib/types/channel-port.js +8 -3
- package/lib/types/command-defs.d.ts +5 -0
- package/lib/types/command-defs.js +33 -0
- package/lib/types/command-surface.d.ts +1 -1
- package/lib/types/command-surface.js +10 -43
- package/lib/types/commands.d.ts +3 -4
- package/lib/types/commands.js +33 -72
- package/lib/types/commit-assembly.d.ts +14 -1
- package/lib/types/commit-assembly.js +65 -9
- package/lib/types/commit-execute.d.ts +7 -1
- package/lib/types/commit-execute.js +54 -22
- package/lib/types/commit-plan.d.ts +7 -1
- package/lib/types/commit-plan.js +4 -0
- package/lib/types/commit-receipt.d.ts +3 -0
- package/lib/types/contracts.d.ts +43 -0
- package/lib/types/doctor.d.ts +11 -1
- package/lib/types/doctor.js +81 -27
- package/lib/types/domain/authoring-manifest.d.ts +26 -0
- package/lib/types/domain/authoring-manifest.js +43 -0
- package/lib/types/domain/engine-criteria.d.ts +10 -0
- package/lib/types/domain/engine-criteria.js +11 -0
- package/lib/types/domain/engine-input.d.ts +10 -0
- package/lib/types/domain/engine-input.js +26 -0
- package/lib/types/domain/errors.d.ts +15 -1
- package/lib/types/domain/errors.js +23 -4
- package/lib/types/domain/evidence-hash.d.ts +3 -0
- package/lib/types/domain/evidence-hash.js +14 -0
- package/lib/types/domain/failure-route.d.ts +14 -0
- package/lib/types/domain/failure-route.js +12 -0
- package/lib/types/domain/live-facts.d.ts +31 -0
- package/lib/types/domain/live-facts.js +54 -0
- package/lib/types/domain/merge-limits.d.ts +18 -0
- package/lib/types/domain/merge-limits.js +22 -0
- package/lib/types/domain/merge-report.d.ts +41 -0
- package/lib/types/domain/merge-report.js +2 -0
- package/lib/types/domain/merge-snapshot.d.ts +99 -0
- package/lib/types/domain/merge-snapshot.js +82 -0
- package/lib/types/domain/pair-ledger.d.ts +34 -0
- package/lib/types/domain/pair-ledger.js +96 -0
- package/lib/types/domain/pre-1b-backup.d.ts +43 -0
- package/lib/types/domain/pre-1b-backup.js +140 -0
- package/lib/types/domain/provider-key.d.ts +2 -0
- package/lib/types/domain/provider-key.js +11 -0
- package/lib/types/domain/read-probe.d.ts +5 -0
- package/lib/types/domain/read-probe.js +11 -0
- package/lib/types/domain/reconcile.d.ts +47 -0
- package/lib/types/domain/reconcile.js +178 -0
- package/lib/types/domain/resolve-pair.d.ts +2 -9
- package/lib/types/domain/resolve-pair.js +2 -15
- package/lib/types/domain/row-v4.d.ts +148 -0
- package/lib/types/domain/row-v4.js +159 -0
- package/lib/types/domain/run-coordinator.d.ts +51 -0
- package/lib/types/domain/run-coordinator.js +45 -0
- package/lib/types/domain/schema4.d.ts +53 -0
- package/lib/types/domain/schema4.js +129 -0
- package/lib/types/domain/selection-policy.d.ts +14 -0
- package/lib/types/domain/selection-policy.js +6 -0
- package/lib/types/domain/skipped-expiry.d.ts +5 -0
- package/lib/types/domain/skipped-expiry.js +16 -0
- package/lib/types/domain/stage-aggregate.d.ts +96 -0
- package/lib/types/domain/stage-aggregate.js +293 -0
- package/lib/types/domain/staging-index.d.ts +1 -0
- package/lib/types/domain/state-bootstrap.d.ts +43 -0
- package/lib/types/domain/state-bootstrap.js +267 -0
- package/lib/types/domain/state-v3.d.ts +3 -14
- package/lib/types/domain/state-v3.js +3 -79
- package/lib/types/domain/undo-journal.d.ts +19 -0
- package/lib/types/domain/undo-journal.js +20 -0
- package/lib/types/domain/undo-receipt.d.ts +18 -0
- package/lib/types/domain/undo-receipt.js +22 -0
- package/lib/types/domain/validation-plan.d.ts +65 -0
- package/lib/types/domain/validation-plan.js +49 -0
- package/lib/types/facts.d.ts +3 -1
- package/lib/types/facts.js +13 -16
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +181 -68
- package/lib/types/merge-command.d.ts +3 -1
- package/lib/types/merge-command.js +10 -6
- package/lib/types/merge-routes.d.ts +10 -5
- package/lib/types/merge-routes.js +72 -7
- package/lib/types/merge-tool.d.ts +1 -1
- package/lib/types/merge-tool.js +2 -2
- package/lib/types/metrics.js +1 -1
- package/lib/types/new-child-authoring.d.ts +2 -0
- package/lib/types/new-child-authoring.js +57 -10
- package/lib/types/params.d.ts +2 -4
- package/lib/types/params.js +13 -0
- package/lib/types/pending-actions.d.ts +2 -2
- package/lib/types/pending-actions.js +4 -4
- package/lib/types/prompts.d.ts +2 -4
- package/lib/types/prompts.js +21 -8
- package/lib/types/read-commands.d.ts +3 -3
- package/lib/types/read-commands.js +19 -28
- package/lib/types/recovery-coordinator.d.ts +34 -0
- package/lib/types/recovery-coordinator.js +84 -0
- package/lib/types/recovery-projection.d.ts +31 -0
- package/lib/types/recovery-projection.js +96 -0
- package/lib/types/resume-command.d.ts +5 -1
- package/lib/types/resume-command.js +46 -13
- package/lib/types/revision.d.ts +17 -0
- package/lib/types/revision.js +39 -3
- package/lib/types/round2-outcome.d.ts +2 -1
- package/lib/types/round2-outcome.js +59 -11
- package/lib/types/run-shell.d.ts +16 -2
- package/lib/types/run-shell.js +49 -5
- package/lib/types/runtime.d.ts +0 -5
- package/lib/types/runtime.js +274 -89
- package/lib/types/scan-command.d.ts +1 -0
- package/lib/types/scan-command.js +2 -0
- package/lib/types/scan-ports.d.ts +18 -0
- package/lib/types/scan-ports.js +49 -23
- package/lib/types/scan-run.d.ts +17 -0
- package/lib/types/scan-run.js +143 -48
- package/lib/types/stage-tool-registry.d.ts +7 -0
- package/lib/types/stage-tool-registry.js +12 -1
- package/lib/types/state.d.ts +5 -5
- package/lib/types/state.js +22 -69
- package/lib/types/status.d.ts +6 -1
- package/lib/types/status.js +47 -0
- package/lib/types/surface/body.d.ts +4 -0
- package/lib/types/surface/body.js +16 -0
- package/lib/types/surface/merge-view.d.ts +14 -0
- package/lib/types/surface/merge-view.js +126 -0
- package/lib/types/undo-operation.d.ts +24 -0
- package/lib/types/undo-operation.js +135 -0
- package/lib/types/wire.d.ts +22 -3
- package/package.json +6 -4
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
function usageOf(value) {
|
|
2
|
+
if (value === null || typeof value !== 'object')
|
|
3
|
+
return undefined;
|
|
4
|
+
const raw = value;
|
|
5
|
+
const num = (input) => typeof input === 'number' && Number.isFinite(input) ? input : undefined;
|
|
6
|
+
const inputTokens = num(raw.inputTokens);
|
|
7
|
+
const outputTokens = num(raw.outputTokens);
|
|
8
|
+
const totalTokens = num(raw.totalTokens);
|
|
9
|
+
const costCny = num(raw.costCny);
|
|
10
|
+
if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined && costCny === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
return { ...(inputTokens === undefined ? {} : { inputTokens }), ...(outputTokens === undefined ? {} : { outputTokens }), ...(totalTokens === undefined ? {} : { totalTokens }), ...(costCny === undefined ? {} : { costCny }) };
|
|
13
|
+
}
|
|
1
14
|
function convert(outcome) {
|
|
2
15
|
if (outcome.kind !== 'ok')
|
|
3
16
|
return outcome;
|
|
17
|
+
const usage = usageOf(outcome.turn.usage);
|
|
4
18
|
return {
|
|
5
19
|
kind: 'ok',
|
|
6
20
|
childId: String(outcome.childId),
|
|
7
|
-
turn: { text: outcome.turn.text, stopReason: outcome.turn.stopReason },
|
|
21
|
+
turn: { text: outcome.turn.text, stopReason: outcome.turn.stopReason, ...(usage === undefined ? {} : { usage }) },
|
|
8
22
|
};
|
|
9
23
|
}
|
|
10
24
|
export function subagentChannelAdapter(channel) {
|
|
@@ -47,7 +47,7 @@ export interface EngineScreeningDeps {
|
|
|
47
47
|
readonly select: (report: Record<string, unknown>) => CandidateSelection;
|
|
48
48
|
readonly cfg?: Record<string, unknown>;
|
|
49
49
|
readonly runner?: (prompt: string, cfg?: Record<string, unknown>) => Promise<string>;
|
|
50
|
-
readonly onSelection?: (selection: CandidateSelection, runId: string) => Promise<void>;
|
|
50
|
+
readonly onSelection?: (selection: CandidateSelection, runId: string, cards: readonly SkillCard[]) => Promise<void>;
|
|
51
51
|
}
|
|
52
52
|
/** Write the exact card bodies as a support-free corpus snapshot and return its root. */
|
|
53
53
|
export declare function snapshotCards(io: EvolutionIoLike, home: string, runId: string, cards: readonly SkillCard[]): Promise<string>;
|
|
@@ -38,8 +38,11 @@ export function createEngineScreening(deps) {
|
|
|
38
38
|
}
|
|
39
39
|
const selection = deps.select(result.report);
|
|
40
40
|
if (deps.onSelection !== undefined)
|
|
41
|
-
await deps.onSelection(selection, runId);
|
|
42
|
-
|
|
41
|
+
await deps.onSelection(selection, runId, cards);
|
|
42
|
+
const degraded = selection.reportOnly
|
|
43
|
+
.filter(row => row.error === true)
|
|
44
|
+
.map(row => { const value = row; return 'gate error: ' + String(value.a ?? '?') + ' + ' + String(value.b ?? '?'); });
|
|
45
|
+
return Object.assign(selection.candidates.slice(), selection.candidatePairs === undefined ? {} : { candidatePairs: selection.candidatePairs }, degraded.length === 0 ? {} : { degraded }, selection.reportOnly.length === 0 ? {} : { reportOnly: selection.reportOnly });
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
48
|
//# sourceMappingURL=engine-screen.js.map
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DeepSeek judge runner over the platform llm service. The engine calls
|
|
3
|
-
* runner(prompt, cfg) and expects raw model text; the platform owns provider
|
|
4
|
-
* selection, usage accounting and transport.
|
|
5
|
-
*/
|
|
6
1
|
export interface ModelRoute {
|
|
7
2
|
readonly provider: string;
|
|
8
3
|
readonly model: string;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek judge runner over the platform llm service. The engine calls
|
|
3
|
+
* runner(prompt, cfg) and expects raw model text; the platform owns provider
|
|
4
|
+
* selection, usage accounting and transport.
|
|
5
|
+
*/
|
|
6
|
+
import { normalizeProviderKey } from "../domain/provider-key.js";
|
|
1
7
|
function append(chunk, text) {
|
|
2
8
|
if (chunk.type === 'text-delta' || chunk.type === 'text')
|
|
3
9
|
return text + (chunk.text ?? '');
|
|
@@ -12,7 +18,7 @@ export function createEngineRunner(llm, route) {
|
|
|
12
18
|
let text = '';
|
|
13
19
|
if (llm.stream !== undefined) {
|
|
14
20
|
for await (const chunk of llm.stream({
|
|
15
|
-
provider: selection.provider,
|
|
21
|
+
provider: normalizeProviderKey(selection.provider),
|
|
16
22
|
model: selection.model,
|
|
17
23
|
messages: [{ role: 'user', content: [{ type: 'text', text: prompt }] }],
|
|
18
24
|
temperature: 0,
|
|
@@ -22,7 +28,7 @@ export function createEngineRunner(llm, route) {
|
|
|
22
28
|
}
|
|
23
29
|
if (llm.prepareCall === undefined)
|
|
24
30
|
throw new Error('llm service has neither stream nor prepareCall');
|
|
25
|
-
const call = await llm.prepareCall({ provider: selection.provider, model: selection.model, temperature: 0 });
|
|
31
|
+
const call = await llm.prepareCall({ provider: normalizeProviderKey(selection.provider), model: selection.model, temperature: 0 });
|
|
26
32
|
for await (const chunk of call.stream({
|
|
27
33
|
...call.config,
|
|
28
34
|
messages: [{ role: 'user', content: [{ type: 'text', text: prompt }] }],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeProviderKey } from "../domain/provider-key.js";
|
|
1
2
|
function append(chunk, text) {
|
|
2
3
|
if (chunk.type === 'text-delta' || chunk.type === 'text')
|
|
3
4
|
return text + (chunk.text ?? '');
|
|
@@ -13,7 +14,7 @@ export function createModelPort(llm, route) {
|
|
|
13
14
|
let text = '';
|
|
14
15
|
if (llm.stream !== undefined) {
|
|
15
16
|
for await (const chunk of llm.stream({
|
|
16
|
-
provider: selection.provider,
|
|
17
|
+
provider: normalizeProviderKey(selection.provider),
|
|
17
18
|
model: selection.model,
|
|
18
19
|
system: request.system,
|
|
19
20
|
messages: [{ role: 'user', content: [{ type: 'text', text: request.user }] }],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Host selector correction: engine selector does not route judge errors to candidates. */
|
|
2
|
-
import { pairKeyOf, selectCandidates
|
|
2
|
+
import { pairKeyOf, selectCandidates } from '@lmzhen/dsh-evolution-merge-engine';
|
|
3
3
|
export function selectWithErrors(report) {
|
|
4
4
|
const base = selectCandidates(report);
|
|
5
5
|
const doc = (report.reportDoc ?? report);
|
|
@@ -13,22 +13,26 @@ export function selectWithErrors(report) {
|
|
|
13
13
|
if (a !== '' && b !== '')
|
|
14
14
|
errors.add(pairKeyOf(a, b));
|
|
15
15
|
}
|
|
16
|
-
const candidates = base.candidates.filter(candidate => !errors.has(pairKeyOf(candidate.a, candidate.b)))
|
|
17
|
-
|
|
16
|
+
const candidates = base.candidates.filter(candidate => !errors.has(pairKeyOf(candidate.a, candidate.b))).filter(candidate => {
|
|
17
|
+
const gate = candidate.gate;
|
|
18
|
+
return gate?.level === 'L2' || gate?.verdict === 1;
|
|
19
|
+
});
|
|
18
20
|
const reportOnly = base.reportOnly.filter((row) => {
|
|
19
21
|
const value = row;
|
|
20
22
|
if (typeof value.a !== 'string' || typeof value.b !== 'string')
|
|
21
23
|
return true;
|
|
22
24
|
return !errors.has(pairKeyOf(value.a, value.b));
|
|
23
25
|
});
|
|
26
|
+
for (const candidate of base.candidates) {
|
|
27
|
+
const level = candidate.gate?.level;
|
|
28
|
+
if (level !== 'L2')
|
|
29
|
+
reportOnly.push(candidate);
|
|
30
|
+
}
|
|
31
|
+
const skipped = base.skipped.filter(item => !errors.has(item.pairKey));
|
|
24
32
|
for (const row of rows) {
|
|
25
33
|
if (row.error !== true)
|
|
26
34
|
continue;
|
|
27
|
-
|
|
28
|
-
const b = typeof row.b === 'string' ? row.b : '';
|
|
29
|
-
if (a === '' || b === '')
|
|
30
|
-
continue;
|
|
31
|
-
candidates.push(toEngineCandidate(row, typeof row.reason === 'string' ? row.reason : 'judge-error'));
|
|
35
|
+
reportOnly.push(row);
|
|
32
36
|
}
|
|
33
37
|
return { ...base, candidates, skipped, reportOnly };
|
|
34
38
|
}
|
|
@@ -2,11 +2,19 @@
|
|
|
2
2
|
* approve command: one pending row is atomically taken to fuse, then a new run executes
|
|
3
3
|
* round2 with the persisted decision. A second approve loses at approvePending (not-pending).
|
|
4
4
|
*/
|
|
5
|
-
import type
|
|
5
|
+
import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
6
6
|
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
7
|
+
import { type StageEntry } from './domain/stage-aggregate.ts';
|
|
7
8
|
import type { MergeRegistryPort, WithMergeRunResult } from './run-shell.ts';
|
|
8
9
|
import type { ScanRound2Outcome } from './scan-run.ts';
|
|
9
10
|
export interface ApprovePorts {
|
|
11
|
+
stage?(row: MergeRow, decision: MergeDecision): Promise<{
|
|
12
|
+
readonly ok: true;
|
|
13
|
+
readonly entry: StageEntry;
|
|
14
|
+
} | {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
}>;
|
|
10
18
|
round2(row: MergeRow, decision: MergeDecision, signal: AbortSignal, runId: string): Promise<ScanRound2Outcome>;
|
|
11
19
|
}
|
|
12
20
|
export interface RunApproveCommandOptions {
|
|
@@ -16,9 +24,12 @@ export interface RunApproveCommandOptions {
|
|
|
16
24
|
readonly ports: ApprovePorts;
|
|
17
25
|
readonly rowId: string;
|
|
18
26
|
readonly platformWrite: boolean;
|
|
27
|
+
readonly parentRunId?: string;
|
|
19
28
|
readonly detached?: boolean;
|
|
20
29
|
readonly pairTimeoutMinutes?: number;
|
|
21
30
|
readonly now: () => number;
|
|
31
|
+
/** The invocation carried no agent at all (E-305), as opposed to an agent with no parent run. */
|
|
32
|
+
readonly agentless?: boolean;
|
|
22
33
|
}
|
|
23
34
|
export declare function runApproveCommand(options: RunApproveCommandOptions): Promise<WithMergeRunResult>;
|
|
24
35
|
//# sourceMappingURL=approve-command.d.ts.map
|
|
@@ -1,27 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approve command: one pending row is atomically taken to fuse, then a new run executes
|
|
3
|
+
* round2 with the persisted decision. A second approve loses at approvePending (not-pending).
|
|
4
|
+
*/
|
|
5
|
+
import { errorText } from '@lmzhen/dsh-evolution-core';
|
|
1
6
|
import { approvePending } from "./pending-actions.js";
|
|
7
|
+
import { pairLedger } from "./domain/pair-ledger.js";
|
|
8
|
+
import { presentStage } from "./domain/stage-aggregate.js";
|
|
9
|
+
import { v4ToLegacy } from "./domain/row-v4.js";
|
|
2
10
|
import { loadMergeState } from "./state.js";
|
|
3
11
|
import { withMergeRun } from "./run-shell.js";
|
|
4
12
|
import { recordRound2Outcome } from "./round2-outcome.js";
|
|
13
|
+
import { failure } from "./domain/errors.js";
|
|
5
14
|
export async function runApproveCommand(options) {
|
|
6
15
|
const loaded = await loadMergeState(options.io, options.home);
|
|
7
16
|
const before = loaded.state.rows.find(item => item.id === options.rowId);
|
|
8
17
|
if (before === undefined)
|
|
9
|
-
return
|
|
18
|
+
return failure('approve', 'row-not-found', '找不到待批准行 ' + options.rowId, '运行 /evolution merge pending 查看当前待批准行');
|
|
10
19
|
if (before.state !== 'pending')
|
|
11
|
-
return
|
|
20
|
+
return failure('approve', 'row-not-pending', '该行当前状态是 ' + before.state + ',不能批准', '运行 /evolution merge status ' + before.id + ' 查看行状态', before.runId);
|
|
21
|
+
if (options.agentless === true)
|
|
22
|
+
return failure('approve', 'e-305', errorText('e-305-this-invocation-carries-no', { a1: 'approve' }), '在 GUI 或 CLI 的会话里运行 /evolution merge approve ' + before.id);
|
|
23
|
+
const parentRunId = options.parentRunId ?? before.runId;
|
|
24
|
+
if (parentRunId === undefined)
|
|
25
|
+
return failure('approve', 'merge-parent-required', '缺 parent run id(E-305 提示:该行可能由无会话调用创建,或父 run 已丢失)', '在 agent 会话中执行 /evolution merge approve ' + before.id, before.runId);
|
|
12
26
|
const taken = await approvePending(options.io, options.home, options.now, options.rowId);
|
|
13
27
|
if (!taken.ok)
|
|
14
|
-
return
|
|
15
|
-
|
|
28
|
+
return failure('approve', 'approve-' + taken.reason, taken.message, '重试;若行状态已变化,运行 /evolution merge status ' + options.rowId, before.runId);
|
|
29
|
+
let row = taken.row;
|
|
16
30
|
const decision = row.decision;
|
|
17
31
|
if (decision === undefined)
|
|
18
|
-
return
|
|
32
|
+
return failure('approve', 'row-invalid', '该待批准行没有持久化决策', '重新 pair 该技能对后再次批准', row.runId);
|
|
33
|
+
if (options.ports.stage !== undefined) {
|
|
34
|
+
const staged = await options.ports.stage(row, decision);
|
|
35
|
+
if (!staged.ok)
|
|
36
|
+
return failure('approve', 'stage-failed', staged.message, '检查暂存目录后重试;不影响已记录的决策', row.runId);
|
|
37
|
+
const fused = await pairLedger(options.io, options.home).transact(tx => {
|
|
38
|
+
const current = tx.rows().find(item => item.id === row.id);
|
|
39
|
+
if (current === undefined)
|
|
40
|
+
return null;
|
|
41
|
+
const moved = tx.transition(row.id, { type: 'staged' }, current.version);
|
|
42
|
+
return moved.ok && moved.row !== null ? v4ToLegacy(moved.row) : null;
|
|
43
|
+
});
|
|
44
|
+
if (fused === null)
|
|
45
|
+
return failure('approve', 'row-changed', '行在暂存完成前被并发修改:' + row.id, '运行 /evolution merge status ' + row.id + ' 后重试', row.runId);
|
|
46
|
+
await presentStage(options.io, options.home, staged.entry);
|
|
47
|
+
row = fused;
|
|
48
|
+
}
|
|
19
49
|
return await withMergeRun({
|
|
20
50
|
io: options.io,
|
|
21
51
|
home: options.home,
|
|
22
52
|
registry: options.registry,
|
|
23
53
|
command: 'approve',
|
|
24
54
|
approval: { platformWrite: options.platformWrite, mergeApproval: true },
|
|
55
|
+
pairKey: row.pairKey,
|
|
56
|
+
parentRunId,
|
|
25
57
|
now: options.now,
|
|
26
58
|
...(options.detached === true ? { detached: true } : {}),
|
|
27
59
|
async body(ctx) {
|
|
@@ -35,7 +67,7 @@ export async function runApproveCommand(options) {
|
|
|
35
67
|
outcome = { kind: 'failed', failure: { stage: 'author', code: 'merge-failed', message: error instanceof Error ? error.message : String(error), retryable: true, retryFrom: 'agent2' } };
|
|
36
68
|
}
|
|
37
69
|
const base = { scope: { candidatePairs: 1, queueBefore: { verify: 0, pending: 0, fuse: 1, skipped: 0 } }, notes: [] };
|
|
38
|
-
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome);
|
|
70
|
+
const record = await recordRound2Outcome(options.io, options.home, options.now, row, outcome, true);
|
|
39
71
|
return {
|
|
40
72
|
...base,
|
|
41
73
|
stopReason: record.merged ? 'completed' : 'failed',
|
|
@@ -43,6 +75,7 @@ export async function runApproveCommand(options) {
|
|
|
43
75
|
totals: record.merged
|
|
44
76
|
? { merged: 1, kept: 0, pending: 0, failed: 0 }
|
|
45
77
|
: { merged: 0, kept: 0, pending: 0, failed: 1 },
|
|
78
|
+
...(record.finalize === undefined ? {} : { finalize: record.finalize }),
|
|
46
79
|
};
|
|
47
80
|
},
|
|
48
81
|
});
|
|
@@ -7,7 +7,15 @@ import type { EvolutionIoLike, UnitTransaction } from '@lmzhen/dsh-evolution-cor
|
|
|
7
7
|
import { type RuleFinding } from './rules.ts';
|
|
8
8
|
import { type CommitPort, type CommitStagedResult } from './commit-execute.ts';
|
|
9
9
|
import type { StagedTree } from './rules.ts';
|
|
10
|
+
import type { RevisionRef } from './contracts.ts';
|
|
10
11
|
import type { SkillCard } from './skill-card.ts';
|
|
12
|
+
import { type SkillLimits } from '@lmzhen/dsh-evolution-core';
|
|
13
|
+
/** Decision-time anchors threaded from round 1 into the commit CAS. */
|
|
14
|
+
export interface CommitRevisionRefs {
|
|
15
|
+
readonly targetRevision?: RevisionRef;
|
|
16
|
+
readonly sourceRevisions?: Readonly<Record<string, RevisionRef>>;
|
|
17
|
+
readonly supportDigest?: string;
|
|
18
|
+
}
|
|
11
19
|
export interface RunAuthoringOptions {
|
|
12
20
|
readonly io: EvolutionIoLike;
|
|
13
21
|
readonly home: string;
|
|
@@ -26,17 +34,25 @@ export interface RunAuthoringOptions {
|
|
|
26
34
|
readonly summary: string;
|
|
27
35
|
readonly isBlocked?: (name: string) => boolean | Promise<boolean>;
|
|
28
36
|
readonly unit?: UnitTransaction | undefined;
|
|
37
|
+
readonly limits?: SkillLimits;
|
|
38
|
+
readonly promptVersion?: string;
|
|
39
|
+
readonly promptHash?: string;
|
|
40
|
+
/** Round-1 decision anchors; when absent the commit keeps the legacy body-only CAS. */
|
|
41
|
+
readonly revisions?: CommitRevisionRefs;
|
|
29
42
|
}
|
|
30
43
|
export type AuthoringResult = {
|
|
31
44
|
readonly ok: true;
|
|
32
45
|
readonly tree: StagedTree;
|
|
46
|
+
readonly validationPlan: import('./domain/validation-plan.ts').ValidationPlan;
|
|
33
47
|
readonly commit: Extract<CommitStagedResult, {
|
|
34
48
|
readonly ok: true;
|
|
35
49
|
}>;
|
|
36
50
|
} | {
|
|
37
51
|
readonly ok: false;
|
|
52
|
+
readonly code?: string;
|
|
38
53
|
readonly message: string;
|
|
39
54
|
readonly findings?: readonly RuleFinding[];
|
|
55
|
+
readonly lengths?: Readonly<Record<string, number>>;
|
|
40
56
|
};
|
|
41
57
|
export declare function runAuthoring(options: RunAuthoringOptions): Promise<AuthoringResult>;
|
|
42
58
|
//# sourceMappingURL=authoring-flow.d.ts.map
|
|
@@ -2,22 +2,28 @@ import { createStaging, setStagingTarget } from "./staging.js";
|
|
|
2
2
|
import { readStagedTree } from "./staged-tree.js";
|
|
3
3
|
import { validateStagedTree, hasBlockingFinding } from "./rules.js";
|
|
4
4
|
import { commitStagedRun } from "./commit-execute.js";
|
|
5
|
+
import { DEFAULT_SKILL_LIMITS } from '@lmzhen/dsh-evolution-core';
|
|
6
|
+
import { limitsSnapshotOf } from "./domain/merge-limits.js";
|
|
7
|
+
import { buildValidationPlan } from "./domain/validation-plan.js";
|
|
8
|
+
import { manifestFilesOf, missingManifestFile, parseAuthoringManifest } from "./domain/authoring-manifest.js";
|
|
5
9
|
export async function runAuthoring(options) {
|
|
10
|
+
if (options.target.includes('/') || options.target.includes(':'))
|
|
11
|
+
return { ok: false, code: 'target-invalid', message: 'target must be a bare skill name: ' + options.target };
|
|
6
12
|
const targetCard = options.cards.find(card => card.name === options.target);
|
|
7
13
|
const mode = options.mode ?? (targetCard === undefined ? 'create-umbrella' : 'merge-into-existing');
|
|
8
14
|
const createMode = mode === 'create-umbrella';
|
|
9
15
|
if (!createMode && targetCard !== undefined) {
|
|
10
16
|
if (targetCard.managed !== true)
|
|
11
|
-
return { ok: false, message: 'target is not family-managed: ' + options.target };
|
|
17
|
+
return { ok: false, code: 'target-not-managed', message: 'target is not family-managed: ' + options.target };
|
|
12
18
|
if (targetCard.protectionUnknown === true || targetCard.protectedBy != null)
|
|
13
|
-
return { ok: false, message: 'target is protected (' + (targetCard.protectedBy ?? 'unknown') + '): ' + options.target };
|
|
19
|
+
return { ok: false, code: 'target-protected', message: 'target is protected (' + (targetCard.protectedBy ?? 'unknown') + '): ' + options.target };
|
|
14
20
|
}
|
|
15
21
|
if (!createMode && await options.isBlocked?.(options.target) === true)
|
|
16
|
-
return { ok: false, message: 'target is blocked by curator: ' + options.target };
|
|
22
|
+
return { ok: false, code: 'target-blocked', message: 'target is blocked by curator: ' + options.target };
|
|
17
23
|
const stageKey = options.stageKey ?? options.runId;
|
|
18
24
|
if (options.reuseStaging === true) {
|
|
19
25
|
if (!await setStagingTarget(options.io, options.home, stageKey, options.target, mode))
|
|
20
|
-
return { ok: false, message: 'staging missing or unreadable: ' + stageKey };
|
|
26
|
+
return { ok: false, code: 'staging-missing', message: 'staging missing or unreadable: ' + stageKey };
|
|
21
27
|
}
|
|
22
28
|
else {
|
|
23
29
|
const created = await createStaging(options.io, options.home, {
|
|
@@ -30,29 +36,47 @@ export async function runAuthoring(options) {
|
|
|
30
36
|
now: options.now,
|
|
31
37
|
});
|
|
32
38
|
if (!created.ok)
|
|
33
|
-
return { ok: false, message: created.message };
|
|
39
|
+
return { ok: false, code: 'staging-create', message: created.message };
|
|
34
40
|
}
|
|
41
|
+
let authored;
|
|
35
42
|
try {
|
|
36
|
-
await options.author({ home: options.home, runId: stageKey });
|
|
43
|
+
authored = await options.author({ home: options.home, runId: stageKey });
|
|
37
44
|
}
|
|
38
45
|
catch (error) {
|
|
39
|
-
return { ok: false, message: 'author failed: ' + (error instanceof Error ? error.message : String(error)) };
|
|
46
|
+
return { ok: false, code: 'author-failed', message: 'author failed: ' + (error instanceof Error ? error.message : String(error)) };
|
|
40
47
|
}
|
|
41
48
|
const tree = await readStagedTree(options.io, options.home, stageKey);
|
|
42
49
|
if (tree.kind !== 'present')
|
|
43
|
-
return { ok: false, message: 'staged tree is not readable: ' + (tree.kind === 'unknown' ? tree.reason : 'absent') };
|
|
44
|
-
|
|
50
|
+
return { ok: false, code: 'staging-missing', message: 'staged tree is not readable: ' + (tree.kind === 'unknown' ? tree.reason : 'absent') };
|
|
51
|
+
// FR-VAL-03: the same-child path parses the child's files[] declaration too, so a
|
|
52
|
+
// manifest that promises a file the tree does not contain fails here — not only on the
|
|
53
|
+
// new-child approve path.
|
|
54
|
+
const manifest = parseAuthoringManifest(authored);
|
|
55
|
+
const stagedPaths = tree.value.files.map(file => file.path);
|
|
56
|
+
const manifestFiles = manifestFilesOf(manifest, stagedPaths);
|
|
57
|
+
const missing = missingManifestFile(manifestFiles, stagedPaths);
|
|
58
|
+
if (missing !== null)
|
|
59
|
+
return { ok: false, code: 'merge-manifest-missing', message: 'manifest file missing from staged tree: ' + missing };
|
|
60
|
+
const limitsSnapshot = limitsSnapshotOf(options.limits ?? DEFAULT_SKILL_LIMITS);
|
|
61
|
+
const supportSources = options.cards.flatMap(card => (card.files ?? []).map(file => ({ path: file.path, content: file.content, source: card.name })));
|
|
62
|
+
const validationPlan = buildValidationPlan({ tree: tree.value, target: options.target, limits: limitsSnapshot, cards: options.cards, supportSources, ...(options.promptVersion === undefined ? {} : { promptVersion: options.promptVersion }), ...(options.promptHash === undefined ? {} : { promptHash: options.promptHash }), supportFiles: tree.value.files.map(file => ({ path: file.path, content: file.content, source: 'staged' })), manifestFiles, existingSupport: (options.cards.find(card => card.name === options.target)?.files ?? []).map(file => file.path) });
|
|
63
|
+
const findings = [...validateStagedTree(tree.value, limitsSnapshot.skillLimits), ...validationPlan.findings];
|
|
45
64
|
if (hasBlockingFinding(findings))
|
|
46
|
-
return { ok: false, message: 'staged tree failed the rule gate', findings };
|
|
65
|
+
return { ok: false, code: findings.some(finding => finding.rule === 'limit') ? 'merge-over-limit' : 'merge-validation', message: 'staged tree failed the rule gate', findings, lengths: { bodyBytes: Buffer.byteLength(tree.value.body, 'utf8'), fileBytes: tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, 'utf8'), 0), totalBytes: Buffer.byteLength(tree.value.body, 'utf8') + tree.value.files.reduce((sum, file) => sum + Buffer.byteLength(file.content, 'utf8'), 0) } };
|
|
47
66
|
const commit = await commitStagedRun(options.io, options.home, stageKey, options.commitPort, {
|
|
48
67
|
origin: 'foreground',
|
|
49
68
|
summary: options.summary,
|
|
50
69
|
now: options.now,
|
|
51
70
|
...(options.isBlocked === undefined ? {} : { isBlocked: options.isBlocked }),
|
|
52
71
|
...(options.unit === undefined ? {} : { unit: options.unit }),
|
|
72
|
+
...(options.limits === undefined ? {} : { limits: options.limits }),
|
|
73
|
+
validationPlan: { planVersion: validationPlan.planVersion, stageDigest: validationPlan.stageDigest, revision: validationPlan.revision },
|
|
74
|
+
...(options.revisions?.targetRevision === undefined ? {} : { targetRevision: options.revisions.targetRevision }),
|
|
75
|
+
...(options.revisions?.sourceRevisions === undefined ? {} : { sourceRevisions: options.revisions.sourceRevisions }),
|
|
76
|
+
...(options.revisions?.supportDigest === undefined ? {} : { supportDigest: options.revisions.supportDigest }),
|
|
53
77
|
});
|
|
54
|
-
if (
|
|
55
|
-
return { ok:
|
|
56
|
-
return { ok:
|
|
78
|
+
if (commit.ok)
|
|
79
|
+
return { ok: true, tree: tree.value, validationPlan, commit };
|
|
80
|
+
return { ok: false, code: commit.code ?? 'commit-failed', message: commit.message };
|
|
57
81
|
}
|
|
58
82
|
//# sourceMappingURL=authoring-flow.js.map
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* an author callback for round 2 when the same child continues. Commit is deliberately
|
|
4
4
|
* outside this adapter: the caller owns staging -> commitStagedRun.
|
|
5
5
|
*/
|
|
6
|
-
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
6
|
+
import type { MergeDecision, MergeRow, MergeUsage } from './contracts.ts';
|
|
7
7
|
import type { SkillCard } from './skill-card.ts';
|
|
8
8
|
import type { ScanRound1Outcome } from './scan-run.ts';
|
|
9
9
|
export interface ChannelTurnLike {
|
|
10
10
|
readonly text: string;
|
|
11
11
|
readonly stopReason: string;
|
|
12
12
|
readonly cacheRead?: number;
|
|
13
|
+
readonly usage?: MergeUsage;
|
|
13
14
|
}
|
|
14
15
|
export type ChannelTurnOutcome = {
|
|
15
16
|
readonly kind: 'ok';
|
|
@@ -51,6 +52,7 @@ export interface ChannelRound1Options {
|
|
|
51
52
|
export interface ChannelRound1Result {
|
|
52
53
|
readonly outcome: ScanRound1Outcome;
|
|
53
54
|
readonly childId?: string;
|
|
55
|
+
readonly usage?: () => MergeUsage | undefined;
|
|
54
56
|
/** Same-child round 2; absent for keep/fail/unavailable. */
|
|
55
57
|
readonly author?: (row: MergeRow, decision: MergeDecision, signal: AbortSignal) => Promise<string>;
|
|
56
58
|
readonly dispose: () => Promise<void>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { parseDecision } from "./decision.js";
|
|
2
|
-
import { buildRound1Prompt, buildRound2Prompt, STANDBY_PROMPT } from "./prompts.js";
|
|
2
|
+
import { buildRound1Prompt, buildRound2Prompt, MERGE_PROMPT_VERSION, promptHashOf, STANDBY_PROMPT } from "./prompts.js";
|
|
3
3
|
export async function runRound1OverChannel(options, candidate, signal) {
|
|
4
4
|
let childId;
|
|
5
|
+
let lastUsage;
|
|
5
6
|
const dispose = async () => {
|
|
6
7
|
if (childId !== undefined) {
|
|
7
8
|
const id = childId;
|
|
@@ -38,7 +39,8 @@ export async function runRound1OverChannel(options, candidate, signal) {
|
|
|
38
39
|
await dispose();
|
|
39
40
|
return { outcome: { kind: 'failed', failure: { stage: 'decide', code: 'child-bind', message: String(error), retryable: true, retryFrom: 'agent1' } }, dispose };
|
|
40
41
|
}
|
|
41
|
-
const
|
|
42
|
+
const prompt = buildRound1Prompt(candidate, options.cards);
|
|
43
|
+
const round1 = await options.channel.continueTurn(opened.childId, prompt, { timeoutMs: options.timeoutMs, signal });
|
|
42
44
|
if (round1.kind !== 'ok') {
|
|
43
45
|
await dispose();
|
|
44
46
|
return { outcome: { kind: 'failed', failure: { stage: 'decide', code: round1.kind, message: round1.kind === 'unavailable' ? round1.reason : 'round1 aborted', retryable: round1.kind !== 'aborted', retryFrom: round1.kind === 'aborted' ? 'none' : 'agent1' } }, dispose };
|
|
@@ -52,7 +54,8 @@ export async function runRound1OverChannel(options, candidate, signal) {
|
|
|
52
54
|
await dispose();
|
|
53
55
|
return { outcome: { kind: 'failed', failure: { stage: 'decide', code: parsed.code, message: parsed.message, retryable: true, retryFrom: 'agent1' } }, dispose };
|
|
54
56
|
}
|
|
55
|
-
|
|
57
|
+
lastUsage = round1.turn.usage;
|
|
58
|
+
const decision = { ...parsed.decision, promptVersion: MERGE_PROMPT_VERSION, promptHash: promptHashOf(prompt), ...(round1.turn.cacheRead === undefined ? {} : { cacheRead: round1.turn.cacheRead }) };
|
|
56
59
|
if (!decision.shouldMerge || decision.mode === 'keep') {
|
|
57
60
|
await dispose();
|
|
58
61
|
return { outcome: { kind: 'keep', decision }, dispose };
|
|
@@ -62,12 +65,14 @@ export async function runRound1OverChannel(options, candidate, signal) {
|
|
|
62
65
|
outcome: { kind: 'merge', decision },
|
|
63
66
|
childId: child,
|
|
64
67
|
dispose,
|
|
68
|
+
usage: () => lastUsage,
|
|
65
69
|
async author(row, decision, turnSignal) {
|
|
66
70
|
const turn = await options.channel.continueTurn(child, buildRound2Prompt(row, decision), { timeoutMs: options.timeoutMs, signal: turnSignal });
|
|
67
71
|
if (turn.kind !== 'ok')
|
|
68
72
|
throw new Error(turn.kind === 'unavailable' ? turn.reason : 'round2 aborted');
|
|
69
73
|
if (turn.turn.stopReason !== 'completed')
|
|
70
74
|
throw new Error('round2 did not complete: ' + turn.turn.stopReason);
|
|
75
|
+
lastUsage = turn.turn.usage;
|
|
71
76
|
return turn.turn.text;
|
|
72
77
|
},
|
|
73
78
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommandDef } from '@lmzhen/dsh-evolution-commands';
|
|
2
|
+
export declare const MERGE_PACKAGE_VERSION: string;
|
|
3
|
+
export declare const MERGE_ALIAS_DEF: CommandDef;
|
|
4
|
+
export declare const MERGE_COMMAND_DEFS: readonly CommandDef[];
|
|
5
|
+
//# sourceMappingURL=command-defs.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Merge CommandDef data; parser/help/surface/fallback live in evolution-commands. */
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
4
|
+
export const MERGE_PACKAGE_VERSION = manifest.version ?? '0.30.18';
|
|
5
|
+
export const MERGE_ALIAS_DEF = { id: 'merge.alias', words: ['merge'], summaryKey: 'merge.alias', helpKey: 'merge.alias.help', flags: [], positional: [], readOnly: false, needsRun: false, fallback: '/merge', deprecatedAt: '0.31' };
|
|
6
|
+
const pos = (name, kind, required = true) => ({ name, kind, required });
|
|
7
|
+
const flag = (name, kind, valueName, required = false) => ({ name, kind, ...(valueName === undefined ? {} : { valueName }), ...(required ? { required: true } : {}) });
|
|
8
|
+
const action = (id, command, confirm = false) => ({ id, command, confirm });
|
|
9
|
+
const cmd = (id, words, flags, positional, readOnly, needsRun, rowActions, fallback) => ({ id, words, summaryKey: id, helpKey: id + '.help', flags, positional, readOnly, needsRun, rowActions, fallback });
|
|
10
|
+
export const MERGE_COMMAND_DEFS = [
|
|
11
|
+
({ ...({ ...cmd('merge.doctor', ['doctor'], [], [], false, false, [], '/evolution merge doctor'), usage: false }), surface: false }),
|
|
12
|
+
({ ...cmd('merge.reconcile', ['reconcile'], [], [], false, false, [], '/evolution merge reconcile'), surface: false }),
|
|
13
|
+
cmd('merge.scan', ['scan'], [], [], false, true, [], '/evolution merge scan'),
|
|
14
|
+
cmd('merge.pair', ['pair'], [], [pos('a', 'skill'), pos('b', 'skill')], false, true, [action('pair', '/evolution merge pair {a} {b}', true)], '/evolution merge pair <a> <b>'),
|
|
15
|
+
cmd('merge.pair.force', ['pair'], [flag('force', 'boolean')], [pos('a', 'skill'), pos('b', 'skill')], false, true, [action('pair', '/evolution merge pair {a} {b}', true)], '/evolution merge pair --force <a> <b>'),
|
|
16
|
+
cmd('merge.status', ['status'], [], [pos('id', 'id', false)], true, false, [action('status', '/evolution merge status {runId}')], '/evolution merge status'),
|
|
17
|
+
cmd('merge.report', ['report'], [], [pos('id', 'id', false)], true, false, [action('report', '/evolution merge report {runId}')], '/evolution merge report'),
|
|
18
|
+
cmd('merge.cancel', ['cancel'], [], [pos('id', 'id', false)], false, false, [action('cancel', '/evolution merge cancel {runId}')], '/evolution merge cancel <id>'),
|
|
19
|
+
cmd('merge.runs', ['runs'], [], [], true, false, [], '/evolution merge runs'),
|
|
20
|
+
cmd('merge.pending', ['pending'], [], [], true, false, [], '/evolution merge pending'),
|
|
21
|
+
cmd('merge.pending.expired', ['pending'], [flag('expired', 'boolean')], [], true, false, [], '/evolution merge pending --expired'),
|
|
22
|
+
cmd('merge.approve', ['approve'], [], [pos('id', 'id')], false, true, [action('approve', '/evolution merge approve {id}')], '/evolution merge approve <id>'),
|
|
23
|
+
cmd('merge.reject', ['reject'], [], [pos('id', 'id')], false, false, [action('reject', '/evolution merge reject {id}', true)], '/evolution merge reject <id>'),
|
|
24
|
+
cmd('merge.skipped', ['skipped'], [], [], true, false, [], '/evolution merge skipped'),
|
|
25
|
+
cmd('merge.skipped.restore', ['skipped'], [flag('restore', 'value', 'id')], [], false, false, [action('restore', '/evolution merge skipped --restore {id}', true)], '/evolution merge skipped --restore <id>'),
|
|
26
|
+
cmd('merge.candidates', ['candidates'], [], [], true, false, [], '/evolution merge candidates'),
|
|
27
|
+
cmd('merge.candidates.retry', ['candidates'], [flag('retry', 'boolean')], [pos('id', 'id', false)], false, true, [action('retry-candidates', '/evolution merge candidates --retry {id}')], '/evolution merge candidates --retry <id>'),
|
|
28
|
+
cmd('merge.resume', ['resume'], [flag('clear', 'value', 'id')], [pos('id', 'id', false)], true, false, [action('retry-resume', '/evolution merge resume --retry {id}')], '/evolution merge resume'),
|
|
29
|
+
cmd('merge.resume.retry', ['resume'], [flag('retry', 'boolean'), flag('clear', 'value', 'id')], [pos('id', 'id', false)], false, true, [action('retry-resume', '/evolution merge resume --retry {id}')], '/evolution merge resume --retry <id>'),
|
|
30
|
+
cmd('merge.discard', ['discard'], [], [pos('id', 'id')], false, false, [action('discard', '/evolution merge discard {id}', true)], '/evolution merge discard <id>'),
|
|
31
|
+
cmd('merge.undo', ['undo'], [flag('run', 'value', 'id', true)], [pos('target', 'target')], false, false, [], '/evolution merge undo <target> --run <id>'),
|
|
32
|
+
];
|
|
33
|
+
//# sourceMappingURL=command-defs.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Generated merge CommandSurface from command-defs; no handwritten table. */
|
|
2
2
|
import type { CommandSpec, CommandSurface } from './wire.ts';
|
|
3
3
|
export declare const MERGE_COMMANDS: readonly CommandSpec[];
|
|
4
4
|
export declare const MERGE_COMMAND_SURFACE: CommandSurface;
|
|
@@ -1,44 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
spec('merge.status', ['status'], [], [pos('id', 'id', false)], true, false, [action('status', '/evolution merge status')], '/evolution merge status'),
|
|
12
|
-
spec('merge.report', ['report'], [], [pos('id', 'id', false)], true, false, [action('report', '/evolution merge report')], '/evolution merge report'),
|
|
13
|
-
spec('merge.cancel', ['cancel'], [], [pos('id', 'id', false)], false, false, [action('cancel', '/evolution merge cancel')], '/evolution merge cancel <id>'),
|
|
14
|
-
spec('merge.runs', ['runs'], [], [], true, false, [], '/evolution merge runs'),
|
|
15
|
-
spec('merge.pending', ['pending'], [], [], true, false, [], '/evolution merge pending'),
|
|
16
|
-
spec('merge.pending.expired', ['pending'], [flag('expired')], [], true, false, [], '/evolution merge pending --expired'),
|
|
17
|
-
spec('merge.approve', ['approve'], [], [pos('id', 'id')], false, true, [action('approve', '/evolution merge approve <id>')], '/evolution merge approve <id>'),
|
|
18
|
-
spec('merge.reject', ['reject'], [], [pos('id', 'id')], false, false, [action('reject', '/evolution merge reject <id>', true)], '/evolution merge reject <id>'),
|
|
19
|
-
spec('merge.skipped', ['skipped'], [], [], true, false, [], '/evolution merge skipped'),
|
|
20
|
-
spec('merge.skipped.restore', ['skipped'], [flag('restore', 'value', 'id')], [], false, false, [action('restore', '/evolution merge skipped --restore <id>', true)], '/evolution merge skipped --restore <id>'),
|
|
21
|
-
spec('merge.candidates', ['candidates'], [], [], true, false, [], '/evolution merge candidates'),
|
|
22
|
-
spec('merge.candidates.retry', ['candidates'], [flag('retry', 'value', 'id')], [], false, true, [action('retry-candidates', '/evolution merge candidates --retry <id>')], '/evolution merge candidates --retry <id>'),
|
|
23
|
-
spec('merge.resume', ['resume'], [], [pos('id', 'id', false)], true, false, [], '/evolution merge resume'),
|
|
24
|
-
spec('merge.resume.retry', ['resume'], [flag('retry', 'value', 'id')], [], false, true, [action('retry-resume', '/evolution merge resume --retry <id>')], '/evolution merge resume --retry <id>'),
|
|
25
|
-
spec('merge.discard', ['discard'], [], [pos('id', 'id')], false, false, [action('discard', '/evolution merge discard <id>', true)], '/evolution merge discard <id>'),
|
|
26
|
-
spec('merge.undo', ['undo'], [flag('run', 'value', 'id')], [pos('target', 'target')], false, false, [], '/evolution merge undo <target> --run <id>'),
|
|
27
|
-
];
|
|
28
|
-
export const MERGE_COMMAND_SURFACE = {
|
|
29
|
-
wireVersion: 1,
|
|
30
|
-
prefix: '/evolution merge',
|
|
31
|
-
commands: MERGE_COMMANDS,
|
|
32
|
-
fallback: [
|
|
33
|
-
{ id: 'scan', command: '/evolution merge scan' },
|
|
34
|
-
{ id: 'pair', command: '/evolution merge pair <a> <b>' },
|
|
35
|
-
{ id: 'candidates', command: '/evolution merge candidates' },
|
|
36
|
-
{ id: 'pending', command: '/evolution merge pending' },
|
|
37
|
-
{ id: 'skipped', command: '/evolution merge skipped' },
|
|
38
|
-
{ id: 'resume', command: '/evolution merge resume' },
|
|
39
|
-
{ id: 'status', command: '/evolution merge status' },
|
|
40
|
-
{ id: 'report', command: '/evolution merge report' },
|
|
41
|
-
{ id: 'doctor', command: '/evolution merge doctor' },
|
|
42
|
-
],
|
|
43
|
-
};
|
|
1
|
+
import { buildSurface } from '@lmzhen/dsh-evolution-commands';
|
|
2
|
+
import { MERGE_COMMAND_DEFS } from "./command-defs.js";
|
|
3
|
+
const generated = buildSurface(MERGE_COMMAND_DEFS, '/evolution merge');
|
|
4
|
+
export const MERGE_COMMANDS = generated.commands.map(command => ({
|
|
5
|
+
id: command.id, words: command.words, flags: command.flags, positional: command.positional,
|
|
6
|
+
summary: command.id, readOnly: command.readOnly, needsRun: command.needsRun, defaults: command.defaults,
|
|
7
|
+
rowActions: command.rowActions.map(action => ({ id: action.id, command: action.command, confirm: action.confirm })),
|
|
8
|
+
fallback: command.fallback,
|
|
9
|
+
}));
|
|
10
|
+
export const MERGE_COMMAND_SURFACE = { wireVersion: 1, prefix: '/evolution merge', commands: MERGE_COMMANDS, fallback: generated.fallback.map(item => ({ id: item.id, command: item.command })) };
|
|
44
11
|
//# sourceMappingURL=command-surface.js.map
|
package/lib/types/commands.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure command-surface parser for the /evolution merge prefix. The command layer only
|
|
3
|
-
* validates shape; run/state effects are injected by the host shell.
|
|
4
|
-
*/
|
|
5
1
|
export type MergeCliCommand = {
|
|
6
2
|
readonly verb: 'scan';
|
|
7
3
|
} | {
|
|
@@ -22,12 +18,15 @@ export type MergeCliCommand = {
|
|
|
22
18
|
readonly verb: 'runs';
|
|
23
19
|
} | {
|
|
24
20
|
readonly verb: 'doctor';
|
|
21
|
+
} | {
|
|
22
|
+
readonly verb: 'reconcile';
|
|
25
23
|
} | {
|
|
26
24
|
readonly verb: 'pending';
|
|
27
25
|
readonly expired?: true;
|
|
28
26
|
} | {
|
|
29
27
|
readonly verb: 'candidates';
|
|
30
28
|
readonly retry?: true;
|
|
29
|
+
readonly id?: string;
|
|
31
30
|
} | {
|
|
32
31
|
readonly verb: 'approve';
|
|
33
32
|
readonly id: string;
|