agent-work-loop 0.6.31 → 0.6.34

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.
Files changed (47) hide show
  1. package/README.md +6 -0
  2. package/dist/{brief-ZZSQ6RQS.js → brief-2UCDBAB6.js} +5 -5
  3. package/dist/{changelog-JWLVVHZL.js → changelog-AICBEFUV.js} +5 -5
  4. package/dist/{chunk-SV4D72NX.js → chunk-3R52QFNQ.js} +2 -2
  5. package/dist/{chunk-Q7V3WVKT.js → chunk-4Q4FDZXN.js} +4 -4
  6. package/dist/{chunk-RNZCRXE3.js → chunk-EPVWUYXP.js} +5 -5
  7. package/dist/{chunk-2DAP3QBU.js → chunk-N5QEEXCO.js} +231 -129
  8. package/dist/{chunk-7NOHUUXV.js → chunk-O7V4DHPZ.js} +73 -12
  9. package/dist/{chunk-DIRYW77S.js → chunk-QFHXKTV6.js} +5 -5
  10. package/dist/{chunk-RRQYSRRT.js → chunk-RG6FT7I2.js} +5 -5
  11. package/dist/{chunk-3UPF7LZT.js → chunk-SDNJULFU.js} +5 -5
  12. package/dist/{chunk-45X7T64Y.js → chunk-SUF5ISJM.js} +63 -20
  13. package/dist/{chunk-JPFGMDQ4.js → chunk-SZQNXMYU.js} +62 -42
  14. package/dist/{chunk-22SUY5RE.js → chunk-TUV6NSTM.js} +6 -6
  15. package/dist/{chunk-77EZYCLF.js → chunk-VKLSQMAW.js} +2 -2
  16. package/dist/{chunk-5APWNDRG.js → chunk-VR74OBHK.js} +2 -2
  17. package/dist/{chunk-TTZY2GLK.js → chunk-W2AMCPKI.js} +1 -1
  18. package/dist/{chunk-Z3QCPOOA.js → chunk-WL674RWY.js} +2 -2
  19. package/dist/{chunk-YP4VCWPV.js → chunk-WPZK7LJU.js} +3 -3
  20. package/dist/cli.js +122 -51
  21. package/dist/{commit-3UZE7XJM.js → commit-PQE62KLZ.js} +6 -6
  22. package/dist/{config-DAD4LIWR.js → config-LHWI2J4M.js} +3 -3
  23. package/dist/{doctor-AW65MZKN.js → doctor-MEYI2ROW.js} +8 -8
  24. package/dist/{evolve-K4WKRA6U.js → evolve-KSUWIZCY.js} +7 -7
  25. package/dist/{feedback-3C33YHKF.js → feedback-633KAKEZ.js} +5 -5
  26. package/dist/{gotchas-IGEQILOO.js → gotchas-R236D2U2.js} +7 -7
  27. package/dist/{hold-recheck-THSJKF5G.js → hold-recheck-7D47ITAP.js} +16 -16
  28. package/dist/{init-MRJEU7IR.js → init-U7F6ECXI.js} +12 -2
  29. package/dist/lane-2OZBXFKL.js +41 -0
  30. package/dist/{loop-summary-MPEKQ2EC.js → loop-summary-HBADJTOZ.js} +6 -6
  31. package/dist/{metrics-64WBBQQX.js → metrics-WI2GOPGG.js} +4 -4
  32. package/dist/{record-GAWMF7KL.js → record-CEEEHNNF.js} +5 -5
  33. package/dist/{remove-ELP4MFVE.js → remove-GWSIRW36.js} +25 -15
  34. package/dist/{review-A5Q7NSOD.js → review-F6Y2TD5B.js} +13 -13
  35. package/dist/{rules-BEQT7YVN.js → rules-A7YTU3HV.js} +8 -8
  36. package/dist/{state-RDKIOU25.js → state-KHZNSJRS.js} +4 -4
  37. package/dist/status-HU3RLDSB.js +40 -0
  38. package/dist/{update-7RQQJ2TH.js → update-7BLNEJ52.js} +5 -5
  39. package/dist/{verify-UUJ6KD37.js → verify-WJNFIF4Y.js} +10 -10
  40. package/dist/version-check-DFNWGNLY.js +14 -0
  41. package/dist/work-LDPAHEPH.js +60 -0
  42. package/engine/version.json +1 -1
  43. package/package.json +1 -1
  44. package/dist/lane-ZQVD7EDE.js +0 -41
  45. package/dist/status-FP73XOZS.js +0 -40
  46. package/dist/version-check-H2T3WW3I.js +0 -14
  47. package/dist/work-HWJSUWBR.js +0 -50
@@ -1,6 +1,12 @@
1
1
  import {
2
2
  caps,
3
3
  engineDir,
4
+ flowActiveNode,
5
+ flowClose,
6
+ flowConnector,
7
+ flowOpen,
8
+ flowSelectLine,
9
+ flowStepLine,
4
10
  globalRoot,
5
11
  installedEngineVersion,
6
12
  makeColors,
@@ -12,7 +18,7 @@ import {
12
18
  sectionBox,
13
19
  signal,
14
20
  stringWidth
15
- } from "./chunk-45X7T64Y.js";
21
+ } from "./chunk-SUF5ISJM.js";
16
22
 
17
23
  // src/commands/init.ts
18
24
  import fs from "fs";
@@ -107,14 +113,21 @@ function moveCursorUp(n) {
107
113
  }
108
114
  function renderInteractiveSelect(options, state, multi, c, presentation) {
109
115
  const color = makeColors(c.color);
110
- const lines = renderSelectOptions(options, state, multi, c).split("\n");
116
+ const lines = [];
117
+ if (presentation.body && presentation.body.length > 0) {
118
+ lines.push(...presentation.body, "");
119
+ }
120
+ lines.push(...renderSelectOptions(options, state, multi, c).split("\n"));
111
121
  if (presentation.hint) {
112
122
  lines.push("");
113
123
  lines.push(color.dim(presentation.hint));
114
124
  }
115
- const text = sectionBox(presentation.title ?? "\uC120\uD0DD", lines, c);
125
+ const text = flowActiveNode(presentation.title ?? "\uC120\uD0DD", lines, c);
116
126
  return { text, lineCount: text.split("\n").length };
117
127
  }
128
+ function clearFrame(n) {
129
+ return n > 0 ? `${moveCursorUp(n)}\x1B[0J` : "";
130
+ }
118
131
  async function runInteractiveSelect(options, defaultIndex, multi, c, defaultChecked = [], presentation = {}) {
119
132
  let state = initSelectState(defaultIndex, defaultChecked);
120
133
  process.stdin.setRawMode(true);
@@ -134,9 +147,45 @@ async function runInteractiveSelect(options, defaultIndex, multi, c, defaultChec
134
147
  process.stdin.setRawMode(false);
135
148
  process.stdin.pause();
136
149
  }
150
+ process.stdout.write(clearFrame(rendered.lineCount));
137
151
  return state.cancelled ? null : { index: state.index, checked: [...state.checked] };
138
152
  }
139
153
 
154
+ // src/core/flow.ts
155
+ function openFlow(title, c) {
156
+ process.stdout.write(`${flowOpen(title, c)}
157
+ `);
158
+ return { c };
159
+ }
160
+ function step(session, title) {
161
+ process.stdout.write(`${flowConnector(session.c)}
162
+ ${flowStepLine(title, session.c)}
163
+ `);
164
+ }
165
+ function selectStep(session, title) {
166
+ process.stdout.write(`${flowConnector(session.c)}
167
+ ${flowSelectLine(title, session.c)}
168
+ `);
169
+ }
170
+ async function activeSelect(session, options, defaultIndex, multi, defaultChecked = [], presentation = {}) {
171
+ process.stdout.write(`${flowConnector(session.c)}
172
+ `);
173
+ const result = await runInteractiveSelect(
174
+ options,
175
+ defaultIndex,
176
+ multi,
177
+ session.c,
178
+ defaultChecked,
179
+ presentation
180
+ );
181
+ process.stdout.write("\x1B[1A\x1B[0J");
182
+ return result;
183
+ }
184
+ function closeFlow(session) {
185
+ process.stdout.write(`${flowClose(session.c)}
186
+ `);
187
+ }
188
+
140
189
  // src/commands/init.ts
141
190
  function exists(p) {
142
191
  try {
@@ -576,7 +625,7 @@ function writeSkillsVersionStamp(projectRoot, installed, engineVersion) {
576
625
  fs.writeFileSync(p, `${JSON.stringify(next, null, 2)}
577
626
  `);
578
627
  }
579
- function syncExistingInstall(projectRoot, engineVersion) {
628
+ function syncExistingInstall(projectRoot, engineVersion, now) {
580
629
  let configUpdated = false;
581
630
  const configPath = path.join(projectRoot, ".awl", "config.json");
582
631
  const raw = readJson(configPath);
@@ -585,6 +634,13 @@ function syncExistingInstall(projectRoot, engineVersion) {
585
634
  `);
586
635
  configUpdated = true;
587
636
  }
637
+ registerProject({
638
+ name: typeof raw?.project === "string" ? raw.project : path.basename(projectRoot),
639
+ path: projectRoot,
640
+ mainLanguage: Array.isArray(raw?.mainLanguage) ? raw.mainLanguage : [],
641
+ character: typeof raw?.character === "string" ? raw.character : "",
642
+ registeredAt: now
643
+ });
588
644
  const skills = [];
589
645
  const claudeInUse = claudeSkillNames().some(
590
646
  (name) => exists(path.join(projectRoot, ".claude", "skills", name))
@@ -670,8 +726,8 @@ function applyInit(projectRoot, inputs, now) {
670
726
  };
671
727
  }
672
728
  var WIDTH = 64;
673
- function stepBox(step, title, lines, c) {
674
- return sectionBox(`${step}${c.unicode ? " \xB7 " : " - "}${title}`, lines, c, WIDTH - 4);
729
+ function stepBox(step2, title, lines, c) {
730
+ return sectionBox(`${step2}${c.unicode ? " \xB7 " : " - "}${title}`, lines, c, WIDTH - 4);
675
731
  }
676
732
  var VERIFY_LABELS = {
677
733
  typecheck: "\uD0C0\uC785\uCCB4\uD06C",
@@ -709,7 +765,7 @@ function renderNonTtyNotice() {
709
765
  ""
710
766
  ].join("\n");
711
767
  }
712
- function renderResult(result, inputs, c) {
768
+ function resultSetupLines(result, c) {
713
769
  const color = makeColors(c.color);
714
770
  const t = makeTokens(c);
715
771
  const line = (name, value, note = "") => ` ${padEndDisplay(name, 20)}${t.emphasis(value)}${note ? ` ${color.dim(note)}` : ""}`;
@@ -729,6 +785,10 @@ function renderResult(result, inputs, c) {
729
785
  `\uADDC\uCE59 ${result.ruleCount}\uAC1C \xB7 \uAD50\uD6C8 ${result.lessonCount}\uAC1C \xB7 \uB4F1\uB85D\uB41C \uD504\uB85C\uC81D\uD2B8 ${result.projectCount}\uAC1C \xB7 1\uC138\uB300`
730
786
  )
731
787
  );
788
+ return setupLines;
789
+ }
790
+ function resultNextLines(inputs, c) {
791
+ const color = makeColors(c.color);
732
792
  const nextLines = [];
733
793
  if (inputs.skills.claude) {
734
794
  nextLines.push("Claude Code \uB97C \uC5F4\uACE0 \uC774\uB807\uAC8C \uB9D0\uD558\uC138\uC694.");
@@ -741,13 +801,26 @@ function renderResult(result, inputs, c) {
741
801
  } else {
742
802
  nextLines.push("\uB098\uC911\uC5D0 \uC2A4\uD0AC\uC744 \uC124\uCE58\uD558\uB824\uBA74 awl init \uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.");
743
803
  }
804
+ return nextLines;
805
+ }
806
+ function renderResult(result, inputs, c) {
744
807
  return [
745
808
  "",
746
- sectionBox("\uC124\uC815 \uC644\uB8CC", setupLines, c, WIDTH - 4),
809
+ sectionBox("\uC124\uC815 \uC644\uB8CC", resultSetupLines(result, c), c, WIDTH - 4),
747
810
  "",
748
- sectionBox("\uB2E4\uC74C \uB2E8\uACC4", nextLines, c, WIDTH - 4)
811
+ sectionBox("\uB2E4\uC74C \uB2E8\uACC4", resultNextLines(inputs, c), c, WIDTH - 4)
749
812
  ].join("\n");
750
813
  }
814
+ function commitResultFlow(session, result, inputs, c) {
815
+ for (const line of resultSetupLines(result, c)) {
816
+ step(session, line);
817
+ }
818
+ step(session, "\uB2E4\uC74C \uB2E8\uACC4");
819
+ for (const line of resultNextLines(inputs, c)) {
820
+ process.stdout.write(`${flowConnector(c)} ${line}
821
+ `);
822
+ }
823
+ }
751
824
  var LANG_OPTIONS = ["TypeScript", "JavaScript", "Python", "\uC9C1\uC811 \uC785\uB825"];
752
825
  var LANG_VALUES = ["typescript", "javascript", "python", ""];
753
826
  function langDefaultIndices(projectRoot) {
@@ -760,10 +833,19 @@ function langDefaultIndices(projectRoot) {
760
833
  }
761
834
  function buildScreens(projectRoot, hasGlobal, c) {
762
835
  const project = path.basename(projectRoot);
763
- const defLangIndices = langDefaultIndices(projectRoot);
764
836
  const verify = detectVerify(projectRoot);
765
837
  const agents = detectAgents(projectRoot);
766
- const welcome = hasGlobal ? null : [
838
+ const welcome = hasGlobal ? null : welcomeLines(project, projectRoot).join("\n");
839
+ return {
840
+ welcome,
841
+ lang: stepBox("1/3", "\uC8FC \uC5B8\uC5B4", langScreenLines(projectRoot), c),
842
+ verify: stepBox("2/3", "\uAC80\uC99D \uBA85\uB839\uC5B4", verifyStepLines(verify), c),
843
+ character: stepBox("3/3", "\uADDC\uCE59\uACFC \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uC131\uACA9", characterScreenLines(), c),
844
+ skills: stepBox("\uC2A4\uD0AC", "\uC2A4\uD0AC \uC124\uCE58", skillsScreenLines(agents), c)
845
+ };
846
+ }
847
+ function welcomeLines(project, projectRoot) {
848
+ return [
767
849
  "",
768
850
  " ~/.awl \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uCC98\uC74C \uC624\uC168\uAD70\uC694.",
769
851
  "",
@@ -776,46 +858,38 @@ function buildScreens(projectRoot, hasGlobal, c) {
776
858
  " \uB2E4\uB978 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C\uB3C4 awl init \uC744 \uC2E4\uD589\uD558\uBA74 \uADF8\uB54C \uB4F1\uB85D\uB429\uB2C8\uB2E4.",
777
859
  " \uADDC\uCE59\uACFC \uAD50\uD6C8\uC740 \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uB77C \uB2F9\uC2E0\uC5D0\uAC8C \uC313\uC774\uACE0,",
778
860
  " \uB4F1\uB85D\uB41C \uBAA8\uB4E0 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uD568\uAED8 \uC4F0\uC785\uB2C8\uB2E4."
779
- ].join("\n");
861
+ ];
862
+ }
863
+ function langScreenLines(projectRoot) {
864
+ const defLangIndices = langDefaultIndices(projectRoot);
780
865
  const detectedLangLabels = defLangIndices.map((i) => LANG_OPTIONS[i] ?? "TypeScript").join(", ");
781
- const langLines = [
866
+ return [
782
867
  `\uC790\uB3D9 \uAC10\uC9C0: ${detectedLangLabels || "TypeScript"}`,
783
868
  "",
784
869
  "\uC5EC\uB7EC \uAC1C\uB97C \uC4F0\uB294 \uD504\uB85C\uC81D\uD2B8\uBA74(\uC608: TypeScript \uD504\uB860\uD2B8 + Python \uBC31\uC5D4\uB4DC) \uC804\uBD80 \uACE0\uB974\uC138\uC694.",
785
870
  "\u2191\u2193 \uB610\uB294 j/k \uB85C \uC774\uB3D9, Space \uB85C \uC120\uD0DD, Enter \uB85C \uD655\uC815\uD558\uC138\uC694.",
786
871
  "\uD0A4 \uC785\uB825\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD130\uBBF8\uB110\uC5D0\uC11C\uB294 \uBC88\uD638\uB97C \uC27C\uD45C\uB85C \uC785\uB825\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
787
872
  ];
788
- return {
789
- welcome,
790
- lang: stepBox("1/3", "\uC8FC \uC5B8\uC5B4", langLines, c),
791
- verify: stepBox("2/3", "\uAC80\uC99D \uBA85\uB839\uC5B4", verifyStepLines(verify), c),
792
- character: stepBox(
793
- "3/3",
794
- "\uADDC\uCE59\uACFC \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uC131\uACA9",
795
- [
796
- "\uAC19\uC740 \uC2E4\uD328\uAC00 \uC313\uC774\uBA74 \uADDC\uCE59\uC774 \uB418\uACE0, \uB2E4\uC74C \uD504\uB85C\uC81D\uD2B8\uC5D0\uB3C4 \uC804\uD30C\uB429\uB2C8\uB2E4.",
797
- "\uD504\uB85C\uC81D\uD2B8\uC758 \uC131\uACA9\uC740 \uADF8 \uADDC\uCE59\uC744 \uC5EC\uAE30\uC5D0\uB3C4 \uC801\uC6A9\uD560\uC9C0 \uD310\uB2E8\uD558\uB294 \uADFC\uAC70\uC785\uB2C8\uB2E4.",
798
- "",
799
- "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uC5B4\uB5A4 \uACF3\uC785\uB2C8\uAE4C?",
800
- '(\uC608\uC2DC: "React + TailwindCSS \uC6F9 \uD504\uB860\uD2B8\uC5D4\uB4DC", "Python Fast API \uBD84\uC11D \uC11C\uBC84",',
801
- ' "TypeScript \uB77C\uC774\uBE0C\uB7EC\uB9AC \uD328\uD0A4\uC9C0")'
802
- ],
803
- c
804
- ),
805
- skills: stepBox(
806
- "\uC2A4\uD0AC",
807
- "\uC2A4\uD0AC \uC124\uCE58",
808
- [
809
- "awl \uC740 \uD310\uB2E8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uACFC \uC0C1\uD0DC\uB9CC \uAD00\uB9AC\uD569\uB2C8\uB2E4.",
810
- "\uD310\uB2E8\uC740 \uC774\uBBF8 \uC4F0\uACE0 \uACC4\uC2E0 \uC5D0\uC774\uC804\uD2B8\uAC00 \uD569\uB2C8\uB2E4.",
811
- "",
812
- "\uBC14\uB85C \uC544\uB798\uC5D0\uC11C \uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8\uB97C \uACE0\uB985\uB2C8\uB2E4.",
813
- "\uCCAB \uBC88\uC9F8 \u201C\uBAA8\uB450 \uC124\uCE58\u201D\uB97C \uACE0\uB974\uAC70\uB098, Space \uB85C \uD544\uC694\uD55C \uD56D\uBAA9\uB9CC \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
814
- `\uAC10\uC9C0\uB428: Claude Code ${agents.claude ? "\uC788\uC74C" : "\uC5C6\uC74C"} \xB7 Codex ${agents.codex ? "\uC788\uC74C" : "\uC5C6\uC74C"}`
815
- ],
816
- c
817
- )
818
- };
873
+ }
874
+ function characterScreenLines() {
875
+ return [
876
+ "\uAC19\uC740 \uC2E4\uD328\uAC00 \uC313\uC774\uBA74 \uADDC\uCE59\uC774 \uB418\uACE0, \uB2E4\uC74C \uD504\uB85C\uC81D\uD2B8\uC5D0\uB3C4 \uC804\uD30C\uB429\uB2C8\uB2E4.",
877
+ "\uD504\uB85C\uC81D\uD2B8\uC758 \uC131\uACA9\uC740 \uADF8 \uADDC\uCE59\uC744 \uC5EC\uAE30\uC5D0\uB3C4 \uC801\uC6A9\uD560\uC9C0 \uD310\uB2E8\uD558\uB294 \uADFC\uAC70\uC785\uB2C8\uB2E4.",
878
+ "",
879
+ "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uC5B4\uB5A4 \uACF3\uC785\uB2C8\uAE4C?",
880
+ '(\uC608\uC2DC: "React + TailwindCSS \uC6F9 \uD504\uB860\uD2B8\uC5D4\uB4DC", "Python Fast API \uBD84\uC11D \uC11C\uBC84",',
881
+ ' "TypeScript \uB77C\uC774\uBE0C\uB7EC\uB9AC \uD328\uD0A4\uC9C0")'
882
+ ];
883
+ }
884
+ function skillsScreenLines(agents) {
885
+ return [
886
+ "awl \uC740 \uD310\uB2E8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uACFC \uC0C1\uD0DC\uB9CC \uAD00\uB9AC\uD569\uB2C8\uB2E4.",
887
+ "\uD310\uB2E8\uC740 \uC774\uBBF8 \uC4F0\uACE0 \uACC4\uC2E0 \uC5D0\uC774\uC804\uD2B8\uAC00 \uD569\uB2C8\uB2E4.",
888
+ "",
889
+ "\uBC14\uB85C \uC544\uB798\uC5D0\uC11C \uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8\uB97C \uACE0\uB985\uB2C8\uB2E4.",
890
+ "\uCCAB \uBC88\uC9F8 \u201C\uBAA8\uB450 \uC124\uCE58\u201D\uB97C \uACE0\uB974\uAC70\uB098, Space \uB85C \uD544\uC694\uD55C \uD56D\uBAA9\uB9CC \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
891
+ `\uAC10\uC9C0\uB428: Claude Code ${agents.claude ? "\uC788\uC74C" : "\uC5C6\uC74C"} \xB7 Codex ${agents.codex ? "\uC788\uC74C" : "\uC5C6\uC74C"}`
892
+ ];
819
893
  }
820
894
  function ask(rl, q) {
821
895
  return new Promise((resolve) => rl.question(q, (a) => resolve(a)));
@@ -903,7 +977,6 @@ function applyVerifyCwd(verify, cwd) {
903
977
  return verify;
904
978
  }
905
979
  async function interactiveInputs(projectRoot, hasGlobal, c) {
906
- const screens = buildScreens(projectRoot, hasGlobal, c);
907
980
  const project = path.basename(projectRoot);
908
981
  const useRawMode = rawModeCapable();
909
982
  const session = { rl: null };
@@ -913,29 +986,48 @@ async function interactiveInputs(projectRoot, hasGlobal, c) {
913
986
  }
914
987
  return session.rl;
915
988
  };
989
+ const flow = openFlow("awl init", c);
916
990
  try {
917
- if (screens.welcome) {
918
- process.stdout.write(`${screens.welcome}
991
+ if (!hasGlobal) {
992
+ process.stdout.write(`${flowConnector(c)}
919
993
  `);
920
- }
921
- process.stdout.write(`
922
- ${screens.lang}
923
-
994
+ for (const line of welcomeLines(project, projectRoot)) {
995
+ process.stdout.write(`${flowConnector(c)}${line}
924
996
  `);
997
+ }
998
+ }
925
999
  const defaultLangChecked = langDefaultIndices(projectRoot);
926
- const rawLanguage = useRawMode ? await runInteractiveSelect(LANG_OPTIONS, 0, true, c, defaultLangChecked, {
927
- title: "\uC8FC \uC5B8\uC5B4",
928
- hint: "\u2191\u2193 \uB610\uB294 j/k \uC774\uB3D9 \xB7 Space \uC120\uD0DD \xB7 Enter \uD655\uC815 \xB7 Esc \uAE30\uBCF8\uAC12 \uC720\uC9C0"
929
- }) : null;
930
- const langChecked = rawLanguage?.checked ?? (useRawMode ? defaultLangChecked : await selectMulti(prompt(), LANG_OPTIONS, defaultLangChecked, c, false, "\uC8FC \uC5B8\uC5B4"));
1000
+ const langBody = langScreenLines(projectRoot);
1001
+ let langChecked;
1002
+ if (useRawMode) {
1003
+ const r = await activeSelect(flow, LANG_OPTIONS, 0, true, defaultLangChecked, {
1004
+ title: "\uC8FC \uC5B8\uC5B4",
1005
+ body: langBody,
1006
+ hint: "\u2191\u2193 \uB610\uB294 j/k \uC774\uB3D9 \xB7 Space \uC120\uD0DD \xB7 Enter \uD655\uC815 \xB7 Esc \uAE30\uBCF8\uAC12 \uC720\uC9C0"
1007
+ });
1008
+ langChecked = r?.checked ?? defaultLangChecked;
1009
+ } else {
1010
+ process.stdout.write(`${flowConnector(c)}
1011
+ ${flowActiveNode("\uC8FC \uC5B8\uC5B4", langBody, c)}
1012
+ `);
1013
+ langChecked = await selectMulti(
1014
+ prompt(),
1015
+ LANG_OPTIONS,
1016
+ defaultLangChecked,
1017
+ c,
1018
+ false,
1019
+ "\uC8FC \uC5B8\uC5B4"
1020
+ );
1021
+ }
931
1022
  const manualLangIdx = LANG_OPTIONS.length - 1;
932
1023
  const mainLanguage = langChecked.filter((i) => i !== manualLangIdx).map((i) => LANG_VALUES[i]).filter((v) => typeof v === "string" && v !== "");
933
1024
  if (langChecked.includes(manualLangIdx)) {
934
- const typed = (await ask(prompt(), " \uC8FC \uC5B8\uC5B4\uB97C \uC785\uB825\uD558\uC138\uC694: ")).trim();
1025
+ const typed = (await ask(prompt(), `${flowConnector(c)} \uC8FC \uC5B8\uC5B4\uB97C \uC785\uB825\uD558\uC138\uC694: `)).trim();
935
1026
  if (typed) {
936
1027
  mainLanguage.push(typed);
937
1028
  }
938
1029
  }
1030
+ selectStep(flow, `\uC8FC \uC5B8\uC5B4: ${mainLanguage.length > 0 ? mainLanguage.join(", ") : "(\uC5C6\uC74C)"}`);
939
1031
  const rootVerify = detectVerify(projectRoot);
940
1032
  const located = await promptVerifyLocation(
941
1033
  prompt(),
@@ -944,35 +1036,26 @@ ${screens.lang}
944
1036
  makeColors(c.color)
945
1037
  );
946
1038
  const verify = located.verify;
947
- if (located.cwd) {
948
- process.stdout.write(`
949
- ${stepBox("2/3", "\uAC80\uC99D \uBA85\uB839\uC5B4", verifyStepLines(verify), c)}
950
-
951
- `);
952
- } else {
953
- process.stdout.write(`
954
- ${screens.verify}
955
-
956
- `);
957
- }
1039
+ process.stdout.write(
1040
+ `${flowConnector(c)}
1041
+ ${flowActiveNode("\uAC80\uC99D \uBA85\uB839\uC5B4", verifyStepLines(verify), c)}
1042
+ `
1043
+ );
958
1044
  for (const k of Object.keys(VERIFY_LABELS)) {
959
1045
  const cur = verify[k];
960
1046
  const shown = cur ? cur.cmd : "(\uC5C6\uC74C)";
961
- const answer = (await ask(prompt(), ` ${VERIFY_LABELS[k]} [${shown}]: `)).trim();
1047
+ const answer = (await ask(prompt(), `${flowConnector(c)} ${VERIFY_LABELS[k]} [${shown}]: `)).trim();
962
1048
  if (answer !== "") {
963
1049
  verify[k] = answer.toLowerCase() === "\uC5C6\uC74C" || answer === "-" ? null : splitEnv(answer);
964
1050
  }
965
1051
  }
966
1052
  applyVerifyCwd(verify, located.cwd);
967
- process.stdout.write(`
968
- ${screens.character}
969
-
970
- `);
971
- const character = (await ask(prompt(), " > ")).trim();
972
- process.stdout.write(`
973
- ${screens.skills}
974
-
975
- `);
1053
+ process.stdout.write(
1054
+ `${flowConnector(c)}
1055
+ ${flowActiveNode("\uADDC\uCE59\uACFC \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uC131\uACA9", characterScreenLines(), c)}
1056
+ `
1057
+ );
1058
+ const character = (await ask(prompt(), `${flowConnector(c)} > `)).trim();
976
1059
  const agents = detectAgents(projectRoot);
977
1060
  const skillOptions = [
978
1061
  "\uBAA8\uB450 \uC124\uCE58 (Claude Code + Codex)",
@@ -984,26 +1067,39 @@ ${screens.skills}
984
1067
  session.rl.close();
985
1068
  session.rl = null;
986
1069
  }
987
- const rawSkills = useRawMode ? await runInteractiveSelect(skillOptions, 0, true, c, defaultChecked, {
988
- title: "\uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC",
989
- hint: "\u2191\u2193 \uB610\uB294 j/k \uC774\uB3D9 \xB7 Space \uC120\uD0DD \xB7 Enter \uD655\uC815 \xB7 Esc \uAE30\uBCF8\uAC12 \uC720\uC9C0",
990
- selectAllIndex: 0
991
- }) : null;
992
- const checked = rawSkills?.checked ?? (useRawMode ? defaultChecked : await selectMulti(
993
- prompt(),
994
- skillOptions,
995
- defaultChecked,
996
- c,
997
- false,
998
- "\uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC",
999
- 0
1000
- ));
1070
+ let checked;
1071
+ if (useRawMode) {
1072
+ const r = await activeSelect(flow, skillOptions, 0, true, defaultChecked, {
1073
+ title: "\uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC",
1074
+ body: skillsScreenLines(agents),
1075
+ hint: "\u2191\u2193 \uB610\uB294 j/k \uC774\uB3D9 \xB7 Space \uC120\uD0DD \xB7 Enter \uD655\uC815 \xB7 Esc \uAE30\uBCF8\uAC12 \uC720\uC9C0",
1076
+ selectAllIndex: 0
1077
+ });
1078
+ checked = r?.checked ?? defaultChecked;
1079
+ } else {
1080
+ process.stdout.write(
1081
+ `${flowConnector(c)}
1082
+ ${flowActiveNode("\uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC", skillsScreenLines(agents), c)}
1083
+ `
1084
+ );
1085
+ checked = await selectMulti(
1086
+ prompt(),
1087
+ skillOptions,
1088
+ defaultChecked,
1089
+ c,
1090
+ false,
1091
+ "\uC124\uCE58\uD560 \uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC",
1092
+ 0
1093
+ );
1094
+ }
1001
1095
  const installAll = checked.includes(0);
1002
1096
  const skills = {
1003
1097
  claude: installAll || checked.includes(1),
1004
1098
  codex: installAll || checked.includes(2)
1005
1099
  };
1006
- return { project, mainLanguage, character, verify, skills };
1100
+ const skillsSummary = installAll ? "\uBAA8\uB450 \uC124\uCE58 (Claude Code + Codex)" : [skills.claude && "Claude Code", skills.codex && "Codex"].filter(Boolean).join(", ") || "(\uC5C6\uC74C)";
1101
+ selectStep(flow, `\uC124\uCE58: ${skillsSummary}`);
1102
+ return { inputs: { project, mainLanguage, character, verify, skills }, session: flow };
1007
1103
  } finally {
1008
1104
  session.rl?.close();
1009
1105
  }
@@ -1013,42 +1109,37 @@ async function handleExistingConfig(rl, projectRoot, c, now) {
1013
1109
  const config = raw;
1014
1110
  scaffoldGlobal();
1015
1111
  const installedVer = installedEngineVersion();
1016
- process.stdout.write("\n .awl/config.json \uC774 \uC774\uBBF8 \uC788\uC2B5\uB2C8\uB2E4. \uD300\uC6D0\uC774 \uC124\uC815\uD574\uB450\uC5C8\uAD70\uC694.\n\n");
1017
- process.stdout.write(` \uD504\uB85C\uC81D\uD2B8 ${config?.project ?? "(\uC5C6\uC74C)"}
1018
- `);
1019
- process.stdout.write(` \uC8FC \uC5B8\uC5B4 ${config?.mainLanguage?.join(", ") || "(\uC5C6\uC74C)"}
1020
- `);
1021
- process.stdout.write(` \uC131\uACA9 ${config?.character || "(\uC5C6\uC74C)"}
1022
- `);
1023
1112
  const engineNote = installedVer && config?.engineVersion ? installedVer === config.engineVersion ? `(\uC124\uCE58\uB428: ${installedVer} \uC77C\uCE58)` : `(\uC124\uCE58\uB428: ${installedVer} \uBD88\uC77C\uCE58 -> '\uADF8\uB300\uB85C \uC4F4\uB2E4'\uB97C \uACE0\uB974\uBA74 \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4)` : "";
1024
- process.stdout.write(` \uC5D4\uC9C4 ${config?.engineVersion ?? "(\uC5C6\uC74C)"} ${engineNote}
1025
-
1113
+ const summaryLines = [
1114
+ ".awl/config.json \uC774 \uC774\uBBF8 \uC788\uC2B5\uB2C8\uB2E4. \uD300\uC6D0\uC774 \uC124\uC815\uD574\uB450\uC5C8\uAD70\uC694.",
1115
+ "",
1116
+ `\uD504\uB85C\uC81D\uD2B8 ${config?.project ?? "(\uC5C6\uC74C)"}`,
1117
+ `\uC8FC \uC5B8\uC5B4 ${config?.mainLanguage?.join(", ") || "(\uC5C6\uC74C)"}`,
1118
+ `\uC131\uACA9 ${config?.character || "(\uC5C6\uC74C)"}`,
1119
+ `\uC5D4\uC9C4 ${config?.engineVersion ?? "(\uC5C6\uC74C)"} ${engineNote}`,
1120
+ ...config?.verify ? ["", ...verifyLines(config.verify).map((l) => `\uAC80\uC99D ${l.trim()}`)] : [],
1121
+ "",
1122
+ "\uC774 \uC124\uC815\uC744 \uADF8\uB300\uB85C \uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?"
1123
+ ];
1124
+ const flow = openFlow("\uAE30\uC874 \uC124\uC815", c);
1125
+ process.stdout.write(`${flowConnector(c)}
1126
+ ${flowActiveNode("\uAE30\uC874 \uC124\uC815", summaryLines, c)}
1026
1127
  `);
1027
- if (config?.verify) {
1028
- for (const line of verifyLines(config.verify)) {
1029
- process.stdout.write(` \uAC80\uC99D ${line.trim()}
1128
+ const options = ["\uADF8\uB300\uB85C \uC4F4\uB2E4", "\uAC80\uC99D \uBA85\uB839\uC5B4\uB9CC \uACE0\uCE5C\uB2E4", "\uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC"];
1129
+ for (let i = 0; i < options.length; i++) {
1130
+ process.stdout.write(`${flowConnector(c)} ${i + 1}. ${options[i]}
1030
1131
  `);
1031
- }
1032
- process.stdout.write("\n");
1033
1132
  }
1034
- process.stdout.write(" \uC774 \uC124\uC815\uC744 \uADF8\uB300\uB85C \uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?\n\n");
1035
- const options = ["\uADF8\uB300\uB85C \uC4F4\uB2E4", "\uAC80\uC99D \uBA85\uB839\uC5B4\uB9CC \uACE0\uCE5C\uB2E4", "\uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC"];
1036
- process.stdout.write(
1037
- `${sectionBox(
1038
- "\uAE30\uC874 \uC124\uC815",
1039
- options.map((option, i) => `${i + 1}. ${option}`),
1040
- c
1041
- )}
1042
- `
1043
- );
1044
1133
  const choice = await promptNumber(rl, 0, options.length);
1134
+ selectStep(flow, `\uC120\uD0DD: ${options[choice]}`);
1135
+ closeFlow(flow);
1045
1136
  process.stdout.write(
1046
1137
  `
1047
1138
  ${makeColors(c.color).dim("\uADDC\uCE59\uACFC \uAD50\uD6C8\uC740 \uACF5\uC720\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC800 \uC124\uC815\uB9CC \uD300\uC6D0\uACFC \uAC19\uACE0, \uC313\uC774\uB294 \uAC83\uC740 \uB2F9\uC2E0 \uAC83\uC785\uB2C8\uB2E4.")}
1048
1139
  `
1049
1140
  );
1050
1141
  if (choice === 0) {
1051
- const synced = syncExistingInstall(projectRoot, installedVer ?? "unknown");
1142
+ const synced = syncExistingInstall(projectRoot, installedVer ?? "unknown", now);
1052
1143
  if (synced.configUpdated || synced.skills.length > 0) {
1053
1144
  process.stdout.write(
1054
1145
  `
@@ -1082,11 +1173,10 @@ async function handleExistingConfig(rl, projectRoot, c, now) {
1082
1173
  return;
1083
1174
  }
1084
1175
  rl.close();
1085
- const inputs = await interactiveInputs(projectRoot, isGlobalInstalled(), c);
1176
+ const { inputs, session } = await interactiveInputs(projectRoot, isGlobalInstalled(), c);
1086
1177
  const result = applyInit(projectRoot, inputs, now);
1087
- process.stdout.write(`
1088
- ${renderResult(result, inputs, c)}
1089
- `);
1178
+ commitResultFlow(session, result, inputs, c);
1179
+ closeFlow(session);
1090
1180
  }
1091
1181
  function resolveProjectChoice(idx, candidates) {
1092
1182
  if (idx >= 0 && idx < candidates.length) {
@@ -1157,13 +1247,14 @@ async function pickProjectRoot(cwd, c) {
1157
1247
  }
1158
1248
  return session.rl;
1159
1249
  };
1250
+ const flow = openFlow("\uD504\uB85C\uC81D\uD2B8 \uC120\uD0DD", c);
1160
1251
  const select = async (options, title) => {
1161
1252
  if (raw) {
1162
1253
  if (session.rl) {
1163
1254
  session.rl.close();
1164
1255
  session.rl = null;
1165
1256
  }
1166
- const r = await runInteractiveSelect(options, 0, false, c, [], {
1257
+ const r = await activeSelect(flow, options, 0, false, [], {
1167
1258
  title,
1168
1259
  hint: "\u2191\u2193 \uB610\uB294 j/k \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 Esc \uAE30\uBCF8\uAC12 \uC720\uC9C0"
1169
1260
  });
@@ -1178,6 +1269,7 @@ async function pickProjectRoot(cwd, c) {
1178
1269
  "\uC5B4\uB290 \uD504\uB85C\uC81D\uD2B8\uC5D0 awl \uC744 \uBD99\uC77C\uAE4C\uC694?"
1179
1270
  );
1180
1271
  if (idx === 0) {
1272
+ selectStep(flow, `\uD504\uB85C\uC81D\uD2B8: ${cwd}`);
1181
1273
  return cwd;
1182
1274
  }
1183
1275
  if (idx !== 1) {
@@ -1189,12 +1281,13 @@ async function pickProjectRoot(cwd, c) {
1189
1281
  const title = candidates.length > 0 ? "\uD504\uB85C\uC81D\uD2B8\uB97C \uACE0\uB974\uC138\uC694 (\uCD5C\uADFC \uC218\uC815\uC21C, \uCD5C\uB300 20)\n" : "\uD558\uC704\uC5D0 git \uD504\uB85C\uC81D\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C1\uC811 \uC785\uB825\uD558\uAC70\uB098 \uCDE8\uC18C\n";
1190
1282
  const choice = resolveProjectChoice(await select(labels, title), candidates);
1191
1283
  if (choice.kind === "path") {
1284
+ selectStep(flow, `\uD504\uB85C\uC81D\uD2B8: ${choice.path}`);
1192
1285
  return choice.path;
1193
1286
  }
1194
1287
  if (choice.kind === "cancel") {
1195
1288
  return null;
1196
1289
  }
1197
- const typed = (await ask(prompt(), " \uD504\uB85C\uC81D\uD2B8 \uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694: ")).trim();
1290
+ const typed = (await ask(prompt(), `${flowConnector(c)} \uD504\uB85C\uC81D\uD2B8 \uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694: `)).trim();
1198
1291
  if (!typed) {
1199
1292
  return null;
1200
1293
  }
@@ -1205,11 +1298,13 @@ async function pickProjectRoot(cwd, c) {
1205
1298
  `);
1206
1299
  return null;
1207
1300
  }
1301
+ selectStep(flow, `\uD504\uB85C\uC81D\uD2B8: ${resolved}`);
1208
1302
  return resolved;
1209
1303
  } finally {
1210
1304
  if (session.rl) {
1211
1305
  session.rl.close();
1212
1306
  }
1307
+ closeFlow(flow);
1213
1308
  }
1214
1309
  }
1215
1310
  async function runInit(opts) {
@@ -1226,7 +1321,7 @@ async function runInit(opts) {
1226
1321
  if (configExists) {
1227
1322
  const engine = scaffoldGlobal();
1228
1323
  const hook = installSafetyHook(projectRoot);
1229
- const synced = syncExistingInstall(projectRoot, engine.engineVersion);
1324
+ const synced = syncExistingInstall(projectRoot, engine.engineVersion, now);
1230
1325
  const syncNote = synced.configUpdated || synced.skills.length > 0 ? `
1231
1326
  ${signal(c, "ok")} \uBC84\uC804 \uB9C8\uCEE4\uB97C ${engine.engineVersion} \uB85C \uB3D9\uAE30\uD654\uD588\uC2B5\uB2C8\uB2E4${synced.skills.length ? ` (\uC2A4\uD0AC: ${synced.skills.join(", ")})` : ""}.` : "";
1232
1327
  process.stdout.write(
@@ -1268,14 +1363,16 @@ async function runInit(opts) {
1268
1363
  }
1269
1364
  return;
1270
1365
  }
1271
- const inputs = await interactiveInputs(chosenRoot, isGlobalInstalled(), c);
1366
+ const { inputs, session } = await interactiveInputs(chosenRoot, isGlobalInstalled(), c);
1272
1367
  const result = applyInit(chosenRoot, inputs, now);
1273
- process.stdout.write(`
1274
- ${renderResult(result, inputs, c)}
1275
- `);
1368
+ commitResultFlow(session, result, inputs, c);
1369
+ closeFlow(session);
1276
1370
  }
1277
1371
 
1278
1372
  export {
1373
+ openFlow,
1374
+ step,
1375
+ closeFlow,
1279
1376
  scanGitProjects,
1280
1377
  detectLanguage,
1281
1378
  detectLanguages,
@@ -1303,9 +1400,14 @@ export {
1303
1400
  verifyStepLines,
1304
1401
  renderNonTtyNotice,
1305
1402
  renderResult,
1403
+ commitResultFlow,
1306
1404
  LANG_OPTIONS,
1307
1405
  LANG_VALUES,
1308
1406
  buildScreens,
1407
+ welcomeLines,
1408
+ langScreenLines,
1409
+ characterScreenLines,
1410
+ skillsScreenLines,
1309
1411
  ask,
1310
1412
  promptNumber,
1311
1413
  selectSingle,