@interf/compiler 0.5.1 → 0.6.1
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 +126 -187
- package/builtin-workflows/interf/README.md +22 -10
- package/builtin-workflows/interf/compile/stages/shape/SKILL.md +6 -3
- package/builtin-workflows/interf/compile/stages/structure/SKILL.md +3 -0
- package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +18 -2
- package/builtin-workflows/interf/improve/SKILL.md +2 -2
- package/builtin-workflows/interf/workflow.json +18 -4
- package/builtin-workflows/interf/{compiled.schema.json → workflow.schema.json} +9 -2
- package/dist/commands/check-draft.js +3 -3
- package/dist/commands/compile-controller.js +6 -13
- package/dist/commands/compile.d.ts +19 -1
- package/dist/commands/compile.js +98 -28
- package/dist/commands/create-workflow-wizard.d.ts +20 -2
- package/dist/commands/create-workflow-wizard.js +163 -27
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +67 -60
- package/dist/commands/dataset-selection.d.ts +6 -0
- package/dist/commands/dataset-selection.js +11 -0
- package/dist/commands/default.js +3 -3
- package/dist/commands/doctor.js +8 -8
- package/dist/commands/executor-flow.d.ts +1 -1
- package/dist/commands/executor-flow.js +5 -2
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +56 -48
- package/dist/commands/list.js +6 -3
- package/dist/commands/reset.js +1 -1
- package/dist/commands/source-config-wizard.d.ts +2 -2
- package/dist/commands/source-config-wizard.js +50 -17
- package/dist/commands/test.d.ts +0 -6
- package/dist/commands/test.js +9 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/agent-args.d.ts +1 -0
- package/dist/lib/agent-args.js +10 -0
- package/dist/lib/agent-execution.js +2 -1
- package/dist/lib/agent-preflight.js +2 -1
- package/dist/lib/agent-shells.d.ts +26 -1
- package/dist/lib/agent-shells.js +213 -39
- package/dist/lib/agents.d.ts +1 -1
- package/dist/lib/agents.js +1 -1
- package/dist/lib/builtin-compiled-workflow.d.ts +6 -97
- package/dist/lib/builtin-compiled-workflow.js +66 -125
- package/dist/lib/compiled-compile.d.ts +0 -4
- package/dist/lib/compiled-compile.js +9 -28
- package/dist/lib/compiled-paths.d.ts +1 -0
- package/dist/lib/compiled-paths.js +3 -0
- package/dist/lib/compiled-reset.d.ts +1 -0
- package/dist/lib/compiled-reset.js +42 -14
- package/dist/lib/compiled-schema.d.ts +9 -5
- package/dist/lib/compiled-schema.js +45 -14
- package/dist/lib/discovery.d.ts +1 -1
- package/dist/lib/discovery.js +2 -2
- package/dist/lib/executors.d.ts +1 -1
- package/dist/lib/executors.js +2 -2
- package/dist/lib/interf-scaffold.js +4 -11
- package/dist/lib/interf-workflow-package.d.ts +8 -3
- package/dist/lib/interf-workflow-package.js +128 -62
- package/dist/lib/local-workflows.d.ts +4 -3
- package/dist/lib/local-workflows.js +126 -103
- package/dist/lib/runtime-acceptance.js +15 -3
- package/dist/lib/runtime-contracts.js +3 -2
- package/dist/lib/runtime-paths.d.ts +1 -0
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-prompt.js +3 -1
- package/dist/lib/runtime-reconcile.js +88 -51
- package/dist/lib/runtime-runs.js +27 -15
- package/dist/lib/runtime.d.ts +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/schema.d.ts +71 -14
- package/dist/lib/schema.js +15 -12
- package/dist/lib/state-view.js +6 -6
- package/dist/lib/state.d.ts +1 -0
- package/dist/lib/state.js +7 -0
- package/dist/lib/test-execution.js +2 -2
- package/dist/lib/validate-compiled.js +9 -6
- package/dist/lib/validate.d.ts +3 -1
- package/dist/lib/validate.js +4 -11
- package/dist/lib/workflow-authoring.d.ts +26 -0
- package/dist/lib/workflow-authoring.js +119 -0
- package/dist/lib/workflow-definitions.d.ts +11 -1
- package/dist/lib/workflow-definitions.js +12 -15
- package/dist/lib/workflow-edit-session.d.ts +16 -0
- package/dist/lib/workflow-edit-session.js +57 -0
- package/dist/lib/workflow-edit-utils.d.ts +10 -0
- package/dist/lib/workflow-edit-utils.js +39 -0
- package/dist/lib/workflow-improvement.js +30 -217
- package/dist/lib/workflow-stage-policy.d.ts +5 -0
- package/dist/lib/workflow-stage-policy.js +31 -0
- package/package.json +4 -5
- package/dist/lib/obsidian.d.ts +0 -1
- package/dist/lib/obsidian.js +0 -15
- package/dist/lib/summarize-plan.d.ts +0 -17
- package/dist/lib/summarize-plan.js +0 -120
package/dist/lib/validate.js
CHANGED
|
@@ -7,12 +7,6 @@ import { listFilesRecursive } from "./filesystem.js";
|
|
|
7
7
|
import { readInterfConfig } from "./interf.js";
|
|
8
8
|
import { parseJsonFrontmatter } from "./parse.js";
|
|
9
9
|
import { compiledInterfConfigPath } from "./compiled-paths.js";
|
|
10
|
-
const REQUIRED_DIGEST_FIELDS = [
|
|
11
|
-
"source_kind",
|
|
12
|
-
"evidence_tier",
|
|
13
|
-
"truth_mode",
|
|
14
|
-
"state",
|
|
15
|
-
];
|
|
16
10
|
const MIN_ABSTRACT_WORDS = 5;
|
|
17
11
|
const WIKILINK_PATTERN = /!?\[\[([^[\]]+)\]\]/g;
|
|
18
12
|
export function readCompiledConfig(dirPath) {
|
|
@@ -36,7 +30,7 @@ export function readCompiledConfig(dirPath) {
|
|
|
36
30
|
},
|
|
37
31
|
};
|
|
38
32
|
}
|
|
39
|
-
export function validateSynthFiles(files) {
|
|
33
|
+
export function validateSynthFiles(files, options) {
|
|
40
34
|
let invalidFrontmatter = 0;
|
|
41
35
|
let shortAbstracts = 0;
|
|
42
36
|
for (const filePath of files) {
|
|
@@ -44,7 +38,7 @@ export function validateSynthFiles(files) {
|
|
|
44
38
|
if (content === null)
|
|
45
39
|
continue;
|
|
46
40
|
const parsed = parseJsonFrontmatter(content);
|
|
47
|
-
if (!parsed || !
|
|
41
|
+
if (!parsed || !hasRequiredFrontmatterKeys(parsed.frontmatter, options?.requiredFrontmatterKeys)) {
|
|
48
42
|
invalidFrontmatter += 1;
|
|
49
43
|
continue;
|
|
50
44
|
}
|
|
@@ -138,9 +132,8 @@ function dedupeFiles(dirPaths) {
|
|
|
138
132
|
}
|
|
139
133
|
return files;
|
|
140
134
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
return hasSourceReference && REQUIRED_DIGEST_FIELDS.every((field) => field in frontmatter);
|
|
135
|
+
function hasRequiredFrontmatterKeys(frontmatter, requiredKeys) {
|
|
136
|
+
return (requiredKeys ?? []).every((field) => field in frontmatter);
|
|
144
137
|
}
|
|
145
138
|
function extractBodyAbstract(body) {
|
|
146
139
|
const lines = body.split(/\r?\n/);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { WorkflowExecutor } from "./executors.js";
|
|
2
|
+
import { validateWorkflowPackage } from "./local-workflows.js";
|
|
3
|
+
import type { SourceTruthCheck } from "./schema.js";
|
|
4
|
+
import { type WorkflowReporter } from "./workflows.js";
|
|
5
|
+
export interface WorkflowAuthoringRunResult {
|
|
6
|
+
status: "updated" | "no-change" | "invalid" | "executor-failed";
|
|
7
|
+
changed: boolean;
|
|
8
|
+
summary: string;
|
|
9
|
+
validation: ReturnType<typeof validateWorkflowPackage> | null;
|
|
10
|
+
workflowPath: string;
|
|
11
|
+
shellPath: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function runWorkflowAuthoringDraft(options: {
|
|
14
|
+
sourcePath: string;
|
|
15
|
+
datasetPath: string;
|
|
16
|
+
baseWorkflowId: string;
|
|
17
|
+
workflowId: string;
|
|
18
|
+
label: string;
|
|
19
|
+
hint: string;
|
|
20
|
+
taskPrompt: string;
|
|
21
|
+
checks?: SourceTruthCheck[];
|
|
22
|
+
executor: WorkflowExecutor;
|
|
23
|
+
preparePreview?: boolean;
|
|
24
|
+
previewReporter?: WorkflowReporter | null;
|
|
25
|
+
}): Promise<WorkflowAuthoringRunResult>;
|
|
26
|
+
export declare function resolveWorkflowDraftPath(sourcePath: string, workflowId: string): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createWorkflowAuthoringShell } from "./agent-shells.js";
|
|
5
|
+
import { WORKFLOW_SCHEMA_FILE } from "./compiled-schema.js";
|
|
6
|
+
import { projectRawSnapshot } from "./compiled-raw.js";
|
|
7
|
+
import { seedLocalWorkflowPackageFromBase } from "./interf-workflow-package.js";
|
|
8
|
+
import { validateWorkflowPackage, workflowDefinitionPath } from "./local-workflows.js";
|
|
9
|
+
import { runWorkflowEditSession } from "./workflow-edit-session.js";
|
|
10
|
+
import { copyDirectory } from "./workflow-edit-utils.js";
|
|
11
|
+
import { createCompiled, compileCompiled } from "./workflows.js";
|
|
12
|
+
function buildWorkflowAuthoringPrompt() {
|
|
13
|
+
return [
|
|
14
|
+
"This is an automated Interf workflow-authoring run, not an open-ended chat session.",
|
|
15
|
+
"Execute it now.",
|
|
16
|
+
"Read `runtime/authoring-context.json` first.",
|
|
17
|
+
`Then read \`workflow/README.md\`, \`workflow/workflow.json\`, and \`workflow/${WORKFLOW_SCHEMA_FILE}\`.`,
|
|
18
|
+
"Review `artifacts/source-dataset/raw/` and any `artifacts/preview-compiled/` notes before editing the package.",
|
|
19
|
+
"Prefer direct file-reading and search tools over shell commands for routine file inspection.",
|
|
20
|
+
"Do not use shell helpers like `cat`, `sed`, `ls`, or `find` when a native read/search tool can inspect the same files.",
|
|
21
|
+
"Edit only files under `workflow/`.",
|
|
22
|
+
"Keep the workflow valid for the current compiler API and workflow schema.",
|
|
23
|
+
"Make the package materially more specific to this dataset task than the seeded base workflow. A no-op copy is not acceptable.",
|
|
24
|
+
"Prefer explicit stage-doc, stage-policy, purpose, and zone-contract edits over vague rewrites.",
|
|
25
|
+
"Do not introduce wikilinks unless the workflow also creates the target note by exact basename or explicit relative path.",
|
|
26
|
+
"Respect stage boundaries: a stage may only introduce links that resolve within that stage's declared writes or already-existing read zones.",
|
|
27
|
+
"Do not make one stage point at files or notes that are only created later by another stage.",
|
|
28
|
+
"Prefer conservative retrieval routing over speculative note sprawl.",
|
|
29
|
+
"Do not narrate plans or ask follow-up questions.",
|
|
30
|
+
"Only emit user-visible updates that begin with STATUS:, DONE:, BLOCKED:, or ERROR:.",
|
|
31
|
+
"As soon as the workflow edits are complete, stop.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
async function prepareWorkflowAuthoringPreview(options) {
|
|
35
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "interf-workflow-preview-"));
|
|
36
|
+
projectRawSnapshot({
|
|
37
|
+
sourcePath: options.datasetPath,
|
|
38
|
+
destinationPath: tempRoot,
|
|
39
|
+
compiledPath: tempRoot,
|
|
40
|
+
mode: "copy",
|
|
41
|
+
prune: true,
|
|
42
|
+
preserveTimestamps: true,
|
|
43
|
+
});
|
|
44
|
+
copyDirectory(options.workflowPath, workflowDefinitionPath(tempRoot, options.workflowId));
|
|
45
|
+
const compiledPath = createCompiled("preview", tempRoot, options.workflowId, options.about, tempRoot);
|
|
46
|
+
const result = await compileCompiled({
|
|
47
|
+
executor: options.executor,
|
|
48
|
+
compiledPath,
|
|
49
|
+
...(options.reporter ? { reporter: options.reporter } : {}),
|
|
50
|
+
preserveStageShells: "on-failure",
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
tempRoot,
|
|
54
|
+
compiledPath,
|
|
55
|
+
compileResult: {
|
|
56
|
+
ok: result.ok,
|
|
57
|
+
failedStage: result.failedStage,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function runWorkflowAuthoringDraft(options) {
|
|
62
|
+
const workflowPath = seedLocalWorkflowPackageFromBase({
|
|
63
|
+
sourcePath: options.sourcePath,
|
|
64
|
+
baseWorkflowId: options.baseWorkflowId,
|
|
65
|
+
workflowId: options.workflowId,
|
|
66
|
+
label: options.label,
|
|
67
|
+
hint: options.hint,
|
|
68
|
+
});
|
|
69
|
+
let preview = null;
|
|
70
|
+
try {
|
|
71
|
+
if (options.preparePreview !== false) {
|
|
72
|
+
preview = await prepareWorkflowAuthoringPreview({
|
|
73
|
+
datasetPath: options.datasetPath,
|
|
74
|
+
about: options.taskPrompt,
|
|
75
|
+
workflowId: options.workflowId,
|
|
76
|
+
workflowPath,
|
|
77
|
+
executor: options.executor,
|
|
78
|
+
reporter: options.previewReporter ?? null,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const shell = createWorkflowAuthoringShell({
|
|
82
|
+
workflowPath,
|
|
83
|
+
workflowId: options.workflowId,
|
|
84
|
+
label: options.label,
|
|
85
|
+
baseWorkflowId: options.baseWorkflowId,
|
|
86
|
+
datasetPath: options.datasetPath,
|
|
87
|
+
taskPrompt: options.taskPrompt,
|
|
88
|
+
checks: options.checks ?? [],
|
|
89
|
+
preview,
|
|
90
|
+
});
|
|
91
|
+
if (preview) {
|
|
92
|
+
rmSync(preview.tempRoot, { recursive: true, force: true });
|
|
93
|
+
preview = null;
|
|
94
|
+
}
|
|
95
|
+
const session = await runWorkflowEditSession({
|
|
96
|
+
executor: options.executor,
|
|
97
|
+
workflowPath,
|
|
98
|
+
shell,
|
|
99
|
+
prompt: buildWorkflowAuthoringPrompt(),
|
|
100
|
+
validate: validateWorkflowPackage,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
status: session.status,
|
|
104
|
+
changed: session.changed,
|
|
105
|
+
summary: session.summary,
|
|
106
|
+
validation: session.validation,
|
|
107
|
+
workflowPath,
|
|
108
|
+
shellPath: shell.rootPath,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (preview) {
|
|
113
|
+
rmSync(preview.tempRoot, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function resolveWorkflowDraftPath(sourcePath, workflowId) {
|
|
118
|
+
return workflowDefinitionPath(sourcePath, workflowId);
|
|
119
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeContractType, RuntimeStageAcceptance, WorkflowCompilerApi, WorkflowId, WorkflowZoneId } from "./schema.js";
|
|
1
|
+
import type { RuntimeContractType, RuntimeStageAcceptance, WorkflowCompilerApi, WorkflowCompiledSchema, WorkflowId, WorkflowZoneId } from "./schema.js";
|
|
2
2
|
export interface WorkflowStarterDoc {
|
|
3
3
|
relativePath: string;
|
|
4
4
|
content: string;
|
|
@@ -16,9 +16,14 @@ export interface WorkflowStageDefinition {
|
|
|
16
16
|
export interface WorkflowDefinition<TId extends string> {
|
|
17
17
|
id: TId;
|
|
18
18
|
compilerApi?: WorkflowCompilerApi;
|
|
19
|
+
purpose?: {
|
|
20
|
+
label: string;
|
|
21
|
+
taskHint: string;
|
|
22
|
+
};
|
|
19
23
|
label: string;
|
|
20
24
|
hint: string;
|
|
21
25
|
recommended?: boolean;
|
|
26
|
+
schema?: WorkflowCompiledSchema;
|
|
22
27
|
stages: WorkflowStageDefinition[];
|
|
23
28
|
stagePolicyNotes?: Record<string, string[]>;
|
|
24
29
|
starterDocs?: WorkflowStarterDoc[];
|
|
@@ -28,6 +33,10 @@ export type CompiledWorkflowId = string;
|
|
|
28
33
|
export declare function standaloneWorkflowDefinitionFromLocalPackage(local: {
|
|
29
34
|
id: string;
|
|
30
35
|
compiler_api?: WorkflowCompilerApi;
|
|
36
|
+
purpose?: {
|
|
37
|
+
label: string;
|
|
38
|
+
task_hint: string;
|
|
39
|
+
};
|
|
31
40
|
label: string;
|
|
32
41
|
hint: string;
|
|
33
42
|
stages?: Array<{
|
|
@@ -41,6 +50,7 @@ export declare function standaloneWorkflowDefinitionFromLocalPackage(local: {
|
|
|
41
50
|
acceptance?: RuntimeStageAcceptance;
|
|
42
51
|
}>;
|
|
43
52
|
stage_policy_notes?: Record<string, string[]>;
|
|
53
|
+
workflow_schema?: WorkflowCompiledSchema;
|
|
44
54
|
starter_docs: WorkflowStarterDoc[];
|
|
45
55
|
}): WorkflowDefinition<string>;
|
|
46
56
|
export declare function listCompiledWorkflowChoices(sourcePath?: string): WorkflowDefinition<string>[];
|
|
@@ -5,6 +5,7 @@ import { PACKAGE_ROOT } from "./config.js";
|
|
|
5
5
|
import { resolveSourceControlPath } from "./interf.js";
|
|
6
6
|
import { warnInterf } from "./logger.js";
|
|
7
7
|
import { workflowPackagePathForCompiled } from "./compiled-paths.js";
|
|
8
|
+
import { mergeStagePolicyNotesForStages } from "./workflow-stage-policy.js";
|
|
8
9
|
let builtinInterfWorkflowCache = null;
|
|
9
10
|
function toWorkflowStages(stages, fallbackStages) {
|
|
10
11
|
if (!stages || stages.length === 0)
|
|
@@ -20,19 +21,6 @@ function toWorkflowStages(stages, fallbackStages) {
|
|
|
20
21
|
...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
|
|
21
22
|
}));
|
|
22
23
|
}
|
|
23
|
-
function mergeStagePolicyNotes(stages, baseNotes, overrideNotes) {
|
|
24
|
-
const merged = {};
|
|
25
|
-
for (const definition of stages) {
|
|
26
|
-
const combined = Array.from(new Set([
|
|
27
|
-
...(baseNotes?.[definition.id] ?? []),
|
|
28
|
-
...((overrideNotes?.[definition.id] ?? []).filter((note) => note.trim().length > 0)),
|
|
29
|
-
]));
|
|
30
|
-
if (combined.length > 0) {
|
|
31
|
-
merged[definition.id] = combined;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
35
|
-
}
|
|
36
24
|
export function standaloneWorkflowDefinitionFromLocalPackage(local) {
|
|
37
25
|
if (!local.stages || local.stages.length === 0) {
|
|
38
26
|
throw new Error(`Workflow package "${local.id}" is missing explicit stages. Compiled-local and portable workflow packages must be standalone.`);
|
|
@@ -44,11 +32,20 @@ export function standaloneWorkflowDefinitionFromLocalPackage(local) {
|
|
|
44
32
|
kind: "compiled",
|
|
45
33
|
version: 1,
|
|
46
34
|
},
|
|
35
|
+
...(local.purpose
|
|
36
|
+
? {
|
|
37
|
+
purpose: {
|
|
38
|
+
label: local.purpose.label,
|
|
39
|
+
taskHint: local.purpose.task_hint,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
47
43
|
label: local.label,
|
|
48
44
|
hint: local.hint,
|
|
49
45
|
recommended: false,
|
|
46
|
+
...(local.workflow_schema ? { schema: local.workflow_schema } : {}),
|
|
50
47
|
stages,
|
|
51
|
-
stagePolicyNotes:
|
|
48
|
+
stagePolicyNotes: mergeStagePolicyNotesForStages(stages, undefined, local.stage_policy_notes),
|
|
52
49
|
starterDocs: [...local.starter_docs],
|
|
53
50
|
scope: "local",
|
|
54
51
|
};
|
|
@@ -113,7 +110,7 @@ export function getCompiledWorkflow(workflowId, options = {}) {
|
|
|
113
110
|
return local;
|
|
114
111
|
throw new Error(`No local workflow package found for "${workflowId}" under ${join(options.sourcePath, "interf", "workflows", workflowId)}.`);
|
|
115
112
|
}
|
|
116
|
-
throw new Error(`Workflow "${workflowId}" is not built-in. Provide a source path so Interf
|
|
113
|
+
throw new Error(`Workflow "${workflowId}" is not built-in. Provide a source path so Interf can resolve a local workflow package.`);
|
|
117
114
|
}
|
|
118
115
|
export function getActiveCompiledWorkflow(compiledPath, fallbackWorkflowId) {
|
|
119
116
|
const workflowPath = workflowPackagePathForCompiled(compiledPath);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WorkflowExecutor } from "./executors.js";
|
|
2
|
+
import type { WorkflowValidationResult } from "./local-workflows.js";
|
|
3
|
+
import { type WorkflowEditShellArtifacts } from "./workflow-edit-utils.js";
|
|
4
|
+
export interface WorkflowEditSessionResult {
|
|
5
|
+
status: "updated" | "no-change" | "invalid" | "executor-failed";
|
|
6
|
+
changed: boolean;
|
|
7
|
+
validation: WorkflowValidationResult | null;
|
|
8
|
+
summary: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function runWorkflowEditSession(options: {
|
|
11
|
+
executor: WorkflowExecutor;
|
|
12
|
+
workflowPath: string;
|
|
13
|
+
shell: WorkflowEditShellArtifacts;
|
|
14
|
+
prompt: string;
|
|
15
|
+
validate: (workflowPath: string) => WorkflowValidationResult;
|
|
16
|
+
}): Promise<WorkflowEditSessionResult>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { copyDirectory, directoriesMatch } from "./workflow-edit-utils.js";
|
|
3
|
+
export async function runWorkflowEditSession(options) {
|
|
4
|
+
copyDirectory(options.workflowPath, options.shell.workflowBeforePath);
|
|
5
|
+
writeFileSync(options.shell.promptLogPath, `${options.prompt}\n`);
|
|
6
|
+
let code = 1;
|
|
7
|
+
let executeError = null;
|
|
8
|
+
try {
|
|
9
|
+
code = await options.executor.execute(options.shell.rootPath, options.prompt, {
|
|
10
|
+
eventLogPath: options.shell.eventLogPath,
|
|
11
|
+
statusLogPath: options.shell.statusLogPath,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
executeError = error instanceof Error ? error.message : String(error);
|
|
16
|
+
}
|
|
17
|
+
copyDirectory(options.workflowPath, options.shell.workflowAfterPath);
|
|
18
|
+
const changed = !directoriesMatch(options.shell.workflowBeforePath, options.shell.workflowAfterPath);
|
|
19
|
+
if (executeError) {
|
|
20
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
21
|
+
return {
|
|
22
|
+
status: "executor-failed",
|
|
23
|
+
changed,
|
|
24
|
+
validation: null,
|
|
25
|
+
summary: `Workflow editor failed before completing: ${executeError}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (code !== 0) {
|
|
29
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
30
|
+
return {
|
|
31
|
+
status: "executor-failed",
|
|
32
|
+
changed,
|
|
33
|
+
validation: null,
|
|
34
|
+
summary: `Workflow editor exited with code ${code}.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const validation = options.validate(options.workflowPath);
|
|
38
|
+
if (!validation.ok) {
|
|
39
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
40
|
+
return {
|
|
41
|
+
status: "invalid",
|
|
42
|
+
changed,
|
|
43
|
+
validation,
|
|
44
|
+
summary: changed
|
|
45
|
+
? `Workflow package failed validation: ${validation.summary}`
|
|
46
|
+
: `Workflow package is invalid without any workflow edits: ${validation.summary}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
status: changed ? "updated" : "no-change",
|
|
51
|
+
changed,
|
|
52
|
+
validation,
|
|
53
|
+
summary: changed
|
|
54
|
+
? "Workflow package updated and validated."
|
|
55
|
+
: "Workflow package is valid without additional edits.",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function copyDirectory(sourcePath: string, targetPath: string): void;
|
|
2
|
+
export declare function directoriesMatch(leftPath: string, rightPath: string): boolean;
|
|
3
|
+
export interface WorkflowEditShellArtifacts {
|
|
4
|
+
rootPath: string;
|
|
5
|
+
workflowBeforePath: string;
|
|
6
|
+
workflowAfterPath: string;
|
|
7
|
+
promptLogPath: string;
|
|
8
|
+
eventLogPath: string;
|
|
9
|
+
statusLogPath: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
export function copyDirectory(sourcePath, targetPath) {
|
|
4
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
5
|
+
mkdirSync(targetPath, { recursive: true });
|
|
6
|
+
cpSync(sourcePath, targetPath, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
function listFileSnapshot(dirPath) {
|
|
9
|
+
const snapshot = new Map();
|
|
10
|
+
if (!existsSync(dirPath))
|
|
11
|
+
return snapshot;
|
|
12
|
+
const collect = (currentPath) => {
|
|
13
|
+
for (const entry of readdirSync(currentPath)) {
|
|
14
|
+
const fullPath = join(currentPath, entry);
|
|
15
|
+
const stat = statSync(fullPath);
|
|
16
|
+
if (stat.isDirectory()) {
|
|
17
|
+
collect(fullPath);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (!stat.isFile())
|
|
21
|
+
continue;
|
|
22
|
+
snapshot.set(relative(dirPath, fullPath), readFileSync(fullPath, "utf8"));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
collect(dirPath);
|
|
26
|
+
return snapshot;
|
|
27
|
+
}
|
|
28
|
+
export function directoriesMatch(leftPath, rightPath) {
|
|
29
|
+
const left = listFileSnapshot(leftPath);
|
|
30
|
+
const right = listFileSnapshot(rightPath);
|
|
31
|
+
if (left.size !== right.size)
|
|
32
|
+
return false;
|
|
33
|
+
for (const [relativePath, content] of left.entries()) {
|
|
34
|
+
if (right.get(relativePath) !== content) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|