silvermoon 0.1.3 → 0.2.0-rc.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/src/whatsnext.js CHANGED
@@ -2,13 +2,12 @@ import { resolve } from "node:path";
2
2
 
3
3
  import {
4
4
  ACTIVE_STATES,
5
- createEnvelope,
6
5
  diagnosticInstruction,
7
6
  diagnosticProblem,
8
7
  dialogueReadyObservation,
9
8
  localize,
10
- outcome,
11
9
  } from "./dialogue.js";
10
+ import { createCommandRun } from "./domain.js";
12
11
  import { inspectPhaseGuidance } from "./guidance.js";
13
12
  import {
14
13
  compareCommits,
@@ -236,7 +235,7 @@ function navigationInstruction(ideas, language) {
236
235
 
237
236
  async function assessRepositoryReadinessInternal({
238
237
  observed,
239
- outcomes,
238
+ runtime,
240
239
  requirePrimaryBranch = false,
241
240
  recheckCommand = "silvermoon whats-next",
242
241
  root,
@@ -364,22 +363,22 @@ async function assessRepositoryReadinessInternal({
364
363
  branch.repository !== observed.config.primaryRepository
365
364
  || branch.upstreamBranch !== observed.config.primaryBranch
366
365
  ) {
367
- const actual = branch.remote === null
368
- ? localize(language, "none", "无")
369
- : `${branch.repository ?? branch.remote}#${branch.upstreamBranch ?? localize(language, "unknown", "未知")}`;
370
- localProblems.push({
371
- type: "primary-upstream-mismatch",
372
- summary: localize(
366
+ const actual = branch.remote === null
367
+ ? localize(language, "none", "无")
368
+ : `${branch.repository ?? branch.remote}#${branch.upstreamBranch ?? localize(language, "unknown", "未知")}`;
369
+ localProblems.push({
370
+ type: "primary-upstream-mismatch",
371
+ summary: localize(
372
+ language,
373
+ `Branch ${branch.branch} has upstream ${actual}; expected ${observed.config.primaryRepository}#${observed.config.primaryBranch}.`,
374
+ `分支 ${branch.branch} 的 upstream 是 ${actual};预期为 ${observed.config.primaryRepository}#${observed.config.primaryBranch}。`,
375
+ ),
376
+ });
377
+ localSteps.push(localize(
373
378
  language,
374
- `Branch ${branch.branch} has upstream ${actual}; expected ${observed.config.primaryRepository}#${observed.config.primaryBranch}.`,
375
- `分支 ${branch.branch} 的 upstream 是 ${actual};预期为 ${observed.config.primaryRepository}#${observed.config.primaryBranch}。`,
376
- ),
377
- });
378
- localSteps.push(localize(
379
- language,
380
- `Configure a named remote for ${observed.config.primaryRepository}, then set branch ${branch.branch} to track that remote's ${observed.config.primaryBranch} branch. Verify with ${command(root, 'git -C "<root>" rev-parse --abbrev-ref --symbolic-full-name \'@{upstream}\'')}.`,
381
- `为 ${observed.config.primaryRepository} 配置 named remote,再将分支 ${branch.branch} 的 upstream 设为该 remote 的 ${observed.config.primaryBranch}。使用 ${command(root, 'git -C "<root>" rev-parse --abbrev-ref --symbolic-full-name \'@{upstream}\'')} 验证。`,
382
- ));
379
+ `Configure a named remote for ${observed.config.primaryRepository}, then set branch ${branch.branch} to track that remote's ${observed.config.primaryBranch} branch. Verify with ${command(root, 'git -C "<root>" rev-parse --abbrev-ref --symbolic-full-name \'@{upstream}\'')}.`,
380
+ `为 ${observed.config.primaryRepository} 配置 named remote,再将分支 ${branch.branch} 的 upstream 设为该 remote 的 ${observed.config.primaryBranch}。使用 ${command(root, 'git -C "<root>" rev-parse --abbrev-ref --symbolic-full-name \'@{upstream}\'')} 验证。`,
381
+ ));
383
382
  }
384
383
  }
385
384
 
@@ -412,29 +411,24 @@ async function assessRepositoryReadinessInternal({
412
411
  };
413
412
  }
414
413
 
415
- let primary;
416
- try {
417
- primary = fetchPrimary(root, observed.config);
418
- outcomes.push(outcome(
419
- "fetch-primary",
420
- "success",
421
- localize(
422
- language,
423
- `Fetched ${observed.config.primaryRepository}#${observed.config.primaryBranch} at ${primary} without moving the worktree, index, branch, or named refs.`,
424
- `已 fetch ${observed.config.primaryRepository}#${observed.config.primaryBranch} 的 ${primary},未移动 worktree、index、branch 或 named refs。`,
425
- ),
426
- ));
427
- } catch (caught) {
428
- const message = sanitizeGitMessage(caught.message);
429
- outcomes.push(outcome(
430
- "fetch-primary",
431
- "failure",
432
- localize(
433
- language,
434
- `Could not fetch configured primary: ${message}`,
435
- `无法 fetch configured primary:${message}`,
436
- ),
437
- ));
414
+ const fetched = await runtime.performAction(
415
+ {
416
+ type: "fetch-primary",
417
+ repository: observed.config.primaryRepository,
418
+ branch: observed.config.primaryBranch,
419
+ },
420
+ () => ({
421
+ commit: fetchPrimary(root, observed.config),
422
+ }),
423
+ (caught) => ({
424
+ problem: {
425
+ type: "primary-fetch-failed",
426
+ summary: sanitizeGitMessage(caught.message),
427
+ },
428
+ }),
429
+ );
430
+ if (fetched.status === "failure") {
431
+ const message = fetched.problem.summary;
438
432
  return {
439
433
  observation: repositoryProblemObservation(observed.observation, [{
440
434
  type: "primary-fetch-failed",
@@ -448,6 +442,7 @@ async function assessRepositoryReadinessInternal({
448
442
  ready: false,
449
443
  };
450
444
  }
445
+ const primary = fetched.result.commit;
451
446
 
452
447
  let relation;
453
448
  try {
@@ -556,30 +551,31 @@ export async function whatsNext({
556
551
  language: canonicalLanguage ?? null,
557
552
  },
558
553
  };
554
+ const runtime = createCommandRun(intention);
559
555
  const baseRecheckCommand = selector === undefined
560
556
  ? "silvermoon whats-next"
561
557
  : `silvermoon whats-next ${JSON.stringify(selector)}`;
562
558
  const recheckCommand = canonicalLanguage === undefined
563
559
  ? baseRecheckCommand
564
560
  : `${baseRecheckCommand} --language ${canonicalLanguage}`;
565
- const outcomes = [];
566
561
  let observed = await observeSnapshot({
567
562
  outputLanguage: canonicalLanguage,
568
563
  root: requestedRoot,
569
564
  userHome,
570
565
  version: { type: "worktree" },
571
566
  });
567
+ runtime.observe(observed.observation, { factType: "project.snapshot" });
572
568
  if (!observed.projectReady) {
573
- return createEnvelope(
574
- intention,
569
+ return runtime.complete(
575
570
  observed.observation,
576
- outcomes,
577
- projectInstructions(
578
- observed,
579
- observed.observation.root,
580
- observed.outputLanguage,
581
- recheckCommand,
582
- ),
571
+ {
572
+ nextSteps: projectInstructions(
573
+ observed,
574
+ observed.observation.root,
575
+ observed.outputLanguage,
576
+ recheckCommand,
577
+ ),
578
+ },
583
579
  );
584
580
  }
585
581
 
@@ -589,42 +585,43 @@ export async function whatsNext({
589
585
  }
590
586
  const readiness = await assessRepositoryReadiness({
591
587
  observed,
592
- outcomes,
588
+ runtime,
593
589
  recheckCommand,
594
590
  root: observed.observation.root,
595
591
  });
596
592
  if (!readiness.ready) {
597
- return createEnvelope(
598
- intention,
593
+ return runtime.complete(
599
594
  readiness.observation,
600
- outcomes,
601
- readiness.instructions,
595
+ { nextSteps: readiness.instructions },
602
596
  );
603
597
  }
604
598
 
605
599
  if (selector === undefined) {
606
- return createEnvelope(
607
- intention,
600
+ return runtime.complete(
608
601
  { ...readiness.observation, state: "navigation-ready" },
609
- outcomes,
610
- navigationInstruction(observed.layout.ideas, observed.outputLanguage),
602
+ {
603
+ nextSteps: navigationInstruction(
604
+ observed.layout.ideas,
605
+ observed.outputLanguage,
606
+ ),
607
+ },
611
608
  );
612
609
  }
613
610
  if (!selected) {
614
- return createEnvelope(
615
- intention,
611
+ return runtime.complete(
616
612
  dialogueReadyObservation(readiness.observation, "idea-not-found", {
617
613
  candidates: readiness.observation.ideas.activeIdeas,
618
614
  }),
619
- outcomes,
620
- joinInstructions([
621
- localize(
622
- observed.outputLanguage,
623
- `Idea ${selector} does not match an observed ULID or unique alias.`,
624
- `Idea ${selector} 未匹配任何已观察到的 ULID 或唯一 alias。`,
625
- ),
626
- navigationInstruction(observed.layout.ideas, observed.outputLanguage),
627
- ]),
615
+ {
616
+ nextSteps: joinInstructions([
617
+ localize(
618
+ observed.outputLanguage,
619
+ `Idea ${selector} does not match an observed ULID or unique alias.`,
620
+ `Idea ${selector} 未匹配任何已观察到的 ULID 或唯一 alias。`,
621
+ ),
622
+ navigationInstruction(observed.layout.ideas, observed.outputLanguage),
623
+ ]),
624
+ },
628
625
  );
629
626
  }
630
627
 
@@ -641,8 +638,7 @@ export async function whatsNext({
641
638
  snapshotTree: readiness.primary,
642
639
  });
643
640
  if (inspected.state === "invalid") {
644
- return createEnvelope(
645
- intention,
641
+ return runtime.complete(
646
642
  dialogueReadyObservation(
647
643
  readiness.observation,
648
644
  "phase-guidance-invalid",
@@ -653,24 +649,25 @@ export async function whatsNext({
653
649
  ),
654
650
  },
655
651
  ),
656
- outcomes,
657
- phaseGuidanceInstructions(
658
- inspected.diagnostics,
659
- readiness.observation.root,
660
- observed.outputLanguage,
661
- recheckCommand,
662
- ),
652
+ {
653
+ nextSteps: phaseGuidanceInstructions(
654
+ inspected.diagnostics,
655
+ readiness.observation.root,
656
+ observed.outputLanguage,
657
+ recheckCommand,
658
+ ),
659
+ },
663
660
  );
664
661
  }
665
662
  guidance = inspected.guidance;
666
663
  }
667
- return createEnvelope(
668
- intention,
664
+ return runtime.complete(
669
665
  dialogueReadyObservation(readiness.observation, "idea-selected", {
670
666
  selectedIdea,
671
667
  ...(guidance === undefined ? {} : { guidance }),
672
668
  }),
673
- outcomes,
674
- lifecycleInstruction(selected, observed.outputLanguage),
669
+ {
670
+ nextSteps: lifecycleInstruction(selected, observed.outputLanguage),
671
+ },
675
672
  );
676
673
  }