imprint-mcp 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.
@@ -82,7 +82,9 @@ import {
82
82
  isExistingTeachFile as isExistingFile,
83
83
  loadTeachState,
84
84
  nextTeachStep as nextStep,
85
+ pruneStalePendingTeachWorkflows,
85
86
  resolveTeachStatePath,
87
+ resolveWorkflowTriagedPath,
86
88
  saveTeachState,
87
89
  toRelativeTeachStatePath as toRelative,
88
90
  } from './teach-state.ts';
@@ -98,7 +100,11 @@ import { setSpanAttributes, traced } from './tracing.ts';
98
100
  import { CronConfigSchema, SessionSchema, WorkflowSchema } from './types.ts';
99
101
  import type { CronConfig, Playbook, Session, Workflow } from './types.ts';
100
102
 
101
- export { buildTeachStateFromSession, resolveTeachStatePath } from './teach-state.ts';
103
+ export {
104
+ buildTeachStateFromSession,
105
+ resolveTeachStatePath,
106
+ resolveWorkflowTriagedPath,
107
+ } from './teach-state.ts';
102
108
 
103
109
  /**
104
110
  * How many compile agents run in parallel when more than one tool is selected.
@@ -396,6 +402,9 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
396
402
 
397
403
  const completedWorkflows = discoverCompletedWorkflows(site);
398
404
  const completedSet = new Set(completedWorkflows);
405
+ if (pruneStalePendingTeachWorkflows(site, state)) {
406
+ saveTeachState(site, state);
407
+ }
399
408
  const incompleteWorkflows = Object.entries(state.workflows).filter(
400
409
  ([name]) => !completedSet.has(name),
401
410
  );
@@ -725,7 +734,6 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
725
734
  // Run them in parallel so the user can select tools while replay runs.
726
735
  let siteClassifications: ClassifiedValue[] | undefined;
727
736
  let triageResult: TriageResult | undefined;
728
- let triagedPath: string | null = null;
729
737
  let plans: CandidateCompilePlan[];
730
738
 
731
739
  let needsReplay = startIdx <= STEPS.indexOf('replay-and-diff') && !opts.skipReplay;
@@ -783,7 +791,7 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
783
791
 
784
792
  // Branch B: triage → detect-candidates → user selection (fast, ~30s)
785
793
  type CandidateChainResult = {
786
- triageRes?: { result: TriageResult; sessionPath: string };
794
+ triageResult?: TriageResult;
787
795
  plans: CandidateCompilePlan[];
788
796
  };
789
797
  const candidatePromise = (async (): Promise<CandidateChainResult> => {
@@ -836,8 +844,11 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
836
844
  );
837
845
  } else {
838
846
  const ws = state.workflows[workflowKey];
839
- if (ws?.triagedPath) {
840
- localTriagedPath = resolveTeachStatePath(site, ws.triagedPath);
847
+ localTriagedPath = resolveWorkflowTriagedPath(site, ws);
848
+ if (ws && localTriagedPath && !ws.triagedPath) {
849
+ updateCheckpoint(site, state, workflowKey, 'triage', {
850
+ triagedPath: toRelative(site, localTriagedPath),
851
+ });
841
852
  }
842
853
  }
843
854
 
@@ -875,6 +886,9 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
875
886
  kind: 'raw',
876
887
  });
877
888
  const baseState = buildTeachStateFromSession(site, rawSessionPath, redactedPath);
889
+ if (localTriagedPath) {
890
+ baseState.triagedPath = toRelative(site, localTriagedPath);
891
+ }
878
892
  const candidatePlans = selected.map((candidate) => {
879
893
  checkpoint(site, state, candidate.toolName, {
880
894
  ...baseState,
@@ -896,9 +910,7 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
896
910
  }
897
911
 
898
912
  return {
899
- triageRes: localTriageResult
900
- ? { result: localTriageResult, sessionPath: replaySessionPath }
901
- : undefined,
913
+ triageResult: localTriageResult,
902
914
  plans: candidatePlans,
903
915
  };
904
916
  })();
@@ -907,13 +919,7 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
907
919
  const candidateResult = await candidatePromise;
908
920
  plans = candidateResult.plans;
909
921
 
910
- if (candidateResult.triageRes) {
911
- triageResult = candidateResult.triageRes.result;
912
- triagedPath = candidateResult.triageRes.sessionPath.replace(
913
- /\.redacted\.json$/,
914
- '.triaged.json',
915
- );
916
- }
922
+ triageResult = candidateResult.triageResult;
917
923
 
918
924
  // Wait for replay — may already be done, or show progress while waiting
919
925
  let replaySettled = false;
@@ -938,18 +944,19 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
938
944
  mp.clear();
939
945
 
940
946
  // Checkpoints — write sequentially after both complete
941
- if (needsReplay) {
942
- updateCheckpoint(site, state, workflowKey, 'replay-and-diff', {
943
- classificationsPath: siteClassifications
944
- ? toRelative(site, pathJoin(localSiteDir(site), '.classifications.json'))
945
- : undefined,
946
- });
947
- }
948
- if (candidateResult.triageRes && triagedPath) {
949
- updateCheckpoint(site, state, workflowKey, 'triage', {
950
- triagedPath: toRelative(site, triagedPath),
951
- });
952
- }
947
+ updateCandidateStageCheckpoints({
948
+ site,
949
+ state,
950
+ plans,
951
+ fallbackWorkflowKey: workflowKey,
952
+ replay: needsReplay
953
+ ? {
954
+ classificationsPath: siteClassifications
955
+ ? toRelative(site, pathJoin(localSiteDir(site), '.classifications.json'))
956
+ : undefined,
957
+ }
958
+ : undefined,
959
+ });
953
960
  } finally {
954
961
  unmuteLog();
955
962
  }
@@ -964,8 +971,11 @@ export async function teach(opts: TeachOptions): Promise<TeachResult> {
964
971
  }
965
972
  }
966
973
  const ws = state.workflows[workflowKey];
967
- if (ws?.triagedPath) {
968
- triagedPath = resolveTeachStatePath(site, ws.triagedPath);
974
+ const resolvedTriagedPath = resolveWorkflowTriagedPath(site, ws);
975
+ if (ws && resolvedTriagedPath && !ws.triagedPath) {
976
+ updateCheckpoint(site, state, workflowKey, 'triage', {
977
+ triagedPath: toRelative(site, resolvedTriagedPath),
978
+ });
969
979
  }
970
980
  plans = [
971
981
  {
@@ -1251,6 +1261,29 @@ export interface CandidateCompilePlan {
1251
1261
  sharedContext?: SharedCompileContext;
1252
1262
  }
1253
1263
 
1264
+ function candidateStageCheckpointKeys(
1265
+ plans: CandidateCompilePlan[],
1266
+ fallbackWorkflowKey: string,
1267
+ ): string[] {
1268
+ const keys = plans.map((plan) => plan.workflowKey).filter((key) => key.length > 0);
1269
+ return [...new Set(keys.length > 0 ? keys : [fallbackWorkflowKey])];
1270
+ }
1271
+
1272
+ export function updateCandidateStageCheckpoints(opts: {
1273
+ site: string;
1274
+ state: TeachState;
1275
+ plans: CandidateCompilePlan[];
1276
+ fallbackWorkflowKey: string;
1277
+ replay?: Partial<WorkflowState>;
1278
+ triage?: Partial<WorkflowState>;
1279
+ }): void {
1280
+ const keys = candidateStageCheckpointKeys(opts.plans, opts.fallbackWorkflowKey);
1281
+ for (const key of keys) {
1282
+ if (opts.replay) updateCheckpoint(opts.site, opts.state, key, 'replay-and-diff', opts.replay);
1283
+ if (opts.triage) updateCheckpoint(opts.site, opts.state, key, 'triage', opts.triage);
1284
+ }
1285
+ }
1286
+
1254
1287
  async function detectTeachCandidates(opts: {
1255
1288
  sessionPath: string;
1256
1289
  providerName: ProviderName;