mainflow 0.44.0-alpha.3 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { D as SESSION_ID, P as UNTIL_FORMS, X as parseUntil, Y as parseRef, Z as refExample, ct as stampMs, it as untilProblem, n as CLOSED_STATUS } from "./dist-CXrUfRQp.js";
3
- import { $ as applyOperation, $t as dataFile, A as unhandledNotices, Bt as hubHoldingFile, C as duplicateClaimLines, Ft as MAINLINE_FILE, Gt as registerHub, Ht as listHubs, Jt as resolveHub, Kt as registeredName, Lt as TASK_FILE, M as readSession, Rt as requireSlug, S as untilState, St as MAX_FILE_BYTES, Ut as notRegisteredMessage, Wt as readRegistry, Xt as userDataDir, Yt as unregisterHub, Zt as nodeStorage, _ as receiverFromEnv, an as DATA_DIR, d as groupPlan, et as checkHubName, f as listProgress, ft as remoteMembers, g as retroReportFromReceiver, h as retroReport, ht as nowMinute, i as hostName, it as loadHub, j as loadSessions, m as migrateHub, nn as realPath, on as WriteError, ot as sectionText, p as migrateHubData, pt as unavailableHubs, qt as registryFile, tt as HubError, u as sessionView, ut as memberOfGroup, v as RETRY_RULES, w as sessionBriefing, x as initHub, xt as MAX_CHANGE_PAGE, yt as BLOB_HASH, zt as hubAtRoot } from "./host-CsZY5trM.js";
4
- import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-MKNwQkAY.js";
3
+ import { $t as unregisterHub, At as requestTime, Bt as endedSessionFileIn, C as untilState, Ct as MAX_FILE_BYTES, Ht as sessionFileIn, Jt as readRegistry, Kt as listHubs, Lt as MAINLINE_FILE, M as loadSessions, N as readSession, Nt as hubFormatVersion, Qt as resolveHub, S as initHub, St as MAX_CHANGE_PAGE, T as sessionBriefing, Ut as hubAtRoot, Vt as requireSlug, Wt as hubHoldingFile, Xt as registeredName, Yt as registerHub, Zt as registryFile, _ as retroReportFromReceiver, at as loadHub, bt as BLOB_HASH, d as groupPlan, dt as memberOfGroup, en as userDataDir, et as applyOperation, f as listProgress, g as retroReport, gt as nowMinute, h as relocateSessions, i as hostName, j as unhandledNotices, ln as DATA_DIR, m as migrateHub, mt as unavailableHubs, nt as HubError, on as realPath, p as migrateHubData, pt as remoteMembers, qt as notRegisteredMessage, rn as dataFile, st as sectionText, tn as nodeStorage, tt as checkHubName, u as sessionView, un as WriteError, v as receiverFromEnv, w as duplicateClaimLines, y as RETRY_RULES, zt as TASK_FILE } from "./host-CfBcUcg2.js";
4
+ import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-BmNzQC-L.js";
5
5
  import { COMMAND_SPECS, OPTIONS } from "./facts.js";
6
6
  import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
7
7
  import { claudeCodeSessionId, hookFallback, openLink, parsePayload, runHook } from "./hooks.js";
8
8
  import { runGrantCheck } from "./grant-check.js";
9
9
  import { exitCodeFor, launchUi, networkProbe, readUiRecord, startUiInBackground, watchHubGone } from "./launch.js";
10
- import { randomBytes } from "node:crypto";
10
+ import { randomBytes, randomUUID } from "node:crypto";
11
11
  import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
12
12
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
@@ -54,6 +54,19 @@ function migrationLines(report, dryRun) {
54
54
  ...report.missingDefinitions.length ? [`${report.missingDefinitions.length} open entries still need a definition of done; fill them in the files:`, ...report.missingDefinitions.map((entry) => ` ${entry}`)] : []
55
55
  ];
56
56
  }
57
+ /**
58
+ * What `mainflow migrate` prints of moving the session files: each session moved to local/sessions/ended/, as one that
59
+ * ended or was handed off, and each moved back, as one under way; one line when every file is where it belongs.
60
+ */
61
+ function relocationLines(moved, dryRun) {
62
+ if (moved.ended.length === 0 && moved.resumed.length === 0) return [`sessions: every session file is in place already${dryRun ? " (dry run)" : ""}`];
63
+ const line = (from, to) => ` ${dryRun ? "would move" : "moved"} ${DATA_DIR}/${from} → ${DATA_DIR}/${to}`;
64
+ return [
65
+ `sessions: ${moved.ended.length} to ${DATA_DIR}/local/sessions/ended/, ${moved.resumed.length} back to ${DATA_DIR}/local/sessions/${dryRun ? " (dry run, nothing moved)" : ""}`,
66
+ ...moved.ended.map((id) => line(sessionFileIn(id), endedSessionFileIn(id))),
67
+ ...moved.resumed.map((id) => line(endedSessionFileIn(id), sessionFileIn(id)))
68
+ ];
69
+ }
57
70
  //#endregion
58
71
  //#region src/remote/sync.ts
59
72
  /**
@@ -727,6 +740,50 @@ async function syncMigrate(dirs, opts) {
727
740
  if (!readDaemon(dirs)) console.log("! mainflow sync is not running for this hub; this copy takes the migration in once it runs");
728
741
  return 0;
729
742
  }
743
+ /**
744
+ * Move the session files of a hub the sync server keeps to the directory each belongs in (session.relocate), a batch a
745
+ * request until none is left, straight on the server as `mainflow migrate` converts the data format there. The sync
746
+ * client takes the moves in as it takes any write; one that does not run takes them once it runs. A dry run lists the
747
+ * moves of this machine's copy, which the sync client keeps as the server has it, since the server refuses any
748
+ * operation on a hub whose data format a dry run of the conversion left as it was.
749
+ */
750
+ async function syncRelocate(dirs, opts) {
751
+ if (opts.dryRun) {
752
+ for (const line of relocationLines(relocateSessions(dirs, { dryRun: true }), true)) console.log(line);
753
+ console.log(" (listed from this machine's copy of the hub)");
754
+ return 0;
755
+ }
756
+ const settings = serverOf(dirs);
757
+ if (!settings) return 1;
758
+ const outcome = await onServer(settings, async (api) => {
759
+ const moved = {
760
+ ended: [],
761
+ resumed: []
762
+ };
763
+ for (;;) {
764
+ const request = {
765
+ protocol: 3,
766
+ op: "session.relocate",
767
+ input: { limit: 500 },
768
+ now: requestTime(),
769
+ requestId: randomUUID()
770
+ };
771
+ const answer = await api.send(request);
772
+ if (!answer.ok) return { refused: answer.reason === "unknown-operation" ? `${answer.message}; deploy the sync server of this mainflow version (apps/sync-server)` : answer.message };
773
+ moved.ended.push(...answer.value.ended);
774
+ moved.resumed.push(...answer.value.resumed);
775
+ if (!answer.value.more) return moved;
776
+ }
777
+ }, "operation route");
778
+ if (!outcome) return 1;
779
+ if ("refused" in outcome) {
780
+ console.error(`✗ the sync server refused moving the session files: ${outcome.refused}`);
781
+ return 1;
782
+ }
783
+ for (const line of relocationLines(outcome, false)) console.log(line);
784
+ if (outcome.ended.length + outcome.resumed.length > 0 && !readDaemon(dirs)) console.log("! mainflow sync is not running for this hub; this copy takes the moves in once it runs");
785
+ return 0;
786
+ }
730
787
  //#endregion
731
788
  //#region src/retro.ts
732
789
  const int = (n) => Math.round(n).toLocaleString("en-US");
@@ -1615,8 +1672,10 @@ const RUNS = {
1615
1672
  },
1616
1673
  migrate: ({ dirs, opts }) => {
1617
1674
  const dryRun = opts["dry-run"] ?? false;
1618
- if (remoteMode(dirs)) return syncMigrate(dirs, { dryRun });
1619
- for (const line of migrationLines(migrateHub(dirs, { dryRun }), dryRun)) console.log(line);
1675
+ if (remoteMode(dirs)) return syncMigrate(dirs, { dryRun }).then((code) => code === 0 ? syncRelocate(dirs, { dryRun }) : code);
1676
+ if (hubFormatVersion(nodeStorage(dirs)) === 3) console.log(`format 3: the data format is current already`);
1677
+ else for (const line of migrationLines(migrateHub(dirs, { dryRun }), dryRun)) console.log(line);
1678
+ for (const line of relocationLines(relocateSessions(dirs, { dryRun }), dryRun)) console.log(line);
1620
1679
  return 0;
1621
1680
  },
1622
1681
  ui: ({ dirs, opts }) => {
package/dist/facts.d.ts CHANGED
@@ -741,6 +741,7 @@ export declare const LAYOUT: {
741
741
  readonly group: '.mainflow/groups/w{NNN}-{slug}.md';
742
742
  readonly ops: '.mainflow/ops/{YYYY-MM}.log';
743
743
  readonly sessions: '.mainflow/local/sessions/{sessionId}.yaml';
744
+ readonly endedSessions: '.mainflow/local/sessions/ended/{sessionId}.yaml';
744
745
  };
745
746
  /** Statuses that close each kind of document; the CLI stamps closedAt when a document enters one. */
746
747
  export declare const CLOSED_STATUSES: {
package/dist/facts.js CHANGED
@@ -646,7 +646,8 @@ const LAYOUT = {
646
646
  finding: ".mainflow/findings/f{NNN}-{slug}.md",
647
647
  group: ".mainflow/groups/w{NNN}-{slug}.md",
648
648
  ops: ".mainflow/ops/{YYYY-MM}.log",
649
- sessions: ".mainflow/local/sessions/{sessionId}.yaml"
649
+ sessions: ".mainflow/local/sessions/{sessionId}.yaml",
650
+ endedSessions: ".mainflow/local/sessions/ended/{sessionId}.yaml"
650
651
  };
651
652
  /** Statuses that close each kind of document; the CLI stamps closedAt when a document enters one. */
652
653
  const CLOSED_STATUSES = CLOSED_STATUS;
@@ -1,5 +1,5 @@
1
- import { o as HubDirs } from "./index-jq7lN8V8.js";
2
- import { t as HubWrite } from "./operation-BroV98VB.js";
1
+ import { o as HubDirs } from "./index-lsW80LDU.js";
2
+ import { t as HubWrite } from "./operation-BTQm13OQ.js";
3
3
  //#region src/grant-check.d.ts
4
4
  /** What grant check prints. `grants` lists every grant used when more than one was, as for an install of several units. */
5
5
  export interface GrantVerdict {
@@ -1,6 +1,6 @@
1
1
  import { D as SESSION_ID, I as configSchema, ft as describeRequest, gt as hasControl, tt as repoPaths } from "./dist-CXrUfRQp.js";
2
- import { F as matchGrants, Jt as resolveHub, M as readSession, Mt as hubFormatVersion, P as grantScope, Qt as currentBranch, Zt as nodeStorage, _t as readYaml, en as gitCheckout, jt as formatMismatch, nn as realPath } from "./host-CsZY5trM.js";
3
- import { n as hubWriter, r as writtenLines } from "./operation-MKNwQkAY.js";
2
+ import { F as grantScope, I as matchGrants, Mt as formatMismatch, N as readSession, Nt as hubFormatVersion, Qt as resolveHub, in as gitCheckout, nn as currentBranch, on as realPath, tn as nodeStorage, vt as readYaml } from "./host-CfBcUcg2.js";
3
+ import { n as hubWriter, r as writtenLines } from "./operation-BmNzQC-L.js";
4
4
  import { OWNER_REPO, githubOrigin, quote, readManifest, resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
5
5
  import { claudeCodeSessionId, parsePayload } from "./hooks.js";
6
6
  import { readFileSync } from "node:fs";
package/dist/hooks.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { o as HubDirs } from "./index-jq7lN8V8.js";
2
- import { t as HubWrite } from "./operation-BroV98VB.js";
1
+ import { o as HubDirs } from "./index-lsW80LDU.js";
2
+ import { t as HubWrite } from "./operation-BTQm13OQ.js";
3
3
  //#region src/hooks.d.ts
4
4
  /** Fields Claude Code sends on stdin to every command hook, plus the ones specific to the events handled here. */
5
5
  export interface ClaudeCodePayload {
package/dist/hooks.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { I as configSchema, Y as parseRef, ft as describeRequest, rt as taskSchema, tt as repoPaths } from "./dist-CXrUfRQp.js";
2
- import { $t as dataFile, Bt as hubHoldingFile, F as matchGrants, I as decorateReply, It as SESSIONS_DIR, Jt as resolveHub, L as endsWithMarker, M as readSession, Mt as hubFormatVersion, P as grantScope, Qt as currentBranch, R as headerLine, T as turnReminder, Ut as notRegisteredMessage, Vt as inRepositoryHubAt, Zt as nodeStorage, _t as readYaml, an as DATA_DIR, at as loadMainlines, gt as readFrontMatter, i as hostName, it as loadHub, j as loadSessions, jt as formatMismatch, mt as taskFile, nn as realPath, nt as isOpenMainline, rn as worktreesOf, tn as hubPath, tt as HubError, w as sessionBriefing, z as mayDecorate } from "./host-CsZY5trM.js";
3
- import { n as hubWriter, r as writtenLines } from "./operation-MKNwQkAY.js";
2
+ import { B as mayDecorate, E as turnReminder, F as grantScope, Gt as inRepositoryHubAt, I as matchGrants, It as ENDED_SESSIONS_DIR, L as decorateReply, M as loadSessions, Mt as formatMismatch, N as readSession, Nt as hubFormatVersion, Qt as resolveHub, R as endsWithMarker, Rt as SESSIONS_DIR, T as sessionBriefing, Wt as hubHoldingFile, _t as readFrontMatter, an as hubPath, at as loadHub, ht as taskFile, i as hostName, ln as DATA_DIR, nn as currentBranch, nt as HubError, on as realPath, ot as loadMainlines, qt as notRegisteredMessage, rn as dataFile, rt as isOpenMainline, sn as worktreesOf, tn as nodeStorage, vt as readYaml, z as headerLine } from "./host-CfBcUcg2.js";
3
+ import { n as hubWriter, r as writtenLines } from "./operation-BmNzQC-L.js";
4
4
  import { SCENARIO_LINES, SCENARIO_SKIP_KINDS } from "./facts.js";
5
5
  import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
6
6
  import { createHash } from "node:crypto";
@@ -463,7 +463,7 @@ function inSessionScope(file, session) {
463
463
  const mainlines = `${DATA_DIR}/mainlines/`;
464
464
  const sessions = `${DATA_DIR}/${SESSIONS_DIR}/`;
465
465
  if (file.startsWith(mainlines)) return [...servedMainlines(session)].some((m) => file.startsWith(`${mainlines}${m}-`));
466
- if (file.startsWith(sessions)) return file === `${sessions}${session.id}.yaml`;
466
+ if (file.startsWith(sessions)) return file === `${sessions}${session.id}.yaml` || file === `.mainflow/${ENDED_SESSIONS_DIR}/${session.id}.yaml`;
467
467
  return true;
468
468
  }
469
469
  /**